Typeface.java revision 3660789f06c5fbcb81e6c7c79612048bff8f0f66
1/*
2 * Copyright (C) 2006 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 android.content.res.AssetManager;
20import android.graphics.FontListParser.Family;
21import android.util.Log;
22import android.util.LongSparseArray;
23import android.util.SparseArray;
24
25import org.xmlpull.v1.XmlPullParserException;
26
27import java.io.File;
28import java.io.FileInputStream;
29import java.io.FileNotFoundException;
30import java.io.IOException;
31import java.util.ArrayList;
32import java.util.HashMap;
33import java.util.List;
34import java.util.Map;
35
36/**
37 * The Typeface class specifies the typeface and intrinsic style of a font.
38 * This is used in the paint, along with optionally Paint settings like
39 * textSize, textSkewX, textScaleX to specify
40 * how text appears when drawn (and measured).
41 */
42public class Typeface {
43
44    private static String TAG = "Typeface";
45
46    /** The default NORMAL typeface object */
47    public static final Typeface DEFAULT;
48    /**
49     * The default BOLD typeface object. Note: this may be not actually be
50     * bold, depending on what fonts are installed. Call getStyle() to know
51     * for sure.
52     */
53    public static final Typeface DEFAULT_BOLD;
54    /** The NORMAL style of the default sans serif typeface. */
55    public static final Typeface SANS_SERIF;
56    /** The NORMAL style of the default serif typeface. */
57    public static final Typeface SERIF;
58    /** The NORMAL style of the default monospace typeface. */
59    public static final Typeface MONOSPACE;
60
61    static Typeface[] sDefaults;
62    private static final LongSparseArray<SparseArray<Typeface>> sTypefaceCache =
63            new LongSparseArray<SparseArray<Typeface>>(3);
64
65    static Typeface sDefaultTypeface;
66    static Map<String, Typeface> sSystemFontMap;
67    static FontFamily[] sFallbackFonts;
68
69    static final String SYSTEM_FONTS_CONFIG = "system_fonts.xml";
70    static final String FALLBACK_FONTS_CONFIG = "fallback_fonts.xml";
71
72    /**
73     * @hide
74     */
75    public long native_instance;
76
77    // Style
78    public static final int NORMAL = 0;
79    public static final int BOLD = 1;
80    public static final int ITALIC = 2;
81    public static final int BOLD_ITALIC = 3;
82
83    private int mStyle = 0;
84
85    private static void setDefault(Typeface t) {
86        sDefaultTypeface = t;
87        nativeSetDefault(t.native_instance);
88    }
89
90    /** Returns the typeface's intrinsic style attributes */
91    public int getStyle() {
92        return mStyle;
93    }
94
95    /** Returns true if getStyle() has the BOLD bit set. */
96    public final boolean isBold() {
97        return (mStyle & BOLD) != 0;
98    }
99
100    /** Returns true if getStyle() has the ITALIC bit set. */
101    public final boolean isItalic() {
102        return (mStyle & ITALIC) != 0;
103    }
104
105    /**
106     * Create a typeface object given a family name, and option style information.
107     * If null is passed for the name, then the "default" font will be chosen.
108     * The resulting typeface object can be queried (getStyle()) to discover what
109     * its "real" style characteristics are.
110     *
111     * @param familyName May be null. The name of the font family.
112     * @param style  The style (normal, bold, italic) of the typeface.
113     *               e.g. NORMAL, BOLD, ITALIC, BOLD_ITALIC
114     * @return The best matching typeface.
115     */
116    public static Typeface create(String familyName, int style) {
117        if (sSystemFontMap != null) {
118            return create(sSystemFontMap.get(familyName), style);
119        }
120        return null;
121    }
122
123    /**
124     * Create a typeface object that best matches the specified existing
125     * typeface and the specified Style. Use this call if you want to pick a new
126     * style from the same family of an existing typeface object. If family is
127     * null, this selects from the default font's family.
128     *
129     * @param family May be null. The name of the existing type face.
130     * @param style  The style (normal, bold, italic) of the typeface.
131     *               e.g. NORMAL, BOLD, ITALIC, BOLD_ITALIC
132     * @return The best matching typeface.
133     */
134    public static Typeface create(Typeface family, int style) {
135        long ni = 0;
136        if (family != null) {
137            // Return early if we're asked for the same face/style
138            if (family.mStyle == style) {
139                return family;
140            }
141
142            ni = family.native_instance;
143        }
144
145        Typeface typeface;
146        SparseArray<Typeface> styles = sTypefaceCache.get(ni);
147
148        if (styles != null) {
149            typeface = styles.get(style);
150            if (typeface != null) {
151                return typeface;
152            }
153        }
154
155        typeface = new Typeface(nativeCreateFromTypeface(ni, style));
156        if (styles == null) {
157            styles = new SparseArray<Typeface>(4);
158            sTypefaceCache.put(ni, styles);
159        }
160        styles.put(style, typeface);
161
162        return typeface;
163    }
164
165    /**
166     * Returns one of the default typeface objects, based on the specified style
167     *
168     * @return the default typeface that corresponds to the style
169     */
170    public static Typeface defaultFromStyle(int style) {
171        return sDefaults[style];
172    }
173
174    /**
175     * Create a new typeface from the specified font data.
176     * @param mgr The application's asset manager
177     * @param path  The file name of the font data in the assets directory
178     * @return The new typeface.
179     */
180    public static Typeface createFromAsset(AssetManager mgr, String path) {
181        if (sFallbackFonts != null) {
182            FontFamily fontFamily = new FontFamily();
183            if (fontFamily.addFontFromAsset(mgr, path)) {
184                FontFamily[] families = { fontFamily };
185                return createFromFamiliesWithDefault(families);
186            }
187        }
188        return null;
189    }
190
191    /**
192     * Create a new typeface from the specified font file.
193     *
194     * @param path The path to the font data.
195     * @return The new typeface.
196     */
197    public static Typeface createFromFile(File path) {
198        return createFromFile(path.getAbsolutePath());
199    }
200
201    /**
202     * Create a new typeface from the specified font file.
203     *
204     * @param path The full path to the font data.
205     * @return The new typeface.
206     */
207    public static Typeface createFromFile(String path) {
208        if (sFallbackFonts != null) {
209            FontFamily fontFamily = new FontFamily();
210            if (fontFamily.addFont(path)) {
211                FontFamily[] families = { fontFamily };
212                return createFromFamiliesWithDefault(families);
213            }
214        }
215        return null;
216    }
217
218    /**
219     * Create a new typeface from an array of font families.
220     *
221     * @param families array of font families
222     * @hide
223     */
224    public static Typeface createFromFamilies(FontFamily[] families) {
225        long[] ptrArray = new long[families.length];
226        for (int i = 0; i < families.length; i++) {
227            ptrArray[i] = families[i].mNativePtr;
228        }
229        return new Typeface(nativeCreateFromArray(ptrArray));
230    }
231
232    /**
233     * Create a new typeface from an array of font families, including
234     * also the font families in the fallback list.
235     *
236     * @param families array of font families
237     * @hide
238     */
239    public static Typeface createFromFamiliesWithDefault(FontFamily[] families) {
240        long[] ptrArray = new long[families.length + sFallbackFonts.length];
241        for (int i = 0; i < families.length; i++) {
242            ptrArray[i] = families[i].mNativePtr;
243        }
244        for (int i = 0; i < sFallbackFonts.length; i++) {
245            ptrArray[i + families.length] = sFallbackFonts[i].mNativePtr;
246        }
247        return new Typeface(nativeCreateFromArray(ptrArray));
248    }
249
250    // don't allow clients to call this directly
251    private Typeface(long ni) {
252        if (ni == 0) {
253            throw new RuntimeException("native typeface cannot be made");
254        }
255
256        native_instance = ni;
257        mStyle = nativeGetStyle(ni);
258    }
259
260    private static FontFamily makeFamilyFromParsed(FontListParser.Family family) {
261        // TODO: expand to handle attributes like lang and variant
262        FontFamily fontFamily = new FontFamily(family.lang, family.variant);
263        for (String fontFile : family.fontFiles) {
264            fontFamily.addFont(fontFile);
265        }
266        return fontFamily;
267    }
268
269    /*
270     * (non-Javadoc)
271     *
272     * This should only be called once, from the static class initializer block.
273     */
274    private static void init() {
275        // Load font config and initialize Minikin state
276        File systemFontConfigLocation = getSystemFontConfigLocation();
277        File systemConfigFilename = new File(systemFontConfigLocation, SYSTEM_FONTS_CONFIG);
278        File configFilename = new File(systemFontConfigLocation, FALLBACK_FONTS_CONFIG);
279        try {
280            // TODO: throws an exception non-Minikin builds, to fail early;
281            // remove when Minikin-only
282            new FontFamily();
283
284            FileInputStream systemIn = new FileInputStream(systemConfigFilename);
285            List<FontListParser.Family> systemFontConfig = FontListParser.parse(systemIn);
286
287            FileInputStream fallbackIn = new FileInputStream(configFilename);
288            List<FontFamily> familyList = new ArrayList<FontFamily>();
289            // Note that the default typeface is always present in the fallback list;
290            // this is an enhancement from pre-Minikin behavior.
291            familyList.add(makeFamilyFromParsed(systemFontConfig.get(0)));
292            for (Family f : FontListParser.parse(fallbackIn)) {
293                familyList.add(makeFamilyFromParsed(f));
294            }
295            sFallbackFonts = familyList.toArray(new FontFamily[familyList.size()]);
296            setDefault(Typeface.createFromFamilies(sFallbackFonts));
297
298            Map<String, Typeface> systemFonts = new HashMap<String, Typeface>();
299            for (int i = 0; i < systemFontConfig.size(); i++) {
300                Typeface typeface;
301                Family f = systemFontConfig.get(i);
302                if (i == 0) {
303                    // The first entry is the default typeface; no sense in duplicating
304                    // the corresponding FontFamily.
305                    typeface = sDefaultTypeface;
306                } else {
307                    FontFamily fontFamily = makeFamilyFromParsed(f);
308                    FontFamily[] families = { fontFamily };
309                    typeface = Typeface.createFromFamiliesWithDefault(families);
310                }
311                for (String name : f.names) {
312                    systemFonts.put(name, typeface);
313                }
314            }
315            sSystemFontMap = systemFonts;
316
317        } catch (RuntimeException e) {
318            Log.w(TAG, "Didn't create default family (most likely, non-Minikin build)");
319            // TODO: normal in non-Minikin case, remove or make error when Minikin-only
320        } catch (FileNotFoundException e) {
321            Log.e(TAG, "Error opening " + configFilename);
322        } catch (IOException e) {
323            Log.e(TAG, "Error reading " + configFilename);
324        } catch (XmlPullParserException e) {
325            Log.e(TAG, "XML parse exception for " + configFilename);
326        }
327    }
328
329    static {
330        init();
331        // Set up defaults and typefaces exposed in public API
332        DEFAULT         = create((String) null, 0);
333        DEFAULT_BOLD    = create((String) null, Typeface.BOLD);
334        SANS_SERIF      = create("sans-serif", 0);
335        SERIF           = create("serif", 0);
336        MONOSPACE       = create("monospace", 0);
337
338        sDefaults = new Typeface[] {
339            DEFAULT,
340            DEFAULT_BOLD,
341            create((String) null, Typeface.ITALIC),
342            create((String) null, Typeface.BOLD_ITALIC),
343        };
344
345    }
346
347    private static File getSystemFontConfigLocation() {
348        return new File("/system/etc/");
349    }
350
351    @Override
352    protected void finalize() throws Throwable {
353        try {
354            nativeUnref(native_instance);
355        } finally {
356            super.finalize();
357        }
358    }
359
360    @Override
361    public boolean equals(Object o) {
362        if (this == o) return true;
363        if (o == null || getClass() != o.getClass()) return false;
364
365        Typeface typeface = (Typeface) o;
366
367        return mStyle == typeface.mStyle && native_instance == typeface.native_instance;
368    }
369
370    @Override
371    public int hashCode() {
372        /*
373         * Modified method for hashCode with long native_instance derived from
374         * http://developer.android.com/reference/java/lang/Object.html
375         */
376        int result = 17;
377        result = 31 * result + (int) (native_instance ^ (native_instance >>> 32));
378        result = 31 * result + mStyle;
379        return result;
380    }
381
382    private static native long nativeCreateFromTypeface(long native_instance, int style);
383    private static native void nativeUnref(long native_instance);
384    private static native int  nativeGetStyle(long native_instance);
385    private static native long nativeCreateFromArray(long[] familyArray);
386    private static native void nativeSetDefault(long native_instance);
387}
388