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