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