1baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Gupta/*
2baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Gupta * Copyright (C) 2014 The Android Open Source Project
3baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Gupta *
4baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Gupta * Licensed under the Apache License, Version 2.0 (the "License");
5baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Gupta * you may not use this file except in compliance with the License.
6baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Gupta * You may obtain a copy of the License at
7baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Gupta *
8baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Gupta *      http://www.apache.org/licenses/LICENSE-2.0
9baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Gupta *
10baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Gupta * Unless required by applicable law or agreed to in writing, software
11baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Gupta * distributed under the License is distributed on an "AS IS" BASIS,
12baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Gupta * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Gupta * See the License for the specific language governing permissions and
14baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Gupta * limitations under the License.
15baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Gupta */
16baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Gupta
17baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Guptapackage android.graphics;
18baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Gupta
19d51834bdfad0cd3142ac464ab3a3b0db14f884c3Deepanshu Guptaimport com.android.ide.common.rendering.api.AssetRepository;
20baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Guptaimport com.android.ide.common.rendering.api.LayoutLog;
21baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Guptaimport com.android.layoutlib.bridge.Bridge;
22baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Guptaimport com.android.layoutlib.bridge.impl.DelegateManager;
23baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Guptaimport com.android.tools.layoutlib.annotations.LayoutlibDelegate;
24baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Gupta
25476e582d2ffdf25102d4c55f8c242baa3d21d37fDeepanshu Guptaimport android.annotation.NonNull;
26476e582d2ffdf25102d4c55f8c242baa3d21d37fDeepanshu Guptaimport android.annotation.Nullable;
27ad69aee5ed503d9592c10a0f6d1a5b617d99e6c5Deepanshu Guptaimport android.content.res.AssetManager;
28d51834bdfad0cd3142ac464ab3a3b0db14f884c3Deepanshu Guptaimport android.content.res.BridgeAssetManager;
29ad69aee5ed503d9592c10a0f6d1a5b617d99e6c5Deepanshu Gupta
30baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Guptaimport java.awt.Font;
31b0d767dcf17dd393646b8fc3e8c9f46dfe12e230Deepanshu Guptaimport java.awt.FontFormatException;
32baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Guptaimport java.io.File;
330e4be2540984235a0a7b84ea0466ef3c92d27b07Deepanshu Guptaimport java.io.FileNotFoundException;
34d51834bdfad0cd3142ac464ab3a3b0db14f884c3Deepanshu Guptaimport java.io.IOException;
35d51834bdfad0cd3142ac464ab3a3b0db14f884c3Deepanshu Guptaimport java.io.InputStream;
36baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Guptaimport java.util.ArrayList;
37e644ff8d92ba040d11636be0fb6c433b52bcc6c2Deepanshu Guptaimport java.util.Collections;
38e644ff8d92ba040d11636be0fb6c433b52bcc6c2Deepanshu Guptaimport java.util.HashSet;
39d51834bdfad0cd3142ac464ab3a3b0db14f884c3Deepanshu Guptaimport java.util.LinkedHashMap;
40baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Guptaimport java.util.List;
41d51834bdfad0cd3142ac464ab3a3b0db14f884c3Deepanshu Guptaimport java.util.Map;
42d51834bdfad0cd3142ac464ab3a3b0db14f884c3Deepanshu Guptaimport java.util.Map.Entry;
430e4be2540984235a0a7b84ea0466ef3c92d27b07Deepanshu Guptaimport java.util.Scanner;
44e644ff8d92ba040d11636be0fb6c433b52bcc6c2Deepanshu Guptaimport java.util.Set;
45baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Gupta
46baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Guptaimport static android.graphics.Typeface_Delegate.SYSTEM_FONTS;
47baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Gupta
48baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Gupta/**
49baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Gupta * Delegate implementing the native methods of android.graphics.FontFamily
50baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Gupta *
51baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Gupta * Through the layoutlib_create tool, the original native methods of FontFamily have been replaced
52baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Gupta * by calls to methods of the same name in this delegate class.
53baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Gupta *
54baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Gupta * This class behaves like the original native implementation, but in Java, keeping previously
55baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Gupta * native data into its own objects and mapping them to int that are sent back and forth between
56baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Gupta * it and the original FontFamily class.
57baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Gupta *
58baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Gupta * @see DelegateManager
59baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Gupta */
60baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Guptapublic class FontFamily_Delegate {
61baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Gupta
62145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta    public static final int DEFAULT_FONT_WEIGHT = 400;
63145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta    public static final int BOLD_FONT_WEIGHT_DELTA = 300;
64145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta    public static final int BOLD_FONT_WEIGHT = 700;
65145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta
66baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Gupta    private static final String FONT_SUFFIX_ITALIC = "Italic.ttf";
670e4be2540984235a0a7b84ea0466ef3c92d27b07Deepanshu Gupta    private static final String FN_ALL_FONTS_LIST = "fontsInSdk.txt";
68d51834bdfad0cd3142ac464ab3a3b0db14f884c3Deepanshu Gupta    private static final String EXTENSION_OTF = ".otf";
69d51834bdfad0cd3142ac464ab3a3b0db14f884c3Deepanshu Gupta
70d51834bdfad0cd3142ac464ab3a3b0db14f884c3Deepanshu Gupta    private static final int CACHE_SIZE = 10;
71d51834bdfad0cd3142ac464ab3a3b0db14f884c3Deepanshu Gupta    // The cache has a drawback that if the font file changed after the font object was created,
72d51834bdfad0cd3142ac464ab3a3b0db14f884c3Deepanshu Gupta    // we will not update it.
73d51834bdfad0cd3142ac464ab3a3b0db14f884c3Deepanshu Gupta    private static final Map<String, FontInfo> sCache =
74d51834bdfad0cd3142ac464ab3a3b0db14f884c3Deepanshu Gupta            new LinkedHashMap<String, FontInfo>(CACHE_SIZE) {
75d51834bdfad0cd3142ac464ab3a3b0db14f884c3Deepanshu Gupta        @Override
76d51834bdfad0cd3142ac464ab3a3b0db14f884c3Deepanshu Gupta        protected boolean removeEldestEntry(Entry<String, FontInfo> eldest) {
77d51834bdfad0cd3142ac464ab3a3b0db14f884c3Deepanshu Gupta            return size() > CACHE_SIZE;
78d51834bdfad0cd3142ac464ab3a3b0db14f884c3Deepanshu Gupta        }
79d51834bdfad0cd3142ac464ab3a3b0db14f884c3Deepanshu Gupta
80d51834bdfad0cd3142ac464ab3a3b0db14f884c3Deepanshu Gupta        @Override
81d51834bdfad0cd3142ac464ab3a3b0db14f884c3Deepanshu Gupta        public FontInfo put(String key, FontInfo value) {
82d51834bdfad0cd3142ac464ab3a3b0db14f884c3Deepanshu Gupta            // renew this entry.
83d51834bdfad0cd3142ac464ab3a3b0db14f884c3Deepanshu Gupta            FontInfo removed = remove(key);
84d51834bdfad0cd3142ac464ab3a3b0db14f884c3Deepanshu Gupta            super.put(key, value);
85d51834bdfad0cd3142ac464ab3a3b0db14f884c3Deepanshu Gupta            return removed;
86d51834bdfad0cd3142ac464ab3a3b0db14f884c3Deepanshu Gupta        }
87d51834bdfad0cd3142ac464ab3a3b0db14f884c3Deepanshu Gupta    };
88e644ff8d92ba040d11636be0fb6c433b52bcc6c2Deepanshu Gupta
89baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Gupta    /**
90baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Gupta     * A class associating {@link Font} with its metadata.
91baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Gupta     */
92baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Gupta    private static final class FontInfo {
93145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta        @Nullable
94baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Gupta        Font mFont;
95145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta        int mWeight;
96145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta        boolean mIsItalic;
97baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Gupta    }
98baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Gupta
99baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Gupta    // ---- delegate manager ----
100baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Gupta    private static final DelegateManager<FontFamily_Delegate> sManager =
101baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Gupta            new DelegateManager<FontFamily_Delegate>(FontFamily_Delegate.class);
102baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Gupta
103baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Gupta    // ---- delegate helper data ----
104baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Gupta    private static String sFontLocation;
105baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Gupta    private static final List<FontFamily_Delegate> sPostInitDelegate = new
106baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Gupta            ArrayList<FontFamily_Delegate>();
1070e4be2540984235a0a7b84ea0466ef3c92d27b07Deepanshu Gupta    private static Set<String> SDK_FONTS;
108baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Gupta
109baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Gupta
110baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Gupta    // ---- delegate data ----
111baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Gupta    private List<FontInfo> mFonts = new ArrayList<FontInfo>();
112145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta
113ad69aee5ed503d9592c10a0f6d1a5b617d99e6c5Deepanshu Gupta    /**
114ad69aee5ed503d9592c10a0f6d1a5b617d99e6c5Deepanshu Gupta     * The variant of the Font Family - compact or elegant.
115145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta     * <p/>
116ad69aee5ed503d9592c10a0f6d1a5b617d99e6c5Deepanshu Gupta     * 0 is unspecified, 1 is compact and 2 is elegant. This needs to be kept in sync with values in
117ad69aee5ed503d9592c10a0f6d1a5b617d99e6c5Deepanshu Gupta     * android.graphics.FontFamily
118ad69aee5ed503d9592c10a0f6d1a5b617d99e6c5Deepanshu Gupta     *
119ad69aee5ed503d9592c10a0f6d1a5b617d99e6c5Deepanshu Gupta     * @see Paint#setElegantTextHeight(boolean)
120ad69aee5ed503d9592c10a0f6d1a5b617d99e6c5Deepanshu Gupta     */
121ad69aee5ed503d9592c10a0f6d1a5b617d99e6c5Deepanshu Gupta    private FontVariant mVariant;
122145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta    // List of runnables to process fonts after sFontLoader is initialized.
123145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta    private List<Runnable> mPostInitRunnables = new ArrayList<Runnable>();
124e644ff8d92ba040d11636be0fb6c433b52bcc6c2Deepanshu Gupta    /** @see #isValid() */
125e644ff8d92ba040d11636be0fb6c433b52bcc6c2Deepanshu Gupta    private boolean mValid = false;
126baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Gupta
127baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Gupta
1283c937cf5c730519e750cdee4d5fa61e2a593e33aDeepanshu Gupta    // ---- Public helper class ----
1293c937cf5c730519e750cdee4d5fa61e2a593e33aDeepanshu Gupta
1303c937cf5c730519e750cdee4d5fa61e2a593e33aDeepanshu Gupta    public enum FontVariant {
1313c937cf5c730519e750cdee4d5fa61e2a593e33aDeepanshu Gupta        // The order needs to be kept in sync with android.graphics.FontFamily.
1323c937cf5c730519e750cdee4d5fa61e2a593e33aDeepanshu Gupta        NONE, COMPACT, ELEGANT
1333c937cf5c730519e750cdee4d5fa61e2a593e33aDeepanshu Gupta    }
1343c937cf5c730519e750cdee4d5fa61e2a593e33aDeepanshu Gupta
135baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Gupta    // ---- Public Helper methods ----
136baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Gupta
137baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Gupta    public static FontFamily_Delegate getDelegate(long nativeFontFamily) {
138baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Gupta        return sManager.getDelegate(nativeFontFamily);
139baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Gupta    }
140baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Gupta
141baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Gupta    public static synchronized void setFontLocation(String fontLocation) {
142baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Gupta        sFontLocation = fontLocation;
1430e4be2540984235a0a7b84ea0466ef3c92d27b07Deepanshu Gupta        // init list of bundled fonts.
1440e4be2540984235a0a7b84ea0466ef3c92d27b07Deepanshu Gupta        File allFonts = new File(fontLocation, FN_ALL_FONTS_LIST);
1450e4be2540984235a0a7b84ea0466ef3c92d27b07Deepanshu Gupta        // Current number of fonts is 103. Use the next round number to leave scope for more fonts
1460e4be2540984235a0a7b84ea0466ef3c92d27b07Deepanshu Gupta        // in the future.
1470e4be2540984235a0a7b84ea0466ef3c92d27b07Deepanshu Gupta        Set<String> allFontsList = new HashSet<String>(128);
1480e4be2540984235a0a7b84ea0466ef3c92d27b07Deepanshu Gupta        Scanner scanner = null;
1490e4be2540984235a0a7b84ea0466ef3c92d27b07Deepanshu Gupta        try {
1500e4be2540984235a0a7b84ea0466ef3c92d27b07Deepanshu Gupta            scanner = new Scanner(allFonts);
1510e4be2540984235a0a7b84ea0466ef3c92d27b07Deepanshu Gupta            while (scanner.hasNext()) {
1520e4be2540984235a0a7b84ea0466ef3c92d27b07Deepanshu Gupta                String name = scanner.next();
1530e4be2540984235a0a7b84ea0466ef3c92d27b07Deepanshu Gupta                // Skip font configuration files.
1540e4be2540984235a0a7b84ea0466ef3c92d27b07Deepanshu Gupta                if (!name.endsWith(".xml")) {
1550e4be2540984235a0a7b84ea0466ef3c92d27b07Deepanshu Gupta                    allFontsList.add(name);
1560e4be2540984235a0a7b84ea0466ef3c92d27b07Deepanshu Gupta                }
1570e4be2540984235a0a7b84ea0466ef3c92d27b07Deepanshu Gupta            }
1580e4be2540984235a0a7b84ea0466ef3c92d27b07Deepanshu Gupta        } catch (FileNotFoundException e) {
1590e4be2540984235a0a7b84ea0466ef3c92d27b07Deepanshu Gupta            Bridge.getLog().error(LayoutLog.TAG_BROKEN,
1600e4be2540984235a0a7b84ea0466ef3c92d27b07Deepanshu Gupta                    "Unable to load the list of fonts. Try re-installing the SDK Platform from the SDK Manager.",
1610e4be2540984235a0a7b84ea0466ef3c92d27b07Deepanshu Gupta                    e, null);
1620e4be2540984235a0a7b84ea0466ef3c92d27b07Deepanshu Gupta        } finally {
1630e4be2540984235a0a7b84ea0466ef3c92d27b07Deepanshu Gupta            if (scanner != null) {
1640e4be2540984235a0a7b84ea0466ef3c92d27b07Deepanshu Gupta                scanner.close();
1650e4be2540984235a0a7b84ea0466ef3c92d27b07Deepanshu Gupta            }
1660e4be2540984235a0a7b84ea0466ef3c92d27b07Deepanshu Gupta        }
1670e4be2540984235a0a7b84ea0466ef3c92d27b07Deepanshu Gupta        SDK_FONTS = Collections.unmodifiableSet(allFontsList);
168baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Gupta        for (FontFamily_Delegate fontFamily : sPostInitDelegate) {
169baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Gupta            fontFamily.init();
170baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Gupta        }
171baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Gupta        sPostInitDelegate.clear();
172baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Gupta    }
173baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Gupta
174145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta    @Nullable
175145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta    public Font getFont(int desiredWeight, boolean isItalic) {
176145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta        FontInfo desiredStyle = new FontInfo();
177145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta        desiredStyle.mWeight = desiredWeight;
178145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta        desiredStyle.mIsItalic = isItalic;
179145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta        FontInfo bestFont = null;
180145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta        int bestMatch = Integer.MAX_VALUE;
181baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Gupta        for (FontInfo font : mFonts) {
182145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta            int match = computeMatch(font, desiredStyle);
183145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta            if (match < bestMatch) {
184145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta                bestMatch = match;
185145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta                bestFont = font;
186baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Gupta            }
187baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Gupta        }
188145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta        if (bestFont == null) {
189145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta            return null;
190baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Gupta        }
191145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta        if (bestMatch == 0) {
192145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta            return bestFont.mFont;
193145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta        }
194145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta        // Derive the font as required and add it to the list of Fonts.
195145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta        deriveFont(bestFont, desiredStyle);
196145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta        addFont(desiredStyle);
197145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta        return desiredStyle.mFont;
198baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Gupta    }
199baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Gupta
200ad69aee5ed503d9592c10a0f6d1a5b617d99e6c5Deepanshu Gupta    public FontVariant getVariant() {
201ad69aee5ed503d9592c10a0f6d1a5b617d99e6c5Deepanshu Gupta        return mVariant;
202ad69aee5ed503d9592c10a0f6d1a5b617d99e6c5Deepanshu Gupta    }
203ad69aee5ed503d9592c10a0f6d1a5b617d99e6c5Deepanshu Gupta
204e644ff8d92ba040d11636be0fb6c433b52bcc6c2Deepanshu Gupta    /**
205e644ff8d92ba040d11636be0fb6c433b52bcc6c2Deepanshu Gupta     * Returns if the FontFamily should contain any fonts. If this returns true and
206145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta     * {@link #getFont(int, boolean)} returns an empty list, it means that an error occurred while
207145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta     * loading the fonts. However, some fonts are deliberately skipped, for example they are not
208145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta     * bundled with the SDK. In such a case, this method returns false.
209e644ff8d92ba040d11636be0fb6c433b52bcc6c2Deepanshu Gupta     */
210e644ff8d92ba040d11636be0fb6c433b52bcc6c2Deepanshu Gupta    public boolean isValid() {
211e644ff8d92ba040d11636be0fb6c433b52bcc6c2Deepanshu Gupta        return mValid;
212e644ff8d92ba040d11636be0fb6c433b52bcc6c2Deepanshu Gupta    }
213e644ff8d92ba040d11636be0fb6c433b52bcc6c2Deepanshu Gupta
2143c937cf5c730519e750cdee4d5fa61e2a593e33aDeepanshu Gupta    /*package*/ static Font loadFont(String path) {
2153c937cf5c730519e750cdee4d5fa61e2a593e33aDeepanshu Gupta        if (path.startsWith(SYSTEM_FONTS) ) {
2163c937cf5c730519e750cdee4d5fa61e2a593e33aDeepanshu Gupta            String relativePath = path.substring(SYSTEM_FONTS.length());
2173c937cf5c730519e750cdee4d5fa61e2a593e33aDeepanshu Gupta            File f = new File(sFontLocation, relativePath);
2183c937cf5c730519e750cdee4d5fa61e2a593e33aDeepanshu Gupta
2193c937cf5c730519e750cdee4d5fa61e2a593e33aDeepanshu Gupta            try {
2203c937cf5c730519e750cdee4d5fa61e2a593e33aDeepanshu Gupta                return Font.createFont(Font.TRUETYPE_FONT, f);
2213c937cf5c730519e750cdee4d5fa61e2a593e33aDeepanshu Gupta            } catch (Exception e) {
222d51834bdfad0cd3142ac464ab3a3b0db14f884c3Deepanshu Gupta                if (path.endsWith(EXTENSION_OTF) && e instanceof FontFormatException) {
223b0d767dcf17dd393646b8fc3e8c9f46dfe12e230Deepanshu Gupta                    // If we aren't able to load an Open Type font, don't log a warning just yet.
224b0d767dcf17dd393646b8fc3e8c9f46dfe12e230Deepanshu Gupta                    // We wait for a case where font is being used. Only then we try to log the
225b0d767dcf17dd393646b8fc3e8c9f46dfe12e230Deepanshu Gupta                    // warning.
226b0d767dcf17dd393646b8fc3e8c9f46dfe12e230Deepanshu Gupta                    return null;
227b0d767dcf17dd393646b8fc3e8c9f46dfe12e230Deepanshu Gupta                }
2283c937cf5c730519e750cdee4d5fa61e2a593e33aDeepanshu Gupta                Bridge.getLog().fidelityWarning(LayoutLog.TAG_BROKEN,
2293c937cf5c730519e750cdee4d5fa61e2a593e33aDeepanshu Gupta                        String.format("Unable to load font %1$s", relativePath),
230b0d767dcf17dd393646b8fc3e8c9f46dfe12e230Deepanshu Gupta                        e, null);
2313c937cf5c730519e750cdee4d5fa61e2a593e33aDeepanshu Gupta            }
2323c937cf5c730519e750cdee4d5fa61e2a593e33aDeepanshu Gupta        } else {
2333c937cf5c730519e750cdee4d5fa61e2a593e33aDeepanshu Gupta            Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED,
2343c937cf5c730519e750cdee4d5fa61e2a593e33aDeepanshu Gupta                    "Only platform fonts located in " + SYSTEM_FONTS + "can be loaded.",
235b0d767dcf17dd393646b8fc3e8c9f46dfe12e230Deepanshu Gupta                    null, null);
2363c937cf5c730519e750cdee4d5fa61e2a593e33aDeepanshu Gupta        }
2373c937cf5c730519e750cdee4d5fa61e2a593e33aDeepanshu Gupta
2383c937cf5c730519e750cdee4d5fa61e2a593e33aDeepanshu Gupta        return null;
2393c937cf5c730519e750cdee4d5fa61e2a593e33aDeepanshu Gupta    }
2403c937cf5c730519e750cdee4d5fa61e2a593e33aDeepanshu Gupta
241b69ec7708b61535355d278ac0ea8f9a61540d1fbDeepanshu Gupta    @Nullable
242b69ec7708b61535355d278ac0ea8f9a61540d1fbDeepanshu Gupta    /*package*/ static String getFontLocation() {
243b69ec7708b61535355d278ac0ea8f9a61540d1fbDeepanshu Gupta        return sFontLocation;
244b69ec7708b61535355d278ac0ea8f9a61540d1fbDeepanshu Gupta    }
245ad69aee5ed503d9592c10a0f6d1a5b617d99e6c5Deepanshu Gupta
246baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Gupta    // ---- native methods ----
247baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Gupta
248baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Gupta    @LayoutlibDelegate
249ad69aee5ed503d9592c10a0f6d1a5b617d99e6c5Deepanshu Gupta    /*package*/ static long nCreateFamily(String lang, int variant) {
250ad69aee5ed503d9592c10a0f6d1a5b617d99e6c5Deepanshu Gupta        // TODO: support lang. This is required for japanese locale.
251baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Gupta        FontFamily_Delegate delegate = new FontFamily_Delegate();
252ad69aee5ed503d9592c10a0f6d1a5b617d99e6c5Deepanshu Gupta        // variant can be 0, 1 or 2.
253ad69aee5ed503d9592c10a0f6d1a5b617d99e6c5Deepanshu Gupta        assert variant < 3;
254ad69aee5ed503d9592c10a0f6d1a5b617d99e6c5Deepanshu Gupta        delegate.mVariant = FontVariant.values()[variant];
255baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Gupta        if (sFontLocation != null) {
256baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Gupta            delegate.init();
257baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Gupta        } else {
258baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Gupta            sPostInitDelegate.add(delegate);
259baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Gupta        }
260baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Gupta        return sManager.addNewDelegate(delegate);
261baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Gupta    }
262baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Gupta
263baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Gupta    @LayoutlibDelegate
264baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Gupta    /*package*/ static void nUnrefFamily(long nativePtr) {
2659113968f9570b0c8ada2dec34fa6cf893da7c022Deepanshu Gupta        // Removing the java reference for the object doesn't mean that it's freed for garbage
2669113968f9570b0c8ada2dec34fa6cf893da7c022Deepanshu Gupta        // collection. Typeface_Delegate may still hold a reference for it.
267baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Gupta        sManager.removeJavaReferenceFor(nativePtr);
268baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Gupta    }
269baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Gupta
270baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Gupta    @LayoutlibDelegate
271145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta    /*package*/ static boolean nAddFont(long nativeFamily, final String path) {
272145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta        final FontFamily_Delegate delegate = getDelegate(nativeFamily);
273baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Gupta        if (delegate != null) {
274baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Gupta            if (sFontLocation == null) {
275145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta                delegate.mPostInitRunnables.add(new Runnable() {
276145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta                    @Override
277145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta                    public void run() {
278145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta                        delegate.addFont(path);
279145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta                    }
280145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta                });
281baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Gupta                return true;
282baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Gupta            }
283baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Gupta            return delegate.addFont(path);
284baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Gupta        }
285baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Gupta        return false;
286baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Gupta    }
287baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Gupta
288ad69aee5ed503d9592c10a0f6d1a5b617d99e6c5Deepanshu Gupta    @LayoutlibDelegate
289145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta    /*package*/ static boolean nAddFontWeightStyle(long nativeFamily, final String path,
290145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta            final int weight, final boolean isItalic) {
291145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta        final FontFamily_Delegate delegate = getDelegate(nativeFamily);
292145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta        if (delegate != null) {
293145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta            if (sFontLocation == null) {
294145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta                delegate.mPostInitRunnables.add(new Runnable() {
295145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta                    @Override
296145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta                    public void run() {
297145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta                        delegate.addFont(path, weight, isItalic);
298145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta                    }
299145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta                });
300145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta                return true;
301145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta            }
302145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta            return delegate.addFont(path, weight, isItalic);
303145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta        }
304145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta        return false;
305145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta    }
306145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta
307145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta    @LayoutlibDelegate
308ad69aee5ed503d9592c10a0f6d1a5b617d99e6c5Deepanshu Gupta    /*package*/ static boolean nAddFontFromAsset(long nativeFamily, AssetManager mgr, String path) {
309d51834bdfad0cd3142ac464ab3a3b0db14f884c3Deepanshu Gupta        FontFamily_Delegate ffd = sManager.getDelegate(nativeFamily);
310d51834bdfad0cd3142ac464ab3a3b0db14f884c3Deepanshu Gupta        ffd.mValid = true;
311d51834bdfad0cd3142ac464ab3a3b0db14f884c3Deepanshu Gupta        if (mgr == null) {
312d51834bdfad0cd3142ac464ab3a3b0db14f884c3Deepanshu Gupta            return false;
313d51834bdfad0cd3142ac464ab3a3b0db14f884c3Deepanshu Gupta        }
314d51834bdfad0cd3142ac464ab3a3b0db14f884c3Deepanshu Gupta        if (mgr instanceof BridgeAssetManager) {
315d51834bdfad0cd3142ac464ab3a3b0db14f884c3Deepanshu Gupta            InputStream fontStream = null;
316d51834bdfad0cd3142ac464ab3a3b0db14f884c3Deepanshu Gupta            try {
317d51834bdfad0cd3142ac464ab3a3b0db14f884c3Deepanshu Gupta                AssetRepository assetRepository = ((BridgeAssetManager) mgr).getAssetRepository();
318d51834bdfad0cd3142ac464ab3a3b0db14f884c3Deepanshu Gupta                if (assetRepository == null) {
319d51834bdfad0cd3142ac464ab3a3b0db14f884c3Deepanshu Gupta                    Bridge.getLog().error(LayoutLog.TAG_MISSING_ASSET, "Asset not found: " + path,
320d51834bdfad0cd3142ac464ab3a3b0db14f884c3Deepanshu Gupta                            null);
321d51834bdfad0cd3142ac464ab3a3b0db14f884c3Deepanshu Gupta                    return false;
322d51834bdfad0cd3142ac464ab3a3b0db14f884c3Deepanshu Gupta                }
323d51834bdfad0cd3142ac464ab3a3b0db14f884c3Deepanshu Gupta                if (!assetRepository.isSupported()) {
324d51834bdfad0cd3142ac464ab3a3b0db14f884c3Deepanshu Gupta                    // Don't log any warnings on unsupported IDEs.
325d51834bdfad0cd3142ac464ab3a3b0db14f884c3Deepanshu Gupta                    return false;
326d51834bdfad0cd3142ac464ab3a3b0db14f884c3Deepanshu Gupta                }
327d51834bdfad0cd3142ac464ab3a3b0db14f884c3Deepanshu Gupta                // Check cache
328d51834bdfad0cd3142ac464ab3a3b0db14f884c3Deepanshu Gupta                FontInfo fontInfo = sCache.get(path);
329d51834bdfad0cd3142ac464ab3a3b0db14f884c3Deepanshu Gupta                if (fontInfo != null) {
330d51834bdfad0cd3142ac464ab3a3b0db14f884c3Deepanshu Gupta                    // renew the font's lease.
331d51834bdfad0cd3142ac464ab3a3b0db14f884c3Deepanshu Gupta                    sCache.put(path, fontInfo);
332d51834bdfad0cd3142ac464ab3a3b0db14f884c3Deepanshu Gupta                    ffd.addFont(fontInfo);
333d51834bdfad0cd3142ac464ab3a3b0db14f884c3Deepanshu Gupta                    return true;
334d51834bdfad0cd3142ac464ab3a3b0db14f884c3Deepanshu Gupta                }
335d51834bdfad0cd3142ac464ab3a3b0db14f884c3Deepanshu Gupta                fontStream = assetRepository.openAsset(path, AssetManager.ACCESS_STREAMING);
336d51834bdfad0cd3142ac464ab3a3b0db14f884c3Deepanshu Gupta                if (fontStream == null) {
337d51834bdfad0cd3142ac464ab3a3b0db14f884c3Deepanshu Gupta                    Bridge.getLog().error(LayoutLog.TAG_MISSING_ASSET, "Asset not found: " + path,
338d51834bdfad0cd3142ac464ab3a3b0db14f884c3Deepanshu Gupta                            path);
339d51834bdfad0cd3142ac464ab3a3b0db14f884c3Deepanshu Gupta                    return false;
340d51834bdfad0cd3142ac464ab3a3b0db14f884c3Deepanshu Gupta                }
341d51834bdfad0cd3142ac464ab3a3b0db14f884c3Deepanshu Gupta                Font font = Font.createFont(Font.TRUETYPE_FONT, fontStream);
342d51834bdfad0cd3142ac464ab3a3b0db14f884c3Deepanshu Gupta                fontInfo = new FontInfo();
343d51834bdfad0cd3142ac464ab3a3b0db14f884c3Deepanshu Gupta                fontInfo.mFont = font;
344d51834bdfad0cd3142ac464ab3a3b0db14f884c3Deepanshu Gupta                fontInfo.mWeight = font.isBold() ? BOLD_FONT_WEIGHT : DEFAULT_FONT_WEIGHT;
345d51834bdfad0cd3142ac464ab3a3b0db14f884c3Deepanshu Gupta                fontInfo.mIsItalic = font.isItalic();
346d51834bdfad0cd3142ac464ab3a3b0db14f884c3Deepanshu Gupta                ffd.addFont(fontInfo);
347d51834bdfad0cd3142ac464ab3a3b0db14f884c3Deepanshu Gupta                return true;
348d51834bdfad0cd3142ac464ab3a3b0db14f884c3Deepanshu Gupta            } catch (IOException e) {
349d51834bdfad0cd3142ac464ab3a3b0db14f884c3Deepanshu Gupta                Bridge.getLog().error(LayoutLog.TAG_MISSING_ASSET, "Unable to load font " + path, e,
350d51834bdfad0cd3142ac464ab3a3b0db14f884c3Deepanshu Gupta                        path);
351d51834bdfad0cd3142ac464ab3a3b0db14f884c3Deepanshu Gupta            } catch (FontFormatException e) {
352d51834bdfad0cd3142ac464ab3a3b0db14f884c3Deepanshu Gupta                if (path.endsWith(EXTENSION_OTF)) {
353d51834bdfad0cd3142ac464ab3a3b0db14f884c3Deepanshu Gupta                    // otf fonts are not supported on the user's config (JRE version + OS)
354d51834bdfad0cd3142ac464ab3a3b0db14f884c3Deepanshu Gupta                    Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED,
355d51834bdfad0cd3142ac464ab3a3b0db14f884c3Deepanshu Gupta                            "OpenType fonts are not supported yet: " + path, null, path);
356d51834bdfad0cd3142ac464ab3a3b0db14f884c3Deepanshu Gupta                } else {
357d51834bdfad0cd3142ac464ab3a3b0db14f884c3Deepanshu Gupta                    Bridge.getLog().error(LayoutLog.TAG_BROKEN,
358d51834bdfad0cd3142ac464ab3a3b0db14f884c3Deepanshu Gupta                            "Unable to load font " + path, e, path);
359d51834bdfad0cd3142ac464ab3a3b0db14f884c3Deepanshu Gupta                }
360d51834bdfad0cd3142ac464ab3a3b0db14f884c3Deepanshu Gupta            } finally {
361d51834bdfad0cd3142ac464ab3a3b0db14f884c3Deepanshu Gupta                if (fontStream != null) {
362d51834bdfad0cd3142ac464ab3a3b0db14f884c3Deepanshu Gupta                    try {
363d51834bdfad0cd3142ac464ab3a3b0db14f884c3Deepanshu Gupta                        fontStream.close();
364d51834bdfad0cd3142ac464ab3a3b0db14f884c3Deepanshu Gupta                    } catch (IOException ignored) {
365d51834bdfad0cd3142ac464ab3a3b0db14f884c3Deepanshu Gupta                    }
366d51834bdfad0cd3142ac464ab3a3b0db14f884c3Deepanshu Gupta                }
367d51834bdfad0cd3142ac464ab3a3b0db14f884c3Deepanshu Gupta            }
368d51834bdfad0cd3142ac464ab3a3b0db14f884c3Deepanshu Gupta            return false;
369d51834bdfad0cd3142ac464ab3a3b0db14f884c3Deepanshu Gupta        }
370d51834bdfad0cd3142ac464ab3a3b0db14f884c3Deepanshu Gupta        // This should never happen. AssetManager is a final class (from user's perspective), and
371d51834bdfad0cd3142ac464ab3a3b0db14f884c3Deepanshu Gupta        // we've replaced every creation of AssetManager with our implementation. We create an
372d51834bdfad0cd3142ac464ab3a3b0db14f884c3Deepanshu Gupta        // exception and log it, but continue with rest of the rendering, without loading this font.
373d51834bdfad0cd3142ac464ab3a3b0db14f884c3Deepanshu Gupta        Bridge.getLog().error(LayoutLog.TAG_BROKEN,
374d51834bdfad0cd3142ac464ab3a3b0db14f884c3Deepanshu Gupta                "You have found a bug in the rendering library. Please file a bug at b.android.com.",
375d51834bdfad0cd3142ac464ab3a3b0db14f884c3Deepanshu Gupta                new RuntimeException("Asset Manager is not an instance of BridgeAssetManager"),
376d51834bdfad0cd3142ac464ab3a3b0db14f884c3Deepanshu Gupta                null);
377ad69aee5ed503d9592c10a0f6d1a5b617d99e6c5Deepanshu Gupta        return false;
378ad69aee5ed503d9592c10a0f6d1a5b617d99e6c5Deepanshu Gupta    }
379ad69aee5ed503d9592c10a0f6d1a5b617d99e6c5Deepanshu Gupta
3803c937cf5c730519e750cdee4d5fa61e2a593e33aDeepanshu Gupta
3813c937cf5c730519e750cdee4d5fa61e2a593e33aDeepanshu Gupta    // ---- private helper methods ----
3823c937cf5c730519e750cdee4d5fa61e2a593e33aDeepanshu Gupta
383baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Gupta    private void init() {
384145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta        for (Runnable postInitRunnable : mPostInitRunnables) {
385145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta            postInitRunnable.run();
386baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Gupta        }
387145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta        mPostInitRunnables = null;
388baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Gupta    }
389baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Gupta
390145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta     private boolean addFont(@NonNull String path) {
391145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta         return addFont(path, DEFAULT_FONT_WEIGHT, path.endsWith(FONT_SUFFIX_ITALIC));
392145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta     }
393145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta
394145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta    private boolean addFont(@NonNull String path, int weight, boolean isItalic) {
3950e4be2540984235a0a7b84ea0466ef3c92d27b07Deepanshu Gupta        if (path.startsWith(SYSTEM_FONTS) &&
3960e4be2540984235a0a7b84ea0466ef3c92d27b07Deepanshu Gupta                !SDK_FONTS.contains(path.substring(SYSTEM_FONTS.length()))) {
3970e4be2540984235a0a7b84ea0466ef3c92d27b07Deepanshu Gupta            return mValid = false;
3980e4be2540984235a0a7b84ea0466ef3c92d27b07Deepanshu Gupta        }
3990e4be2540984235a0a7b84ea0466ef3c92d27b07Deepanshu Gupta        // Set valid to true, even if the font fails to load.
4000e4be2540984235a0a7b84ea0466ef3c92d27b07Deepanshu Gupta        mValid = true;
401baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Gupta        Font font = loadFont(path);
402baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Gupta        if (font == null) {
403baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Gupta            return false;
404baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Gupta        }
405baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Gupta        FontInfo fontInfo = new FontInfo();
406baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Gupta        fontInfo.mFont = font;
407145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta        fontInfo.mWeight = weight;
408145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta        fontInfo.mIsItalic = isItalic;
409145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta        addFont(fontInfo);
410145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta        return true;
411145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta    }
412145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta
413145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta    private boolean addFont(@NonNull FontInfo fontInfo) {
414145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta        int weight = fontInfo.mWeight;
415145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta        boolean isItalic = fontInfo.mIsItalic;
416145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta        // The list is usually just two fonts big. So iterating over all isn't as bad as it looks.
417145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta        // It's biggest for roboto where the size is 12.
418145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta        for (FontInfo font : mFonts) {
419145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta            if (font.mWeight == weight && font.mIsItalic == isItalic) {
420145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta                return false;
421145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta            }
422145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta        }
423baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Gupta        mFonts.add(fontInfo);
424baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Gupta        return true;
425baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Gupta    }
426145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta
427145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta    /**
428145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta     * Compute matching metric between two styles - 0 is an exact match.
429145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta     */
430145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta    private static int computeMatch(@NonNull FontInfo font1, @NonNull FontInfo font2) {
431145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta        int score = Math.abs(font1.mWeight - font2.mWeight);
432145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta        if (font1.mIsItalic != font2.mIsItalic) {
433145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta            score += 200;
434145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta        }
435145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta        return score;
436145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta    }
437145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta
438145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta    /**
439145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta     * Try to derive a font from {@code srcFont} for the style in {@code outFont}.
440145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta     * <p/>
441145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta     * {@code outFont} is updated to reflect the style of the derived font.
442145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta     * @param srcFont the source font
443145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta     * @param outFont contains the desired font style. Updated to contain the derived font and
444145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta     *                its style
445145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta     * @return outFont
446145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta     */
447145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta    @NonNull
448145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta    private FontInfo deriveFont(@NonNull FontInfo srcFont, @NonNull FontInfo outFont) {
449145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta        int desiredWeight = outFont.mWeight;
450145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta        int srcWeight = srcFont.mWeight;
451145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta        Font derivedFont = srcFont.mFont;
452145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta        // Embolden the font if required.
453145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta        if (desiredWeight >= BOLD_FONT_WEIGHT && desiredWeight - srcWeight > BOLD_FONT_WEIGHT_DELTA / 2) {
454145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta            derivedFont = derivedFont.deriveFont(Font.BOLD);
455145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta            srcWeight += BOLD_FONT_WEIGHT_DELTA;
456145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta        }
457145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta        // Italicize the font if required.
458145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta        if (outFont.mIsItalic && !srcFont.mIsItalic) {
459145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta            derivedFont = derivedFont.deriveFont(Font.ITALIC);
460145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta        } else if (outFont.mIsItalic != srcFont.mIsItalic) {
461145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta            // The desired font is plain, but the src font is italics. We can't convert it back. So
462145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta            // we update the value to reflect the true style of the font we're deriving.
463145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta            outFont.mIsItalic = srcFont.mIsItalic;
464145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta        }
465145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta        outFont.mFont = derivedFont;
466145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta        outFont.mWeight = srcWeight;
467145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta        // No need to update mIsItalics, as it's already been handled above.
468145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta        return outFont;
469145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta    }
470baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Gupta}
471