FontFamily_Delegate.java revision b0d767dcf17dd393646b8fc3e8c9f46dfe12e230
1/*
2 * Copyright (C) 2014 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package android.graphics;
18
19import com.android.ide.common.rendering.api.LayoutLog;
20import com.android.layoutlib.bridge.Bridge;
21import com.android.layoutlib.bridge.impl.DelegateManager;
22import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
23
24import android.content.res.AssetManager;
25
26import java.awt.Font;
27import java.awt.FontFormatException;
28import java.io.File;
29import java.util.ArrayList;
30import java.util.List;
31
32import static android.graphics.Typeface_Delegate.SYSTEM_FONTS;
33
34/**
35 * Delegate implementing the native methods of android.graphics.FontFamily
36 *
37 * Through the layoutlib_create tool, the original native methods of FontFamily have been replaced
38 * by calls to methods of the same name in this delegate class.
39 *
40 * This class behaves like the original native implementation, but in Java, keeping previously
41 * native data into its own objects and mapping them to int that are sent back and forth between
42 * it and the original FontFamily class.
43 *
44 * @see DelegateManager
45 */
46public class FontFamily_Delegate {
47
48    // FONT_SUFFIX_ITALIC will always match FONT_SUFFIX_BOLDITALIC and hence it must be checked
49    // separately.
50    private static final String FONT_SUFFIX_BOLDITALIC = "BoldItalic.ttf";
51    private static final String FONT_SUFFIX_BOLD = "Bold.ttf";
52    private static final String FONT_SUFFIX_ITALIC = "Italic.ttf";
53
54    /**
55     * A class associating {@link Font} with its metadata.
56     */
57    private static final class FontInfo {
58        Font mFont;
59        /** Regular, Bold, Italic, or BoldItalic. */
60        int mStyle;
61    }
62
63    // ---- delegate manager ----
64    private static final DelegateManager<FontFamily_Delegate> sManager =
65            new DelegateManager<FontFamily_Delegate>(FontFamily_Delegate.class);
66
67    // ---- delegate helper data ----
68    private static String sFontLocation;
69    private static final List<FontFamily_Delegate> sPostInitDelegate = new
70            ArrayList<FontFamily_Delegate>();
71
72
73    // ---- delegate data ----
74    private List<FontInfo> mFonts = new ArrayList<FontInfo>();
75    /**
76     * The variant of the Font Family - compact or elegant.
77     * 0 is unspecified, 1 is compact and 2 is elegant. This needs to be kept in sync with values in
78     * android.graphics.FontFamily
79     *
80     * @see Paint#setElegantTextHeight(boolean)
81     */
82    private FontVariant mVariant;
83    // Path of fonts that haven't been created since sFontLoader hasn't been initialized.
84    private List<String> mPath = new ArrayList<String>();
85
86
87    // ---- Public helper class ----
88
89    public enum FontVariant {
90        // The order needs to be kept in sync with android.graphics.FontFamily.
91        NONE, COMPACT, ELEGANT
92    }
93
94    // ---- Public Helper methods ----
95
96    public static FontFamily_Delegate getDelegate(long nativeFontFamily) {
97        return sManager.getDelegate(nativeFontFamily);
98    }
99
100    public static synchronized void setFontLocation(String fontLocation) {
101        sFontLocation = fontLocation;
102        for (FontFamily_Delegate fontFamily : sPostInitDelegate) {
103            fontFamily.init();
104        }
105        sPostInitDelegate.clear();
106    }
107
108    public Font getFont(int style) {
109        FontInfo plainFont = null;
110        for (FontInfo font : mFonts) {
111            if (font.mStyle == style) {
112                return font.mFont;
113            }
114            if (font.mStyle == Font.PLAIN && plainFont == null) {
115                plainFont = font;
116            }
117        }
118
119        // No font with the mentioned style is found. Try to derive one.
120        if (plainFont != null && style > 0 && style < 4) {
121            FontInfo styledFont = new FontInfo();
122            styledFont.mFont = plainFont.mFont.deriveFont(style);
123            styledFont.mStyle = style;
124            // Add the font to the list of fonts so that we don't have to derive it the next time.
125            mFonts.add(styledFont);
126            return styledFont.mFont;
127        }
128        return null;
129    }
130
131    public FontVariant getVariant() {
132        return mVariant;
133    }
134
135    /*package*/ static int getFontStyle(String path) {
136        int style = Font.PLAIN;
137        String fontName = path.substring(path.lastIndexOf('/'));
138        if (fontName.endsWith(FONT_SUFFIX_BOLDITALIC)) {
139            style = Font.BOLD | Font.ITALIC;
140        } else if (fontName.endsWith(FONT_SUFFIX_BOLD)) {
141            style = Font.BOLD;
142        } else if (fontName.endsWith(FONT_SUFFIX_ITALIC)) {
143            style = Font.ITALIC;
144        }
145        return style;
146    }
147
148    /*package*/ static Font loadFont(String path) {
149        if (path.startsWith(SYSTEM_FONTS) ) {
150            String relativePath = path.substring(SYSTEM_FONTS.length());
151            File f = new File(sFontLocation, relativePath);
152
153            try {
154                return Font.createFont(Font.TRUETYPE_FONT, f);
155            } catch (Exception e) {
156                if (path.endsWith(".otf") && e instanceof FontFormatException) {
157                    // If we aren't able to load an Open Type font, don't log a warning just yet.
158                    // We wait for a case where font is being used. Only then we try to log the
159                    // warning.
160                    return null;
161                }
162                Bridge.getLog().fidelityWarning(LayoutLog.TAG_BROKEN,
163                        String.format("Unable to load font %1$s", relativePath),
164                        e, null);
165            }
166        } else {
167            Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED,
168                    "Only platform fonts located in " + SYSTEM_FONTS + "can be loaded.",
169                    null, null);
170        }
171
172        return null;
173    }
174
175
176    // ---- native methods ----
177
178    @LayoutlibDelegate
179    /*package*/ static long nCreateFamily(String lang, int variant) {
180        // TODO: support lang. This is required for japanese locale.
181        FontFamily_Delegate delegate = new FontFamily_Delegate();
182        // variant can be 0, 1 or 2.
183        assert variant < 3;
184        delegate.mVariant = FontVariant.values()[variant];
185        if (sFontLocation != null) {
186            delegate.init();
187        } else {
188            sPostInitDelegate.add(delegate);
189        }
190        return sManager.addNewDelegate(delegate);
191    }
192
193    @LayoutlibDelegate
194    /*package*/ static void nUnrefFamily(long nativePtr) {
195        // Removing the java reference for the object doesn't mean that it's freed for garbage
196        // collection. Typeface_Delegate may still hold a reference for it.
197        sManager.removeJavaReferenceFor(nativePtr);
198    }
199
200    @LayoutlibDelegate
201    /*package*/ static boolean nAddFont(long nativeFamily, String path) {
202        FontFamily_Delegate delegate = getDelegate(nativeFamily);
203        if (delegate != null) {
204            if (sFontLocation == null) {
205                delegate.mPath.add(path);
206                return true;
207            }
208            return delegate.addFont(path);
209        }
210        return false;
211    }
212
213    @LayoutlibDelegate
214    /*package*/ static boolean nAddFontFromAsset(long nativeFamily, AssetManager mgr, String path) {
215        Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED,
216                "FontFamily.addFontFromAsset is not supported.", null, null);
217        return false;
218    }
219
220
221    // ---- private helper methods ----
222
223    private void init() {
224        for (String path : mPath) {
225            addFont(path);
226        }
227        mPath = null;
228    }
229
230    private boolean addFont(String path) {
231        Font font = loadFont(path);
232        if (font == null) {
233            return false;
234        }
235        FontInfo fontInfo = new FontInfo();
236        fontInfo.mFont = font;
237        fontInfo.mStyle = getFontStyle(path);
238        // TODO ensure that mFonts doesn't have the font with this style already.
239        mFonts.add(fontInfo);
240        return true;
241    }
242}
243