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
19145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Guptaimport com.android.annotations.NonNull;
20145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Guptaimport com.android.annotations.Nullable;
21baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Guptaimport com.android.ide.common.rendering.api.LayoutLog;
22baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Guptaimport com.android.layoutlib.bridge.Bridge;
23baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Guptaimport com.android.layoutlib.bridge.impl.DelegateManager;
24baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Guptaimport com.android.tools.layoutlib.annotations.LayoutlibDelegate;
25baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Gupta
26ad69aee5ed503d9592c10a0f6d1a5b617d99e6c5Deepanshu Guptaimport android.content.res.AssetManager;
27ad69aee5ed503d9592c10a0f6d1a5b617d99e6c5Deepanshu Gupta
28baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Guptaimport java.awt.Font;
29b0d767dcf17dd393646b8fc3e8c9f46dfe12e230Deepanshu Guptaimport java.awt.FontFormatException;
30baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Guptaimport java.io.File;
310e4be2540984235a0a7b84ea0466ef3c92d27b07Deepanshu Guptaimport java.io.FileNotFoundException;
32baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Guptaimport java.util.ArrayList;
33e644ff8d92ba040d11636be0fb6c433b52bcc6c2Deepanshu Guptaimport java.util.Collections;
34e644ff8d92ba040d11636be0fb6c433b52bcc6c2Deepanshu Guptaimport java.util.HashSet;
35baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Guptaimport java.util.List;
360e4be2540984235a0a7b84ea0466ef3c92d27b07Deepanshu Guptaimport java.util.Scanner;
37e644ff8d92ba040d11636be0fb6c433b52bcc6c2Deepanshu Guptaimport java.util.Set;
38baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Gupta
39baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Guptaimport static android.graphics.Typeface_Delegate.SYSTEM_FONTS;
40baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Gupta
41baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Gupta/**
42baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Gupta * Delegate implementing the native methods of android.graphics.FontFamily
43baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Gupta *
44baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Gupta * Through the layoutlib_create tool, the original native methods of FontFamily have been replaced
45baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Gupta * by calls to methods of the same name in this delegate class.
46baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Gupta *
47baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Gupta * This class behaves like the original native implementation, but in Java, keeping previously
48baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Gupta * native data into its own objects and mapping them to int that are sent back and forth between
49baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Gupta * it and the original FontFamily class.
50baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Gupta *
51baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Gupta * @see DelegateManager
52baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Gupta */
53baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Guptapublic class FontFamily_Delegate {
54baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Gupta
55145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta    public static final int DEFAULT_FONT_WEIGHT = 400;
56145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta    public static final int BOLD_FONT_WEIGHT_DELTA = 300;
57145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta    public static final int BOLD_FONT_WEIGHT = 700;
58145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta
59baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Gupta    // FONT_SUFFIX_ITALIC will always match FONT_SUFFIX_BOLDITALIC and hence it must be checked
60baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Gupta    // separately.
61baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Gupta    private static final String FONT_SUFFIX_ITALIC = "Italic.ttf";
620e4be2540984235a0a7b84ea0466ef3c92d27b07Deepanshu Gupta    private static final String FN_ALL_FONTS_LIST = "fontsInSdk.txt";
63e644ff8d92ba040d11636be0fb6c433b52bcc6c2Deepanshu Gupta
64baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Gupta    /**
65baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Gupta     * A class associating {@link Font} with its metadata.
66baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Gupta     */
67baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Gupta    private static final class FontInfo {
68145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta        @Nullable
69baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Gupta        Font mFont;
70145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta        int mWeight;
71145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta        boolean mIsItalic;
72baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Gupta    }
73baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Gupta
74baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Gupta    // ---- delegate manager ----
75baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Gupta    private static final DelegateManager<FontFamily_Delegate> sManager =
76baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Gupta            new DelegateManager<FontFamily_Delegate>(FontFamily_Delegate.class);
77baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Gupta
78baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Gupta    // ---- delegate helper data ----
79baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Gupta    private static String sFontLocation;
80baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Gupta    private static final List<FontFamily_Delegate> sPostInitDelegate = new
81baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Gupta            ArrayList<FontFamily_Delegate>();
820e4be2540984235a0a7b84ea0466ef3c92d27b07Deepanshu Gupta    private static Set<String> SDK_FONTS;
83baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Gupta
84baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Gupta
85baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Gupta    // ---- delegate data ----
86baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Gupta    private List<FontInfo> mFonts = new ArrayList<FontInfo>();
87145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta
88ad69aee5ed503d9592c10a0f6d1a5b617d99e6c5Deepanshu Gupta    /**
89ad69aee5ed503d9592c10a0f6d1a5b617d99e6c5Deepanshu Gupta     * The variant of the Font Family - compact or elegant.
90145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta     * <p/>
91ad69aee5ed503d9592c10a0f6d1a5b617d99e6c5Deepanshu Gupta     * 0 is unspecified, 1 is compact and 2 is elegant. This needs to be kept in sync with values in
92ad69aee5ed503d9592c10a0f6d1a5b617d99e6c5Deepanshu Gupta     * android.graphics.FontFamily
93ad69aee5ed503d9592c10a0f6d1a5b617d99e6c5Deepanshu Gupta     *
94ad69aee5ed503d9592c10a0f6d1a5b617d99e6c5Deepanshu Gupta     * @see Paint#setElegantTextHeight(boolean)
95ad69aee5ed503d9592c10a0f6d1a5b617d99e6c5Deepanshu Gupta     */
96ad69aee5ed503d9592c10a0f6d1a5b617d99e6c5Deepanshu Gupta    private FontVariant mVariant;
97145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta    // List of runnables to process fonts after sFontLoader is initialized.
98145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta    private List<Runnable> mPostInitRunnables = new ArrayList<Runnable>();
99e644ff8d92ba040d11636be0fb6c433b52bcc6c2Deepanshu Gupta    /** @see #isValid() */
100e644ff8d92ba040d11636be0fb6c433b52bcc6c2Deepanshu Gupta    private boolean mValid = false;
101baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Gupta
102baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Gupta
1033c937cf5c730519e750cdee4d5fa61e2a593e33aDeepanshu Gupta    // ---- Public helper class ----
1043c937cf5c730519e750cdee4d5fa61e2a593e33aDeepanshu Gupta
1053c937cf5c730519e750cdee4d5fa61e2a593e33aDeepanshu Gupta    public enum FontVariant {
1063c937cf5c730519e750cdee4d5fa61e2a593e33aDeepanshu Gupta        // The order needs to be kept in sync with android.graphics.FontFamily.
1073c937cf5c730519e750cdee4d5fa61e2a593e33aDeepanshu Gupta        NONE, COMPACT, ELEGANT
1083c937cf5c730519e750cdee4d5fa61e2a593e33aDeepanshu Gupta    }
1093c937cf5c730519e750cdee4d5fa61e2a593e33aDeepanshu Gupta
110baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Gupta    // ---- Public Helper methods ----
111baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Gupta
112baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Gupta    public static FontFamily_Delegate getDelegate(long nativeFontFamily) {
113baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Gupta        return sManager.getDelegate(nativeFontFamily);
114baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Gupta    }
115baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Gupta
116baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Gupta    public static synchronized void setFontLocation(String fontLocation) {
117baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Gupta        sFontLocation = fontLocation;
1180e4be2540984235a0a7b84ea0466ef3c92d27b07Deepanshu Gupta        // init list of bundled fonts.
1190e4be2540984235a0a7b84ea0466ef3c92d27b07Deepanshu Gupta        File allFonts = new File(fontLocation, FN_ALL_FONTS_LIST);
1200e4be2540984235a0a7b84ea0466ef3c92d27b07Deepanshu Gupta        // Current number of fonts is 103. Use the next round number to leave scope for more fonts
1210e4be2540984235a0a7b84ea0466ef3c92d27b07Deepanshu Gupta        // in the future.
1220e4be2540984235a0a7b84ea0466ef3c92d27b07Deepanshu Gupta        Set<String> allFontsList = new HashSet<String>(128);
1230e4be2540984235a0a7b84ea0466ef3c92d27b07Deepanshu Gupta        Scanner scanner = null;
1240e4be2540984235a0a7b84ea0466ef3c92d27b07Deepanshu Gupta        try {
1250e4be2540984235a0a7b84ea0466ef3c92d27b07Deepanshu Gupta            scanner = new Scanner(allFonts);
1260e4be2540984235a0a7b84ea0466ef3c92d27b07Deepanshu Gupta            while (scanner.hasNext()) {
1270e4be2540984235a0a7b84ea0466ef3c92d27b07Deepanshu Gupta                String name = scanner.next();
1280e4be2540984235a0a7b84ea0466ef3c92d27b07Deepanshu Gupta                // Skip font configuration files.
1290e4be2540984235a0a7b84ea0466ef3c92d27b07Deepanshu Gupta                if (!name.endsWith(".xml")) {
1300e4be2540984235a0a7b84ea0466ef3c92d27b07Deepanshu Gupta                    allFontsList.add(name);
1310e4be2540984235a0a7b84ea0466ef3c92d27b07Deepanshu Gupta                }
1320e4be2540984235a0a7b84ea0466ef3c92d27b07Deepanshu Gupta            }
1330e4be2540984235a0a7b84ea0466ef3c92d27b07Deepanshu Gupta        } catch (FileNotFoundException e) {
1340e4be2540984235a0a7b84ea0466ef3c92d27b07Deepanshu Gupta            Bridge.getLog().error(LayoutLog.TAG_BROKEN,
1350e4be2540984235a0a7b84ea0466ef3c92d27b07Deepanshu Gupta                    "Unable to load the list of fonts. Try re-installing the SDK Platform from the SDK Manager.",
1360e4be2540984235a0a7b84ea0466ef3c92d27b07Deepanshu Gupta                    e, null);
1370e4be2540984235a0a7b84ea0466ef3c92d27b07Deepanshu Gupta        } finally {
1380e4be2540984235a0a7b84ea0466ef3c92d27b07Deepanshu Gupta            if (scanner != null) {
1390e4be2540984235a0a7b84ea0466ef3c92d27b07Deepanshu Gupta                scanner.close();
1400e4be2540984235a0a7b84ea0466ef3c92d27b07Deepanshu Gupta            }
1410e4be2540984235a0a7b84ea0466ef3c92d27b07Deepanshu Gupta        }
1420e4be2540984235a0a7b84ea0466ef3c92d27b07Deepanshu Gupta        SDK_FONTS = Collections.unmodifiableSet(allFontsList);
143baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Gupta        for (FontFamily_Delegate fontFamily : sPostInitDelegate) {
144baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Gupta            fontFamily.init();
145baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Gupta        }
146baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Gupta        sPostInitDelegate.clear();
147baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Gupta    }
148baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Gupta
149145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta    @Nullable
150145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta    public Font getFont(int desiredWeight, boolean isItalic) {
151145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta        FontInfo desiredStyle = new FontInfo();
152145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta        desiredStyle.mWeight = desiredWeight;
153145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta        desiredStyle.mIsItalic = isItalic;
154145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta        FontInfo bestFont = null;
155145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta        int bestMatch = Integer.MAX_VALUE;
156baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Gupta        for (FontInfo font : mFonts) {
157145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta            int match = computeMatch(font, desiredStyle);
158145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta            if (match < bestMatch) {
159145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta                bestMatch = match;
160145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta                bestFont = font;
161baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Gupta            }
162baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Gupta        }
163145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta        if (bestFont == null) {
164145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta            return null;
165baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Gupta        }
166145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta        if (bestMatch == 0) {
167145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta            return bestFont.mFont;
168145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta        }
169145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta        // Derive the font as required and add it to the list of Fonts.
170145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta        deriveFont(bestFont, desiredStyle);
171145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta        addFont(desiredStyle);
172145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta        return desiredStyle.mFont;
173baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Gupta    }
174baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Gupta
175ad69aee5ed503d9592c10a0f6d1a5b617d99e6c5Deepanshu Gupta    public FontVariant getVariant() {
176ad69aee5ed503d9592c10a0f6d1a5b617d99e6c5Deepanshu Gupta        return mVariant;
177ad69aee5ed503d9592c10a0f6d1a5b617d99e6c5Deepanshu Gupta    }
178ad69aee5ed503d9592c10a0f6d1a5b617d99e6c5Deepanshu Gupta
179e644ff8d92ba040d11636be0fb6c433b52bcc6c2Deepanshu Gupta    /**
180e644ff8d92ba040d11636be0fb6c433b52bcc6c2Deepanshu Gupta     * Returns if the FontFamily should contain any fonts. If this returns true and
181145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta     * {@link #getFont(int, boolean)} returns an empty list, it means that an error occurred while
182145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta     * loading the fonts. However, some fonts are deliberately skipped, for example they are not
183145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta     * bundled with the SDK. In such a case, this method returns false.
184e644ff8d92ba040d11636be0fb6c433b52bcc6c2Deepanshu Gupta     */
185e644ff8d92ba040d11636be0fb6c433b52bcc6c2Deepanshu Gupta    public boolean isValid() {
186e644ff8d92ba040d11636be0fb6c433b52bcc6c2Deepanshu Gupta        return mValid;
187e644ff8d92ba040d11636be0fb6c433b52bcc6c2Deepanshu Gupta    }
188e644ff8d92ba040d11636be0fb6c433b52bcc6c2Deepanshu Gupta
1893c937cf5c730519e750cdee4d5fa61e2a593e33aDeepanshu Gupta    /*package*/ static Font loadFont(String path) {
1903c937cf5c730519e750cdee4d5fa61e2a593e33aDeepanshu Gupta        if (path.startsWith(SYSTEM_FONTS) ) {
1913c937cf5c730519e750cdee4d5fa61e2a593e33aDeepanshu Gupta            String relativePath = path.substring(SYSTEM_FONTS.length());
1923c937cf5c730519e750cdee4d5fa61e2a593e33aDeepanshu Gupta            File f = new File(sFontLocation, relativePath);
1933c937cf5c730519e750cdee4d5fa61e2a593e33aDeepanshu Gupta
1943c937cf5c730519e750cdee4d5fa61e2a593e33aDeepanshu Gupta            try {
1953c937cf5c730519e750cdee4d5fa61e2a593e33aDeepanshu Gupta                return Font.createFont(Font.TRUETYPE_FONT, f);
1963c937cf5c730519e750cdee4d5fa61e2a593e33aDeepanshu Gupta            } catch (Exception e) {
197b0d767dcf17dd393646b8fc3e8c9f46dfe12e230Deepanshu Gupta                if (path.endsWith(".otf") && e instanceof FontFormatException) {
198b0d767dcf17dd393646b8fc3e8c9f46dfe12e230Deepanshu Gupta                    // If we aren't able to load an Open Type font, don't log a warning just yet.
199b0d767dcf17dd393646b8fc3e8c9f46dfe12e230Deepanshu Gupta                    // We wait for a case where font is being used. Only then we try to log the
200b0d767dcf17dd393646b8fc3e8c9f46dfe12e230Deepanshu Gupta                    // warning.
201b0d767dcf17dd393646b8fc3e8c9f46dfe12e230Deepanshu Gupta                    return null;
202b0d767dcf17dd393646b8fc3e8c9f46dfe12e230Deepanshu Gupta                }
2033c937cf5c730519e750cdee4d5fa61e2a593e33aDeepanshu Gupta                Bridge.getLog().fidelityWarning(LayoutLog.TAG_BROKEN,
2043c937cf5c730519e750cdee4d5fa61e2a593e33aDeepanshu Gupta                        String.format("Unable to load font %1$s", relativePath),
205b0d767dcf17dd393646b8fc3e8c9f46dfe12e230Deepanshu Gupta                        e, null);
2063c937cf5c730519e750cdee4d5fa61e2a593e33aDeepanshu Gupta            }
2073c937cf5c730519e750cdee4d5fa61e2a593e33aDeepanshu Gupta        } else {
2083c937cf5c730519e750cdee4d5fa61e2a593e33aDeepanshu Gupta            Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED,
2093c937cf5c730519e750cdee4d5fa61e2a593e33aDeepanshu Gupta                    "Only platform fonts located in " + SYSTEM_FONTS + "can be loaded.",
210b0d767dcf17dd393646b8fc3e8c9f46dfe12e230Deepanshu Gupta                    null, null);
2113c937cf5c730519e750cdee4d5fa61e2a593e33aDeepanshu Gupta        }
2123c937cf5c730519e750cdee4d5fa61e2a593e33aDeepanshu Gupta
2133c937cf5c730519e750cdee4d5fa61e2a593e33aDeepanshu Gupta        return null;
2143c937cf5c730519e750cdee4d5fa61e2a593e33aDeepanshu Gupta    }
2153c937cf5c730519e750cdee4d5fa61e2a593e33aDeepanshu Gupta
216ad69aee5ed503d9592c10a0f6d1a5b617d99e6c5Deepanshu Gupta
217baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Gupta    // ---- native methods ----
218baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Gupta
219baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Gupta    @LayoutlibDelegate
220ad69aee5ed503d9592c10a0f6d1a5b617d99e6c5Deepanshu Gupta    /*package*/ static long nCreateFamily(String lang, int variant) {
221ad69aee5ed503d9592c10a0f6d1a5b617d99e6c5Deepanshu Gupta        // TODO: support lang. This is required for japanese locale.
222baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Gupta        FontFamily_Delegate delegate = new FontFamily_Delegate();
223ad69aee5ed503d9592c10a0f6d1a5b617d99e6c5Deepanshu Gupta        // variant can be 0, 1 or 2.
224ad69aee5ed503d9592c10a0f6d1a5b617d99e6c5Deepanshu Gupta        assert variant < 3;
225ad69aee5ed503d9592c10a0f6d1a5b617d99e6c5Deepanshu Gupta        delegate.mVariant = FontVariant.values()[variant];
226baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Gupta        if (sFontLocation != null) {
227baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Gupta            delegate.init();
228baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Gupta        } else {
229baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Gupta            sPostInitDelegate.add(delegate);
230baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Gupta        }
231baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Gupta        return sManager.addNewDelegate(delegate);
232baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Gupta    }
233baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Gupta
234baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Gupta    @LayoutlibDelegate
235baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Gupta    /*package*/ static void nUnrefFamily(long nativePtr) {
2369113968f9570b0c8ada2dec34fa6cf893da7c022Deepanshu Gupta        // Removing the java reference for the object doesn't mean that it's freed for garbage
2379113968f9570b0c8ada2dec34fa6cf893da7c022Deepanshu Gupta        // collection. Typeface_Delegate may still hold a reference for it.
238baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Gupta        sManager.removeJavaReferenceFor(nativePtr);
239baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Gupta    }
240baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Gupta
241baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Gupta    @LayoutlibDelegate
242145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta    /*package*/ static boolean nAddFont(long nativeFamily, final String path) {
243145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta        final FontFamily_Delegate delegate = getDelegate(nativeFamily);
244baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Gupta        if (delegate != null) {
245baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Gupta            if (sFontLocation == null) {
246145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta                delegate.mPostInitRunnables.add(new Runnable() {
247145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta                    @Override
248145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta                    public void run() {
249145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta                        delegate.addFont(path);
250145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta                    }
251145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta                });
252baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Gupta                return true;
253baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Gupta            }
254baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Gupta            return delegate.addFont(path);
255baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Gupta        }
256baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Gupta        return false;
257baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Gupta    }
258baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Gupta
259ad69aee5ed503d9592c10a0f6d1a5b617d99e6c5Deepanshu Gupta    @LayoutlibDelegate
260145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta    /*package*/ static boolean nAddFontWeightStyle(long nativeFamily, final String path,
261145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta            final int weight, final boolean isItalic) {
262145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta        final FontFamily_Delegate delegate = getDelegate(nativeFamily);
263145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta        if (delegate != null) {
264145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta            if (sFontLocation == null) {
265145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta                delegate.mPostInitRunnables.add(new Runnable() {
266145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta                    @Override
267145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta                    public void run() {
268145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta                        delegate.addFont(path, weight, isItalic);
269145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta                    }
270145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta                });
271145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta                return true;
272145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta            }
273145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta            return delegate.addFont(path, weight, isItalic);
274145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta        }
275145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta        return false;
276145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta    }
277145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta
278145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta    @LayoutlibDelegate
279ad69aee5ed503d9592c10a0f6d1a5b617d99e6c5Deepanshu Gupta    /*package*/ static boolean nAddFontFromAsset(long nativeFamily, AssetManager mgr, String path) {
280ad69aee5ed503d9592c10a0f6d1a5b617d99e6c5Deepanshu Gupta        Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED,
281b0d767dcf17dd393646b8fc3e8c9f46dfe12e230Deepanshu Gupta                "FontFamily.addFontFromAsset is not supported.", null, null);
282ad69aee5ed503d9592c10a0f6d1a5b617d99e6c5Deepanshu Gupta        return false;
283ad69aee5ed503d9592c10a0f6d1a5b617d99e6c5Deepanshu Gupta    }
284ad69aee5ed503d9592c10a0f6d1a5b617d99e6c5Deepanshu Gupta
2853c937cf5c730519e750cdee4d5fa61e2a593e33aDeepanshu Gupta
2863c937cf5c730519e750cdee4d5fa61e2a593e33aDeepanshu Gupta    // ---- private helper methods ----
2873c937cf5c730519e750cdee4d5fa61e2a593e33aDeepanshu Gupta
288baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Gupta    private void init() {
289145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta        for (Runnable postInitRunnable : mPostInitRunnables) {
290145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta            postInitRunnable.run();
291baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Gupta        }
292145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta        mPostInitRunnables = null;
293baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Gupta    }
294baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Gupta
295145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta     private boolean addFont(@NonNull String path) {
296145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta         return addFont(path, DEFAULT_FONT_WEIGHT, path.endsWith(FONT_SUFFIX_ITALIC));
297145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta     }
298145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta
299145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta    private boolean addFont(@NonNull String path, int weight, boolean isItalic) {
3000e4be2540984235a0a7b84ea0466ef3c92d27b07Deepanshu Gupta        if (path.startsWith(SYSTEM_FONTS) &&
3010e4be2540984235a0a7b84ea0466ef3c92d27b07Deepanshu Gupta                !SDK_FONTS.contains(path.substring(SYSTEM_FONTS.length()))) {
3020e4be2540984235a0a7b84ea0466ef3c92d27b07Deepanshu Gupta            return mValid = false;
3030e4be2540984235a0a7b84ea0466ef3c92d27b07Deepanshu Gupta        }
3040e4be2540984235a0a7b84ea0466ef3c92d27b07Deepanshu Gupta        // Set valid to true, even if the font fails to load.
3050e4be2540984235a0a7b84ea0466ef3c92d27b07Deepanshu Gupta        mValid = true;
306baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Gupta        Font font = loadFont(path);
307baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Gupta        if (font == null) {
308baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Gupta            return false;
309baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Gupta        }
310baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Gupta        FontInfo fontInfo = new FontInfo();
311baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Gupta        fontInfo.mFont = font;
312145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta        fontInfo.mWeight = weight;
313145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta        fontInfo.mIsItalic = isItalic;
314145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta        addFont(fontInfo);
315145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta        return true;
316145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta    }
317145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta
318145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta    private boolean addFont(@NonNull FontInfo fontInfo) {
319145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta        int weight = fontInfo.mWeight;
320145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta        boolean isItalic = fontInfo.mIsItalic;
321145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta        // The list is usually just two fonts big. So iterating over all isn't as bad as it looks.
322145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta        // It's biggest for roboto where the size is 12.
323145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta        for (FontInfo font : mFonts) {
324145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta            if (font.mWeight == weight && font.mIsItalic == isItalic) {
325145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta                return false;
326145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta            }
327145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta        }
328baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Gupta        mFonts.add(fontInfo);
329baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Gupta        return true;
330baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Gupta    }
331145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta
332145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta    /**
333145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta     * Compute matching metric between two styles - 0 is an exact match.
334145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta     */
335145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta    private static int computeMatch(@NonNull FontInfo font1, @NonNull FontInfo font2) {
336145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta        int score = Math.abs(font1.mWeight - font2.mWeight);
337145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta        if (font1.mIsItalic != font2.mIsItalic) {
338145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta            score += 200;
339145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta        }
340145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta        return score;
341145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta    }
342145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta
343145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta    /**
344145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta     * Try to derive a font from {@code srcFont} for the style in {@code outFont}.
345145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta     * <p/>
346145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta     * {@code outFont} is updated to reflect the style of the derived font.
347145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta     * @param srcFont the source font
348145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta     * @param outFont contains the desired font style. Updated to contain the derived font and
349145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta     *                its style
350145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta     * @return outFont
351145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta     */
352145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta    @NonNull
353145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta    private FontInfo deriveFont(@NonNull FontInfo srcFont, @NonNull FontInfo outFont) {
354145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta        int desiredWeight = outFont.mWeight;
355145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta        int srcWeight = srcFont.mWeight;
356145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta        Font derivedFont = srcFont.mFont;
357145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta        // Embolden the font if required.
358145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta        if (desiredWeight >= BOLD_FONT_WEIGHT && desiredWeight - srcWeight > BOLD_FONT_WEIGHT_DELTA / 2) {
359145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta            derivedFont = derivedFont.deriveFont(Font.BOLD);
360145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta            srcWeight += BOLD_FONT_WEIGHT_DELTA;
361145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta        }
362145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta        // Italicize the font if required.
363145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta        if (outFont.mIsItalic && !srcFont.mIsItalic) {
364145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta            derivedFont = derivedFont.deriveFont(Font.ITALIC);
365145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta        } else if (outFont.mIsItalic != srcFont.mIsItalic) {
366145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta            // The desired font is plain, but the src font is italics. We can't convert it back. So
367145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta            // we update the value to reflect the true style of the font we're deriving.
368145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta            outFont.mIsItalic = srcFont.mIsItalic;
369145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta        }
370145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta        outFont.mFont = derivedFont;
371145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta        outFont.mWeight = srcWeight;
372145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta        // No need to update mIsItalics, as it's already been handled above.
373145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta        return outFont;
374145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta    }
375baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Gupta}
376