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.ProviderResourceEntry; 20 import static android.content.res.FontResourcesParser.FontFileResourceEntry; 21 import static android.content.res.FontResourcesParser.FontFamilyFilesResourceEntry; 22 import static android.content.res.FontResourcesParser.FamilyResourceEntry; 23 24 import static java.lang.annotation.RetentionPolicy.SOURCE; 25 26 import android.annotation.IntDef; 27 import android.annotation.IntRange; 28 import android.annotation.NonNull; 29 import android.annotation.Nullable; 30 import android.content.res.AssetManager; 31 import android.graphics.FontListParser; 32 import android.graphics.fonts.FontVariationAxis; 33 import android.net.Uri; 34 import android.os.Bundle; 35 import android.os.Handler; 36 import android.os.ParcelFileDescriptor; 37 import android.os.ResultReceiver; 38 import android.provider.FontRequest; 39 import android.provider.FontsContract; 40 import android.text.FontConfig; 41 import android.util.Base64; 42 import android.util.Log; 43 import android.util.LongSparseArray; 44 import android.util.LruCache; 45 import android.util.SparseArray; 46 47 import com.android.internal.annotations.GuardedBy; 48 import com.android.internal.util.Preconditions; 49 50 import libcore.io.IoUtils; 51 52 import org.xmlpull.v1.XmlPullParserException; 53 54 import java.io.File; 55 import java.io.FileDescriptor; 56 import java.io.FileInputStream; 57 import java.io.FileNotFoundException; 58 import java.io.IOException; 59 import java.lang.annotation.Retention; 60 import java.lang.annotation.RetentionPolicy; 61 import java.nio.ByteBuffer; 62 import java.nio.channels.FileChannel; 63 import java.util.Arrays; 64 import java.util.ArrayList; 65 import java.util.Arrays; 66 import java.util.Collections; 67 import java.util.HashMap; 68 import java.util.List; 69 import java.util.Map; 70 import java.util.concurrent.atomic.AtomicReference; 71 72 /** 73 * The Typeface class specifies the typeface and intrinsic style of a font. 74 * This is used in the paint, along with optionally Paint settings like 75 * textSize, textSkewX, textScaleX to specify 76 * how text appears when drawn (and measured). 77 */ 78 public class Typeface { 79 80 private static String TAG = "Typeface"; 81 82 /** The default NORMAL typeface object */ 83 public static final Typeface DEFAULT; 84 /** 85 * The default BOLD typeface object. Note: this may be not actually be 86 * bold, depending on what fonts are installed. Call getStyle() to know 87 * for sure. 88 */ 89 public static final Typeface DEFAULT_BOLD; 90 /** The NORMAL style of the default sans serif typeface. */ 91 public static final Typeface SANS_SERIF; 92 /** The NORMAL style of the default serif typeface. */ 93 public static final Typeface SERIF; 94 /** The NORMAL style of the default monospace typeface. */ 95 public static final Typeface MONOSPACE; 96 97 static Typeface[] sDefaults; 98 private static final LongSparseArray<SparseArray<Typeface>> sTypefaceCache = 99 new LongSparseArray<>(3); 100 101 /** 102 * Cache for Typeface objects dynamically loaded from assets. Currently max size is 16. 103 */ 104 @GuardedBy("sLock") 105 private static final LruCache<String, Typeface> sDynamicTypefaceCache = new LruCache<>(16); 106 107 static Typeface sDefaultTypeface; 108 static Map<String, Typeface> sSystemFontMap; 109 static FontFamily[] sFallbackFonts; 110 private static final Object sLock = new Object(); 111 112 static final String FONTS_CONFIG = "fonts.xml"; 113 114 /** 115 * @hide 116 */ 117 public long native_instance; 118 119 // Style 120 public static final int NORMAL = 0; 121 public static final int BOLD = 1; 122 public static final int ITALIC = 2; 123 public static final int BOLD_ITALIC = 3; 124 125 private int mStyle = 0; 126 private int mWeight = 0; 127 128 // Value for weight and italic. Indicates the value is resolved by font metadata. 129 // Must be the same as the C++ constant in core/jni/android/graphics/FontFamily.cpp 130 /** @hide */ 131 public static final int RESOLVE_BY_FONT_TABLE = -1; 132 133 // Style value for building typeface. 134 private static final int STYLE_NORMAL = 0; 135 private static final int STYLE_ITALIC = 1; 136 137 private int[] mSupportedAxes; 138 private static final int[] EMPTY_AXES = {}; 139 setDefault(Typeface t)140 private static void setDefault(Typeface t) { 141 sDefaultTypeface = t; 142 nativeSetDefault(t.native_instance); 143 } 144 145 /** Returns the typeface's intrinsic style attributes */ getStyle()146 public int getStyle() { 147 return mStyle; 148 } 149 150 /** Returns true if getStyle() has the BOLD bit set. */ isBold()151 public final boolean isBold() { 152 return (mStyle & BOLD) != 0; 153 } 154 155 /** Returns true if getStyle() has the ITALIC bit set. */ isItalic()156 public final boolean isItalic() { 157 return (mStyle & ITALIC) != 0; 158 } 159 160 /** 161 * @hide 162 * Used by Resources to load a font resource of type font file. 163 */ 164 @Nullable createFromResources(AssetManager mgr, String path, int cookie)165 public static Typeface createFromResources(AssetManager mgr, String path, int cookie) { 166 if (sFallbackFonts != null) { 167 synchronized (sDynamicTypefaceCache) { 168 final String key = Builder.createAssetUid( 169 mgr, path, 0 /* ttcIndex */, null /* axes */, 170 RESOLVE_BY_FONT_TABLE /* weight */, RESOLVE_BY_FONT_TABLE /* italic */); 171 Typeface typeface = sDynamicTypefaceCache.get(key); 172 if (typeface != null) return typeface; 173 174 FontFamily fontFamily = new FontFamily(); 175 // TODO: introduce ttc index and variation settings to resource type font. 176 if (fontFamily.addFontFromAssetManager(mgr, path, cookie, false /* isAsset */, 177 0 /* ttcIndex */, RESOLVE_BY_FONT_TABLE /* weight */, 178 RESOLVE_BY_FONT_TABLE /* italic */, null /* axes */)) { 179 if (!fontFamily.freeze()) { 180 return null; 181 } 182 FontFamily[] families = {fontFamily}; 183 typeface = createFromFamiliesWithDefault(families, 184 RESOLVE_BY_FONT_TABLE, RESOLVE_BY_FONT_TABLE); 185 sDynamicTypefaceCache.put(key, typeface); 186 return typeface; 187 } 188 } 189 } 190 return null; 191 } 192 193 /** 194 * @hide 195 * Used by Resources to load a font resource of type xml. 196 */ 197 @Nullable createFromResources( FamilyResourceEntry entry, AssetManager mgr, String path)198 public static Typeface createFromResources( 199 FamilyResourceEntry entry, AssetManager mgr, String path) { 200 if (sFallbackFonts != null) { 201 if (entry instanceof ProviderResourceEntry) { 202 final ProviderResourceEntry providerEntry = (ProviderResourceEntry) entry; 203 // Downloadable font 204 List<List<String>> givenCerts = providerEntry.getCerts(); 205 List<List<byte[]>> certs = new ArrayList<>(); 206 if (givenCerts != null) { 207 for (int i = 0; i < givenCerts.size(); i++) { 208 List<String> certSet = givenCerts.get(i); 209 List<byte[]> byteArraySet = new ArrayList<>(); 210 for (int j = 0; j < certSet.size(); j++) { 211 byteArraySet.add(Base64.decode(certSet.get(j), Base64.DEFAULT)); 212 } 213 certs.add(byteArraySet); 214 } 215 } 216 // Downloaded font and it wasn't cached, request it again and return a 217 // default font instead (nothing we can do now). 218 FontRequest request = new FontRequest(providerEntry.getAuthority(), 219 providerEntry.getPackage(), providerEntry.getQuery(), certs); 220 Typeface typeface = FontsContract.getFontSync(request); 221 return typeface == null ? DEFAULT : typeface; 222 } 223 224 Typeface typeface = findFromCache(mgr, path); 225 if (typeface != null) return typeface; 226 227 // family is FontFamilyFilesResourceEntry 228 final FontFamilyFilesResourceEntry filesEntry = 229 (FontFamilyFilesResourceEntry) entry; 230 231 FontFamily fontFamily = new FontFamily(); 232 for (final FontFileResourceEntry fontFile : filesEntry.getEntries()) { 233 // TODO: Add ttc and variation font support. (b/37853920) 234 if (!fontFamily.addFontFromAssetManager(mgr, fontFile.getFileName(), 235 0 /* resourceCookie */, false /* isAsset */, 0 /* ttcIndex */, 236 fontFile.getWeight(), fontFile.getItalic(), null /* axes */)) { 237 return null; 238 } 239 } 240 if (!fontFamily.freeze()) { 241 return null; 242 } 243 FontFamily[] familyChain = { fontFamily }; 244 typeface = createFromFamiliesWithDefault(familyChain, 245 RESOLVE_BY_FONT_TABLE, RESOLVE_BY_FONT_TABLE); 246 synchronized (sDynamicTypefaceCache) { 247 final String key = Builder.createAssetUid(mgr, path, 0 /* ttcIndex */, 248 null /* axes */, RESOLVE_BY_FONT_TABLE /* weight */, 249 RESOLVE_BY_FONT_TABLE /* italic */); 250 sDynamicTypefaceCache.put(key, typeface); 251 } 252 return typeface; 253 } 254 return null; 255 } 256 257 /** 258 * Used by resources for cached loading if the font is available. 259 * @hide 260 */ findFromCache(AssetManager mgr, String path)261 public static Typeface findFromCache(AssetManager mgr, String path) { 262 synchronized (sDynamicTypefaceCache) { 263 final String key = Builder.createAssetUid(mgr, path, 0 /* ttcIndex */, null /* axes */, 264 RESOLVE_BY_FONT_TABLE /* weight */, RESOLVE_BY_FONT_TABLE /* italic */); 265 Typeface typeface = sDynamicTypefaceCache.get(key); 266 if (typeface != null) { 267 return typeface; 268 } 269 } 270 return null; 271 } 272 273 /** 274 * A builder class for creating new Typeface instance. 275 * 276 * <p> 277 * Examples, 278 * 1) Create Typeface from ttf file. 279 * <pre> 280 * <code> 281 * Typeface.Builder buidler = new Typeface.Builder("your_font_file.ttf"); 282 * Typeface typeface = builder.build(); 283 * </code> 284 * </pre> 285 * 286 * 2) Create Typeface from ttc file in assets directory. 287 * <pre> 288 * <code> 289 * Typeface.Builder buidler = new Typeface.Builder(getAssets(), "your_font_file.ttc"); 290 * builder.setTtcIndex(2); // Set index of font collection. 291 * Typeface typeface = builder.build(); 292 * </code> 293 * </pre> 294 * 295 * 3) Create Typeface with variation settings. 296 * <pre> 297 * <code> 298 * Typeface.Builder buidler = new Typeface.Builder("your_font_file.ttf"); 299 * builder.setFontVariationSettings("'wght' 700, 'slnt' 20, 'ital' 1"); 300 * builder.setWeight(700); // Tell the system that this is a bold font. 301 * builder.setItalic(true); // Tell the system that this is an italic style font. 302 * Typeface typeface = builder.build(); 303 * </code> 304 * </pre> 305 * </p> 306 */ 307 public static final class Builder { 308 /** @hide */ 309 public static final int NORMAL_WEIGHT = 400; 310 /** @hide */ 311 public static final int BOLD_WEIGHT = 700; 312 313 private int mTtcIndex; 314 private FontVariationAxis[] mAxes; 315 316 private AssetManager mAssetManager; 317 private String mPath; 318 private FileDescriptor mFd; 319 320 private FontsContract.FontInfo[] mFonts; 321 private Map<Uri, ByteBuffer> mFontBuffers; 322 323 private String mFallbackFamilyName; 324 325 private int mWeight = RESOLVE_BY_FONT_TABLE; 326 private int mItalic = RESOLVE_BY_FONT_TABLE; 327 328 /** 329 * Constructs a builder with a file path. 330 * 331 * @param path The file object refers to the font file. 332 */ Builder(@onNull File path)333 public Builder(@NonNull File path) { 334 mPath = path.getAbsolutePath(); 335 } 336 337 /** 338 * Constructs a builder with a file descriptor. 339 * 340 * Caller is responsible for closing the passed file descriptor after {@link #build} is 341 * called. 342 * 343 * @param fd The file descriptor. The passed fd must be mmap-able. 344 */ Builder(@onNull FileDescriptor fd)345 public Builder(@NonNull FileDescriptor fd) { 346 mFd = fd; 347 } 348 349 /** 350 * Constructs a builder with a file path. 351 * 352 * @param path The full path to the font file. 353 */ Builder(@onNull String path)354 public Builder(@NonNull String path) { 355 mPath = path; 356 } 357 358 /** 359 * Constructs a builder from an asset manager and a file path in an asset directory. 360 * 361 * @param assetManager The application's asset manager 362 * @param path The file name of the font data in the asset directory 363 */ Builder(@onNull AssetManager assetManager, @NonNull String path)364 public Builder(@NonNull AssetManager assetManager, @NonNull String path) { 365 mAssetManager = Preconditions.checkNotNull(assetManager); 366 mPath = Preconditions.checkStringNotEmpty(path); 367 } 368 369 /** 370 * Constracts a builder from an array of FontsContract.FontInfo. 371 * 372 * Since {@link FontsContract.FontInfo} holds information about TTC indices and 373 * variation settings, there is no need to call {@link #setTtcIndex} or 374 * {@link #setFontVariationSettings}. Similary, {@link FontsContract.FontInfo} holds 375 * weight and italic information, so {@link #setWeight} and {@link #setItalic} are used 376 * for style matching during font selection. 377 * 378 * @param results The array of {@link FontsContract.FontInfo} 379 * @param buffers The mapping from URI to buffers to be used during building. 380 * @hide 381 */ Builder(@onNull FontsContract.FontInfo[] fonts, @NonNull Map<Uri, ByteBuffer> buffers)382 public Builder(@NonNull FontsContract.FontInfo[] fonts, 383 @NonNull Map<Uri, ByteBuffer> buffers) { 384 mFonts = fonts; 385 mFontBuffers = buffers; 386 } 387 388 /** 389 * Sets weight of the font. 390 * 391 * Tells the system the weight of the given font. If not provided, the system will resolve 392 * the weight value by reading font tables. 393 * @param weight a weight value. 394 */ setWeight(@ntRangefrom = 1, to = 1000) int weight)395 public Builder setWeight(@IntRange(from = 1, to = 1000) int weight) { 396 mWeight = weight; 397 return this; 398 } 399 400 /** 401 * Sets italic information of the font. 402 * 403 * Tells the system the style of the given font. If not provided, the system will resolve 404 * the style by reading font tables. 405 * @param italic {@code true} if the font is italic. Otherwise {@code false}. 406 */ setItalic(boolean italic)407 public Builder setItalic(boolean italic) { 408 mItalic = italic ? STYLE_ITALIC : STYLE_NORMAL; 409 return this; 410 } 411 412 /** 413 * Sets an index of the font collection. 414 * 415 * Can not be used for Typeface source. build() method will return null for invalid index. 416 * @param ttcIndex An index of the font collection. If the font source is not font 417 * collection, do not call this method or specify 0. 418 */ setTtcIndex(@ntRangefrom = 0) int ttcIndex)419 public Builder setTtcIndex(@IntRange(from = 0) int ttcIndex) { 420 if (mFonts != null) { 421 throw new IllegalArgumentException( 422 "TTC index can not be specified for FontResult source."); 423 } 424 mTtcIndex = ttcIndex; 425 return this; 426 } 427 428 /** 429 * Sets a font variation settings. 430 * 431 * @param variationSettings See {@link android.widget.TextView#setFontVariationSettings}. 432 * @throws IllegalArgumentException If given string is not a valid font variation settings 433 * format. 434 */ setFontVariationSettings(@ullable String variationSettings)435 public Builder setFontVariationSettings(@Nullable String variationSettings) { 436 if (mFonts != null) { 437 throw new IllegalArgumentException( 438 "Font variation settings can not be specified for FontResult source."); 439 } 440 if (mAxes != null) { 441 throw new IllegalStateException("Font variation settings are already set."); 442 } 443 mAxes = FontVariationAxis.fromFontVariationSettings(variationSettings); 444 return this; 445 } 446 447 /** 448 * Sets a font variation settings. 449 * 450 * @param axes An array of font variation axis tag-value pairs. 451 */ setFontVariationSettings(@ullable FontVariationAxis[] axes)452 public Builder setFontVariationSettings(@Nullable FontVariationAxis[] axes) { 453 if (mFonts != null) { 454 throw new IllegalArgumentException( 455 "Font variation settings can not be specified for FontResult source."); 456 } 457 if (mAxes != null) { 458 throw new IllegalStateException("Font variation settings are already set."); 459 } 460 mAxes = axes; 461 return this; 462 } 463 464 /** 465 * Sets a fallback family name. 466 * 467 * By specifying a fallback family name, a fallback Typeface will be returned if the 468 * {@link #build} method fails to create a Typeface from the provided font. The fallback 469 * family will be resolved with the provided weight and italic information specified by 470 * {@link #setWeight} and {@link #setItalic}. 471 * 472 * If {@link #setWeight} is not called, the fallback family keeps the default weight. 473 * Similary, if {@link #setItalic} is not called, the fallback family keeps the default 474 * italic information. For example, calling {@code builder.setFallback("sans-serif-light")} 475 * is equivalent to calling {@code builder.setFallback("sans-serif").setWeight(300)} in 476 * terms of fallback. The default weight and italic information are overridden by calling 477 * {@link #setWeight} and {@link #setItalic}. For example, if a Typeface is constructed 478 * using {@code builder.setFallback("sans-serif-light").setWeight(700)}, the fallback text 479 * will render as sans serif bold. 480 * 481 * @param familyName A family name to be used for fallback if the provided font can not be 482 * used. By passing {@code null}, build() returns {@code null}. 483 * If {@link #setFallback} is not called on the builder, {@code null} 484 * is assumed. 485 */ setFallback(@ullable String familyName)486 public Builder setFallback(@Nullable String familyName) { 487 mFallbackFamilyName = familyName; 488 return this; 489 } 490 491 /** 492 * Creates a unique id for a given AssetManager and asset path. 493 * 494 * @param mgr AssetManager instance 495 * @param path The path for the asset. 496 * @param ttcIndex The TTC index for the font. 497 * @param axes The font variation settings. 498 * @return Unique id for a given AssetManager and asset path. 499 */ createAssetUid(final AssetManager mgr, String path, int ttcIndex, @Nullable FontVariationAxis[] axes, int weight, int italic)500 private static String createAssetUid(final AssetManager mgr, String path, int ttcIndex, 501 @Nullable FontVariationAxis[] axes, int weight, int italic) { 502 final SparseArray<String> pkgs = mgr.getAssignedPackageIdentifiers(); 503 final StringBuilder builder = new StringBuilder(); 504 final int size = pkgs.size(); 505 for (int i = 0; i < size; i++) { 506 builder.append(pkgs.valueAt(i)); 507 builder.append("-"); 508 } 509 builder.append(path); 510 builder.append("-"); 511 builder.append(Integer.toString(ttcIndex)); 512 builder.append("-"); 513 builder.append(Integer.toString(weight)); 514 builder.append("-"); 515 builder.append(Integer.toString(italic)); 516 builder.append("-"); 517 if (axes != null) { 518 for (FontVariationAxis axis : axes) { 519 builder.append(axis.getTag()); 520 builder.append("-"); 521 builder.append(Float.toString(axis.getStyleValue())); 522 } 523 } 524 return builder.toString(); 525 } 526 527 private static final Object sLock = new Object(); 528 // TODO: Unify with Typeface.sTypefaceCache. 529 @GuardedBy("sLock") 530 private static final LongSparseArray<SparseArray<Typeface>> sTypefaceCache = 531 new LongSparseArray<>(3); 532 resolveFallbackTypeface()533 private Typeface resolveFallbackTypeface() { 534 if (mFallbackFamilyName == null) { 535 return null; 536 } 537 538 Typeface base = sSystemFontMap.get(mFallbackFamilyName); 539 if (base == null) { 540 base = sDefaultTypeface; 541 } 542 543 if (mWeight == RESOLVE_BY_FONT_TABLE && mItalic == RESOLVE_BY_FONT_TABLE) { 544 return base; 545 } 546 547 final int weight = (mWeight == RESOLVE_BY_FONT_TABLE) ? base.mWeight : mWeight; 548 final boolean italic = 549 (mItalic == RESOLVE_BY_FONT_TABLE) ? (base.mStyle & ITALIC) != 0 : mItalic == 1; 550 final int key = weight << 1 | (italic ? 1 : 0); 551 552 Typeface typeface; 553 synchronized(sLock) { 554 SparseArray<Typeface> innerCache = sTypefaceCache.get(base.native_instance); 555 if (innerCache != null) { 556 typeface = innerCache.get(key); 557 if (typeface != null) { 558 return typeface; 559 } 560 } 561 562 typeface = new Typeface( 563 nativeCreateFromTypefaceWithExactStyle( 564 base.native_instance, weight, italic)); 565 566 if (innerCache == null) { 567 innerCache = new SparseArray<>(4); // [regular, bold] x [upright, italic] 568 sTypefaceCache.put(base.native_instance, innerCache); 569 } 570 innerCache.put(key, typeface); 571 } 572 return typeface; 573 } 574 575 /** 576 * Generates new Typeface from specified configuration. 577 * 578 * @return Newly created Typeface. May return null if some parameters are invalid. 579 */ build()580 public Typeface build() { 581 if (mFd != null) { // Builder is created with file descriptor. 582 try (FileInputStream fis = new FileInputStream(mFd)) { 583 FileChannel channel = fis.getChannel(); 584 long size = channel.size(); 585 ByteBuffer buffer = channel.map(FileChannel.MapMode.READ_ONLY, 0, size); 586 587 final FontFamily fontFamily = new FontFamily(); 588 if (!fontFamily.addFontFromBuffer(buffer, mTtcIndex, mAxes, mWeight, mItalic)) { 589 fontFamily.abortCreation(); 590 return resolveFallbackTypeface(); 591 } 592 if (!fontFamily.freeze()) { 593 return resolveFallbackTypeface(); 594 } 595 FontFamily[] families = { fontFamily }; 596 return createFromFamiliesWithDefault(families, mWeight, mItalic); 597 } catch (IOException e) { 598 return resolveFallbackTypeface(); 599 } 600 } else if (mAssetManager != null) { // Builder is created with asset manager. 601 final String key = createAssetUid( 602 mAssetManager, mPath, mTtcIndex, mAxes, mWeight, mItalic); 603 synchronized (sLock) { 604 Typeface typeface = sDynamicTypefaceCache.get(key); 605 if (typeface != null) return typeface; 606 final FontFamily fontFamily = new FontFamily(); 607 if (!fontFamily.addFontFromAssetManager(mAssetManager, mPath, mTtcIndex, 608 true /* isAsset */, mTtcIndex, mWeight, mItalic, mAxes)) { 609 fontFamily.abortCreation(); 610 return resolveFallbackTypeface(); 611 } 612 if (!fontFamily.freeze()) { 613 return resolveFallbackTypeface(); 614 } 615 FontFamily[] families = { fontFamily }; 616 typeface = createFromFamiliesWithDefault(families, mWeight, mItalic); 617 sDynamicTypefaceCache.put(key, typeface); 618 return typeface; 619 } 620 } else if (mPath != null) { // Builder is created with file path. 621 final FontFamily fontFamily = new FontFamily(); 622 if (!fontFamily.addFont(mPath, mTtcIndex, mAxes, mWeight, mItalic)) { 623 fontFamily.abortCreation(); 624 return resolveFallbackTypeface(); 625 } 626 if (!fontFamily.freeze()) { 627 return resolveFallbackTypeface(); 628 } 629 FontFamily[] families = { fontFamily }; 630 return createFromFamiliesWithDefault(families, mWeight, mItalic); 631 } else if (mFonts != null) { 632 final FontFamily fontFamily = new FontFamily(); 633 boolean atLeastOneFont = false; 634 for (FontsContract.FontInfo font : mFonts) { 635 final ByteBuffer fontBuffer = mFontBuffers.get(font.getUri()); 636 if (fontBuffer == null) { 637 continue; // skip 638 } 639 final boolean success = fontFamily.addFontFromBuffer(fontBuffer, 640 font.getTtcIndex(), font.getAxes(), font.getWeight(), 641 font.isItalic() ? STYLE_ITALIC : STYLE_NORMAL); 642 if (!success) { 643 fontFamily.abortCreation(); 644 return null; 645 } 646 atLeastOneFont = true; 647 } 648 if (!atLeastOneFont) { 649 // No fonts are avaialble. No need to create new Typeface and returns fallback 650 // Typeface instead. 651 fontFamily.abortCreation(); 652 return null; 653 } 654 fontFamily.freeze(); 655 FontFamily[] families = { fontFamily }; 656 return createFromFamiliesWithDefault(families, mWeight, mItalic); 657 } 658 659 // Must not reach here. 660 throw new IllegalArgumentException("No source was set."); 661 } 662 } 663 664 /** 665 * Create a typeface object given a family name, and option style information. 666 * If null is passed for the name, then the "default" font will be chosen. 667 * The resulting typeface object can be queried (getStyle()) to discover what 668 * its "real" style characteristics are. 669 * 670 * @param familyName May be null. The name of the font family. 671 * @param style The style (normal, bold, italic) of the typeface. 672 * e.g. NORMAL, BOLD, ITALIC, BOLD_ITALIC 673 * @return The best matching typeface. 674 */ create(String familyName, int style)675 public static Typeface create(String familyName, int style) { 676 if (sSystemFontMap != null) { 677 return create(sSystemFontMap.get(familyName), style); 678 } 679 return null; 680 } 681 682 /** 683 * Create a typeface object that best matches the specified existing 684 * typeface and the specified Style. Use this call if you want to pick a new 685 * style from the same family of an existing typeface object. If family is 686 * null, this selects from the default font's family. 687 * 688 * @param family May be null. The name of the existing type face. 689 * @param style The style (normal, bold, italic) of the typeface. 690 * e.g. NORMAL, BOLD, ITALIC, BOLD_ITALIC 691 * @return The best matching typeface. 692 */ create(Typeface family, int style)693 public static Typeface create(Typeface family, int style) { 694 if (style < 0 || style > 3) { 695 style = 0; 696 } 697 long ni = 0; 698 if (family != null) { 699 // Return early if we're asked for the same face/style 700 if (family.mStyle == style) { 701 return family; 702 } 703 704 ni = family.native_instance; 705 } 706 707 Typeface typeface; 708 SparseArray<Typeface> styles = sTypefaceCache.get(ni); 709 710 if (styles != null) { 711 typeface = styles.get(style); 712 if (typeface != null) { 713 return typeface; 714 } 715 } 716 717 typeface = new Typeface(nativeCreateFromTypeface(ni, style)); 718 if (styles == null) { 719 styles = new SparseArray<Typeface>(4); 720 sTypefaceCache.put(ni, styles); 721 } 722 styles.put(style, typeface); 723 724 return typeface; 725 } 726 727 /** @hide */ createFromTypefaceWithVariation(@ullable Typeface family, @NonNull List<FontVariationAxis> axes)728 public static Typeface createFromTypefaceWithVariation(@Nullable Typeface family, 729 @NonNull List<FontVariationAxis> axes) { 730 final long ni = family == null ? 0 : family.native_instance; 731 return new Typeface(nativeCreateFromTypefaceWithVariation(ni, axes)); 732 } 733 734 /** 735 * Returns one of the default typeface objects, based on the specified style 736 * 737 * @return the default typeface that corresponds to the style 738 */ defaultFromStyle(int style)739 public static Typeface defaultFromStyle(int style) { 740 return sDefaults[style]; 741 } 742 743 /** 744 * Create a new typeface from the specified font data. 745 * 746 * @param mgr The application's asset manager 747 * @param path The file name of the font data in the assets directory 748 * @return The new typeface. 749 */ createFromAsset(AssetManager mgr, String path)750 public static Typeface createFromAsset(AssetManager mgr, String path) { 751 if (path == null) { 752 throw new NullPointerException(); // for backward compatibility 753 } 754 if (sFallbackFonts != null) { 755 synchronized (sLock) { 756 Typeface typeface = new Builder(mgr, path).build(); 757 if (typeface != null) return typeface; 758 759 final String key = Builder.createAssetUid(mgr, path, 0 /* ttcIndex */, 760 null /* axes */, RESOLVE_BY_FONT_TABLE, RESOLVE_BY_FONT_TABLE); 761 typeface = sDynamicTypefaceCache.get(key); 762 if (typeface != null) return typeface; 763 764 final FontFamily fontFamily = new FontFamily(); 765 if (fontFamily.addFontFromAssetManager(mgr, path, 0, true /* isAsset */, 766 0 /* ttc index */, RESOLVE_BY_FONT_TABLE, RESOLVE_BY_FONT_TABLE, 767 null /* axes */)) { 768 // Due to backward compatibility, even if the font is not supported by our font 769 // stack, we need to place the empty font at the first place. The typeface with 770 // empty font behaves different from default typeface especially in fallback 771 // font selection. 772 fontFamily.allowUnsupportedFont(); 773 fontFamily.freeze(); 774 final FontFamily[] families = { fontFamily }; 775 typeface = createFromFamiliesWithDefault(families, RESOLVE_BY_FONT_TABLE, 776 RESOLVE_BY_FONT_TABLE); 777 sDynamicTypefaceCache.put(key, typeface); 778 return typeface; 779 } else { 780 fontFamily.abortCreation(); 781 } 782 } 783 } 784 throw new RuntimeException("Font asset not found " + path); 785 } 786 787 /** 788 * Creates a unique id for a given font provider and query. 789 */ createProviderUid(String authority, String query)790 private static String createProviderUid(String authority, String query) { 791 final StringBuilder builder = new StringBuilder(); 792 builder.append("provider:"); 793 builder.append(authority); 794 builder.append("-"); 795 builder.append(query); 796 return builder.toString(); 797 } 798 799 /** 800 * Create a new typeface from the specified font file. 801 * 802 * @param path The path to the font data. 803 * @return The new typeface. 804 */ createFromFile(@ullable File path)805 public static Typeface createFromFile(@Nullable File path) { 806 // For the compatibility reasons, leaving possible NPE here. 807 // See android.graphics.cts.TypefaceTest#testCreateFromFileByFileReferenceNull 808 return createFromFile(path.getAbsolutePath()); 809 } 810 811 /** 812 * Create a new typeface from the specified font file. 813 * 814 * @param path The full path to the font data. 815 * @return The new typeface. 816 */ createFromFile(@ullable String path)817 public static Typeface createFromFile(@Nullable String path) { 818 if (sFallbackFonts != null) { 819 final FontFamily fontFamily = new FontFamily(); 820 if (fontFamily.addFont(path, 0 /* ttcIndex */, null /* axes */, 821 RESOLVE_BY_FONT_TABLE, RESOLVE_BY_FONT_TABLE)) { 822 // Due to backward compatibility, even if the font is not supported by our font 823 // stack, we need to place the empty font at the first place. The typeface with 824 // empty font behaves different from default typeface especially in fallback font 825 // selection. 826 fontFamily.allowUnsupportedFont(); 827 fontFamily.freeze(); 828 FontFamily[] families = { fontFamily }; 829 return createFromFamiliesWithDefault(families, RESOLVE_BY_FONT_TABLE, 830 RESOLVE_BY_FONT_TABLE); 831 } else { 832 fontFamily.abortCreation(); 833 } 834 } 835 throw new RuntimeException("Font not found " + path); 836 } 837 838 /** 839 * Create a new typeface from an array of font families. 840 * 841 * @param families array of font families 842 */ createFromFamilies(FontFamily[] families)843 private static Typeface createFromFamilies(FontFamily[] families) { 844 long[] ptrArray = new long[families.length]; 845 for (int i = 0; i < families.length; i++) { 846 ptrArray[i] = families[i].mNativePtr; 847 } 848 return new Typeface(nativeCreateFromArray( 849 ptrArray, RESOLVE_BY_FONT_TABLE, RESOLVE_BY_FONT_TABLE)); 850 } 851 852 /** 853 * Create a new typeface from an array of font families, including 854 * also the font families in the fallback list. 855 * @param weight the weight for this family. {@link RESOLVE_BY_FONT_TABLE} can be used. In that 856 * case, the table information in the first family's font is used. If the first 857 * family has multiple fonts, the closest to the regular weight and upright font 858 * is used. 859 * @param italic the italic information for this family. {@link RESOLVE_BY_FONT_TABLE} can be 860 * used. In that case, the table information in the first family's font is used. 861 * If the first family has multiple fonts, the closest to the regular weight and 862 * upright font is used. 863 * @param families array of font families 864 */ createFromFamiliesWithDefault(FontFamily[] families, int weight, int italic)865 private static Typeface createFromFamiliesWithDefault(FontFamily[] families, 866 int weight, int italic) { 867 long[] ptrArray = new long[families.length + sFallbackFonts.length]; 868 for (int i = 0; i < families.length; i++) { 869 ptrArray[i] = families[i].mNativePtr; 870 } 871 for (int i = 0; i < sFallbackFonts.length; i++) { 872 ptrArray[i + families.length] = sFallbackFonts[i].mNativePtr; 873 } 874 return new Typeface(nativeCreateFromArray(ptrArray, weight, italic)); 875 } 876 877 // don't allow clients to call this directly Typeface(long ni)878 private Typeface(long ni) { 879 if (ni == 0) { 880 throw new RuntimeException("native typeface cannot be made"); 881 } 882 883 native_instance = ni; 884 mStyle = nativeGetStyle(ni); 885 mWeight = nativeGetWeight(ni); 886 } 887 makeFamilyFromParsed(FontConfig.Family family, Map<String, ByteBuffer> bufferForPath)888 private static FontFamily makeFamilyFromParsed(FontConfig.Family family, 889 Map<String, ByteBuffer> bufferForPath) { 890 FontFamily fontFamily = new FontFamily(family.getLanguage(), family.getVariant()); 891 for (FontConfig.Font font : family.getFonts()) { 892 String fullPathName = "/system/fonts/" + font.getFontName(); 893 ByteBuffer fontBuffer = bufferForPath.get(fullPathName); 894 if (fontBuffer == null) { 895 try (FileInputStream file = new FileInputStream(fullPathName)) { 896 FileChannel fileChannel = file.getChannel(); 897 long fontSize = fileChannel.size(); 898 fontBuffer = fileChannel.map(FileChannel.MapMode.READ_ONLY, 0, fontSize); 899 bufferForPath.put(fullPathName, fontBuffer); 900 } catch (IOException e) { 901 Log.e(TAG, "Error mapping font file " + fullPathName); 902 continue; 903 } 904 } 905 if (!fontFamily.addFontFromBuffer(fontBuffer, font.getTtcIndex(), font.getAxes(), 906 font.getWeight(), font.isItalic() ? STYLE_ITALIC : STYLE_NORMAL)) { 907 Log.e(TAG, "Error creating font " + fullPathName + "#" + font.getTtcIndex()); 908 } 909 } 910 if (!fontFamily.freeze()) { 911 // Treat as system error since reaching here means that a system pre-installed font 912 // can't be used by our font stack. 913 Log.e(TAG, "Unable to load Family: " + family.getName() + ":" + family.getLanguage()); 914 return null; 915 } 916 return fontFamily; 917 } 918 919 /* 920 * (non-Javadoc) 921 * 922 * This should only be called once, from the static class initializer block. 923 */ init()924 private static void init() { 925 // Load font config and initialize Minikin state 926 File systemFontConfigLocation = getSystemFontConfigLocation(); 927 File configFilename = new File(systemFontConfigLocation, FONTS_CONFIG); 928 try { 929 FileInputStream fontsIn = new FileInputStream(configFilename); 930 FontConfig fontConfig = FontListParser.parse(fontsIn); 931 932 Map<String, ByteBuffer> bufferForPath = new HashMap<String, ByteBuffer>(); 933 934 List<FontFamily> familyList = new ArrayList<FontFamily>(); 935 // Note that the default typeface is always present in the fallback list; 936 // this is an enhancement from pre-Minikin behavior. 937 for (int i = 0; i < fontConfig.getFamilies().length; i++) { 938 FontConfig.Family f = fontConfig.getFamilies()[i]; 939 if (i == 0 || f.getName() == null) { 940 FontFamily family = makeFamilyFromParsed(f, bufferForPath); 941 if (family != null) { 942 familyList.add(family); 943 } 944 } 945 } 946 sFallbackFonts = familyList.toArray(new FontFamily[familyList.size()]); 947 setDefault(Typeface.createFromFamilies(sFallbackFonts)); 948 949 Map<String, Typeface> systemFonts = new HashMap<String, Typeface>(); 950 for (int i = 0; i < fontConfig.getFamilies().length; i++) { 951 Typeface typeface; 952 FontConfig.Family f = fontConfig.getFamilies()[i]; 953 if (f.getName() != null) { 954 if (i == 0) { 955 // The first entry is the default typeface; no sense in 956 // duplicating the corresponding FontFamily. 957 typeface = sDefaultTypeface; 958 } else { 959 FontFamily fontFamily = makeFamilyFromParsed(f, bufferForPath); 960 if (fontFamily == null) { 961 continue; 962 } 963 FontFamily[] families = { fontFamily }; 964 typeface = Typeface.createFromFamiliesWithDefault(families, 965 RESOLVE_BY_FONT_TABLE, RESOLVE_BY_FONT_TABLE); 966 } 967 systemFonts.put(f.getName(), typeface); 968 } 969 } 970 for (FontConfig.Alias alias : fontConfig.getAliases()) { 971 Typeface base = systemFonts.get(alias.getToName()); 972 Typeface newFace = base; 973 int weight = alias.getWeight(); 974 if (weight != 400) { 975 newFace = new Typeface(nativeCreateWeightAlias(base.native_instance, weight)); 976 } 977 systemFonts.put(alias.getName(), newFace); 978 } 979 sSystemFontMap = systemFonts; 980 981 } catch (RuntimeException e) { 982 Log.w(TAG, "Didn't create default family (most likely, non-Minikin build)", e); 983 // TODO: normal in non-Minikin case, remove or make error when Minikin-only 984 } catch (FileNotFoundException e) { 985 Log.e(TAG, "Error opening " + configFilename, e); 986 } catch (IOException e) { 987 Log.e(TAG, "Error reading " + configFilename, e); 988 } catch (XmlPullParserException e) { 989 Log.e(TAG, "XML parse exception for " + configFilename, e); 990 } 991 } 992 993 static { init()994 init(); 995 // Set up defaults and typefaces exposed in public API 996 DEFAULT = create((String) null, 0); 997 DEFAULT_BOLD = create((String) null, Typeface.BOLD); 998 SANS_SERIF = create("sans-serif", 0); 999 SERIF = create("serif", 0); 1000 MONOSPACE = create("monospace", 0); 1001 1002 sDefaults = new Typeface[] { 1003 DEFAULT, 1004 DEFAULT_BOLD, 1005 create((String) null, Typeface.ITALIC), 1006 create((String) null, Typeface.BOLD_ITALIC), 1007 }; 1008 1009 } 1010 getSystemFontConfigLocation()1011 private static File getSystemFontConfigLocation() { 1012 return new File("/system/etc/"); 1013 } 1014 1015 @Override finalize()1016 protected void finalize() throws Throwable { 1017 try { 1018 nativeUnref(native_instance); 1019 native_instance = 0; // Other finalizers can still call us. 1020 } finally { 1021 super.finalize(); 1022 } 1023 } 1024 1025 @Override equals(Object o)1026 public boolean equals(Object o) { 1027 if (this == o) return true; 1028 if (o == null || getClass() != o.getClass()) return false; 1029 1030 Typeface typeface = (Typeface) o; 1031 1032 return mStyle == typeface.mStyle && native_instance == typeface.native_instance; 1033 } 1034 1035 @Override hashCode()1036 public int hashCode() { 1037 /* 1038 * Modified method for hashCode with long native_instance derived from 1039 * http://developer.android.com/reference/java/lang/Object.html 1040 */ 1041 int result = 17; 1042 result = 31 * result + (int) (native_instance ^ (native_instance >>> 32)); 1043 result = 31 * result + mStyle; 1044 return result; 1045 } 1046 1047 /** @hide */ isSupportedAxes(int axis)1048 public boolean isSupportedAxes(int axis) { 1049 if (mSupportedAxes == null) { 1050 synchronized (this) { 1051 if (mSupportedAxes == null) { 1052 mSupportedAxes = nativeGetSupportedAxes(native_instance); 1053 if (mSupportedAxes == null) { 1054 mSupportedAxes = EMPTY_AXES; 1055 } 1056 } 1057 } 1058 } 1059 return Arrays.binarySearch(mSupportedAxes, axis) >= 0; 1060 } 1061 nativeCreateFromTypeface(long native_instance, int style)1062 private static native long nativeCreateFromTypeface(long native_instance, int style); nativeCreateFromTypefaceWithExactStyle( long native_instance, int weight, boolean italic)1063 private static native long nativeCreateFromTypefaceWithExactStyle( 1064 long native_instance, int weight, boolean italic); 1065 // TODO: clean up: change List<FontVariationAxis> to FontVariationAxis[] nativeCreateFromTypefaceWithVariation( long native_instance, List<FontVariationAxis> axes)1066 private static native long nativeCreateFromTypefaceWithVariation( 1067 long native_instance, List<FontVariationAxis> axes); nativeCreateWeightAlias(long native_instance, int weight)1068 private static native long nativeCreateWeightAlias(long native_instance, int weight); nativeUnref(long native_instance)1069 private static native void nativeUnref(long native_instance); nativeGetStyle(long native_instance)1070 private static native int nativeGetStyle(long native_instance); nativeGetWeight(long native_instance)1071 private static native int nativeGetWeight(long native_instance); nativeCreateFromArray(long[] familyArray, int weight, int italic)1072 private static native long nativeCreateFromArray(long[] familyArray, int weight, int italic); nativeSetDefault(long native_instance)1073 private static native void nativeSetDefault(long native_instance); nativeGetSupportedAxes(long native_instance)1074 private static native int[] nativeGetSupportedAxes(long native_instance); 1075 } 1076