Home | History | Annotate | Download | only in graphics
      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 
    165     private static void setDefault(Typeface t) {
    166         sDefaultTypeface = t;
    167         nativeSetDefault(t.native_instance);
    168     }
    169 
    170     /** Returns the typeface's weight value */
    171     public @IntRange(from = 0, to = 1000) int getWeight() {
    172         return mWeight;
    173     }
    174 
    175     /** Returns the typeface's intrinsic style attributes */
    176     public @Style int getStyle() {
    177         return mStyle;
    178     }
    179 
    180     /** Returns true if getStyle() has the BOLD bit set. */
    181     public final boolean isBold() {
    182         return (mStyle & BOLD) != 0;
    183     }
    184 
    185     /** Returns true if getStyle() has the ITALIC bit set. */
    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
    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
    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      */
    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          */
    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          */
    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          */
    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          */
    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          */
    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          */
    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          */
    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          */
    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          */
    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          */
    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          */
    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          */
    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 
    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          */
    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      */
    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      */
    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      */
    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 
    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 */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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
    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 
    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 
    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 
    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
   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 
   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
   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
   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 */
   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 
   1194     private static native long nativeCreateFromTypeface(long native_instance, int style);
   1195     private static native long nativeCreateFromTypefaceWithExactStyle(
   1196             long native_instance, int weight, boolean italic);
   1197     // TODO: clean up: change List<FontVariationAxis> to FontVariationAxis[]
   1198     private static native long nativeCreateFromTypefaceWithVariation(
   1199             long native_instance, List<FontVariationAxis> axes);
   1200     private static native long nativeCreateWeightAlias(long native_instance, int weight);
   1201     private static native long nativeCreateFromArray(long[] familyArray, int weight, int italic);
   1202     private static native int[] nativeGetSupportedAxes(long native_instance);
   1203 
   1204     @CriticalNative
   1205     private static native void nativeSetDefault(long nativePtr);
   1206 
   1207     @CriticalNative
   1208     private static native int  nativeGetStyle(long nativePtr);
   1209 
   1210     @CriticalNative
   1211     private static native int  nativeGetWeight(long nativePtr);
   1212 
   1213     @CriticalNative
   1214     private static native long nativeGetReleaseFunc();
   1215 }
   1216