2 * Copyright (C) 2006 The Android Open Source Project
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
17 package android.graphics;
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;
24 import static java.lang.annotation.RetentionPolicy.SOURCE;
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;
47 import com.android.internal.annotations.GuardedBy;
48 import com.android.internal.util.Preconditions;
50 import libcore.io.IoUtils;
52 import org.xmlpull.v1.XmlPullParserException;
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;
70 import java.util.concurrent.atomic.AtomicReference;
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).
78 public class Typeface {
80 private static String TAG = "Typeface";
82 /** The default NORMAL typeface object */
83 public static final Typeface DEFAULT;
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
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;
97 static Typeface[] sDefaults;
98 private static final LongSparseArray<SparseArray<Typeface>> sTypefaceCache =
99 new LongSparseArray<>(3);
102 * Cache for Typeface objects dynamically loaded from assets. Currently max size is 16.
105 private static final LruCache<String, Typeface> sDynamicTypefaceCache = new LruCache<>(16);
107 static Typeface sDefaultTypeface;
108 static Map<String, Typeface> sSystemFontMap;
109 static FontFamily[] sFallbackFonts;
110 private static final Object sLock = new Object();
112 static final String FONTS_CONFIG = "fonts.xml";
117 public long native_instance;
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;
125 private int mStyle = 0;
126 private int mWeight = 0;
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
131 public static final int RESOLVE_BY_FONT_TABLE = -1;
133 // Style value for building typeface.
134 private static final int STYLE_NORMAL = 0;
135 private static final int STYLE_ITALIC = 1;
137 private int[] mSupportedAxes;
138 private static final int[] EMPTY_AXES = {};
140 private static void setDefault(Typeface t) {
141 sDefaultTypeface = t;
142 nativeSetDefault(t.native_instance);
145 /** Returns the typeface's intrinsic style attributes */
146 public int getStyle() {
150 /** Returns true if getStyle() has the BOLD bit set. */
151 public final boolean isBold() {
152 return (mStyle & BOLD) != 0;
155 /** Returns true if getStyle() has the ITALIC bit set. */
156 public final boolean isItalic() {
157 return (mStyle & ITALIC) != 0;
162 * Used by Resources to load a font resource of type font file.
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;
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()) {
182 FontFamily[] families = {fontFamily};
183 typeface = createFromFamiliesWithDefault(families,
184 RESOLVE_BY_FONT_TABLE, RESOLVE_BY_FONT_TABLE);
185 sDynamicTypefaceCache.put(key, typeface);
195 * Used by Resources to load a font resource of type xml.
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;
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));
213 certs.add(byteArraySet);
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;
224 Typeface typeface = findFromCache(mgr, path);
225 if (typeface != null) return typeface;
227 // family is FontFamilyFilesResourceEntry
228 final FontFamilyFilesResourceEntry filesEntry =
229 (FontFamilyFilesResourceEntry) entry;
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 */)) {
240 if (!fontFamily.freeze()) {
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);
258 * Used by resources for cached loading if the font is available.
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) {
274 * A builder class for creating new Typeface instance.
278 * 1) Create Typeface from ttf file.
281 * Typeface.Builder buidler = new Typeface.Builder("your_font_file.ttf");
282 * Typeface typeface = builder.build();
286 * 2) Create Typeface from ttc file in assets directory.
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();
295 * 3) Create Typeface with variation settings.
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();
307 public static final class Builder {
309 public static final int NORMAL_WEIGHT = 400;
311 public static final int BOLD_WEIGHT = 700;
313 private int mTtcIndex;
314 private FontVariationAxis[] mAxes;
316 private AssetManager mAssetManager;
317 private String mPath;
318 private FileDescriptor mFd;
320 private FontsContract.FontInfo[] mFonts;
321 private Map<Uri, ByteBuffer> mFontBuffers;
323 private String mFallbackFamilyName;
325 private int mWeight = RESOLVE_BY_FONT_TABLE;
326 private int mItalic = RESOLVE_BY_FONT_TABLE;
329 * Constructs a builder with a file path.
331 * @param path The file object refers to the font file.
333 public Builder(@NonNull File path) {
334 mPath = path.getAbsolutePath();
338 * Constructs a builder with a file descriptor.
340 * Caller is responsible for closing the passed file descriptor after {@link #build} is
343 * @param fd The file descriptor. The passed fd must be mmap-able.
345 public Builder(@NonNull FileDescriptor fd) {
350 * Constructs a builder with a file path.
352 * @param path The full path to the font file.
354 public Builder(@NonNull String path) {
359 * Constructs a builder from an asset manager and a file path in an asset directory.
361 * @param assetManager The application's asset manager
362 * @param path The file name of the font data in the asset directory
364 public Builder(@NonNull AssetManager assetManager, @NonNull String path) {
365 mAssetManager = Preconditions.checkNotNull(assetManager);
366 mPath = Preconditions.checkStringNotEmpty(path);
370 * Constracts a builder from an array of FontsContract.FontInfo.
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.
378 * @param results The array of {@link FontsContract.FontInfo}
379 * @param buffers The mapping from URI to buffers to be used during building.
382 public Builder(@NonNull FontsContract.FontInfo[] fonts,
383 @NonNull Map<Uri, ByteBuffer> buffers) {
385 mFontBuffers = buffers;
389 * Sets weight of the font.
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.
395 public Builder setWeight(@IntRange(from = 1, to = 1000) int weight) {
401 * Sets italic information of the font.
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}.
407 public Builder setItalic(boolean italic) {
408 mItalic = italic ? STYLE_ITALIC : STYLE_NORMAL;
413 * Sets an index of the font collection.
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.
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.");
424 mTtcIndex = ttcIndex;
429 * Sets a font variation settings.
431 * @param variationSettings See {@link android.widget.TextView#setFontVariationSettings}.
432 * @throws IllegalArgumentException If given string is not a valid font variation settings
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.");
441 throw new IllegalStateException("Font variation settings are already set.");
443 mAxes = FontVariationAxis.fromFontVariationSettings(variationSettings);
448 * Sets a font variation settings.
450 * @param axes An array of font variation axis tag-value pairs.
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.");
458 throw new IllegalStateException("Font variation settings are already set.");
465 * Sets a fallback family name.
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}.
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.
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}
486 public Builder setFallback(@Nullable String familyName) {
487 mFallbackFamilyName = familyName;
492 * Creates a unique id for a given AssetManager and asset path.
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.
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));
509 builder.append(path);
511 builder.append(Integer.toString(ttcIndex));
513 builder.append(Integer.toString(weight));
515 builder.append(Integer.toString(italic));
518 for (FontVariationAxis axis : axes) {
519 builder.append(axis.getTag());
521 builder.append(Float.toString(axis.getStyleValue()));
524 return builder.toString();
527 private static final Object sLock = new Object();
528 // TODO: Unify with Typeface.sTypefaceCache.
530 private static final LongSparseArray<SparseArray<Typeface>> sTypefaceCache =
531 new LongSparseArray<>(3);
533 private Typeface resolveFallbackTypeface() {
534 if (mFallbackFamilyName == null) {
538 Typeface base = sSystemFontMap.get(mFallbackFamilyName);
540 base = sDefaultTypeface;
543 if (mWeight == RESOLVE_BY_FONT_TABLE && mItalic == RESOLVE_BY_FONT_TABLE) {
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);
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) {
562 typeface = new Typeface(
563 nativeCreateFromTypefaceWithExactStyle(
564 base.native_instance, weight, italic));
566 if (innerCache == null) {
567 innerCache = new SparseArray<>(4); // [regular, bold] x [upright, italic]
568 sTypefaceCache.put(base.native_instance, innerCache);
570 innerCache.put(key, typeface);
576 * Generates new Typeface from specified configuration.
578 * @return Newly created Typeface. May return null if some parameters are invalid.
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);
587 final FontFamily fontFamily = new FontFamily();
588 if (!fontFamily.addFontFromBuffer(buffer, mTtcIndex, mAxes, mWeight, mItalic)) {
589 fontFamily.abortCreation();
590 return resolveFallbackTypeface();
592 if (!fontFamily.freeze()) {
593 return resolveFallbackTypeface();
595 FontFamily[] families = { fontFamily };
596 return createFromFamiliesWithDefault(families, mWeight, mItalic);
597 } catch (IOException e) {
598 return resolveFallbackTypeface();
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();
612 if (!fontFamily.freeze()) {
613 return resolveFallbackTypeface();
615 FontFamily[] families = { fontFamily };
616 typeface = createFromFamiliesWithDefault(families, mWeight, mItalic);
617 sDynamicTypefaceCache.put(key, typeface);
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();
626 if (!fontFamily.freeze()) {
627 return resolveFallbackTypeface();
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) {
639 final boolean success = fontFamily.addFontFromBuffer(fontBuffer,
640 font.getTtcIndex(), font.getAxes(), font.getWeight(),
641 font.isItalic() ? STYLE_ITALIC : STYLE_NORMAL);
643 fontFamily.abortCreation();
646 atLeastOneFont = true;
648 if (!atLeastOneFont) {
649 // No fonts are avaialble. No need to create new Typeface and returns fallback
651 fontFamily.abortCreation();
655 FontFamily[] families = { fontFamily };
656 return createFromFamiliesWithDefault(families, mWeight, mItalic);
659 // Must not reach here.
660 throw new IllegalArgumentException("No source was set.");
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.
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.
675 public static Typeface create(String familyName, int style) {
676 if (sSystemFontMap != null) {
677 return create(sSystemFontMap.get(familyName), style);
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.
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.
693 public static Typeface create(Typeface family, int style) {
694 if (style < 0 || style > 3) {
698 if (family != null) {
699 // Return early if we're asked for the same face/style
700 if (family.mStyle == style) {
704 ni = family.native_instance;
708 SparseArray<Typeface> styles = sTypefaceCache.get(ni);
710 if (styles != null) {
711 typeface = styles.get(style);
712 if (typeface != null) {
717 typeface = new Typeface(nativeCreateFromTypeface(ni, style));
718 if (styles == null) {
719 styles = new SparseArray<Typeface>(4);
720 sTypefaceCache.put(ni, styles);
722 styles.put(style, typeface);
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));
735 * Returns one of the default typeface objects, based on the specified style
737 * @return the default typeface that corresponds to the style
739 public static Typeface defaultFromStyle(int style) {
740 return sDefaults[style];
744 * Create a new typeface from the specified font data.
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.
750 public static Typeface createFromAsset(AssetManager mgr, String path) {
752 throw new NullPointerException(); // for backward compatibility
754 if (sFallbackFonts != null) {
755 synchronized (sLock) {
756 Typeface typeface = new Builder(mgr, path).build();
757 if (typeface != null) return typeface;
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;
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,
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
772 fontFamily.allowUnsupportedFont();
774 final FontFamily[] families = { fontFamily };
775 typeface = createFromFamiliesWithDefault(families, RESOLVE_BY_FONT_TABLE,
776 RESOLVE_BY_FONT_TABLE);
777 sDynamicTypefaceCache.put(key, typeface);
780 fontFamily.abortCreation();
784 throw new RuntimeException("Font asset not found " + path);
788 * Creates a unique id for a given font provider and query.
790 private static String createProviderUid(String authority, String query) {
791 final StringBuilder builder = new StringBuilder();
792 builder.append("provider:");
793 builder.append(authority);
795 builder.append(query);
796 return builder.toString();
800 * Create a new typeface from the specified font file.
802 * @param path The path to the font data.
803 * @return The new typeface.
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());
812 * Create a new typeface from the specified font file.
814 * @param path The full path to the font data.
815 * @return The new typeface.
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
826 fontFamily.allowUnsupportedFont();
828 FontFamily[] families = { fontFamily };
829 return createFromFamiliesWithDefault(families, RESOLVE_BY_FONT_TABLE,
830 RESOLVE_BY_FONT_TABLE);
832 fontFamily.abortCreation();
835 throw new RuntimeException("Font not found " + path);
839 * Create a new typeface from an array of font families.
841 * @param families array of font families
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;
848 return new Typeface(nativeCreateFromArray(
849 ptrArray, RESOLVE_BY_FONT_TABLE, RESOLVE_BY_FONT_TABLE));
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
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
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;
871 for (int i = 0; i < sFallbackFonts.length; i++) {
872 ptrArray[i + families.length] = sFallbackFonts[i].mNativePtr;
874 return new Typeface(nativeCreateFromArray(ptrArray, weight, italic));
877 // don't allow clients to call this directly
878 private Typeface(long ni) {
880 throw new RuntimeException("native typeface cannot be made");
883 native_instance = ni;
884 mStyle = nativeGetStyle(ni);
885 mWeight = nativeGetWeight(ni);
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);
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());
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());
922 * This should only be called once, from the static class initializer block.
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);
929 FileInputStream fontsIn = new FileInputStream(configFilename);
930 FontConfig fontConfig = FontListParser.parse(fontsIn);
932 Map<String, ByteBuffer> bufferForPath = new HashMap<String, ByteBuffer>();
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);
946 sFallbackFonts = familyList.toArray(new FontFamily[familyList.size()]);
947 setDefault(Typeface.createFromFamilies(sFallbackFonts));
949 Map<String, Typeface> systemFonts = new HashMap<String, Typeface>();
950 for (int i = 0; i < fontConfig.getFamilies().length; i++) {
952 FontConfig.Family f = fontConfig.getFamilies()[i];
953 if (f.getName() != null) {
955 // The first entry is the default typeface; no sense in
956 // duplicating the corresponding FontFamily.
957 typeface = sDefaultTypeface;
959 FontFamily fontFamily = makeFamilyFromParsed(f, bufferForPath);
960 if (fontFamily == null) {
963 FontFamily[] families = { fontFamily };
964 typeface = Typeface.createFromFamiliesWithDefault(families,
965 RESOLVE_BY_FONT_TABLE, RESOLVE_BY_FONT_TABLE);
967 systemFonts.put(f.getName(), typeface);
970 for (FontConfig.Alias alias : fontConfig.getAliases()) {
971 Typeface base = systemFonts.get(alias.getToName());
972 Typeface newFace = base;
973 int weight = alias.getWeight();
975 newFace = new Typeface(nativeCreateWeightAlias(base.native_instance, weight));
977 systemFonts.put(alias.getName(), newFace);
979 sSystemFontMap = systemFonts;
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);
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);
1002 sDefaults = new Typeface[] {
1005 create((String) null, Typeface.ITALIC),
1006 create((String) null, Typeface.BOLD_ITALIC),
1011 private static File getSystemFontConfigLocation() {
1012 return new File("/system/etc/");
1016 protected void finalize() throws Throwable {
1018 nativeUnref(native_instance);
1019 native_instance = 0; // Other finalizers can still call us.
1026 public boolean equals(Object o) {
1027 if (this == o) return true;
1028 if (o == null || getClass() != o.getClass()) return false;
1030 Typeface typeface = (Typeface) o;
1032 return mStyle == typeface.mStyle && native_instance == typeface.native_instance;
1036 public int hashCode() {
1038 * Modified method for hashCode with long native_instance derived from
1039 * http://developer.android.com/reference/java/lang/Object.html
1042 result = 31 * result + (int) (native_instance ^ (native_instance >>> 32));
1043 result = 31 * result + mStyle;
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;
1059 return Arrays.binarySearch(mSupportedAxes, axis) > 0;
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);