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