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