OSDN Git Service

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