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