Typeface.java revision b12397e57e79c5dd9e8b2cb3839f5cd30b5d515f
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 android.annotation.IntDef; 20import android.annotation.NonNull; 21import android.annotation.Nullable; 22import android.content.res.AssetManager; 23import android.graphics.fonts.FontRequest; 24import android.graphics.fonts.FontResult; 25import android.os.Bundle; 26import android.os.Handler; 27import android.os.ParcelFileDescriptor; 28import android.os.ResultReceiver; 29import android.provider.FontsContract; 30import android.text.FontConfig; 31import android.util.Log; 32import android.util.LongSparseArray; 33import android.util.LruCache; 34import android.util.SparseArray; 35import android.graphics.FontListParser; 36 37import com.android.internal.annotations.GuardedBy; 38 39import libcore.io.IoUtils; 40 41import org.xmlpull.v1.XmlPullParserException; 42 43import java.io.File; 44import java.io.FileInputStream; 45import java.io.FileNotFoundException; 46import java.io.IOException; 47import java.lang.annotation.Retention; 48import java.lang.annotation.RetentionPolicy; 49import java.nio.ByteBuffer; 50import java.nio.channels.FileChannel; 51import java.util.ArrayList; 52import java.util.HashMap; 53import java.util.List; 54import java.util.Map; 55 56/** 57 * The Typeface class specifies the typeface and intrinsic style of a font. 58 * This is used in the paint, along with optionally Paint settings like 59 * textSize, textSkewX, textScaleX to specify 60 * how text appears when drawn (and measured). 61 */ 62public class Typeface { 63 64 private static String TAG = "Typeface"; 65 66 /** The default NORMAL typeface object */ 67 public static final Typeface DEFAULT; 68 /** 69 * The default BOLD typeface object. Note: this may be not actually be 70 * bold, depending on what fonts are installed. Call getStyle() to know 71 * for sure. 72 */ 73 public static final Typeface DEFAULT_BOLD; 74 /** The NORMAL style of the default sans serif typeface. */ 75 public static final Typeface SANS_SERIF; 76 /** The NORMAL style of the default serif typeface. */ 77 public static final Typeface SERIF; 78 /** The NORMAL style of the default monospace typeface. */ 79 public static final Typeface MONOSPACE; 80 81 static Typeface[] sDefaults; 82 private static final LongSparseArray<SparseArray<Typeface>> sTypefaceCache = 83 new LongSparseArray<>(3); 84 @GuardedBy("sLock") 85 private static FontsContract sFontsContract; 86 @GuardedBy("sLock") 87 private static Handler mHandler; 88 89 /** 90 * Cache for Typeface objects dynamically loaded from assets. Currently max size is 16. 91 */ 92 private static final LruCache<String, Typeface> sDynamicTypefaceCache = new LruCache<>(16); 93 94 static Typeface sDefaultTypeface; 95 static Map<String, Typeface> sSystemFontMap; 96 static FontFamily[] sFallbackFonts; 97 private static final Object sLock = new Object(); 98 99 static final String FONTS_CONFIG = "fonts.xml"; 100 101 /** 102 * @hide 103 */ 104 public long native_instance; 105 106 // Style 107 public static final int NORMAL = 0; 108 public static final int BOLD = 1; 109 public static final int ITALIC = 2; 110 public static final int BOLD_ITALIC = 3; 111 112 private int mStyle = 0; 113 114 private static void setDefault(Typeface t) { 115 sDefaultTypeface = t; 116 nativeSetDefault(t.native_instance); 117 } 118 119 /** Returns the typeface's intrinsic style attributes */ 120 public int getStyle() { 121 return mStyle; 122 } 123 124 /** Returns true if getStyle() has the BOLD bit set. */ 125 public final boolean isBold() { 126 return (mStyle & BOLD) != 0; 127 } 128 129 /** Returns true if getStyle() has the ITALIC bit set. */ 130 public final boolean isItalic() { 131 return (mStyle & ITALIC) != 0; 132 } 133 134 /** 135 * @hide 136 * Used by Resources to load a font resource of type font file. 137 */ 138 @Nullable 139 public static Typeface createFromResources(AssetManager mgr, String path, int cookie) { 140 if (sFallbackFonts != null) { 141 synchronized (sDynamicTypefaceCache) { 142 final String key = createAssetUid(mgr, path); 143 Typeface typeface = sDynamicTypefaceCache.get(key); 144 if (typeface != null) return typeface; 145 146 FontFamily fontFamily = new FontFamily(); 147 if (fontFamily.addFontFromAssetManager(mgr, path, cookie, false /* isAsset */)) { 148 fontFamily.freeze(); 149 FontFamily[] families = {fontFamily}; 150 typeface = createFromFamiliesWithDefault(families); 151 sDynamicTypefaceCache.put(key, typeface); 152 return typeface; 153 } 154 } 155 } 156 return null; 157 } 158 159 /** 160 * @hide 161 * Used by Resources to load a font resource of type xml. 162 */ 163 @Nullable 164 public static Typeface createFromResources(FontConfig config, AssetManager mgr, String path) { 165 if (sFallbackFonts != null) { 166 synchronized (sDynamicTypefaceCache) { 167 final String key = createAssetUid(mgr, path); 168 Typeface typeface = sDynamicTypefaceCache.get(key); 169 if (typeface != null) return typeface; 170 171 List<FontConfig.Family> families = config.getFamilies(); 172 if (families == null || families.isEmpty()) { 173 throw new RuntimeException("Font resource contained no fonts."); 174 } 175 if (families.size() > 1) { 176 throw new RuntimeException("Font resource contained more than one family."); 177 } 178 FontConfig.Family family = families.get(0); 179 180 FontFamily fontFamily = new FontFamily(); 181 List<FontConfig.Font> fonts = family.getFonts(); 182 for (int i = 0; i < fonts.size(); i++) { 183 FontConfig.Font font = fonts.get(i); 184 // TODO: Use style and weight info 185 if (!fontFamily.addFontFromAssetManager(mgr, font.getFontName(), 186 0 /* resourceCookie */, false /* isAsset */)) { 187 return null; 188 } 189 } 190 fontFamily.freeze(); 191 FontFamily[] familyChain = { fontFamily }; 192 typeface = createFromFamiliesWithDefault(familyChain); 193 sDynamicTypefaceCache.put(key, typeface); 194 return typeface; 195 } 196 } 197 return null; 198 } 199 200 /** 201 * Used by resources for cached loading if the font is available. 202 * @hide 203 */ 204 public static Typeface findFromCache(AssetManager mgr, String path) { 205 synchronized (sDynamicTypefaceCache) { 206 final String key = createAssetUid(mgr, path); 207 Typeface typeface = sDynamicTypefaceCache.get(key); 208 if (typeface != null) { 209 return typeface; 210 } 211 } 212 return null; 213 } 214 215 /** 216 * Create a typeface object given a font request. The font will be asynchronously fetched, 217 * therefore the result is delivered to the given callback. See {@link FontRequest}. 218 * Only one of the methods in callback will be invoked, depending on whether the request 219 * succeeds or fails. These calls will happen on the main thread. 220 * @param request A {@link FontRequest} object that identifies the provider and query for the 221 * request. May not be null. 222 * @param callback A callback that will be triggered when results are obtained. May not be null. 223 */ 224 public static void create(@NonNull FontRequest request, @NonNull FontRequestCallback callback) { 225 // Check the cache first 226 // TODO: would the developer want to avoid a cache hit and always ask for the freshest 227 // result? 228 Typeface cachedTypeface = findFromCache( 229 request.getProviderAuthority(), request.getQuery()); 230 if (cachedTypeface != null) { 231 mHandler.post(() -> callback.onTypefaceRetrieved(cachedTypeface)); 232 return; 233 } 234 synchronized (sLock) { 235 if (sFontsContract == null) { 236 sFontsContract = new FontsContract(); 237 mHandler = new Handler(); 238 } 239 final ResultReceiver receiver = new ResultReceiver(null) { 240 @Override 241 public void onReceiveResult(int resultCode, Bundle resultData) { 242 mHandler.post(() -> receiveResult(request, callback, resultCode, resultData)); 243 } 244 }; 245 sFontsContract.getFont(request, receiver); 246 } 247 } 248 249 private static Typeface findFromCache(String providerAuthority, String query) { 250 synchronized (sDynamicTypefaceCache) { 251 final String key = createProviderUid(providerAuthority, query); 252 Typeface typeface = sDynamicTypefaceCache.get(key); 253 if (typeface != null) { 254 return typeface; 255 } 256 } 257 return null; 258 } 259 260 private static void receiveResult(FontRequest request, FontRequestCallback callback, 261 int resultCode, Bundle resultData) { 262 Typeface cachedTypeface = findFromCache( 263 request.getProviderAuthority(), request.getQuery()); 264 if (cachedTypeface != null) { 265 // We already know the result. 266 // Probably the requester requests the same font again in a short interval. 267 callback.onTypefaceRetrieved(cachedTypeface); 268 return; 269 } 270 if (resultCode == FontsContract.RESULT_CODE_PROVIDER_NOT_FOUND) { 271 callback.onTypefaceRequestFailed( 272 FontRequestCallback.FAIL_REASON_PROVIDER_NOT_FOUND); 273 return; 274 } 275 if (resultCode == FontsContract.RESULT_CODE_FONT_NOT_FOUND 276 || resultData == null) { 277 callback.onTypefaceRequestFailed( 278 FontRequestCallback.FAIL_REASON_FONT_NOT_FOUND); 279 return; 280 } 281 List<FontResult> resultList = 282 resultData.getParcelableArrayList(FontsContract.PARCEL_FONT_RESULTS); 283 if (resultList == null || resultList.isEmpty()) { 284 callback.onTypefaceRequestFailed( 285 FontRequestCallback.FAIL_REASON_FONT_NOT_FOUND); 286 return; 287 } 288 FontFamily fontFamily = new FontFamily(); 289 for (int i = 0; i < resultList.size(); ++i) { 290 FontResult result = resultList.get(i); 291 ParcelFileDescriptor fd = result.getFileDescriptor(); 292 if (fd == null) { 293 callback.onTypefaceRequestFailed( 294 FontRequestCallback.FAIL_REASON_FONT_LOAD_ERROR); 295 return; 296 } 297 try (FileInputStream is = new FileInputStream(fd.getFileDescriptor())) { 298 FileChannel fileChannel = is.getChannel(); 299 long fontSize = fileChannel.size(); 300 ByteBuffer fontBuffer = fileChannel.map( 301 FileChannel.MapMode.READ_ONLY, 0, fontSize); 302 int style = result.getStyle(); 303 int weight = (style & BOLD) != 0 ? 700 : 400; 304 // TODO: this method should be 305 // create(fd, ttcIndex, fontVariationSettings, style). 306 if (!fontFamily.addFontWeightStyle(fontBuffer, result.getTtcIndex(), 307 null, weight, (style & ITALIC) != 0)) { 308 Log.e(TAG, "Error creating font " + request.getQuery()); 309 callback.onTypefaceRequestFailed( 310 FontRequestCallback.FAIL_REASON_FONT_LOAD_ERROR); 311 return; 312 } 313 } catch (IOException e) { 314 Log.e(TAG, "Error reading font " + request.getQuery(), e); 315 callback.onTypefaceRequestFailed( 316 FontRequestCallback.FAIL_REASON_FONT_LOAD_ERROR); 317 return; 318 } finally { 319 IoUtils.closeQuietly(fd); 320 } 321 } 322 fontFamily.freeze(); 323 Typeface typeface = Typeface.createFromFamiliesWithDefault(new FontFamily[] { fontFamily }); 324 synchronized (sDynamicTypefaceCache) { 325 String key = createProviderUid(request.getProviderAuthority(), request.getQuery()); 326 sDynamicTypefaceCache.put(key, typeface); 327 } 328 callback.onTypefaceRetrieved(typeface); 329 } 330 331 /** 332 * Interface used to receive asynchronously fetched typefaces. 333 */ 334 public interface FontRequestCallback { 335 /** 336 * Constant returned by {@link #onTypefaceRequestFailed(int)} signaling that the given 337 * provider was not found on the device. 338 */ 339 int FAIL_REASON_PROVIDER_NOT_FOUND = 0; 340 /** 341 * Constant returned by {@link #onTypefaceRequestFailed(int)} signaling that the font 342 * returned by the provider was not loaded properly. 343 */ 344 int FAIL_REASON_FONT_LOAD_ERROR = 1; 345 /** 346 * Constant returned by {@link #onTypefaceRequestFailed(int)} signaling that the given 347 * provider did not return any results for the given query. 348 */ 349 int FAIL_REASON_FONT_NOT_FOUND = 2; 350 351 /** @hide */ 352 @IntDef({FAIL_REASON_PROVIDER_NOT_FOUND, FAIL_REASON_FONT_LOAD_ERROR, 353 FAIL_REASON_FONT_NOT_FOUND}) 354 @Retention(RetentionPolicy.SOURCE) 355 @interface FontRequestFailReason {} 356 357 /** 358 * Called then a Typeface request done via {@link Typeface#create(FontRequest, 359 * FontRequestCallback)} is complete. Note that this method will not be called if 360 * {@link #onTypefaceRequestFailed(int)} is called instead. 361 * @param typeface The Typeface object retrieved. 362 */ 363 void onTypefaceRetrieved(Typeface typeface); 364 365 /** 366 * Called when a Typeface request done via {@link Typeface#create(FontRequest, 367 * FontRequestCallback)} fails. 368 * @param reason One of {@link #FAIL_REASON_PROVIDER_NOT_FOUND}, 369 * {@link #FAIL_REASON_FONT_NOT_FOUND} or 370 * {@link #FAIL_REASON_FONT_LOAD_ERROR}. 371 */ 372 void onTypefaceRequestFailed(@FontRequestFailReason int reason); 373 } 374 375 /** 376 * Create a typeface object given a family name, and option style information. 377 * If null is passed for the name, then the "default" font will be chosen. 378 * The resulting typeface object can be queried (getStyle()) to discover what 379 * its "real" style characteristics are. 380 * 381 * @param familyName May be null. The name of the font family. 382 * @param style The style (normal, bold, italic) of the typeface. 383 * e.g. NORMAL, BOLD, ITALIC, BOLD_ITALIC 384 * @return The best matching typeface. 385 */ 386 public static Typeface create(String familyName, int style) { 387 if (sSystemFontMap != null) { 388 return create(sSystemFontMap.get(familyName), style); 389 } 390 return null; 391 } 392 393 /** 394 * Create a typeface object that best matches the specified existing 395 * typeface and the specified Style. Use this call if you want to pick a new 396 * style from the same family of an existing typeface object. If family is 397 * null, this selects from the default font's family. 398 * 399 * @param family May be null. The name of the existing type face. 400 * @param style The style (normal, bold, italic) of the typeface. 401 * e.g. NORMAL, BOLD, ITALIC, BOLD_ITALIC 402 * @return The best matching typeface. 403 */ 404 public static Typeface create(Typeface family, int style) { 405 if (style < 0 || style > 3) { 406 style = 0; 407 } 408 long ni = 0; 409 if (family != null) { 410 // Return early if we're asked for the same face/style 411 if (family.mStyle == style) { 412 return family; 413 } 414 415 ni = family.native_instance; 416 } 417 418 Typeface typeface; 419 SparseArray<Typeface> styles = sTypefaceCache.get(ni); 420 421 if (styles != null) { 422 typeface = styles.get(style); 423 if (typeface != null) { 424 return typeface; 425 } 426 } 427 428 typeface = new Typeface(nativeCreateFromTypeface(ni, style)); 429 if (styles == null) { 430 styles = new SparseArray<Typeface>(4); 431 sTypefaceCache.put(ni, styles); 432 } 433 styles.put(style, typeface); 434 435 return typeface; 436 } 437 438 /** @hide */ 439 public static Typeface createFromTypefaceWithVariation(Typeface family, 440 String fontVariationSettings) { 441 final long ni = family == null ? 0 : family.native_instance; 442 ArrayList<FontConfig.Axis> axes = 443 FontListParser.parseFontVariationSettings(fontVariationSettings); 444 return new Typeface(nativeCreateFromTypefaceWithVariation(ni, axes)); 445 } 446 447 /** 448 * Returns one of the default typeface objects, based on the specified style 449 * 450 * @return the default typeface that corresponds to the style 451 */ 452 public static Typeface defaultFromStyle(int style) { 453 return sDefaults[style]; 454 } 455 456 /** 457 * Create a new typeface from the specified font data. 458 * 459 * @param mgr The application's asset manager 460 * @param path The file name of the font data in the assets directory 461 * @return The new typeface. 462 */ 463 public static Typeface createFromAsset(AssetManager mgr, String path) { 464 if (sFallbackFonts != null) { 465 synchronized (sDynamicTypefaceCache) { 466 final String key = createAssetUid(mgr, path); 467 Typeface typeface = sDynamicTypefaceCache.get(key); 468 if (typeface != null) return typeface; 469 470 FontFamily fontFamily = new FontFamily(); 471 if (fontFamily.addFontFromAssetManager(mgr, path, 0, true /* isAsset */)) { 472 fontFamily.freeze(); 473 FontFamily[] families = { fontFamily }; 474 typeface = createFromFamiliesWithDefault(families); 475 sDynamicTypefaceCache.put(key, typeface); 476 return typeface; 477 } else { 478 fontFamily.abortCreation(); 479 } 480 } 481 } 482 throw new RuntimeException("Font asset not found " + path); 483 } 484 485 /** 486 * Creates a unique id for a given AssetManager and asset path. 487 * 488 * @param mgr AssetManager instance 489 * @param path The path for the asset. 490 * @return Unique id for a given AssetManager and asset path. 491 */ 492 private static String createAssetUid(final AssetManager mgr, String path) { 493 final SparseArray<String> pkgs = mgr.getAssignedPackageIdentifiers(); 494 final StringBuilder builder = new StringBuilder(); 495 builder.append("asset:"); 496 final int size = pkgs.size(); 497 for (int i = 0; i < size; i++) { 498 builder.append(pkgs.valueAt(i)); 499 builder.append("-"); 500 } 501 builder.append(path); 502 return builder.toString(); 503 } 504 505 /** 506 * Creates a unique id for a given font provider and query. 507 */ 508 private static String createProviderUid(String authority, String query) { 509 final StringBuilder builder = new StringBuilder(); 510 builder.append("provider:"); 511 builder.append(authority); 512 builder.append("-"); 513 builder.append(query); 514 return builder.toString(); 515 } 516 517 /** 518 * Create a new typeface from the specified font file. 519 * 520 * @param path The path to the font data. 521 * @return The new typeface. 522 */ 523 public static Typeface createFromFile(File path) { 524 return createFromFile(path.getAbsolutePath()); 525 } 526 527 /** 528 * Create a new typeface from the specified font file. 529 * 530 * @param path The full path to the font data. 531 * @return The new typeface. 532 */ 533 public static Typeface createFromFile(String path) { 534 if (sFallbackFonts != null) { 535 FontFamily fontFamily = new FontFamily(); 536 if (fontFamily.addFont(path, 0 /* ttcIndex */)) { 537 fontFamily.freeze(); 538 FontFamily[] families = { fontFamily }; 539 return createFromFamiliesWithDefault(families); 540 } else { 541 fontFamily.abortCreation(); 542 } 543 } 544 throw new RuntimeException("Font not found " + path); 545 } 546 547 /** 548 * Create a new typeface from an array of font families. 549 * 550 * @param families array of font families 551 * @hide 552 */ 553 public static Typeface createFromFamilies(FontFamily[] families) { 554 long[] ptrArray = new long[families.length]; 555 for (int i = 0; i < families.length; i++) { 556 ptrArray[i] = families[i].mNativePtr; 557 } 558 return new Typeface(nativeCreateFromArray(ptrArray)); 559 } 560 561 /** 562 * Create a new typeface from an array of font families, including 563 * also the font families in the fallback list. 564 * 565 * @param families array of font families 566 * @hide 567 */ 568 public static Typeface createFromFamiliesWithDefault(FontFamily[] families) { 569 long[] ptrArray = new long[families.length + sFallbackFonts.length]; 570 for (int i = 0; i < families.length; i++) { 571 ptrArray[i] = families[i].mNativePtr; 572 } 573 for (int i = 0; i < sFallbackFonts.length; i++) { 574 ptrArray[i + families.length] = sFallbackFonts[i].mNativePtr; 575 } 576 return new Typeface(nativeCreateFromArray(ptrArray)); 577 } 578 579 // don't allow clients to call this directly 580 private Typeface(long ni) { 581 if (ni == 0) { 582 throw new RuntimeException("native typeface cannot be made"); 583 } 584 585 native_instance = ni; 586 mStyle = nativeGetStyle(ni); 587 } 588 589 private static FontFamily makeFamilyFromParsed(FontConfig.Family family, 590 Map<String, ByteBuffer> bufferForPath) { 591 FontFamily fontFamily = new FontFamily(family.getLanguage(), family.getVariant()); 592 for (FontConfig.Font font : family.getFonts()) { 593 ByteBuffer fontBuffer = bufferForPath.get(font.getFontName()); 594 if (fontBuffer == null) { 595 try (FileInputStream file = new FileInputStream(font.getFontName())) { 596 FileChannel fileChannel = file.getChannel(); 597 long fontSize = fileChannel.size(); 598 fontBuffer = fileChannel.map(FileChannel.MapMode.READ_ONLY, 0, fontSize); 599 bufferForPath.put(font.getFontName(), fontBuffer); 600 } catch (IOException e) { 601 Log.e(TAG, "Error mapping font file " + font.getFontName()); 602 continue; 603 } 604 } 605 if (!fontFamily.addFontWeightStyle(fontBuffer, font.getTtcIndex(), font.getAxes(), 606 font.getWeight(), font.isItalic())) { 607 Log.e(TAG, "Error creating font " + font.getFontName() + "#" + font.getTtcIndex()); 608 } 609 } 610 fontFamily.freeze(); 611 return fontFamily; 612 } 613 614 /* 615 * (non-Javadoc) 616 * 617 * This should only be called once, from the static class initializer block. 618 */ 619 private static void init() { 620 // Load font config and initialize Minikin state 621 File systemFontConfigLocation = getSystemFontConfigLocation(); 622 File configFilename = new File(systemFontConfigLocation, FONTS_CONFIG); 623 try { 624 FileInputStream fontsIn = new FileInputStream(configFilename); 625 FontConfig fontConfig = FontListParser.parse(fontsIn); 626 627 Map<String, ByteBuffer> bufferForPath = new HashMap<String, ByteBuffer>(); 628 629 List<FontFamily> familyList = new ArrayList<FontFamily>(); 630 // Note that the default typeface is always present in the fallback list; 631 // this is an enhancement from pre-Minikin behavior. 632 for (int i = 0; i < fontConfig.getFamilies().size(); i++) { 633 FontConfig.Family f = fontConfig.getFamilies().get(i); 634 if (i == 0 || f.getName() == null) { 635 familyList.add(makeFamilyFromParsed(f, bufferForPath)); 636 } 637 } 638 sFallbackFonts = familyList.toArray(new FontFamily[familyList.size()]); 639 setDefault(Typeface.createFromFamilies(sFallbackFonts)); 640 641 Map<String, Typeface> systemFonts = new HashMap<String, Typeface>(); 642 for (int i = 0; i < fontConfig.getFamilies().size(); i++) { 643 Typeface typeface; 644 FontConfig.Family f = fontConfig.getFamilies().get(i); 645 if (f.getName() != null) { 646 if (i == 0) { 647 // The first entry is the default typeface; no sense in 648 // duplicating the corresponding FontFamily. 649 typeface = sDefaultTypeface; 650 } else { 651 FontFamily fontFamily = makeFamilyFromParsed(f, bufferForPath); 652 FontFamily[] families = { fontFamily }; 653 typeface = Typeface.createFromFamiliesWithDefault(families); 654 } 655 systemFonts.put(f.getName(), typeface); 656 } 657 } 658 for (FontConfig.Alias alias : fontConfig.getAliases()) { 659 Typeface base = systemFonts.get(alias.getToName()); 660 Typeface newFace = base; 661 int weight = alias.getWeight(); 662 if (weight != 400) { 663 newFace = new Typeface(nativeCreateWeightAlias(base.native_instance, weight)); 664 } 665 systemFonts.put(alias.getName(), newFace); 666 } 667 sSystemFontMap = systemFonts; 668 669 } catch (RuntimeException e) { 670 Log.w(TAG, "Didn't create default family (most likely, non-Minikin build)", e); 671 // TODO: normal in non-Minikin case, remove or make error when Minikin-only 672 } catch (FileNotFoundException e) { 673 Log.e(TAG, "Error opening " + configFilename, e); 674 } catch (IOException e) { 675 Log.e(TAG, "Error reading " + configFilename, e); 676 } catch (XmlPullParserException e) { 677 Log.e(TAG, "XML parse exception for " + configFilename, e); 678 } 679 } 680 681 static { 682 init(); 683 // Set up defaults and typefaces exposed in public API 684 DEFAULT = create((String) null, 0); 685 DEFAULT_BOLD = create((String) null, Typeface.BOLD); 686 SANS_SERIF = create("sans-serif", 0); 687 SERIF = create("serif", 0); 688 MONOSPACE = create("monospace", 0); 689 690 sDefaults = new Typeface[] { 691 DEFAULT, 692 DEFAULT_BOLD, 693 create((String) null, Typeface.ITALIC), 694 create((String) null, Typeface.BOLD_ITALIC), 695 }; 696 697 } 698 699 private static File getSystemFontConfigLocation() { 700 return new File("/system/etc/"); 701 } 702 703 @Override 704 protected void finalize() throws Throwable { 705 try { 706 nativeUnref(native_instance); 707 native_instance = 0; // Other finalizers can still call us. 708 } finally { 709 super.finalize(); 710 } 711 } 712 713 @Override 714 public boolean equals(Object o) { 715 if (this == o) return true; 716 if (o == null || getClass() != o.getClass()) return false; 717 718 Typeface typeface = (Typeface) o; 719 720 return mStyle == typeface.mStyle && native_instance == typeface.native_instance; 721 } 722 723 @Override 724 public int hashCode() { 725 /* 726 * Modified method for hashCode with long native_instance derived from 727 * http://developer.android.com/reference/java/lang/Object.html 728 */ 729 int result = 17; 730 result = 31 * result + (int) (native_instance ^ (native_instance >>> 32)); 731 result = 31 * result + mStyle; 732 return result; 733 } 734 735 private static native long nativeCreateFromTypeface(long native_instance, int style); 736 private static native long nativeCreateFromTypefaceWithVariation( 737 long native_instance, List<FontConfig.Axis> axes); 738 private static native long nativeCreateWeightAlias(long native_instance, int weight); 739 private static native void nativeUnref(long native_instance); 740 private static native int nativeGetStyle(long native_instance); 741 private static native long nativeCreateFromArray(long[] familyArray); 742 private static native void nativeSetDefault(long native_instance); 743} 744