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