Typeface.java revision fb95699364e555148b437cfa1e5c69384f843845
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.util.Log;
21import android.util.LongSparseArray;
22import android.util.SparseArray;
23
24import org.xmlpull.v1.XmlPullParserException;
25
26import java.io.File;
27import java.io.FileInputStream;
28import java.io.FileNotFoundException;
29import java.io.IOException;
30import java.nio.ByteBuffer;
31import java.nio.channels.FileChannel;
32import java.util.ArrayList;
33import java.util.HashMap;
34import java.util.List;
35import java.util.Map;
36
37/**
38 * The Typeface class specifies the typeface and intrinsic style of a font.
39 * This is used in the paint, along with optionally Paint settings like
40 * textSize, textSkewX, textScaleX to specify
41 * how text appears when drawn (and measured).
42 */
43public class Typeface {
44
45    private static String TAG = "Typeface";
46
47    /** The default NORMAL typeface object */
48    public static final Typeface DEFAULT;
49    /**
50     * The default BOLD typeface object. Note: this may be not actually be
51     * bold, depending on what fonts are installed. Call getStyle() to know
52     * for sure.
53     */
54    public static final Typeface DEFAULT_BOLD;
55    /** The NORMAL style of the default sans serif typeface. */
56    public static final Typeface SANS_SERIF;
57    /** The NORMAL style of the default serif typeface. */
58    public static final Typeface SERIF;
59    /** The NORMAL style of the default monospace typeface. */
60    public static final Typeface MONOSPACE;
61
62    static Typeface[] sDefaults;
63    private static final LongSparseArray<SparseArray<Typeface>> sTypefaceCache =
64            new LongSparseArray<SparseArray<Typeface>>(3);
65
66    static Typeface sDefaultTypeface;
67    static Map<String, Typeface> sSystemFontMap;
68    static FontFamily[] sFallbackFonts;
69
70    static final String FONTS_CONFIG = "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        if (style < 0 || style > 3) {
136            style = 0;
137        }
138        long ni = 0;
139        if (family != null) {
140            // Return early if we're asked for the same face/style
141            if (family.mStyle == style) {
142                return family;
143            }
144
145            ni = family.native_instance;
146        }
147
148        Typeface typeface;
149        SparseArray<Typeface> styles = sTypefaceCache.get(ni);
150
151        if (styles != null) {
152            typeface = styles.get(style);
153            if (typeface != null) {
154                return typeface;
155            }
156        }
157
158        typeface = new Typeface(nativeCreateFromTypeface(ni, style));
159        if (styles == null) {
160            styles = new SparseArray<Typeface>(4);
161            sTypefaceCache.put(ni, styles);
162        }
163        styles.put(style, typeface);
164
165        return typeface;
166    }
167
168    /**
169     * Returns one of the default typeface objects, based on the specified style
170     *
171     * @return the default typeface that corresponds to the style
172     */
173    public static Typeface defaultFromStyle(int style) {
174        return sDefaults[style];
175    }
176
177    /**
178     * Create a new typeface from the specified font data.
179     * @param mgr The application's asset manager
180     * @param path  The file name of the font data in the assets directory
181     * @return The new typeface.
182     */
183    public static Typeface createFromAsset(AssetManager mgr, String path) {
184        if (sFallbackFonts != null) {
185            FontFamily fontFamily = new FontFamily();
186            if (fontFamily.addFontFromAsset(mgr, path)) {
187                FontFamily[] families = { fontFamily };
188                return createFromFamiliesWithDefault(families);
189            }
190        }
191        throw new RuntimeException("Font asset not found " + path);
192    }
193
194    /**
195     * Create a new typeface from the specified font file.
196     *
197     * @param path The path to the font data.
198     * @return The new typeface.
199     */
200    public static Typeface createFromFile(File path) {
201        return createFromFile(path.getAbsolutePath());
202    }
203
204    /**
205     * Create a new typeface from the specified font file.
206     *
207     * @param path The full path to the font data.
208     * @return The new typeface.
209     */
210    public static Typeface createFromFile(String path) {
211        if (sFallbackFonts != null) {
212            FontFamily fontFamily = new FontFamily();
213            if (fontFamily.addFont(path, 0 /* ttcIndex */)) {
214                FontFamily[] families = { fontFamily };
215                return createFromFamiliesWithDefault(families);
216            }
217        }
218        throw new RuntimeException("Font not found " + path);
219    }
220
221    /**
222     * Create a new typeface from an array of font families.
223     *
224     * @param families array of font families
225     * @hide
226     */
227    public static Typeface createFromFamilies(FontFamily[] families) {
228        long[] ptrArray = new long[families.length];
229        for (int i = 0; i < families.length; i++) {
230            ptrArray[i] = families[i].mNativePtr;
231        }
232        return new Typeface(nativeCreateFromArray(ptrArray));
233    }
234
235    /**
236     * Create a new typeface from an array of font families, including
237     * also the font families in the fallback list.
238     *
239     * @param families array of font families
240     * @hide
241     */
242    public static Typeface createFromFamiliesWithDefault(FontFamily[] families) {
243        long[] ptrArray = new long[families.length + sFallbackFonts.length];
244        for (int i = 0; i < families.length; i++) {
245            ptrArray[i] = families[i].mNativePtr;
246        }
247        for (int i = 0; i < sFallbackFonts.length; i++) {
248            ptrArray[i + families.length] = sFallbackFonts[i].mNativePtr;
249        }
250        return new Typeface(nativeCreateFromArray(ptrArray));
251    }
252
253    // don't allow clients to call this directly
254    private Typeface(long ni) {
255        if (ni == 0) {
256            throw new RuntimeException("native typeface cannot be made");
257        }
258
259        native_instance = ni;
260        mStyle = nativeGetStyle(ni);
261    }
262
263    private static FontFamily makeFamilyFromParsed(FontListParser.Family family,
264            Map<String, ByteBuffer> bufferForPath) {
265        FontFamily fontFamily = new FontFamily(family.lang, family.variant);
266        for (FontListParser.Font font : family.fonts) {
267            ByteBuffer fontBuffer = bufferForPath.get(font.fontName);
268            if (fontBuffer == null) {
269                try (FileInputStream file = new FileInputStream(font.fontName)) {
270                    FileChannel fileChannel = file.getChannel();
271                    long fontSize = fileChannel.size();
272                    fontBuffer = fileChannel.map(FileChannel.MapMode.READ_ONLY, 0, fontSize);
273                    bufferForPath.put(font.fontName, fontBuffer);
274                } catch (IOException e) {
275                    Log.e(TAG, "Error mapping font file " + font.fontName);
276                    continue;
277                }
278            }
279            if (!fontFamily.addFontWeightStyle(fontBuffer, font.ttcIndex, font.axes,
280                    font.weight, font.isItalic)) {
281                Log.e(TAG, "Error creating font " + font.fontName + "#" + font.ttcIndex);
282            }
283        }
284        return fontFamily;
285    }
286
287    /*
288     * (non-Javadoc)
289     *
290     * This should only be called once, from the static class initializer block.
291     */
292    private static void init() {
293        // Load font config and initialize Minikin state
294        File systemFontConfigLocation = getSystemFontConfigLocation();
295        File configFilename = new File(systemFontConfigLocation, FONTS_CONFIG);
296        try {
297            FileInputStream fontsIn = new FileInputStream(configFilename);
298            FontListParser.Config fontConfig = FontListParser.parse(fontsIn);
299
300            Map<String, ByteBuffer> bufferForPath = new HashMap<String, ByteBuffer>();
301
302            List<FontFamily> familyList = new ArrayList<FontFamily>();
303            // Note that the default typeface is always present in the fallback list;
304            // this is an enhancement from pre-Minikin behavior.
305            for (int i = 0; i < fontConfig.families.size(); i++) {
306                FontListParser.Family f = fontConfig.families.get(i);
307                if (i == 0 || f.name == null) {
308                    familyList.add(makeFamilyFromParsed(f, bufferForPath));
309                }
310            }
311            sFallbackFonts = familyList.toArray(new FontFamily[familyList.size()]);
312            setDefault(Typeface.createFromFamilies(sFallbackFonts));
313
314            Map<String, Typeface> systemFonts = new HashMap<String, Typeface>();
315            for (int i = 0; i < fontConfig.families.size(); i++) {
316                Typeface typeface;
317                FontListParser.Family f = fontConfig.families.get(i);
318                if (f.name != null) {
319                    if (i == 0) {
320                        // The first entry is the default typeface; no sense in
321                        // duplicating the corresponding FontFamily.
322                        typeface = sDefaultTypeface;
323                    } else {
324                        FontFamily fontFamily = makeFamilyFromParsed(f, bufferForPath);
325                        FontFamily[] families = { fontFamily };
326                        typeface = Typeface.createFromFamiliesWithDefault(families);
327                    }
328                    systemFonts.put(f.name, typeface);
329                }
330            }
331            for (FontListParser.Alias alias : fontConfig.aliases) {
332                Typeface base = systemFonts.get(alias.toName);
333                Typeface newFace = base;
334                int weight = alias.weight;
335                if (weight != 400) {
336                    newFace = new Typeface(nativeCreateWeightAlias(base.native_instance, weight));
337                }
338                systemFonts.put(alias.name, newFace);
339            }
340            sSystemFontMap = systemFonts;
341
342        } catch (RuntimeException e) {
343            Log.w(TAG, "Didn't create default family (most likely, non-Minikin build)", e);
344            // TODO: normal in non-Minikin case, remove or make error when Minikin-only
345        } catch (FileNotFoundException e) {
346            Log.e(TAG, "Error opening " + configFilename, e);
347        } catch (IOException e) {
348            Log.e(TAG, "Error reading " + configFilename, e);
349        } catch (XmlPullParserException e) {
350            Log.e(TAG, "XML parse exception for " + configFilename, e);
351        }
352    }
353
354    static {
355        init();
356        // Set up defaults and typefaces exposed in public API
357        DEFAULT         = create((String) null, 0);
358        DEFAULT_BOLD    = create((String) null, Typeface.BOLD);
359        SANS_SERIF      = create("sans-serif", 0);
360        SERIF           = create("serif", 0);
361        MONOSPACE       = create("monospace", 0);
362
363        sDefaults = new Typeface[] {
364            DEFAULT,
365            DEFAULT_BOLD,
366            create((String) null, Typeface.ITALIC),
367            create((String) null, Typeface.BOLD_ITALIC),
368        };
369
370    }
371
372    private static File getSystemFontConfigLocation() {
373        return new File("/system/etc/");
374    }
375
376    @Override
377    protected void finalize() throws Throwable {
378        try {
379            nativeUnref(native_instance);
380            native_instance = 0;  // Other finalizers can still call us.
381        } finally {
382            super.finalize();
383        }
384    }
385
386    @Override
387    public boolean equals(Object o) {
388        if (this == o) return true;
389        if (o == null || getClass() != o.getClass()) return false;
390
391        Typeface typeface = (Typeface) o;
392
393        return mStyle == typeface.mStyle && native_instance == typeface.native_instance;
394    }
395
396    @Override
397    public int hashCode() {
398        /*
399         * Modified method for hashCode with long native_instance derived from
400         * http://developer.android.com/reference/java/lang/Object.html
401         */
402        int result = 17;
403        result = 31 * result + (int) (native_instance ^ (native_instance >>> 32));
404        result = 31 * result + mStyle;
405        return result;
406    }
407
408    private static native long nativeCreateFromTypeface(long native_instance, int style);
409    private static native long nativeCreateWeightAlias(long native_instance, int weight);
410    private static native void nativeUnref(long native_instance);
411    private static native int  nativeGetStyle(long native_instance);
412    private static native long nativeCreateFromArray(long[] familyArray);
413    private static native void nativeSetDefault(long native_instance);
414}
415