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.Context;
31 import android.content.res.AssetManager;
32 import android.graphics.FontListParser;
33 import android.graphics.fonts.FontRequest;
34 import android.graphics.fonts.FontResult;
35 import android.graphics.fonts.FontVariationAxis;
36 import android.graphics.fonts.FontVariationAxis.InvalidFormatException;
37 import android.net.Uri;
38 import android.os.Bundle;
39 import android.os.Handler;
40 import android.os.ParcelFileDescriptor;
41 import android.os.ResultReceiver;
42 import android.provider.FontsContract;
43 import android.text.FontConfig;
44 import android.util.Base64;
45 import android.util.Log;
46 import android.util.LongSparseArray;
47 import android.util.LruCache;
48 import android.util.SparseArray;
50 import com.android.internal.annotations.GuardedBy;
51 import com.android.internal.util.Preconditions;
53 import libcore.io.IoUtils;
55 import org.xmlpull.v1.XmlPullParserException;
58 import java.io.FileDescriptor;
59 import java.io.FileInputStream;
60 import java.io.FileNotFoundException;
61 import java.io.IOException;
62 import java.lang.annotation.Retention;
63 import java.lang.annotation.RetentionPolicy;
64 import java.nio.ByteBuffer;
65 import java.nio.channels.FileChannel;
66 import java.util.Arrays;
67 import java.util.ArrayList;
68 import java.util.Arrays;
69 import java.util.Collections;
70 import java.util.HashMap;
71 import java.util.List;
73 import java.util.concurrent.atomic.AtomicReference;
76 * The Typeface class specifies the typeface and intrinsic style of a font.
77 * This is used in the paint, along with optionally Paint settings like
78 * textSize, textSkewX, textScaleX to specify
79 * how text appears when drawn (and measured).
81 public class Typeface {
83 private static String TAG = "Typeface";
85 /** The default NORMAL typeface object */
86 public static final Typeface DEFAULT;
88 * The default BOLD typeface object. Note: this may be not actually be
89 * bold, depending on what fonts are installed. Call getStyle() to know
92 public static final Typeface DEFAULT_BOLD;
93 /** The NORMAL style of the default sans serif typeface. */
94 public static final Typeface SANS_SERIF;
95 /** The NORMAL style of the default serif typeface. */
96 public static final Typeface SERIF;
97 /** The NORMAL style of the default monospace typeface. */
98 public static final Typeface MONOSPACE;
100 static Typeface[] sDefaults;
101 private static final LongSparseArray<SparseArray<Typeface>> sTypefaceCache =
102 new LongSparseArray<>(3);
104 private static FontsContract sFontsContract;
106 private static Handler sHandler;
109 * Cache for Typeface objects dynamically loaded from assets. Currently max size is 16.
112 private static final LruCache<String, Typeface> sDynamicTypefaceCache = new LruCache<>(16);
114 static Typeface sDefaultTypeface;
115 static Map<String, Typeface> sSystemFontMap;
116 static FontFamily[] sFallbackFonts;
117 private static final Object sLock = new Object();
119 static final String FONTS_CONFIG = "fonts.xml";
124 public long native_instance;
127 public static final int NORMAL = 0;
128 public static final int BOLD = 1;
129 public static final int ITALIC = 2;
130 public static final int BOLD_ITALIC = 3;
132 private int mStyle = 0;
133 private int mBaseWeight = 0;
135 // Value for weight and italic. Indicates the value is resolved by font metadata.
136 // Must be the same as the C++ constant in core/jni/android/graphics/FontFamily.cpp
138 public static final int RESOLVE_BY_FONT_TABLE = -1;
140 // Style value for building typeface.
141 private static final int STYLE_NORMAL = 0;
142 private static final int STYLE_ITALIC = 1;
144 private int[] mSupportedAxes;
145 private static final int[] EMPTY_AXES = {};
147 private static void setDefault(Typeface t) {
148 sDefaultTypeface = t;
149 nativeSetDefault(t.native_instance);
152 /** Returns the typeface's intrinsic style attributes */
153 public int getStyle() {
157 /** Returns true if getStyle() has the BOLD bit set. */
158 public final boolean isBold() {
159 return (mStyle & BOLD) != 0;
162 /** Returns true if getStyle() has the ITALIC bit set. */
163 public final boolean isItalic() {
164 return (mStyle & ITALIC) != 0;
169 * Used by Resources to load a font resource of type font file.
172 public static Typeface createFromResources(AssetManager mgr, String path, int cookie) {
173 if (sFallbackFonts != null) {
174 synchronized (sDynamicTypefaceCache) {
175 final String key = Builder.createAssetUid(
176 mgr, path, 0 /* ttcIndex */, null /* axes */);
177 Typeface typeface = sDynamicTypefaceCache.get(key);
178 if (typeface != null) return typeface;
180 FontFamily fontFamily = new FontFamily();
181 // TODO: introduce ttc index and variation settings to resource type font.
182 if (fontFamily.addFontFromAssetManager(mgr, path, cookie, false /* isAsset */,
183 0 /* ttcIndex */, RESOLVE_BY_FONT_TABLE /* weight */,
184 RESOLVE_BY_FONT_TABLE /* italic */, null /* axes */)) {
185 if (!fontFamily.freeze()) {
188 FontFamily[] families = {fontFamily};
189 typeface = createFromFamiliesWithDefault(families);
190 sDynamicTypefaceCache.put(key, typeface);
200 * Used by Resources to load a font resource of type xml.
203 public static Typeface createFromResources(
204 FamilyResourceEntry entry, AssetManager mgr, String path) {
205 if (sFallbackFonts != null) {
206 Typeface typeface = findFromCache(mgr, path);
207 if (typeface != null) return typeface;
209 if (entry instanceof ProviderResourceEntry) {
210 final ProviderResourceEntry providerEntry = (ProviderResourceEntry) entry;
212 typeface = findFromCache(providerEntry.getAuthority(), providerEntry.getQuery());
213 if (typeface != null) {
216 List<List<String>> givenCerts = providerEntry.getCerts();
217 List<List<byte[]>> certs = new ArrayList<>();
218 if (givenCerts != null) {
219 for (int i = 0; i < givenCerts.size(); i++) {
220 List<String> certSet = givenCerts.get(i);
221 List<byte[]> byteArraySet = new ArrayList<>();
222 for (int j = 0; j < certSet.size(); j++) {
223 byteArraySet.add(Base64.decode(certSet.get(j), Base64.DEFAULT));
225 certs.add(byteArraySet);
228 // Downloaded font and it wasn't cached, request it again and return a
229 // default font instead (nothing we can do now).
230 create(new FontRequest(providerEntry.getAuthority(), providerEntry.getPackage(),
231 providerEntry.getQuery(), certs), NO_OP_REQUEST_CALLBACK);
235 // family is FontFamilyFilesResourceEntry
236 final FontFamilyFilesResourceEntry filesEntry =
237 (FontFamilyFilesResourceEntry) entry;
239 FontFamily fontFamily = new FontFamily();
240 for (final FontFileResourceEntry fontFile : filesEntry.getEntries()) {
241 if (!fontFamily.addFontFromAssetManager(mgr, fontFile.getFileName(),
242 0 /* resourceCookie */, false /* isAsset */, 0 /* ttcIndex */,
243 fontFile.getWeight(), fontFile.isItalic() ? STYLE_ITALIC : STYLE_NORMAL,
248 // Due to backward compatibility, even if the font is not supported by our font stack,
249 // we need to place the empty font at the first place. The typeface with empty font
250 // behaves different from default typeface especially in fallback font selection.
251 fontFamily.allowUnsupportedFont();
253 FontFamily[] familyChain = { fontFamily };
254 typeface = createFromFamiliesWithDefault(familyChain);
255 synchronized (sDynamicTypefaceCache) {
256 final String key = Builder.createAssetUid(mgr, path, 0 /* ttcIndex */,
258 sDynamicTypefaceCache.put(key, typeface);
266 * Used by resources for cached loading if the font is available.
269 public static Typeface findFromCache(AssetManager mgr, String path) {
270 synchronized (sDynamicTypefaceCache) {
271 final String key = Builder.createAssetUid(mgr, path, 0 /* ttcIndex */, null /* axes */);
272 Typeface typeface = sDynamicTypefaceCache.get(key);
273 if (typeface != null) {
281 * Set the application context so we can generate font requests from the provider. This should
282 * be called from ActivityThread when the application binds, as we preload fonts.
285 public static void setApplicationContext(Context context) {
286 synchronized (sLock) {
287 if (sFontsContract == null) {
288 sFontsContract = new FontsContract(context);
289 sHandler = new Handler();
295 * Create a typeface object given a font request. The font will be asynchronously fetched,
296 * therefore the result is delivered to the given callback. See {@link FontRequest}.
297 * Only one of the methods in callback will be invoked, depending on whether the request
298 * succeeds or fails. These calls will happen on the main thread.
299 * @param request A {@link FontRequest} object that identifies the provider and query for the
300 * request. May not be null.
301 * @param callback A callback that will be triggered when results are obtained. May not be null.
304 public static void create(@NonNull FontRequest request, @NonNull FontRequestCallback callback) {
305 // Check the cache first
306 // TODO: would the developer want to avoid a cache hit and always ask for the freshest
308 Typeface cachedTypeface = findFromCache(
309 request.getProviderAuthority(), request.getQuery());
310 if (cachedTypeface != null) {
311 sHandler.post(() -> callback.onTypefaceRetrieved(cachedTypeface));
314 synchronized (sLock) {
315 if (sFontsContract == null) {
316 throw new RuntimeException("Context not initialized, can't query provider");
318 final ResultReceiver receiver = new ResultReceiver(null) {
320 public void onReceiveResult(int resultCode, Bundle resultData) {
321 sHandler.post(() -> receiveResult(request, callback, resultCode, resultData));
324 sFontsContract.getFont(request, receiver);
328 private static Typeface findFromCache(String providerAuthority, String query) {
329 synchronized (sDynamicTypefaceCache) {
330 final String key = createProviderUid(providerAuthority, query);
331 Typeface typeface = sDynamicTypefaceCache.get(key);
332 if (typeface != null) {
339 private static void receiveResult(FontRequest request, FontRequestCallback callback,
340 int resultCode, Bundle resultData) {
341 Typeface cachedTypeface = findFromCache(
342 request.getProviderAuthority(), request.getQuery());
343 if (cachedTypeface != null) {
344 // We already know the result.
345 // Probably the requester requests the same font again in a short interval.
346 callback.onTypefaceRetrieved(cachedTypeface);
349 if (resultCode != FontsContract.Columns.RESULT_CODE_OK) {
350 callback.onTypefaceRequestFailed(resultCode);
353 if (resultData == null) {
354 callback.onTypefaceRequestFailed(
355 FontRequestCallback.FAIL_REASON_FONT_NOT_FOUND);
358 List<FontResult> resultList =
359 resultData.getParcelableArrayList(FontsContract.PARCEL_FONT_RESULTS);
360 if (resultList == null || resultList.isEmpty()) {
361 callback.onTypefaceRequestFailed(
362 FontRequestCallback.FAIL_REASON_FONT_NOT_FOUND);
365 FontFamily fontFamily = new FontFamily();
366 for (int i = 0; i < resultList.size(); ++i) {
367 FontResult result = resultList.get(i);
368 ParcelFileDescriptor fd = result.getFileDescriptor();
370 callback.onTypefaceRequestFailed(
371 FontRequestCallback.FAIL_REASON_FONT_LOAD_ERROR);
374 try (FileInputStream is = new FileInputStream(fd.getFileDescriptor())) {
375 FileChannel fileChannel = is.getChannel();
376 long fontSize = fileChannel.size();
377 ByteBuffer fontBuffer = fileChannel.map(
378 FileChannel.MapMode.READ_ONLY, 0, fontSize);
379 int weight = result.getWeight();
380 int italic = result.getItalic() ? STYLE_ITALIC : STYLE_NORMAL;
381 FontVariationAxis[] axes = null;
383 axes = FontVariationAxis.fromFontVariationSettings(
384 result.getFontVariationSettings());
385 } catch (FontVariationAxis.InvalidFormatException e) {
386 // TODO: Nice to pass FontVariationAxis[] directly instead of string.
388 if (!fontFamily.addFontFromBuffer(fontBuffer, result.getTtcIndex(),
389 axes, weight, italic)) {
390 Log.e(TAG, "Error creating font " + request.getQuery());
391 callback.onTypefaceRequestFailed(
392 FontRequestCallback.FAIL_REASON_FONT_LOAD_ERROR);
395 } catch (IOException e) {
396 Log.e(TAG, "Error reading font " + request.getQuery(), e);
397 callback.onTypefaceRequestFailed(
398 FontRequestCallback.FAIL_REASON_FONT_LOAD_ERROR);
401 IoUtils.closeQuietly(fd);
404 if (!fontFamily.freeze()) {
405 callback.onTypefaceRequestFailed(
406 FontRequestCallback.FAIL_REASON_FONT_LOAD_ERROR);
409 Typeface typeface = Typeface.createFromFamiliesWithDefault(new FontFamily[] { fontFamily });
410 synchronized (sDynamicTypefaceCache) {
411 String key = createProviderUid(request.getProviderAuthority(), request.getQuery());
412 sDynamicTypefaceCache.put(key, typeface);
414 callback.onTypefaceRetrieved(typeface);
418 * Interface used to receive asynchronously fetched typefaces.
421 public interface FontRequestCallback {
423 * Constant returned by {@link #onTypefaceRequestFailed(int)} signaling that the given
424 * provider was not found on the device.
426 int FAIL_REASON_PROVIDER_NOT_FOUND = FontsContract.RESULT_CODE_PROVIDER_NOT_FOUND;
428 * Constant returned by {@link #onTypefaceRequestFailed(int)} signaling that the given
429 * provider must be authenticated and the given certificates do not match its signature.
431 int FAIL_REASON_WRONG_CERTIFICATES = FontsContract.RESULT_CODE_WRONG_CERTIFICATES;
433 * Constant returned by {@link #onTypefaceRequestFailed(int)} signaling that the font
434 * returned by the provider was not loaded properly.
436 int FAIL_REASON_FONT_LOAD_ERROR = -3;
438 * Constant returned by {@link #onTypefaceRequestFailed(int)} signaling that the font
439 * provider did not return any results for the given query.
441 int FAIL_REASON_FONT_NOT_FOUND = FontsContract.Columns.RESULT_CODE_FONT_NOT_FOUND;
443 * Constant returned by {@link #onTypefaceRequestFailed(int)} signaling that the font
444 * provider found the queried font, but it is currently unavailable.
446 int FAIL_REASON_FONT_UNAVAILABLE = FontsContract.Columns.RESULT_CODE_FONT_UNAVAILABLE;
448 * Constant returned by {@link #onTypefaceRequestFailed(int)} signaling that the given
449 * query was not supported by the provider.
451 int FAIL_REASON_MALFORMED_QUERY = FontsContract.Columns.RESULT_CODE_MALFORMED_QUERY;
454 @IntDef({ FAIL_REASON_PROVIDER_NOT_FOUND, FAIL_REASON_FONT_LOAD_ERROR,
455 FAIL_REASON_FONT_NOT_FOUND, FAIL_REASON_FONT_UNAVAILABLE,
456 FAIL_REASON_MALFORMED_QUERY })
457 @Retention(RetentionPolicy.SOURCE)
458 @interface FontRequestFailReason {}
461 * Called then a Typeface request done via {@link Typeface#create(FontRequest,
462 * FontRequestCallback)} is complete. Note that this method will not be called if
463 * {@link #onTypefaceRequestFailed(int)} is called instead.
464 * @param typeface The Typeface object retrieved.
466 void onTypefaceRetrieved(Typeface typeface);
469 * Called when a Typeface request done via {@link Typeface#create(FontRequest,
470 * FontRequestCallback)} fails.
471 * @param reason One of {@link #FAIL_REASON_PROVIDER_NOT_FOUND},
472 * {@link #FAIL_REASON_FONT_NOT_FOUND},
473 * {@link #FAIL_REASON_FONT_LOAD_ERROR},
474 * {@link #FAIL_REASON_FONT_UNAVAILABLE} or
475 * {@link #FAIL_REASON_MALFORMED_QUERY}.
477 void onTypefaceRequestFailed(@FontRequestFailReason int reason);
480 private static final FontRequestCallback NO_OP_REQUEST_CALLBACK = new FontRequestCallback() {
482 public void onTypefaceRetrieved(Typeface typeface) {
487 public void onTypefaceRequestFailed(@FontRequestFailReason int reason) {
493 * A builder class for creating new Typeface instance.
497 * 1) Create Typeface from ttf file.
500 * Typeface.Builder buidler = new Typeface.Builder("your_font_file.ttf");
501 * Typeface typeface = builder.build();
505 * 2) Create Typeface from ttc file in assets directory.
508 * Typeface.Builder buidler = new Typeface.Builder(getAssets(), "your_font_file.ttc");
509 * builder.setTtcIndex(2); // Set index of font collection.
510 * Typeface typeface = builder.build();
514 * 3) Create Typeface with variation settings.
517 * Typeface.Builder buidler = new Typeface.Builder("your_font_file.ttf");
518 * builder.setFontVariationSettings("'wght' 700, 'slnt' 20, 'ital' 1");
519 * builder.setWeight(700); // Tell the system that this is a bold font.
520 * builder.setItalic(true); // Tell the system that this is an italic style font.
521 * Typeface typeface = builder.build();
526 public static final class Builder {
528 public static final int NORMAL_WEIGHT = 400;
530 public static final int BOLD_WEIGHT = 700;
532 private int mTtcIndex;
533 private FontVariationAxis[] mAxes;
535 private AssetManager mAssetManager;
536 private String mPath;
537 private FileDescriptor mFd;
539 private FontsContract.FontInfo[] mFonts;
540 private Map<Uri, ByteBuffer> mFontBuffers;
542 private String mFallbackFamilyName;
544 private int mWeight = RESOLVE_BY_FONT_TABLE;
545 private int mItalic = RESOLVE_BY_FONT_TABLE;
548 * Constructs a builder with a file path.
550 * @param path The file object refers to the font file.
552 public Builder(@NonNull File path) {
553 mPath = path.getAbsolutePath();
557 * Constructs a builder with a file descriptor.
559 * @param fd The file descriptor. The passed fd must be mmap-able.
561 public Builder(@NonNull FileDescriptor fd) {
566 * Constructs a builder with a file path.
568 * @param path The full path to the font file.
570 public Builder(@NonNull String path) {
575 * Constructs a builder from an asset manager and a file path in an asset directory.
577 * @param assetManager The application's asset manager
578 * @param path The file name of the font data in the asset directory
580 public Builder(@NonNull AssetManager assetManager, @NonNull String path) {
581 mAssetManager = Preconditions.checkNotNull(assetManager);
582 mPath = Preconditions.checkStringNotEmpty(path);
586 * Constracts a builder from an array of FontsContract.FontInfo.
588 * Since {@link FontsContract.FontInfo} holds information about TTC indices and
589 * variation settings, there is no need to call {@link #setTtcIndex} or
590 * {@link #setFontVariationSettings}. Similary, {@link FontsContract.FontInfo} holds
591 * weight and italic information, so {@link #setWeight} and {@link #setItalic} are used
592 * for style matching during font selection.
594 * @param results The array of {@link FontsContract.FontInfo}
595 * @param buffers The mapping from URI to buffers to be used during building.
598 public Builder(@NonNull FontsContract.FontInfo[] fonts,
599 @NonNull Map<Uri, ByteBuffer> buffers) {
601 mFontBuffers = buffers;
605 * Sets weight of the font.
607 * Tells the system the weight of the given font. If not provided, the system will resolve
608 * the weight value by reading font tables.
609 * @param weight a weight value.
611 public Builder setWeight(@IntRange(from = 1, to = 1000) int weight) {
617 * Sets italic information of the font.
619 * Tells the system the style of the given font. If not provided, the system will resolve
620 * the style by reading font tables.
621 * @param italic {@code true} if the font is italic. Otherwise {@code false}.
623 public Builder setItalic(boolean italic) {
624 mItalic = italic ? STYLE_ITALIC : STYLE_NORMAL;
629 * Sets an index of the font collection.
631 * Can not be used for Typeface source. build() method will return null for invalid index.
632 * @param ttcIndex An index of the font collection. If the font source is not font
633 * collection, do not call this method or specify 0.
635 public Builder setTtcIndex(@IntRange(from = 0) int ttcIndex) {
636 if (mFonts != null) {
637 throw new IllegalArgumentException(
638 "TTC index can not be specified for FontResult source.");
640 mTtcIndex = ttcIndex;
645 * Sets a font variation settings.
647 * @param variationSettings See {@link android.widget.TextView#setFontVariationSettings}.
648 * @throws InvalidFormatException If given string is not a valid font variation settings
651 public Builder setFontVariationSettings(@Nullable String variationSettings)
652 throws InvalidFormatException {
653 if (mFonts != null) {
654 throw new IllegalArgumentException(
655 "Font variation settings can not be specified for FontResult source.");
658 throw new IllegalStateException("Font variation settings are already set.");
660 mAxes = FontVariationAxis.fromFontVariationSettings(variationSettings);
665 * Sets a font variation settings.
667 * @param axes An array of font variation axis tag-value pairs.
669 public Builder setFontVariationSettings(@Nullable FontVariationAxis[] axes) {
670 if (mFonts != null) {
671 throw new IllegalArgumentException(
672 "Font variation settings can not be specified for FontResult source.");
675 throw new IllegalStateException("Font variation settings are already set.");
682 * Sets a fallback family name.
684 * By specifying a fallback family name, a fallback Typeface will be returned if the
685 * {@link #build} method fails to create a Typeface from the provided font. The fallback
686 * family will be resolved with the provided weight and italic information specified by
687 * {@link #setWeight} and {@link #setItalic}.
689 * If {@link #setWeight} is not called, the fallback family keeps the default weight.
690 * Similary, if {@link #setItalic} is not called, the fallback family keeps the default
691 * italic information. For example, calling {@code builder.setFallback("sans-serif-light")}
692 * is equivalent to calling {@code builder.setFallback("sans-serif").setWeight(300)} in
693 * terms of fallback. The default weight and italic information are overridden by calling
694 * {@link #setWeight} and {@link #setItalic}. For example, if a Typeface is constructed
695 * using {@code builder.setFallback("sans-serif-light").setWeight(700)}, the fallback text
696 * will render as sans serif bold.
698 * @param familyName A family name to be used for fallback if the provided font can not be
699 * used. By passing {@code null}, build() returns {@code null}.
700 * If {@link #setFallback} is not called on the builder, {@code null}
703 public Builder setFallback(@Nullable String familyName) {
704 mFallbackFamilyName = familyName;
709 * Creates a unique id for a given AssetManager and asset path.
711 * @param mgr AssetManager instance
712 * @param path The path for the asset.
713 * @param ttcIndex The TTC index for the font.
714 * @param axes The font variation settings.
715 * @return Unique id for a given AssetManager and asset path.
717 private static String createAssetUid(final AssetManager mgr, String path, int ttcIndex,
718 @Nullable FontVariationAxis[] axes) {
719 final SparseArray<String> pkgs = mgr.getAssignedPackageIdentifiers();
720 final StringBuilder builder = new StringBuilder();
721 final int size = pkgs.size();
722 for (int i = 0; i < size; i++) {
723 builder.append(pkgs.valueAt(i));
726 builder.append(path);
728 builder.append(Integer.toString(ttcIndex));
731 for (FontVariationAxis axis : axes) {
732 builder.append(axis.getTag());
734 builder.append(Float.toString(axis.getStyleValue()));
737 return builder.toString();
740 private static final Object sLock = new Object();
741 // TODO: Unify with Typeface.sTypefaceCache.
743 private static final LongSparseArray<SparseArray<Typeface>> sTypefaceCache =
744 new LongSparseArray<>(3);
746 private Typeface resolveFallbackTypeface() {
747 if (mFallbackFamilyName == null) {
751 Typeface base = sSystemFontMap.get(mFallbackFamilyName);
753 base = sDefaultTypeface;
756 if (mWeight == RESOLVE_BY_FONT_TABLE && mItalic == RESOLVE_BY_FONT_TABLE) {
760 final int weight = (mWeight == RESOLVE_BY_FONT_TABLE) ? base.mBaseWeight : mWeight;
761 final boolean italic =
762 (mItalic == RESOLVE_BY_FONT_TABLE) ? (base.mStyle & ITALIC) != 0 : mItalic == 1;
763 final int key = weight << 1 | (italic ? 1 : 0);
766 synchronized(sLock) {
767 SparseArray<Typeface> innerCache = sTypefaceCache.get(base.native_instance);
768 if (innerCache != null) {
769 typeface = innerCache.get(key);
770 if (typeface != null) {
775 typeface = new Typeface(
776 nativeCreateFromTypefaceWithExactStyle(
777 base.native_instance, weight, italic));
779 if (innerCache == null) {
780 innerCache = new SparseArray<>(4); // [regular, bold] x [upright, italic]
781 sTypefaceCache.put(base.native_instance, innerCache);
783 innerCache.put(key, typeface);
789 * Generates new Typeface from specified configuration.
791 * @return Newly created Typeface. May return null if some parameters are invalid.
793 public Typeface build() {
794 if (mFd != null) { // set source by setSourceFromFile(FileDescriptor)
795 try (FileInputStream fis = new FileInputStream(mFd)) {
796 FileChannel channel = fis.getChannel();
797 long size = channel.size();
798 ByteBuffer buffer = channel.map(FileChannel.MapMode.READ_ONLY, 0, size);
800 final FontFamily fontFamily = new FontFamily();
801 if (!fontFamily.addFontFromBuffer(buffer, mTtcIndex, mAxes, mWeight, mItalic)) {
802 fontFamily.abortCreation();
803 return resolveFallbackTypeface();
805 if (!fontFamily.freeze()) {
806 return resolveFallbackTypeface();
808 FontFamily[] families = { fontFamily };
809 return createFromFamiliesWithDefault(families);
810 } catch (IOException e) {
811 return resolveFallbackTypeface();
813 } else if (mAssetManager != null) { // set source by setSourceFromAsset()
814 final String key = createAssetUid(mAssetManager, mPath, mTtcIndex, mAxes);
815 synchronized (sLock) {
816 Typeface typeface = sDynamicTypefaceCache.get(key);
817 if (typeface != null) return typeface;
818 final FontFamily fontFamily = new FontFamily();
819 if (!fontFamily.addFontFromAssetManager(mAssetManager, mPath, mTtcIndex,
820 true /* isAsset */, mTtcIndex, mWeight, mItalic, mAxes)) {
821 fontFamily.abortCreation();
822 return resolveFallbackTypeface();
824 if (!fontFamily.freeze()) {
825 return resolveFallbackTypeface();
827 FontFamily[] families = { fontFamily };
828 typeface = createFromFamiliesWithDefault(families);
829 sDynamicTypefaceCache.put(key, typeface);
832 } else if (mPath != null) { // set source by setSourceFromFile(File)
833 final FontFamily fontFamily = new FontFamily();
834 if (!fontFamily.addFont(mPath, mTtcIndex, mAxes, mWeight, mItalic)) {
835 fontFamily.abortCreation();
836 return resolveFallbackTypeface();
838 if (!fontFamily.freeze()) {
839 return resolveFallbackTypeface();
841 FontFamily[] families = { fontFamily };
842 return createFromFamiliesWithDefault(families);
843 } else if (mFonts != null) {
844 final FontFamily fontFamily = new FontFamily();
845 boolean atLeastOneFont = false;
846 for (FontsContract.FontInfo font : mFonts) {
847 final ByteBuffer fontBuffer = mFontBuffers.get(font.getUri());
848 if (fontBuffer == null) {
851 final boolean success = fontFamily.addFontFromBuffer(fontBuffer,
852 font.getTtcIndex(), font.getAxes(), font.getWeight(),
853 font.isItalic() ? STYLE_ITALIC : STYLE_NORMAL);
855 fontFamily.abortCreation();
858 atLeastOneFont = true;
860 if (!atLeastOneFont) {
861 // No fonts are avaialble. No need to create new Typeface and returns fallback
863 fontFamily.abortCreation();
867 FontFamily[] families = { fontFamily };
868 return createFromFamiliesWithDefault(families);
871 // Must not reach here.
872 throw new IllegalArgumentException("No source was set.");
877 * Create a typeface object given a family name, and option style information.
878 * If null is passed for the name, then the "default" font will be chosen.
879 * The resulting typeface object can be queried (getStyle()) to discover what
880 * its "real" style characteristics are.
882 * @param familyName May be null. The name of the font family.
883 * @param style The style (normal, bold, italic) of the typeface.
884 * e.g. NORMAL, BOLD, ITALIC, BOLD_ITALIC
885 * @return The best matching typeface.
887 public static Typeface create(String familyName, int style) {
888 if (sSystemFontMap != null) {
889 return create(sSystemFontMap.get(familyName), style);
895 * Create a typeface object that best matches the specified existing
896 * typeface and the specified Style. Use this call if you want to pick a new
897 * style from the same family of an existing typeface object. If family is
898 * null, this selects from the default font's family.
900 * @param family May be null. The name of the existing type face.
901 * @param style The style (normal, bold, italic) of the typeface.
902 * e.g. NORMAL, BOLD, ITALIC, BOLD_ITALIC
903 * @return The best matching typeface.
905 public static Typeface create(Typeface family, int style) {
906 if (style < 0 || style > 3) {
910 if (family != null) {
911 // Return early if we're asked for the same face/style
912 if (family.mStyle == style) {
916 ni = family.native_instance;
920 SparseArray<Typeface> styles = sTypefaceCache.get(ni);
922 if (styles != null) {
923 typeface = styles.get(style);
924 if (typeface != null) {
929 typeface = new Typeface(nativeCreateFromTypeface(ni, style));
930 if (styles == null) {
931 styles = new SparseArray<Typeface>(4);
932 sTypefaceCache.put(ni, styles);
934 styles.put(style, typeface);
940 public static Typeface createFromTypefaceWithVariation(Typeface family,
941 List<FontVariationAxis> axes) {
942 final long ni = family == null ? 0 : family.native_instance;
943 return new Typeface(nativeCreateFromTypefaceWithVariation(ni, axes));
947 * Returns one of the default typeface objects, based on the specified style
949 * @return the default typeface that corresponds to the style
951 public static Typeface defaultFromStyle(int style) {
952 return sDefaults[style];
956 * Create a new typeface from the specified font data.
958 * @param mgr The application's asset manager
959 * @param path The file name of the font data in the assets directory
960 * @return The new typeface.
962 public static Typeface createFromAsset(AssetManager mgr, String path) {
964 throw new NullPointerException(); // for backward compatibility
966 if (sFallbackFonts != null) {
967 synchronized (sLock) {
968 Typeface typeface = new Builder(mgr, path).build();
969 if (typeface != null) return typeface;
971 final String key = Builder.createAssetUid(mgr, path, 0 /* ttcIndex */,
973 typeface = sDynamicTypefaceCache.get(key);
974 if (typeface != null) return typeface;
976 final FontFamily fontFamily = new FontFamily();
977 if (fontFamily.addFontFromAssetManager(mgr, path, 0, true /* isAsset */,
978 0 /* ttc index */, RESOLVE_BY_FONT_TABLE, RESOLVE_BY_FONT_TABLE,
980 // Due to backward compatibility, even if the font is not supported by our font
981 // stack, we need to place the empty font at the first place. The typeface with
982 // empty font behaves different from default typeface especially in fallback
984 fontFamily.allowUnsupportedFont();
986 final FontFamily[] families = { fontFamily };
987 typeface = createFromFamiliesWithDefault(families);
988 sDynamicTypefaceCache.put(key, typeface);
991 fontFamily.abortCreation();
995 throw new RuntimeException("Font asset not found " + path);
999 * Creates a unique id for a given font provider and query.
1001 private static String createProviderUid(String authority, String query) {
1002 final StringBuilder builder = new StringBuilder();
1003 builder.append("provider:");
1004 builder.append(authority);
1005 builder.append("-");
1006 builder.append(query);
1007 return builder.toString();
1011 * Create a new typeface from the specified font file.
1013 * @param path The path to the font data.
1014 * @return The new typeface.
1016 public static Typeface createFromFile(@Nullable File path) {
1017 // For the compatibility reasons, leaving possible NPE here.
1018 // See android.graphics.cts.TypefaceTest#testCreateFromFileByFileReferenceNull
1019 return createFromFile(path.getAbsolutePath());
1023 * Create a new typeface from the specified font file.
1025 * @param path The full path to the font data.
1026 * @return The new typeface.
1028 public static Typeface createFromFile(@Nullable String path) {
1029 if (sFallbackFonts != null) {
1030 final FontFamily fontFamily = new FontFamily();
1031 if (fontFamily.addFont(path, 0 /* ttcIndex */, null /* axes */,
1032 RESOLVE_BY_FONT_TABLE, RESOLVE_BY_FONT_TABLE)) {
1033 // Due to backward compatibility, even if the font is not supported by our font
1034 // stack, we need to place the empty font at the first place. The typeface with
1035 // empty font behaves different from default typeface especially in fallback font
1037 fontFamily.allowUnsupportedFont();
1038 fontFamily.freeze();
1039 FontFamily[] families = { fontFamily };
1040 return createFromFamiliesWithDefault(families);
1042 fontFamily.abortCreation();
1045 throw new RuntimeException("Font not found " + path);
1049 * Create a new typeface from an array of font families.
1051 * @param families array of font families
1053 private static Typeface createFromFamilies(FontFamily[] families) {
1054 long[] ptrArray = new long[families.length];
1055 for (int i = 0; i < families.length; i++) {
1056 ptrArray[i] = families[i].mNativePtr;
1058 return new Typeface(nativeCreateFromArray(ptrArray));
1062 * Create a new typeface from an array of font families, including
1063 * also the font families in the fallback list.
1065 * @param families array of font families
1067 private static Typeface createFromFamiliesWithDefault(FontFamily[] families) {
1068 long[] ptrArray = new long[families.length + sFallbackFonts.length];
1069 for (int i = 0; i < families.length; i++) {
1070 ptrArray[i] = families[i].mNativePtr;
1072 for (int i = 0; i < sFallbackFonts.length; i++) {
1073 ptrArray[i + families.length] = sFallbackFonts[i].mNativePtr;
1075 return new Typeface(nativeCreateFromArray(ptrArray));
1078 // don't allow clients to call this directly
1079 private Typeface(long ni) {
1081 throw new RuntimeException("native typeface cannot be made");
1084 native_instance = ni;
1085 mStyle = nativeGetStyle(ni);
1086 mBaseWeight = nativeGetBaseWeight(ni);
1089 private static FontFamily makeFamilyFromParsed(FontConfig.Family family,
1090 Map<String, ByteBuffer> bufferForPath) {
1091 FontFamily fontFamily = new FontFamily(family.getLanguage(), family.getVariant());
1092 for (FontConfig.Font font : family.getFonts()) {
1093 String fullPathName = "/system/fonts/" + font.getFontName();
1094 ByteBuffer fontBuffer = bufferForPath.get(fullPathName);
1095 if (fontBuffer == null) {
1096 try (FileInputStream file = new FileInputStream(fullPathName)) {
1097 FileChannel fileChannel = file.getChannel();
1098 long fontSize = fileChannel.size();
1099 fontBuffer = fileChannel.map(FileChannel.MapMode.READ_ONLY, 0, fontSize);
1100 bufferForPath.put(fullPathName, fontBuffer);
1101 } catch (IOException e) {
1102 Log.e(TAG, "Error mapping font file " + fullPathName);
1106 if (!fontFamily.addFontFromBuffer(fontBuffer, font.getTtcIndex(), font.getAxes(),
1107 font.getWeight(), font.isItalic() ? STYLE_ITALIC : STYLE_NORMAL)) {
1108 Log.e(TAG, "Error creating font " + fullPathName + "#" + font.getTtcIndex());
1111 if (!fontFamily.freeze()) {
1112 // Treat as system error since reaching here means that a system pre-installed font
1113 // can't be used by our font stack.
1114 Log.e(TAG, "Unable to load Family: " + family.getName() + ":" + family.getLanguage());
1123 * This should only be called once, from the static class initializer block.
1125 private static void init() {
1126 // Load font config and initialize Minikin state
1127 File systemFontConfigLocation = getSystemFontConfigLocation();
1128 File configFilename = new File(systemFontConfigLocation, FONTS_CONFIG);
1130 FileInputStream fontsIn = new FileInputStream(configFilename);
1131 FontConfig fontConfig = FontListParser.parse(fontsIn);
1133 Map<String, ByteBuffer> bufferForPath = new HashMap<String, ByteBuffer>();
1135 List<FontFamily> familyList = new ArrayList<FontFamily>();
1136 // Note that the default typeface is always present in the fallback list;
1137 // this is an enhancement from pre-Minikin behavior.
1138 for (int i = 0; i < fontConfig.getFamilies().length; i++) {
1139 FontConfig.Family f = fontConfig.getFamilies()[i];
1140 if (i == 0 || f.getName() == null) {
1141 FontFamily family = makeFamilyFromParsed(f, bufferForPath);
1142 if (family != null) {
1143 familyList.add(family);
1147 sFallbackFonts = familyList.toArray(new FontFamily[familyList.size()]);
1148 setDefault(Typeface.createFromFamilies(sFallbackFonts));
1150 Map<String, Typeface> systemFonts = new HashMap<String, Typeface>();
1151 for (int i = 0; i < fontConfig.getFamilies().length; i++) {
1153 FontConfig.Family f = fontConfig.getFamilies()[i];
1154 if (f.getName() != null) {
1156 // The first entry is the default typeface; no sense in
1157 // duplicating the corresponding FontFamily.
1158 typeface = sDefaultTypeface;
1160 FontFamily fontFamily = makeFamilyFromParsed(f, bufferForPath);
1161 if (fontFamily == null) {
1164 FontFamily[] families = { fontFamily };
1165 typeface = Typeface.createFromFamiliesWithDefault(families);
1167 systemFonts.put(f.getName(), typeface);
1170 for (FontConfig.Alias alias : fontConfig.getAliases()) {
1171 Typeface base = systemFonts.get(alias.getToName());
1172 Typeface newFace = base;
1173 int weight = alias.getWeight();
1174 if (weight != 400) {
1175 newFace = new Typeface(nativeCreateWeightAlias(base.native_instance, weight));
1177 systemFonts.put(alias.getName(), newFace);
1179 sSystemFontMap = systemFonts;
1181 } catch (RuntimeException e) {
1182 Log.w(TAG, "Didn't create default family (most likely, non-Minikin build)", e);
1183 // TODO: normal in non-Minikin case, remove or make error when Minikin-only
1184 } catch (FileNotFoundException e) {
1185 Log.e(TAG, "Error opening " + configFilename, e);
1186 } catch (IOException e) {
1187 Log.e(TAG, "Error reading " + configFilename, e);
1188 } catch (XmlPullParserException e) {
1189 Log.e(TAG, "XML parse exception for " + configFilename, e);
1195 // Set up defaults and typefaces exposed in public API
1196 DEFAULT = create((String) null, 0);
1197 DEFAULT_BOLD = create((String) null, Typeface.BOLD);
1198 SANS_SERIF = create("sans-serif", 0);
1199 SERIF = create("serif", 0);
1200 MONOSPACE = create("monospace", 0);
1202 sDefaults = new Typeface[] {
1205 create((String) null, Typeface.ITALIC),
1206 create((String) null, Typeface.BOLD_ITALIC),
1211 private static File getSystemFontConfigLocation() {
1212 return new File("/system/etc/");
1216 protected void finalize() throws Throwable {
1218 nativeUnref(native_instance);
1219 native_instance = 0; // Other finalizers can still call us.
1226 public boolean equals(Object o) {
1227 if (this == o) return true;
1228 if (o == null || getClass() != o.getClass()) return false;
1230 Typeface typeface = (Typeface) o;
1232 return mStyle == typeface.mStyle && native_instance == typeface.native_instance;
1236 public int hashCode() {
1238 * Modified method for hashCode with long native_instance derived from
1239 * http://developer.android.com/reference/java/lang/Object.html
1242 result = 31 * result + (int) (native_instance ^ (native_instance >>> 32));
1243 result = 31 * result + mStyle;
1248 public boolean isSupportedAxes(int axis) {
1249 if (mSupportedAxes == null) {
1250 synchronized (this) {
1251 if (mSupportedAxes == null) {
1252 mSupportedAxes = nativeGetSupportedAxes(native_instance);
1253 if (mSupportedAxes == null) {
1254 mSupportedAxes = EMPTY_AXES;
1259 return Arrays.binarySearch(mSupportedAxes, axis) > 0;
1262 private static native long nativeCreateFromTypeface(long native_instance, int style);
1263 private static native long nativeCreateFromTypefaceWithExactStyle(
1264 long native_instance, int weight, boolean italic);
1265 // TODO: clean up: change List<FontVariationAxis> to FontVariationAxis[]
1266 private static native long nativeCreateFromTypefaceWithVariation(
1267 long native_instance, List<FontVariationAxis> axes);
1268 private static native long nativeCreateWeightAlias(long native_instance, int weight);
1269 private static native void nativeUnref(long native_instance);
1270 private static native int nativeGetStyle(long native_instance);
1271 private static native int nativeGetBaseWeight(long native_instance);
1272 private static native long nativeCreateFromArray(long[] familyArray);
1273 private static native void nativeSetDefault(long native_instance);
1274 private static native int[] nativeGetSupportedAxes(long native_instance);