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 false /* isDefaultFallback */); 119 } 120 121 /** @hide */ build(@onNull String langTags, int variant, boolean isCustomFallback, boolean isDefaultFallback)122 public @NonNull FontFamily build(@NonNull String langTags, int variant, 123 boolean isCustomFallback, boolean isDefaultFallback) { 124 final long builderPtr = nInitBuilder(); 125 for (int i = 0; i < mFonts.size(); ++i) { 126 nAddFont(builderPtr, mFonts.get(i).getNativePtr()); 127 } 128 final long ptr = nBuild(builderPtr, langTags, variant, isCustomFallback, 129 isDefaultFallback); 130 final FontFamily family = new FontFamily(ptr); 131 sFamilyRegistory.registerNativeAllocation(family, ptr); 132 return family; 133 } 134 makeStyleIdentifier(@onNull Font font)135 private static int makeStyleIdentifier(@NonNull Font font) { 136 return font.getStyle().getWeight() | (font.getStyle().getSlant() << 16); 137 } 138 nInitBuilder()139 private static native long nInitBuilder(); 140 @CriticalNative nAddFont(long builderPtr, long fontPtr)141 private static native void nAddFont(long builderPtr, long fontPtr); nBuild(long builderPtr, String langTags, int variant, boolean isCustomFallback, boolean isDefaultFallback)142 private static native long nBuild(long builderPtr, String langTags, int variant, 143 boolean isCustomFallback, boolean isDefaultFallback); 144 @CriticalNative nGetReleaseNativeFamily()145 private static native long nGetReleaseNativeFamily(); 146 } 147 148 private final long mNativePtr; 149 150 // Use Builder instead. 151 /** @hide */ FontFamily(long ptr)152 public FontFamily(long ptr) { 153 mNativePtr = ptr; 154 } 155 156 /** 157 * Returns a BCP-47 compliant language tags associated with this font family. 158 * @hide 159 * @return a BCP-47 compliant language tag. 160 */ getLangTags()161 public @Nullable String getLangTags() { 162 return nGetLangTags(mNativePtr); 163 } 164 165 /** 166 * @hide 167 * @return a family variant 168 */ getVariant()169 public int getVariant() { 170 return nGetVariant(mNativePtr); 171 } 172 173 /** 174 * Returns a font 175 * 176 * @param index an index of the font 177 * @return a registered font 178 */ getFont(@ntRangefrom = 0) int index)179 public @NonNull Font getFont(@IntRange(from = 0) int index) { 180 if (index < 0 || getSize() <= index) { 181 throw new IndexOutOfBoundsException(); 182 } 183 return new Font(nGetFont(mNativePtr, index)); 184 } 185 186 /** 187 * Returns the number of fonts in this FontFamily. 188 * 189 * @return the number of fonts registered in this family. 190 */ getSize()191 public @IntRange(from = 1) int getSize() { 192 return nGetFontSize(mNativePtr); 193 } 194 195 /** @hide */ getNativePtr()196 public long getNativePtr() { 197 return mNativePtr; 198 } 199 200 @CriticalNative nGetFontSize(long family)201 private static native int nGetFontSize(long family); 202 203 @CriticalNative nGetFont(long family, int i)204 private static native long nGetFont(long family, int i); 205 206 @FastNative nGetLangTags(long family)207 private static native String nGetLangTags(long family); 208 209 @CriticalNative nGetVariant(long family)210 private static native int nGetVariant(long family); 211 } 212