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