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