1 /* 2 * Copyright (C) 2018 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package android.graphics.fonts; 18 19 import android.annotation.IntRange; 20 import android.annotation.NonNull; 21 import android.annotation.Nullable; 22 import android.text.FontConfig; 23 import android.util.SparseIntArray; 24 25 import com.android.internal.util.Preconditions; 26 27 import dalvik.annotation.optimization.CriticalNative; 28 import dalvik.annotation.optimization.FastNative; 29 30 import libcore.util.NativeAllocationRegistry; 31 32 import java.util.ArrayList; 33 34 /** 35 * A font family class can be used for creating Typeface. 36 * 37 * <p> 38 * A font family is a bundle of fonts for drawing text in various styles. 39 * For example, you can bundle regular style font and bold style font into a single font family, 40 * then system will select the correct style font from family for drawing. 41 * 42 * <pre> 43 * FontFamily family = new FontFamily.Builder(new Font.Builder("regular.ttf").build()) 44 * .addFont(new Font.Builder("bold.ttf").build()).build(); 45 * Typeface typeface = new Typeface.Builder2(family).build(); 46 * 47 * SpannableStringBuilder ssb = new SpannableStringBuilder("Hello, World."); 48 * ssb.setSpan(new StyleSpan(Typeface.Bold), 6, 12, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); 49 * 50 * textView.setTypeface(typeface); 51 * textView.setText(ssb); 52 * </pre> 53 * 54 * In this example, "Hello, " is drawn with "regular.ttf", and "World." is drawn with "bold.ttf". 55 * 56 * If there is no font exactly matches with the text style, the system will select the closest font. 57 * </p> 58 * 59 */ 60 public final class FontFamily { 61 private static final String TAG = "FontFamily"; 62 63 /** 64 * A builder class for creating new FontFamily. 65 */ 66 public static final class Builder { 67 private static final NativeAllocationRegistry sFamilyRegistory = 68 NativeAllocationRegistry.createMalloced(FontFamily.class.getClassLoader(), 69 nGetReleaseNativeFamily()); 70 71 private final ArrayList<Font> mFonts = new ArrayList<>(); 72 // Most FontFamily only has regular, bold, italic, bold-italic. Thus 4 should be good for 73 // initial capacity. 74 private final SparseIntArray mStyles = new SparseIntArray(4); 75 76 /** 77 * Constructs a builder. 78 * 79 * @param font a font 80 */ Builder(@onNull Font font)81 public Builder(@NonNull Font font) { 82 Preconditions.checkNotNull(font, "font can not be null"); 83 mStyles.append(makeStyleIdentifier(font), 0); 84 mFonts.add(font); 85 } 86 87 /** 88 * Adds different style font to the builder. 89 * 90 * System will select the font if the text style is closest to the font. 91 * If the same style font is already added to the builder, this method will fail with 92 * {@link IllegalArgumentException}. 93 * 94 * Note that system assumes all fonts bundled in FontFamily have the same coverage for the 95 * code points. For example, regular style font and bold style font must have the same code 96 * point coverage, otherwise some character may be shown as tofu. 97 * 98 * @param font a font 99 * @return this builder 100 */ addFont(@onNull Font font)101 public @NonNull Builder addFont(@NonNull Font font) { 102 Preconditions.checkNotNull(font, "font can not be null"); 103 int key = makeStyleIdentifier(font); 104 if (mStyles.indexOfKey(key) >= 0) { 105 throw new IllegalArgumentException(font + " has already been added"); 106 } 107 mStyles.append(key, 0); 108 mFonts.add(font); 109 return this; 110 } 111 112 /** 113 * Build the font family 114 * @return a font family 115 */ build()116 public @NonNull FontFamily build() { 117 return build("", FontConfig.FontFamily.VARIANT_DEFAULT, true /* isCustomFallback */); 118 } 119 120 /** @hide */ build(@onNull String langTags, int variant, boolean isCustomFallback)121 public @NonNull FontFamily build(@NonNull String langTags, int variant, 122 boolean isCustomFallback) { 123 final long builderPtr = nInitBuilder(); 124 for (int i = 0; i < mFonts.size(); ++i) { 125 nAddFont(builderPtr, mFonts.get(i).getNativePtr()); 126 } 127 final long ptr = nBuild(builderPtr, langTags, variant, isCustomFallback); 128 final FontFamily family = new FontFamily(ptr); 129 sFamilyRegistory.registerNativeAllocation(family, ptr); 130 return family; 131 } 132 makeStyleIdentifier(@onNull Font font)133 private static int makeStyleIdentifier(@NonNull Font font) { 134 return font.getStyle().getWeight() | (font.getStyle().getSlant() << 16); 135 } 136 nInitBuilder()137 private static native long nInitBuilder(); 138 @CriticalNative nAddFont(long builderPtr, long fontPtr)139 private static native void nAddFont(long builderPtr, long fontPtr); nBuild(long builderPtr, String langTags, int variant, boolean isCustomFallback)140 private static native long nBuild(long builderPtr, String langTags, int variant, 141 boolean isCustomFallback); 142 @CriticalNative nGetReleaseNativeFamily()143 private static native long nGetReleaseNativeFamily(); 144 } 145 146 private final long mNativePtr; 147 148 // Use Builder instead. 149 /** @hide */ FontFamily(long ptr)150 public FontFamily(long ptr) { 151 mNativePtr = ptr; 152 } 153 154 /** 155 * Returns a BCP-47 compliant language tags associated with this font family. 156 * @hide 157 * @return a BCP-47 compliant language tag. 158 */ getLangTags()159 public @Nullable String getLangTags() { 160 return nGetLangTags(mNativePtr); 161 } 162 163 /** 164 * @hide 165 * @return a family variant 166 */ getVariant()167 public int getVariant() { 168 return nGetVariant(mNativePtr); 169 } 170 171 /** 172 * Returns a font 173 * 174 * @param index an index of the font 175 * @return a registered font 176 */ getFont(@ntRangefrom = 0) int index)177 public @NonNull Font getFont(@IntRange(from = 0) int index) { 178 if (index < 0 || getSize() <= index) { 179 throw new IndexOutOfBoundsException(); 180 } 181 return new Font(nGetFont(mNativePtr, index)); 182 } 183 184 /** 185 * Returns the number of fonts in this FontFamily. 186 * 187 * @return the number of fonts registered in this family. 188 */ getSize()189 public @IntRange(from = 1) int getSize() { 190 return nGetFontSize(mNativePtr); 191 } 192 193 /** @hide */ getNativePtr()194 public long getNativePtr() { 195 return mNativePtr; 196 } 197 198 @CriticalNative nGetFontSize(long family)199 private static native int nGetFontSize(long family); 200 201 @CriticalNative nGetFont(long family, int i)202 private static native long nGetFont(long family, int i); 203 204 @FastNative nGetLangTags(long family)205 private static native String nGetLangTags(long family); 206 207 @CriticalNative nGetVariant(long family)208 private static native int nGetVariant(long family); 209 } 210