1 /* 2 * Copyright (C) 2006 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; 18 19 import static android.content.res.FontResourcesParser.FamilyResourceEntry; 20 import static android.content.res.FontResourcesParser.FontFamilyFilesResourceEntry; 21 import static android.content.res.FontResourcesParser.FontFileResourceEntry; 22 import static android.content.res.FontResourcesParser.ProviderResourceEntry; 23 24 import android.annotation.IntDef; 25 import android.annotation.IntRange; 26 import android.annotation.NonNull; 27 import android.annotation.Nullable; 28 import android.annotation.TestApi; 29 import android.annotation.UiThread; 30 import android.compat.annotation.UnsupportedAppUsage; 31 import android.content.res.AssetManager; 32 import android.graphics.fonts.Font; 33 import android.graphics.fonts.FontFamily; 34 import android.graphics.fonts.FontStyle; 35 import android.graphics.fonts.FontVariationAxis; 36 import android.graphics.fonts.SystemFonts; 37 import android.icu.util.ULocale; 38 import android.os.Build; 39 import android.os.ParcelFileDescriptor; 40 import android.os.SharedMemory; 41 import android.os.SystemProperties; 42 import android.os.Trace; 43 import android.provider.FontRequest; 44 import android.provider.FontsContract; 45 import android.system.ErrnoException; 46 import android.system.OsConstants; 47 import android.text.FontConfig; 48 import android.util.ArrayMap; 49 import android.util.Base64; 50 import android.util.Log; 51 import android.util.LongSparseArray; 52 import android.util.LruCache; 53 import android.util.SparseArray; 54 55 import com.android.internal.annotations.GuardedBy; 56 import com.android.internal.annotations.VisibleForTesting; 57 import com.android.internal.util.Preconditions; 58 59 import dalvik.annotation.optimization.CriticalNative; 60 61 import libcore.util.NativeAllocationRegistry; 62 63 import java.io.ByteArrayOutputStream; 64 import java.io.File; 65 import java.io.FileDescriptor; 66 import java.io.IOException; 67 import java.io.InputStream; 68 import java.lang.annotation.Retention; 69 import java.lang.annotation.RetentionPolicy; 70 import java.nio.ByteBuffer; 71 import java.nio.ByteOrder; 72 import java.util.ArrayList; 73 import java.util.Arrays; 74 import java.util.Collections; 75 import java.util.List; 76 import java.util.Map; 77 import java.util.Objects; 78 79 /** 80 * The Typeface class specifies the typeface and intrinsic style of a font. 81 * This is used in the paint, along with optionally Paint settings like 82 * textSize, textSkewX, textScaleX to specify 83 * how text appears when drawn (and measured). 84 */ 85 public class Typeface { 86 87 private static String TAG = "Typeface"; 88 89 /** @hide */ 90 public static final boolean ENABLE_LAZY_TYPEFACE_INITIALIZATION = true; 91 92 private static final NativeAllocationRegistry sRegistry = 93 NativeAllocationRegistry.createMalloced( 94 Typeface.class.getClassLoader(), nativeGetReleaseFunc()); 95 96 /** The default NORMAL typeface object */ 97 public static final Typeface DEFAULT = null; 98 /** 99 * The default BOLD typeface object. Note: this may be not actually be 100 * bold, depending on what fonts are installed. Call getStyle() to know 101 * for sure. 102 */ 103 public static final Typeface DEFAULT_BOLD = null; 104 /** The NORMAL style of the default sans serif typeface. */ 105 public static final Typeface SANS_SERIF = null; 106 /** The NORMAL style of the default serif typeface. */ 107 public static final Typeface SERIF = null; 108 /** The NORMAL style of the default monospace typeface. */ 109 public static final Typeface MONOSPACE = null; 110 111 /** 112 * The default {@link Typeface}s for different text styles. 113 * Call {@link #defaultFromStyle(int)} to get the default typeface for the given text style. 114 * It shouldn't be changed for app wide typeface settings. Please use theme and font XML for 115 * the same purpose. 116 */ 117 @GuardedBy("SYSTEM_FONT_MAP_LOCK") 118 @UnsupportedAppUsage(trackingBug = 123769446) 119 static Typeface[] sDefaults; 120 121 /** 122 * Cache for Typeface objects for style variant. Currently max size is 3. 123 */ 124 @GuardedBy("sStyledCacheLock") 125 private static final LongSparseArray<SparseArray<Typeface>> sStyledTypefaceCache = 126 new LongSparseArray<>(3); 127 private static final Object sStyledCacheLock = new Object(); 128 129 /** 130 * Cache for Typeface objects for weight variant. Currently max size is 3. 131 */ 132 @GuardedBy("sWeightCacheLock") 133 private static final LongSparseArray<SparseArray<Typeface>> sWeightTypefaceCache = 134 new LongSparseArray<>(3); 135 private static final Object sWeightCacheLock = new Object(); 136 137 /** 138 * Cache for Typeface objects dynamically loaded from assets. Currently max size is 16. 139 */ 140 @GuardedBy("sDynamicCacheLock") 141 private static final LruCache<String, Typeface> sDynamicTypefaceCache = new LruCache<>(16); 142 private static final Object sDynamicCacheLock = new Object(); 143 144 145 @GuardedBy("SYSTEM_FONT_MAP_LOCK") 146 static Typeface sDefaultTypeface; 147 148 /** 149 * sSystemFontMap is read only and unmodifiable. 150 * Use public API {@link #create(String, int)} to get the typeface for given familyName. 151 */ 152 @GuardedBy("SYSTEM_FONT_MAP_LOCK") 153 @UnsupportedAppUsage(trackingBug = 123769347) 154 static final Map<String, Typeface> sSystemFontMap = new ArrayMap<>(); 155 156 // DirectByteBuffer object to hold sSystemFontMap's backing memory mapping. 157 static ByteBuffer sSystemFontMapBuffer = null; 158 static SharedMemory sSystemFontMapSharedMemory = null; 159 160 // Lock to guard sSystemFontMap and derived default or public typefaces. 161 // sStyledCacheLock may be held while this lock is held. Holding them in the reverse order may 162 // introduce deadlock. 163 private static final Object SYSTEM_FONT_MAP_LOCK = new Object(); 164 165 // This field is used but left for hiddenapi private list 166 // We cannot support sSystemFallbackMap since we will migrate to public FontFamily API. 167 /** 168 * @deprecated Use {@link android.graphics.fonts.FontFamily} instead. 169 */ 170 @UnsupportedAppUsage(trackingBug = 123768928) 171 @Deprecated 172 static final Map<String, android.graphics.FontFamily[]> sSystemFallbackMap = 173 Collections.emptyMap(); 174 175 /** 176 * Returns the shared memory that used for creating Typefaces. 177 * 178 * @return A SharedMemory used for creating Typeface. Maybe null if the lazy initialization is 179 * disabled or inside SystemServer or Zygote. 180 * @hide 181 */ 182 @TestApi getSystemFontMapSharedMemory()183 public static @Nullable SharedMemory getSystemFontMapSharedMemory() { 184 if (ENABLE_LAZY_TYPEFACE_INITIALIZATION) { 185 Objects.requireNonNull(sSystemFontMapSharedMemory); 186 } 187 return sSystemFontMapSharedMemory; 188 } 189 190 /** 191 * @hide 192 */ 193 @UnsupportedAppUsage 194 public final long native_instance; 195 196 private final Runnable mCleaner; 197 198 /** @hide */ 199 @IntDef(value = {NORMAL, BOLD, ITALIC, BOLD_ITALIC}) 200 @Retention(RetentionPolicy.SOURCE) 201 public @interface Style {} 202 203 // Style 204 public static final int NORMAL = 0; 205 public static final int BOLD = 1; 206 public static final int ITALIC = 2; 207 public static final int BOLD_ITALIC = 3; 208 /** @hide */ public static final int STYLE_MASK = 0x03; 209 210 @UnsupportedAppUsage 211 private @Style final int mStyle; 212 213 private @IntRange(from = 0, to = FontStyle.FONT_WEIGHT_MAX) final int mWeight; 214 215 // Value for weight and italic. Indicates the value is resolved by font metadata. 216 // Must be the same as the C++ constant in core/jni/android/graphics/FontFamily.cpp 217 /** @hide */ 218 public static final int RESOLVE_BY_FONT_TABLE = -1; 219 /** 220 * The key of the default font family. 221 * @hide 222 */ 223 public static final String DEFAULT_FAMILY = "sans-serif"; 224 225 // Style value for building typeface. 226 private static final int STYLE_NORMAL = 0; 227 private static final int STYLE_ITALIC = 1; 228 229 @GuardedBy("this") 230 private int[] mSupportedAxes; 231 private static final int[] EMPTY_AXES = {}; 232 233 /** 234 * Please use font in xml and also your application global theme to change the default Typeface. 235 * android:textViewStyle and its attribute android:textAppearance can be used in order to change 236 * typeface and other text related properties. 237 */ 238 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P) setDefault(Typeface t)239 private static void setDefault(Typeface t) { 240 synchronized (SYSTEM_FONT_MAP_LOCK) { 241 sDefaultTypeface = t; 242 nativeSetDefault(t.native_instance); 243 } 244 } 245 getDefault()246 private static Typeface getDefault() { 247 synchronized (SYSTEM_FONT_MAP_LOCK) { 248 return sDefaultTypeface; 249 } 250 } 251 252 /** Returns the typeface's weight value */ getWeight()253 public @IntRange(from = 0, to = 1000) int getWeight() { 254 return mWeight; 255 } 256 257 /** Returns the typeface's intrinsic style attributes */ getStyle()258 public @Style int getStyle() { 259 return mStyle; 260 } 261 262 /** Returns true if getStyle() has the BOLD bit set. */ isBold()263 public final boolean isBold() { 264 return (mStyle & BOLD) != 0; 265 } 266 267 /** Returns true if getStyle() has the ITALIC bit set. */ isItalic()268 public final boolean isItalic() { 269 return (mStyle & ITALIC) != 0; 270 } 271 272 /** 273 * Returns true if the system has the font family with the name [familyName]. For example 274 * querying with "sans-serif" would check if the "sans-serif" family is defined in the system 275 * and return true if does. 276 * 277 * @param familyName The name of the font family, cannot be null. If null, exception will be 278 * thrown. 279 */ hasFontFamily(@onNull String familyName)280 private static boolean hasFontFamily(@NonNull String familyName) { 281 Objects.requireNonNull(familyName, "familyName cannot be null"); 282 synchronized (SYSTEM_FONT_MAP_LOCK) { 283 return sSystemFontMap.containsKey(familyName); 284 } 285 } 286 287 /** 288 * @hide 289 * Used by Resources to load a font resource of type xml. 290 */ 291 @Nullable createFromResources( FamilyResourceEntry entry, AssetManager mgr, String path)292 public static Typeface createFromResources( 293 FamilyResourceEntry entry, AssetManager mgr, String path) { 294 if (entry instanceof ProviderResourceEntry) { 295 final ProviderResourceEntry providerEntry = (ProviderResourceEntry) entry; 296 297 String systemFontFamilyName = providerEntry.getSystemFontFamilyName(); 298 if (systemFontFamilyName != null && hasFontFamily(systemFontFamilyName)) { 299 return Typeface.create(systemFontFamilyName, NORMAL); 300 } 301 // Downloadable font 302 List<List<String>> givenCerts = providerEntry.getCerts(); 303 List<List<byte[]>> certs = new ArrayList<>(); 304 if (givenCerts != null) { 305 for (int i = 0; i < givenCerts.size(); i++) { 306 List<String> certSet = givenCerts.get(i); 307 List<byte[]> byteArraySet = new ArrayList<>(); 308 for (int j = 0; j < certSet.size(); j++) { 309 byteArraySet.add(Base64.decode(certSet.get(j), Base64.DEFAULT)); 310 } 311 certs.add(byteArraySet); 312 } 313 } 314 // Downloaded font and it wasn't cached, request it again and return a 315 // default font instead (nothing we can do now). 316 FontRequest request = new FontRequest(providerEntry.getAuthority(), 317 providerEntry.getPackage(), providerEntry.getQuery(), certs); 318 Typeface typeface = FontsContract.getFontSync(request); 319 return typeface == null ? DEFAULT : typeface; 320 } 321 322 Typeface typeface = findFromCache(mgr, path); 323 if (typeface != null) return typeface; 324 325 // family is FontFamilyFilesResourceEntry 326 final FontFamilyFilesResourceEntry filesEntry = (FontFamilyFilesResourceEntry) entry; 327 328 try { 329 FontFamily.Builder familyBuilder = null; 330 for (final FontFileResourceEntry fontFile : filesEntry.getEntries()) { 331 final Font.Builder fontBuilder = new Font.Builder(mgr, fontFile.getFileName(), 332 false /* isAsset */, 0 /* cookie */) 333 .setTtcIndex(fontFile.getTtcIndex()) 334 .setFontVariationSettings(fontFile.getVariationSettings()); 335 if (fontFile.getWeight() != Typeface.RESOLVE_BY_FONT_TABLE) { 336 fontBuilder.setWeight(fontFile.getWeight()); 337 } 338 if (fontFile.getItalic() != Typeface.RESOLVE_BY_FONT_TABLE) { 339 fontBuilder.setSlant(fontFile.getItalic() == FontFileResourceEntry.ITALIC 340 ? FontStyle.FONT_SLANT_ITALIC : FontStyle.FONT_SLANT_UPRIGHT); 341 } 342 343 if (familyBuilder == null) { 344 familyBuilder = new FontFamily.Builder(fontBuilder.build()); 345 } else { 346 familyBuilder.addFont(fontBuilder.build()); 347 } 348 } 349 if (familyBuilder == null) { 350 return Typeface.DEFAULT; 351 } 352 final FontFamily family = familyBuilder.build(); 353 final FontStyle normal = new FontStyle(FontStyle.FONT_WEIGHT_NORMAL, 354 FontStyle.FONT_SLANT_UPRIGHT); 355 Font bestFont = family.getFont(0); 356 int bestScore = normal.getMatchScore(bestFont.getStyle()); 357 for (int i = 1; i < family.getSize(); ++i) { 358 final Font candidate = family.getFont(i); 359 final int score = normal.getMatchScore(candidate.getStyle()); 360 if (score < bestScore) { 361 bestFont = candidate; 362 bestScore = score; 363 } 364 } 365 typeface = new Typeface.CustomFallbackBuilder(family) 366 .setStyle(bestFont.getStyle()) 367 .build(); 368 } catch (IllegalArgumentException e) { 369 // To be a compatible behavior with API28 or before, catch IllegalArgumentExcetpion 370 // thrown by native code and returns null. 371 return null; 372 } catch (IOException e) { 373 typeface = Typeface.DEFAULT; 374 } 375 synchronized (sDynamicCacheLock) { 376 final String key = Builder.createAssetUid(mgr, path, 0 /* ttcIndex */, 377 null /* axes */, RESOLVE_BY_FONT_TABLE /* weight */, 378 RESOLVE_BY_FONT_TABLE /* italic */, DEFAULT_FAMILY); 379 sDynamicTypefaceCache.put(key, typeface); 380 } 381 return typeface; 382 } 383 384 /** 385 * Used by resources for cached loading if the font is available. 386 * @hide 387 */ findFromCache(AssetManager mgr, String path)388 public static Typeface findFromCache(AssetManager mgr, String path) { 389 synchronized (sDynamicCacheLock) { 390 final String key = Builder.createAssetUid(mgr, path, 0 /* ttcIndex */, null /* axes */, 391 RESOLVE_BY_FONT_TABLE /* weight */, RESOLVE_BY_FONT_TABLE /* italic */, 392 DEFAULT_FAMILY); 393 Typeface typeface = sDynamicTypefaceCache.get(key); 394 if (typeface != null) { 395 return typeface; 396 } 397 } 398 return null; 399 } 400 401 /** 402 * A builder class for creating new Typeface instance. 403 * 404 * <p> 405 * Examples, 406 * 1) Create Typeface from ttf file. 407 * <pre> 408 * <code> 409 * Typeface.Builder builder = new Typeface.Builder("your_font_file.ttf"); 410 * Typeface typeface = builder.build(); 411 * </code> 412 * </pre> 413 * 414 * 2) Create Typeface from ttc file in assets directory. 415 * <pre> 416 * <code> 417 * Typeface.Builder builder = new Typeface.Builder(getAssets(), "your_font_file.ttc"); 418 * builder.setTtcIndex(2); // Set index of font collection. 419 * Typeface typeface = builder.build(); 420 * </code> 421 * </pre> 422 * 423 * 3) Create Typeface with variation settings. 424 * <pre> 425 * <code> 426 * Typeface.Builder builder = new Typeface.Builder("your_font_file.ttf"); 427 * builder.setFontVariationSettings("'wght' 700, 'slnt' 20, 'ital' 1"); 428 * builder.setWeight(700); // Tell the system that this is a bold font. 429 * builder.setItalic(true); // Tell the system that this is an italic style font. 430 * Typeface typeface = builder.build(); 431 * </code> 432 * </pre> 433 * </p> 434 */ 435 public static final class Builder { 436 /** @hide */ 437 public static final int NORMAL_WEIGHT = 400; 438 /** @hide */ 439 public static final int BOLD_WEIGHT = 700; 440 441 // Kept for generating asset cache key. 442 private final AssetManager mAssetManager; 443 private final String mPath; 444 445 private final @Nullable Font.Builder mFontBuilder; 446 447 private String mFallbackFamilyName; 448 449 private int mWeight = RESOLVE_BY_FONT_TABLE; 450 private int mItalic = RESOLVE_BY_FONT_TABLE; 451 452 /** 453 * Constructs a builder with a file path. 454 * 455 * @param path The file object refers to the font file. 456 */ Builder(@onNull File path)457 public Builder(@NonNull File path) { 458 mFontBuilder = new Font.Builder(path); 459 mAssetManager = null; 460 mPath = null; 461 } 462 463 /** 464 * Constructs a builder with a file descriptor. 465 * 466 * Caller is responsible for closing the passed file descriptor after {@link #build} is 467 * called. 468 * 469 * @param fd The file descriptor. The passed fd must be mmap-able. 470 */ Builder(@onNull FileDescriptor fd)471 public Builder(@NonNull FileDescriptor fd) { 472 Font.Builder builder; 473 try { 474 builder = new Font.Builder(ParcelFileDescriptor.dup(fd)); 475 } catch (IOException e) { 476 // We cannot tell the error to developer at this moment since we cannot change the 477 // public API signature. Instead, silently fallbacks to system fallback in the build 478 // method as the same as other error cases. 479 builder = null; 480 } 481 mFontBuilder = builder; 482 mAssetManager = null; 483 mPath = null; 484 } 485 486 /** 487 * Constructs a builder with a file path. 488 * 489 * @param path The full path to the font file. 490 */ Builder(@onNull String path)491 public Builder(@NonNull String path) { 492 mFontBuilder = new Font.Builder(new File(path)); 493 mAssetManager = null; 494 mPath = null; 495 } 496 497 /** 498 * Constructs a builder from an asset manager and a file path in an asset directory. 499 * 500 * @param assetManager The application's asset manager 501 * @param path The file name of the font data in the asset directory 502 */ Builder(@onNull AssetManager assetManager, @NonNull String path)503 public Builder(@NonNull AssetManager assetManager, @NonNull String path) { 504 this(assetManager, path, true /* is asset */, 0 /* cookie */); 505 } 506 507 /** 508 * Constructs a builder from an asset manager and a file path in an asset directory. 509 * 510 * @param assetManager The application's asset manager 511 * @param path The file name of the font data in the asset directory 512 * @param cookie a cookie for the asset 513 * @hide 514 */ Builder(@onNull AssetManager assetManager, @NonNull String path, boolean isAsset, int cookie)515 public Builder(@NonNull AssetManager assetManager, @NonNull String path, boolean isAsset, 516 int cookie) { 517 mFontBuilder = new Font.Builder(assetManager, path, isAsset, cookie); 518 mAssetManager = assetManager; 519 mPath = path; 520 } 521 522 /** 523 * Sets weight of the font. 524 * 525 * Tells the system the weight of the given font. If not provided, the system will resolve 526 * the weight value by reading font tables. 527 * @param weight a weight value. 528 */ setWeight(@ntRangefrom = 1, to = 1000) int weight)529 public Builder setWeight(@IntRange(from = 1, to = 1000) int weight) { 530 mWeight = weight; 531 mFontBuilder.setWeight(weight); 532 return this; 533 } 534 535 /** 536 * Sets italic information of the font. 537 * 538 * Tells the system the style of the given font. If not provided, the system will resolve 539 * the style by reading font tables. 540 * @param italic {@code true} if the font is italic. Otherwise {@code false}. 541 */ setItalic(boolean italic)542 public Builder setItalic(boolean italic) { 543 mItalic = italic ? FontStyle.FONT_SLANT_ITALIC : FontStyle.FONT_SLANT_UPRIGHT; 544 mFontBuilder.setSlant(mItalic); 545 return this; 546 } 547 548 /** 549 * Sets an index of the font collection. See {@link android.R.attr#ttcIndex}. 550 * 551 * Can not be used for Typeface source. build() method will return null for invalid index. 552 * @param ttcIndex An index of the font collection. If the font source is not font 553 * collection, do not call this method or specify 0. 554 */ setTtcIndex(@ntRangefrom = 0) int ttcIndex)555 public Builder setTtcIndex(@IntRange(from = 0) int ttcIndex) { 556 mFontBuilder.setTtcIndex(ttcIndex); 557 return this; 558 } 559 560 /** 561 * Sets a font variation settings. 562 * 563 * @param variationSettings See {@link android.widget.TextView#setFontVariationSettings}. 564 * @throws IllegalArgumentException If given string is not a valid font variation settings 565 * format. 566 */ setFontVariationSettings(@ullable String variationSettings)567 public Builder setFontVariationSettings(@Nullable String variationSettings) { 568 mFontBuilder.setFontVariationSettings(variationSettings); 569 return this; 570 } 571 572 /** 573 * Sets a font variation settings. 574 * 575 * @param axes An array of font variation axis tag-value pairs. 576 */ setFontVariationSettings(@ullable FontVariationAxis[] axes)577 public Builder setFontVariationSettings(@Nullable FontVariationAxis[] axes) { 578 mFontBuilder.setFontVariationSettings(axes); 579 return this; 580 } 581 582 /** 583 * Sets a fallback family name. 584 * 585 * By specifying a fallback family name, a fallback Typeface will be returned if the 586 * {@link #build} method fails to create a Typeface from the provided font. The fallback 587 * family will be resolved with the provided weight and italic information specified by 588 * {@link #setWeight} and {@link #setItalic}. 589 * 590 * If {@link #setWeight} is not called, the fallback family keeps the default weight. 591 * Similary, if {@link #setItalic} is not called, the fallback family keeps the default 592 * italic information. For example, calling {@code builder.setFallback("sans-serif-light")} 593 * is equivalent to calling {@code builder.setFallback("sans-serif").setWeight(300)} in 594 * terms of fallback. The default weight and italic information are overridden by calling 595 * {@link #setWeight} and {@link #setItalic}. For example, if a Typeface is constructed 596 * using {@code builder.setFallback("sans-serif-light").setWeight(700)}, the fallback text 597 * will render as sans serif bold. 598 * 599 * @param familyName A family name to be used for fallback if the provided font can not be 600 * used. By passing {@code null}, build() returns {@code null}. 601 * If {@link #setFallback} is not called on the builder, {@code null} 602 * is assumed. 603 */ setFallback(@ullable String familyName)604 public Builder setFallback(@Nullable String familyName) { 605 mFallbackFamilyName = familyName; 606 return this; 607 } 608 609 /** 610 * Creates a unique id for a given AssetManager and asset path. 611 * 612 * @param mgr AssetManager instance 613 * @param path The path for the asset. 614 * @param ttcIndex The TTC index for the font. 615 * @param axes The font variation settings. 616 * @return Unique id for a given AssetManager and asset path. 617 */ createAssetUid(final AssetManager mgr, String path, int ttcIndex, @Nullable FontVariationAxis[] axes, int weight, int italic, String fallback)618 private static String createAssetUid(final AssetManager mgr, String path, int ttcIndex, 619 @Nullable FontVariationAxis[] axes, int weight, int italic, String fallback) { 620 final SparseArray<String> pkgs = mgr.getAssignedPackageIdentifiers(); 621 final StringBuilder builder = new StringBuilder(); 622 final int size = pkgs.size(); 623 for (int i = 0; i < size; i++) { 624 builder.append(pkgs.valueAt(i)); 625 builder.append("-"); 626 } 627 builder.append(path); 628 builder.append("-"); 629 builder.append(Integer.toString(ttcIndex)); 630 builder.append("-"); 631 builder.append(Integer.toString(weight)); 632 builder.append("-"); 633 builder.append(Integer.toString(italic)); 634 // Family name may contain hyphen. Use double hyphen for avoiding key conflicts before 635 // and after appending falblack name. 636 builder.append("--"); 637 builder.append(fallback); 638 builder.append("--"); 639 if (axes != null) { 640 for (FontVariationAxis axis : axes) { 641 builder.append(axis.getTag()); 642 builder.append("-"); 643 builder.append(Float.toString(axis.getStyleValue())); 644 } 645 } 646 return builder.toString(); 647 } 648 resolveFallbackTypeface()649 private Typeface resolveFallbackTypeface() { 650 if (mFallbackFamilyName == null) { 651 return null; 652 } 653 654 final Typeface base = getSystemDefaultTypeface(mFallbackFamilyName); 655 if (mWeight == RESOLVE_BY_FONT_TABLE && mItalic == RESOLVE_BY_FONT_TABLE) { 656 return base; 657 } 658 659 final int weight = (mWeight == RESOLVE_BY_FONT_TABLE) ? base.mWeight : mWeight; 660 final boolean italic = 661 (mItalic == RESOLVE_BY_FONT_TABLE) ? (base.mStyle & ITALIC) != 0 : mItalic == 1; 662 return createWeightStyle(base, weight, italic); 663 } 664 665 /** 666 * Generates new Typeface from specified configuration. 667 * 668 * @return Newly created Typeface. May return null if some parameters are invalid. 669 */ build()670 public Typeface build() { 671 if (mFontBuilder == null) { 672 return resolveFallbackTypeface(); 673 } 674 try { 675 final Font font = mFontBuilder.build(); 676 final String key = mAssetManager == null ? null : createAssetUid( 677 mAssetManager, mPath, font.getTtcIndex(), font.getAxes(), 678 mWeight, mItalic, 679 mFallbackFamilyName == null ? DEFAULT_FAMILY : mFallbackFamilyName); 680 if (key != null) { 681 // Dynamic cache lookup is only for assets. 682 synchronized (sDynamicCacheLock) { 683 final Typeface typeface = sDynamicTypefaceCache.get(key); 684 if (typeface != null) { 685 return typeface; 686 } 687 } 688 } 689 final FontFamily family = new FontFamily.Builder(font).build(); 690 final int weight = mWeight == RESOLVE_BY_FONT_TABLE 691 ? font.getStyle().getWeight() : mWeight; 692 final int slant = mItalic == RESOLVE_BY_FONT_TABLE 693 ? font.getStyle().getSlant() : mItalic; 694 final CustomFallbackBuilder builder = new CustomFallbackBuilder(family) 695 .setStyle(new FontStyle(weight, slant)); 696 if (mFallbackFamilyName != null) { 697 builder.setSystemFallback(mFallbackFamilyName); 698 } 699 final Typeface typeface = builder.build(); 700 if (key != null) { 701 synchronized (sDynamicCacheLock) { 702 sDynamicTypefaceCache.put(key, typeface); 703 } 704 } 705 return typeface; 706 } catch (IOException | IllegalArgumentException e) { 707 return resolveFallbackTypeface(); 708 } 709 } 710 } 711 712 /** 713 * A builder class for creating new Typeface instance. 714 * 715 * There are two font fallback mechanisms, custom font fallback and system font fallback. 716 * The custom font fallback is a simple ordered list. The text renderer tries to see if it can 717 * render a character with the first font and if that font does not support the character, try 718 * next one and so on. It will keep trying until end of the custom fallback chain. The maximum 719 * length of the custom fallback chain is 64. 720 * The system font fallback is a system pre-defined fallback chain. The system fallback is 721 * processed only when no matching font is found in the custom font fallback. 722 * 723 * <p> 724 * Examples, 725 * 1) Create Typeface from single ttf file. 726 * <pre> 727 * <code> 728 * Font font = new Font.Builder("your_font_file.ttf").build(); 729 * FontFamily family = new FontFamily.Builder(font).build(); 730 * Typeface typeface = new Typeface.CustomFallbackBuilder(family).build(); 731 * </code> 732 * </pre> 733 * 734 * 2) Create Typeface from multiple font files and select bold style by default. 735 * <pre> 736 * <code> 737 * Font regularFont = new Font.Builder("regular.ttf").build(); 738 * Font boldFont = new Font.Builder("bold.ttf").build(); 739 * FontFamily family = new FontFamily.Builder(regularFont) 740 * .addFont(boldFont).build(); 741 * Typeface typeface = new Typeface.CustomFallbackBuilder(family) 742 * .setWeight(Font.FONT_WEIGHT_BOLD) // Set bold style as the default style. 743 * // If the font family doesn't have bold style font, 744 * // system will select the closest font. 745 * .build(); 746 * </code> 747 * </pre> 748 * 749 * 3) Create Typeface from single ttf file and if that font does not have glyph for the 750 * characters, use "serif" font family instead. 751 * <pre> 752 * <code> 753 * Font font = new Font.Builder("your_font_file.ttf").build(); 754 * FontFamily family = new FontFamily.Builder(font).build(); 755 * Typeface typeface = new Typeface.CustomFallbackBuilder(family) 756 * .setSystemFallback("serif") // Set serif font family as the fallback. 757 * .build(); 758 * </code> 759 * </pre> 760 * 4) Create Typeface from single ttf file and set another ttf file for the fallback. 761 * <pre> 762 * <code> 763 * Font font = new Font.Builder("English.ttf").build(); 764 * FontFamily family = new FontFamily.Builder(font).build(); 765 * 766 * Font fallbackFont = new Font.Builder("Arabic.ttf").build(); 767 * FontFamily fallbackFamily = new FontFamily.Builder(fallbackFont).build(); 768 * Typeface typeface = new Typeface.CustomFallbackBuilder(family) 769 * .addCustomFallback(fallbackFamily) // Specify fallback family. 770 * .setSystemFallback("serif") // Set serif font family as the fallback. 771 * .build(); 772 * </code> 773 * </pre> 774 * </p> 775 */ 776 public static final class CustomFallbackBuilder { 777 private static final int MAX_CUSTOM_FALLBACK = 64; 778 private final ArrayList<FontFamily> mFamilies = new ArrayList<>(); 779 private String mFallbackName = null; 780 private @Nullable FontStyle mStyle; 781 782 /** 783 * Returns the maximum capacity of custom fallback families. 784 * 785 * This includes the the first font family passed to the constructor. 786 * It is guaranteed that the value will be greater than or equal to 64. 787 * 788 * @return the maximum number of font families for the custom fallback 789 */ getMaxCustomFallbackCount()790 public static @IntRange(from = 64) int getMaxCustomFallbackCount() { 791 return MAX_CUSTOM_FALLBACK; 792 } 793 794 /** 795 * Constructs a builder with a font family. 796 * 797 * @param family a family object 798 */ CustomFallbackBuilder(@onNull FontFamily family)799 public CustomFallbackBuilder(@NonNull FontFamily family) { 800 Preconditions.checkNotNull(family); 801 mFamilies.add(family); 802 } 803 804 /** 805 * Sets a system fallback by name. 806 * 807 * You can specify generic font familiy names or OEM specific family names. If the system 808 * don't have a specified fallback, the default fallback is used instead. 809 * For more information about generic font families, see <a 810 * href="https://www.w3.org/TR/css-fonts-4/#generic-font-families">CSS specification</a> 811 * 812 * For more information about fallback, see class description. 813 * 814 * @param familyName a family name to be used for fallback if the provided fonts can not be 815 * used 816 */ setSystemFallback(@onNull String familyName)817 public @NonNull CustomFallbackBuilder setSystemFallback(@NonNull String familyName) { 818 Preconditions.checkNotNull(familyName); 819 mFallbackName = familyName; 820 return this; 821 } 822 823 /** 824 * Sets a font style of the Typeface. 825 * 826 * If the font family doesn't have a font of given style, system will select the closest 827 * font from font family. For example, if a font family has fonts of 300 weight and 700 828 * weight then setWeight(400) is called, system will select the font of 300 weight. 829 * 830 * @param style a font style 831 */ setStyle(@onNull FontStyle style)832 public @NonNull CustomFallbackBuilder setStyle(@NonNull FontStyle style) { 833 mStyle = style; 834 return this; 835 } 836 837 /** 838 * Append a font family to the end of the custom font fallback. 839 * 840 * You can set up to 64 custom fallback families including the first font family you passed 841 * to the constructor. 842 * For more information about fallback, see class description. 843 * 844 * @param family a fallback family 845 * @throws IllegalArgumentException if you give more than 64 custom fallback families 846 */ addCustomFallback(@onNull FontFamily family)847 public @NonNull CustomFallbackBuilder addCustomFallback(@NonNull FontFamily family) { 848 Preconditions.checkNotNull(family); 849 Preconditions.checkArgument(mFamilies.size() < getMaxCustomFallbackCount(), 850 "Custom fallback limit exceeded(%d)", getMaxCustomFallbackCount()); 851 mFamilies.add(family); 852 return this; 853 } 854 855 /** 856 * Create the Typeface based on the configured values. 857 * 858 * @return the Typeface object 859 */ 860 public @NonNull Typeface build() { 861 final int userFallbackSize = mFamilies.size(); 862 final Typeface fallbackTypeface = getSystemDefaultTypeface(mFallbackName); 863 final long[] ptrArray = new long[userFallbackSize]; 864 for (int i = 0; i < userFallbackSize; ++i) { 865 ptrArray[i] = mFamilies.get(i).getNativePtr(); 866 } 867 final int weight = mStyle == null ? 400 : mStyle.getWeight(); 868 final int italic = 869 (mStyle == null || mStyle.getSlant() == FontStyle.FONT_SLANT_UPRIGHT) ? 0 : 1; 870 return new Typeface(nativeCreateFromArray( 871 ptrArray, fallbackTypeface.native_instance, weight, italic)); 872 } 873 } 874 875 /** 876 * Create a typeface object given a family name, and option style information. 877 * If null is passed for the name, then the "default" font will be chosen. 878 * The resulting typeface object can be queried (getStyle()) to discover what 879 * its "real" style characteristics are. 880 * 881 * @param familyName May be null. The name of the font family. 882 * @param style The style (normal, bold, italic) of the typeface. 883 * e.g. NORMAL, BOLD, ITALIC, BOLD_ITALIC 884 * @return The best matching typeface. 885 */ 886 public static Typeface create(String familyName, @Style int style) { 887 return create(getSystemDefaultTypeface(familyName), style); 888 } 889 890 /** 891 * Create a typeface object that best matches the specified existing 892 * typeface and the specified Style. Use this call if you want to pick a new 893 * style from the same family of an existing typeface object. If family is 894 * null, this selects from the default font's family. 895 * 896 * <p> 897 * This method is not thread safe on API 27 or before. 898 * This method is thread safe on API 28 or after. 899 * </p> 900 * 901 * @param family An existing {@link Typeface} object. In case of {@code null}, the default 902 * typeface is used instead. 903 * @param style The style (normal, bold, italic) of the typeface. 904 * e.g. NORMAL, BOLD, ITALIC, BOLD_ITALIC 905 * @return The best matching typeface. 906 */ 907 public static Typeface create(Typeface family, @Style int style) { 908 if ((style & ~STYLE_MASK) != 0) { 909 style = NORMAL; 910 } 911 if (family == null) { 912 family = getDefault(); 913 } 914 915 // Return early if we're asked for the same face/style 916 if (family.mStyle == style) { 917 return family; 918 } 919 920 final long ni = family.native_instance; 921 922 Typeface typeface; 923 synchronized (sStyledCacheLock) { 924 SparseArray<Typeface> styles = sStyledTypefaceCache.get(ni); 925 926 if (styles == null) { 927 styles = new SparseArray<Typeface>(4); 928 sStyledTypefaceCache.put(ni, styles); 929 } else { 930 typeface = styles.get(style); 931 if (typeface != null) { 932 return typeface; 933 } 934 } 935 936 typeface = new Typeface(nativeCreateFromTypeface(ni, style)); 937 styles.put(style, typeface); 938 } 939 return typeface; 940 } 941 942 /** 943 * Creates a typeface object that best matches the specified existing typeface and the specified 944 * weight and italic style 945 * <p>Below are numerical values and corresponding common weight names.</p> 946 * <table> 947 * <thead> 948 * <tr><th>Value</th><th>Common weight name</th></tr> 949 * </thead> 950 * <tbody> 951 * <tr><td>100</td><td>Thin</td></tr> 952 * <tr><td>200</td><td>Extra Light</td></tr> 953 * <tr><td>300</td><td>Light</td></tr> 954 * <tr><td>400</td><td>Normal</td></tr> 955 * <tr><td>500</td><td>Medium</td></tr> 956 * <tr><td>600</td><td>Semi Bold</td></tr> 957 * <tr><td>700</td><td>Bold</td></tr> 958 * <tr><td>800</td><td>Extra Bold</td></tr> 959 * <tr><td>900</td><td>Black</td></tr> 960 * </tbody> 961 * </table> 962 * 963 * <p> 964 * This method is thread safe. 965 * </p> 966 * 967 * @param family An existing {@link Typeface} object. In case of {@code null}, the default 968 * typeface is used instead. 969 * @param weight The desired weight to be drawn. 970 * @param italic {@code true} if italic style is desired to be drawn. Otherwise, {@code false} 971 * @return A {@link Typeface} object for drawing specified weight and italic style. Never 972 * returns {@code null} 973 * 974 * @see #getWeight() 975 * @see #isItalic() 976 */ 977 public static @NonNull Typeface create(@Nullable Typeface family, 978 @IntRange(from = 1, to = 1000) int weight, boolean italic) { 979 Preconditions.checkArgumentInRange(weight, 0, 1000, "weight"); 980 if (family == null) { 981 family = getDefault(); 982 } 983 return createWeightStyle(family, weight, italic); 984 } 985 986 private static @NonNull Typeface createWeightStyle(@NonNull Typeface base, 987 @IntRange(from = 1, to = 1000) int weight, boolean italic) { 988 final int key = (weight << 1) | (italic ? 1 : 0); 989 990 Typeface typeface; 991 synchronized(sWeightCacheLock) { 992 SparseArray<Typeface> innerCache = sWeightTypefaceCache.get(base.native_instance); 993 if (innerCache == null) { 994 innerCache = new SparseArray<>(4); 995 sWeightTypefaceCache.put(base.native_instance, innerCache); 996 } else { 997 typeface = innerCache.get(key); 998 if (typeface != null) { 999 return typeface; 1000 } 1001 } 1002 1003 typeface = new Typeface( 1004 nativeCreateFromTypefaceWithExactStyle(base.native_instance, weight, italic)); 1005 innerCache.put(key, typeface); 1006 } 1007 return typeface; 1008 } 1009 1010 /** @hide */ 1011 public static Typeface createFromTypefaceWithVariation(@Nullable Typeface family, 1012 @NonNull List<FontVariationAxis> axes) { 1013 final Typeface base = family == null ? Typeface.DEFAULT : family; 1014 return new Typeface(nativeCreateFromTypefaceWithVariation(base.native_instance, axes)); 1015 } 1016 1017 /** 1018 * Returns one of the default typeface objects, based on the specified style 1019 * 1020 * @return the default typeface that corresponds to the style 1021 */ 1022 public static Typeface defaultFromStyle(@Style int style) { 1023 synchronized (SYSTEM_FONT_MAP_LOCK) { 1024 return sDefaults[style]; 1025 } 1026 } 1027 1028 /** 1029 * Create a new typeface from the specified font data. 1030 * 1031 * @param mgr The application's asset manager 1032 * @param path The file name of the font data in the assets directory 1033 * @return The new typeface. 1034 */ 1035 public static Typeface createFromAsset(AssetManager mgr, String path) { 1036 Preconditions.checkNotNull(path); // for backward compatibility 1037 Preconditions.checkNotNull(mgr); 1038 1039 Typeface typeface = new Builder(mgr, path).build(); 1040 if (typeface != null) return typeface; 1041 // check if the file exists, and throw an exception for backward compatibility 1042 try (InputStream inputStream = mgr.open(path)) { 1043 } catch (IOException e) { 1044 throw new RuntimeException("Font asset not found " + path); 1045 } 1046 1047 return Typeface.DEFAULT; 1048 } 1049 1050 /** 1051 * Creates a unique id for a given font provider and query. 1052 */ 1053 private static String createProviderUid(String authority, String query) { 1054 final StringBuilder builder = new StringBuilder(); 1055 builder.append("provider:"); 1056 builder.append(authority); 1057 builder.append("-"); 1058 builder.append(query); 1059 return builder.toString(); 1060 } 1061 1062 /** 1063 * Create a new typeface from the specified font file. 1064 * 1065 * @param file The path to the font data. 1066 * @return The new typeface. 1067 */ 1068 public static Typeface createFromFile(@Nullable File file) { 1069 // For the compatibility reasons, leaving possible NPE here. 1070 // See android.graphics.cts.TypefaceTest#testCreateFromFileByFileReferenceNull 1071 1072 Typeface typeface = new Builder(file).build(); 1073 if (typeface != null) return typeface; 1074 1075 // check if the file exists, and throw an exception for backward compatibility 1076 if (!file.exists()) { 1077 throw new RuntimeException("Font asset not found " + file.getAbsolutePath()); 1078 } 1079 1080 return Typeface.DEFAULT; 1081 } 1082 1083 /** 1084 * Create a new typeface from the specified font file. 1085 * 1086 * @param path The full path to the font data. 1087 * @return The new typeface. 1088 */ 1089 public static Typeface createFromFile(@Nullable String path) { 1090 Preconditions.checkNotNull(path); // for backward compatibility 1091 return createFromFile(new File(path)); 1092 } 1093 1094 /** 1095 * Create a new typeface from an array of font families. 1096 * 1097 * @param families array of font families 1098 * @deprecated 1099 */ 1100 @Deprecated 1101 @UnsupportedAppUsage(trackingBug = 123768928) 1102 private static Typeface createFromFamilies(android.graphics.FontFamily[] families) { 1103 long[] ptrArray = new long[families.length]; 1104 for (int i = 0; i < families.length; i++) { 1105 ptrArray[i] = families[i].mNativePtr; 1106 } 1107 return new Typeface(nativeCreateFromArray( 1108 ptrArray, 0, RESOLVE_BY_FONT_TABLE, 1109 RESOLVE_BY_FONT_TABLE)); 1110 } 1111 1112 /** 1113 * Create a new typeface from an array of android.graphics.fonts.FontFamily. 1114 * 1115 * @param families array of font families 1116 */ 1117 private static Typeface createFromFamilies(@Nullable FontFamily[] families) { 1118 final long[] ptrArray = new long[families.length]; 1119 for (int i = 0; i < families.length; ++i) { 1120 ptrArray[i] = families[i].getNativePtr(); 1121 } 1122 return new Typeface(nativeCreateFromArray(ptrArray, 0, 1123 RESOLVE_BY_FONT_TABLE, RESOLVE_BY_FONT_TABLE)); 1124 } 1125 1126 /** 1127 * This method is used by supportlib-v27. 1128 * 1129 * @deprecated Use {@link android.graphics.fonts.FontFamily} instead. 1130 */ 1131 @UnsupportedAppUsage(trackingBug = 123768395) 1132 @Deprecated 1133 private static Typeface createFromFamiliesWithDefault( 1134 android.graphics.FontFamily[] families, int weight, int italic) { 1135 return createFromFamiliesWithDefault(families, DEFAULT_FAMILY, weight, italic); 1136 } 1137 1138 /** 1139 * Create a new typeface from an array of font families, including 1140 * also the font families in the fallback list. 1141 * @param fallbackName the family name. If given families don't support characters, the 1142 * characters will be rendered with this family. 1143 * @param weight the weight for this family. In that case, the table information in the first 1144 * family's font is used. If the first family has multiple fonts, the closest to 1145 * the regular weight and upright font is used. 1146 * @param italic the italic information for this family. In that case, the table information in 1147 * the first family's font is used. If the first family has multiple fonts, the 1148 * closest to the regular weight and upright font is used. 1149 * @param families array of font families 1150 * 1151 * @deprecated Use {@link android.graphics.fonts.FontFamily} instead. 1152 */ 1153 @UnsupportedAppUsage(trackingBug = 123768928) 1154 @Deprecated 1155 private static Typeface createFromFamiliesWithDefault(android.graphics.FontFamily[] families, 1156 String fallbackName, int weight, int italic) { 1157 final Typeface fallbackTypeface = getSystemDefaultTypeface(fallbackName); 1158 long[] ptrArray = new long[families.length]; 1159 for (int i = 0; i < families.length; i++) { 1160 ptrArray[i] = families[i].mNativePtr; 1161 } 1162 return new Typeface(nativeCreateFromArray( 1163 ptrArray, fallbackTypeface.native_instance, weight, italic)); 1164 } 1165 1166 // don't allow clients to call this directly 1167 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023) 1168 private Typeface(long ni) { 1169 if (ni == 0) { 1170 throw new RuntimeException("native typeface cannot be made"); 1171 } 1172 1173 native_instance = ni; 1174 mCleaner = sRegistry.registerNativeAllocation(this, native_instance); 1175 mStyle = nativeGetStyle(ni); 1176 mWeight = nativeGetWeight(ni); 1177 } 1178 1179 private static Typeface getSystemDefaultTypeface(@NonNull String familyName) { 1180 Typeface tf = sSystemFontMap.get(familyName); 1181 return tf == null ? Typeface.DEFAULT : tf; 1182 } 1183 1184 /** @hide */ 1185 @VisibleForTesting 1186 public static void initSystemDefaultTypefaces(Map<String, FontFamily[]> fallbacks, 1187 List<FontConfig.Alias> aliases, 1188 Map<String, Typeface> outSystemFontMap) { 1189 for (Map.Entry<String, FontFamily[]> entry : fallbacks.entrySet()) { 1190 outSystemFontMap.put(entry.getKey(), createFromFamilies(entry.getValue())); 1191 } 1192 1193 for (int i = 0; i < aliases.size(); ++i) { 1194 final FontConfig.Alias alias = aliases.get(i); 1195 if (outSystemFontMap.containsKey(alias.getName())) { 1196 continue; // If alias and named family are conflict, use named family. 1197 } 1198 final Typeface base = outSystemFontMap.get(alias.getOriginal()); 1199 if (base == null) { 1200 // The missing target is a valid thing, some configuration don't have font files, 1201 // e.g. wear devices. Just skip this alias. 1202 continue; 1203 } 1204 final int weight = alias.getWeight(); 1205 final Typeface newFace = weight == 400 ? base : 1206 new Typeface(nativeCreateWeightAlias(base.native_instance, weight)); 1207 outSystemFontMap.put(alias.getName(), newFace); 1208 } 1209 } 1210 1211 private static void registerGenericFamilyNative(@NonNull String familyName, 1212 @Nullable Typeface typeface) { 1213 if (typeface != null) { 1214 nativeRegisterGenericFamily(familyName, typeface.native_instance); 1215 } 1216 } 1217 1218 /** 1219 * Create a serialized system font mappings. 1220 * 1221 * @hide 1222 */ 1223 @TestApi 1224 public static @NonNull SharedMemory serializeFontMap(@NonNull Map<String, Typeface> fontMap) 1225 throws IOException, ErrnoException { 1226 long[] nativePtrs = new long[fontMap.size()]; 1227 // The name table will not be large, so let's create a byte array in memory. 1228 ByteArrayOutputStream namesBytes = new ByteArrayOutputStream(); 1229 int i = 0; 1230 for (Map.Entry<String, Typeface> entry : fontMap.entrySet()) { 1231 nativePtrs[i++] = entry.getValue().native_instance; 1232 writeString(namesBytes, entry.getKey()); 1233 } 1234 int typefacesBytesCount = nativeWriteTypefaces(null, nativePtrs); 1235 // int (typefacesBytesCount), typefaces, namesBytes 1236 SharedMemory sharedMemory = SharedMemory.create( 1237 "fontMap", Integer.BYTES + typefacesBytesCount + namesBytes.size()); 1238 ByteBuffer writableBuffer = sharedMemory.mapReadWrite().order(ByteOrder.BIG_ENDIAN); 1239 try { 1240 writableBuffer.putInt(typefacesBytesCount); 1241 int writtenBytesCount = nativeWriteTypefaces(writableBuffer.slice(), nativePtrs); 1242 if (writtenBytesCount != typefacesBytesCount) { 1243 throw new IOException(String.format("Unexpected bytes written: %d, expected: %d", 1244 writtenBytesCount, typefacesBytesCount)); 1245 } 1246 writableBuffer.position(writableBuffer.position() + writtenBytesCount); 1247 writableBuffer.put(namesBytes.toByteArray()); 1248 } finally { 1249 SharedMemory.unmap(writableBuffer); 1250 } 1251 sharedMemory.setProtect(OsConstants.PROT_READ); 1252 return sharedMemory; 1253 } 1254 1255 // buffer's byte order should be BIG_ENDIAN. 1256 /** 1257 * Deserialize the font mapping from the serialized byte buffer. 1258 * 1259 * @hide 1260 */ 1261 @TestApi 1262 public static @NonNull long[] deserializeFontMap( 1263 @NonNull ByteBuffer buffer, @NonNull Map<String, Typeface> out) 1264 throws IOException { 1265 int typefacesBytesCount = buffer.getInt(); 1266 long[] nativePtrs = nativeReadTypefaces(buffer.slice()); 1267 if (nativePtrs == null) { 1268 throw new IOException("Could not read typefaces"); 1269 } 1270 out.clear(); 1271 buffer.position(buffer.position() + typefacesBytesCount); 1272 for (long nativePtr : nativePtrs) { 1273 String name = readString(buffer); 1274 out.put(name, new Typeface(nativePtr)); 1275 } 1276 return nativePtrs; 1277 } 1278 1279 private static String readString(ByteBuffer buffer) { 1280 int length = buffer.getInt(); 1281 byte[] bytes = new byte[length]; 1282 buffer.get(bytes); 1283 return new String(bytes); 1284 } 1285 1286 private static void writeString(ByteArrayOutputStream bos, String value) throws IOException { 1287 byte[] bytes = value.getBytes(); 1288 writeInt(bos, bytes.length); 1289 bos.write(bytes); 1290 } 1291 1292 private static void writeInt(ByteArrayOutputStream bos, int value) { 1293 // Write in the big endian order. 1294 bos.write((value >> 24) & 0xFF); 1295 bos.write((value >> 16) & 0xFF); 1296 bos.write((value >> 8) & 0xFF); 1297 bos.write(value & 0xFF); 1298 } 1299 1300 /** @hide */ 1301 public static Map<String, Typeface> getSystemFontMap() { 1302 synchronized (SYSTEM_FONT_MAP_LOCK) { 1303 return sSystemFontMap; 1304 } 1305 } 1306 1307 /** 1308 * Deserialize font map and set it as system font map. This method should be called at most once 1309 * per process. 1310 */ 1311 /** @hide */ 1312 @UiThread 1313 public static void setSystemFontMap(@Nullable SharedMemory sharedMemory) 1314 throws IOException, ErrnoException { 1315 if (sSystemFontMapBuffer != null) { 1316 // Apps can re-send BIND_APPLICATION message from their code. This is a work around to 1317 // detect it and avoid crashing. 1318 if (sharedMemory == null || sharedMemory == sSystemFontMapSharedMemory) { 1319 return; 1320 } 1321 throw new UnsupportedOperationException( 1322 "Once set, buffer-based system font map cannot be updated"); 1323 } 1324 sSystemFontMapSharedMemory = sharedMemory; 1325 Trace.traceBegin(Trace.TRACE_TAG_GRAPHICS, "setSystemFontMap"); 1326 try { 1327 if (sharedMemory == null) { 1328 // FontManagerService is not started. This may happen in FACTORY_TEST_LOW_LEVEL 1329 // mode for example. 1330 loadPreinstalledSystemFontMap(); 1331 return; 1332 } 1333 sSystemFontMapBuffer = sharedMemory.mapReadOnly().order(ByteOrder.BIG_ENDIAN); 1334 Map<String, Typeface> systemFontMap = new ArrayMap<>(); 1335 long[] nativePtrs = deserializeFontMap(sSystemFontMapBuffer, systemFontMap); 1336 1337 // Initialize native font APIs. The native font API will read fonts.xml by itself if 1338 // Typeface is initialized with loadPreinstalledSystemFontMap. 1339 for (long ptr : nativePtrs) { 1340 nativeAddFontCollections(ptr); 1341 } 1342 setSystemFontMap(systemFontMap); 1343 } finally { 1344 Trace.traceEnd(Trace.TRACE_TAG_GRAPHICS); 1345 } 1346 } 1347 1348 /** @hide */ 1349 @VisibleForTesting 1350 public static void setSystemFontMap(Map<String, Typeface> systemFontMap) { 1351 synchronized (SYSTEM_FONT_MAP_LOCK) { 1352 sSystemFontMap.clear(); 1353 sSystemFontMap.putAll(systemFontMap); 1354 1355 // We can't assume DEFAULT_FAMILY available on Roboletric. 1356 if (sSystemFontMap.containsKey(DEFAULT_FAMILY)) { 1357 setDefault(sSystemFontMap.get(DEFAULT_FAMILY)); 1358 } 1359 1360 // Set up defaults and typefaces exposed in public API 1361 // Use sDefaultTypeface here, because create(String, int) uses DEFAULT as fallback. 1362 nativeForceSetStaticFinalField("DEFAULT", create(sDefaultTypeface, 0)); 1363 nativeForceSetStaticFinalField("DEFAULT_BOLD", create(sDefaultTypeface, Typeface.BOLD)); 1364 nativeForceSetStaticFinalField("SANS_SERIF", create("sans-serif", 0)); 1365 nativeForceSetStaticFinalField("SERIF", create("serif", 0)); 1366 nativeForceSetStaticFinalField("MONOSPACE", create("monospace", 0)); 1367 1368 sDefaults = new Typeface[]{ 1369 DEFAULT, 1370 DEFAULT_BOLD, 1371 create((String) null, Typeface.ITALIC), 1372 create((String) null, Typeface.BOLD_ITALIC), 1373 }; 1374 1375 // A list of generic families to be registered in native. 1376 // https://www.w3.org/TR/css-fonts-4/#generic-font-families 1377 String[] genericFamilies = { 1378 "serif", "sans-serif", "cursive", "fantasy", "monospace", "system-ui" 1379 }; 1380 1381 for (String genericFamily : genericFamilies) { 1382 registerGenericFamilyNative(genericFamily, systemFontMap.get(genericFamily)); 1383 } 1384 } 1385 } 1386 1387 static { 1388 // Preload Roboto-Regular.ttf in Zygote for improving app launch performance. 1389 preloadFontFile("/system/fonts/Roboto-Regular.ttf"); 1390 preloadFontFile("/system/fonts/RobotoStatic-Regular.ttf"); 1391 1392 String locale = SystemProperties.get("persist.sys.locale", "en-US"); 1393 String script = ULocale.addLikelySubtags(ULocale.forLanguageTag(locale)).getScript(); 1394 1395 FontConfig config = SystemFonts.getSystemPreinstalledFontConfig(); 1396 for (int i = 0; i < config.getFontFamilies().size(); ++i) { 1397 FontConfig.FontFamily family = config.getFontFamilies().get(i); 1398 boolean loadFamily = false; 1399 for (int j = 0; j < family.getLocaleList().size(); ++j) { 1400 String fontScript = ULocale.addLikelySubtags( 1401 ULocale.forLocale(family.getLocaleList().get(j))).getScript(); 1402 loadFamily = fontScript.equals(script); 1403 if (loadFamily) { 1404 break; 1405 } 1406 } 1407 if (loadFamily) { 1408 for (int j = 0; j < family.getFontList().size(); ++j) { 1409 preloadFontFile(family.getFontList().get(j).getFile().getAbsolutePath()); 1410 } 1411 } 1412 } 1413 } 1414 1415 private static void preloadFontFile(String filePath) { 1416 File file = new File(filePath); 1417 if (file.exists()) { 1418 Log.i(TAG, "Preloading " + file.getAbsolutePath()); 1419 nativeWarmUpCache(filePath); 1420 } 1421 } 1422 1423 /** @hide */ 1424 @VisibleForTesting 1425 public static void destroySystemFontMap() { 1426 synchronized (SYSTEM_FONT_MAP_LOCK) { 1427 for (Typeface typeface : sSystemFontMap.values()) { 1428 typeface.mCleaner.run(); 1429 } 1430 sSystemFontMap.clear(); 1431 if (sSystemFontMapBuffer != null) { 1432 SharedMemory.unmap(sSystemFontMapBuffer); 1433 } 1434 sSystemFontMapBuffer = null; 1435 sSystemFontMapSharedMemory = null; 1436 } 1437 } 1438 1439 /** @hide */ 1440 public static void loadPreinstalledSystemFontMap() { 1441 final FontConfig fontConfig = SystemFonts.getSystemPreinstalledFontConfig(); 1442 final Map<String, FontFamily[]> fallback = SystemFonts.buildSystemFallback(fontConfig); 1443 final Map<String, Typeface> typefaceMap = 1444 SystemFonts.buildSystemTypefaces(fontConfig, fallback); 1445 setSystemFontMap(typefaceMap); 1446 } 1447 1448 static { 1449 if (!ENABLE_LAZY_TYPEFACE_INITIALIZATION) { 1450 loadPreinstalledSystemFontMap(); 1451 } 1452 } 1453 1454 @Override 1455 public boolean equals(Object o) { 1456 if (this == o) return true; 1457 if (o == null || getClass() != o.getClass()) return false; 1458 1459 Typeface typeface = (Typeface) o; 1460 1461 return mStyle == typeface.mStyle && native_instance == typeface.native_instance; 1462 } 1463 1464 @Override 1465 public int hashCode() { 1466 /* 1467 * Modified method for hashCode with long native_instance derived from 1468 * http://developer.android.com/reference/java/lang/Object.html 1469 */ 1470 int result = 17; 1471 result = 31 * result + (int) (native_instance ^ (native_instance >>> 32)); 1472 result = 31 * result + mStyle; 1473 return result; 1474 } 1475 1476 /** @hide */ 1477 public boolean isSupportedAxes(int axis) { 1478 synchronized (this) { 1479 if (mSupportedAxes == null) { 1480 mSupportedAxes = nativeGetSupportedAxes(native_instance); 1481 if (mSupportedAxes == null) { 1482 mSupportedAxes = EMPTY_AXES; 1483 } 1484 } 1485 } 1486 return Arrays.binarySearch(mSupportedAxes, axis) >= 0; 1487 } 1488 1489 /** @hide */ 1490 public List<FontFamily> getFallback() { 1491 ArrayList<FontFamily> families = new ArrayList<>(); 1492 int familySize = nativeGetFamilySize(native_instance); 1493 for (int i = 0; i < familySize; ++i) { 1494 families.add(new FontFamily(nativeGetFamily(native_instance, i))); 1495 } 1496 return families; 1497 } 1498 1499 private static native long nativeCreateFromTypeface(long native_instance, int style); 1500 private static native long nativeCreateFromTypefaceWithExactStyle( 1501 long native_instance, int weight, boolean italic); 1502 // TODO: clean up: change List<FontVariationAxis> to FontVariationAxis[] 1503 private static native long nativeCreateFromTypefaceWithVariation( 1504 long native_instance, List<FontVariationAxis> axes); 1505 @UnsupportedAppUsage 1506 private static native long nativeCreateWeightAlias(long native_instance, int weight); 1507 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) 1508 private static native long nativeCreateFromArray( 1509 long[] familyArray, long fallbackTypeface, int weight, int italic); 1510 private static native int[] nativeGetSupportedAxes(long native_instance); 1511 1512 @CriticalNative 1513 private static native void nativeSetDefault(long nativePtr); 1514 1515 @CriticalNative 1516 private static native int nativeGetStyle(long nativePtr); 1517 1518 @CriticalNative 1519 private static native int nativeGetWeight(long nativePtr); 1520 1521 @CriticalNative 1522 private static native long nativeGetReleaseFunc(); 1523 1524 @CriticalNative 1525 private static native int nativeGetFamilySize(long naitvePtr); 1526 1527 @CriticalNative 1528 private static native long nativeGetFamily(long nativePtr, int index); 1529 1530 1531 private static native void nativeRegisterGenericFamily(String str, long nativePtr); 1532 1533 private static native int nativeWriteTypefaces( 1534 @Nullable ByteBuffer buffer, @NonNull long[] nativePtrs); 1535 1536 private static native @Nullable long[] nativeReadTypefaces(@NonNull ByteBuffer buffer); 1537 1538 private static native void nativeForceSetStaticFinalField(String fieldName, Typeface typeface); 1539 1540 @CriticalNative 1541 private static native void nativeAddFontCollections(long nativePtr); 1542 1543 private static native void nativeWarmUpCache(String fileName); 1544 } 1545