Typeface.java revision a87b07d7fafd59ae26073a80cd742b17ea427ecd
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.util.ArrayList; 31import java.util.HashMap; 32import java.util.List; 33import java.util.Map; 34 35/** 36 * The Typeface class specifies the typeface and intrinsic style of a font. 37 * This is used in the paint, along with optionally Paint settings like 38 * textSize, textSkewX, textScaleX to specify 39 * how text appears when drawn (and measured). 40 */ 41public class Typeface { 42 43 private static String TAG = "Typeface"; 44 45 /** The default NORMAL typeface object */ 46 public static final Typeface DEFAULT; 47 /** 48 * The default BOLD typeface object. Note: this may be not actually be 49 * bold, depending on what fonts are installed. Call getStyle() to know 50 * for sure. 51 */ 52 public static final Typeface DEFAULT_BOLD; 53 /** The NORMAL style of the default sans serif typeface. */ 54 public static final Typeface SANS_SERIF; 55 /** The NORMAL style of the default serif typeface. */ 56 public static final Typeface SERIF; 57 /** The NORMAL style of the default monospace typeface. */ 58 public static final Typeface MONOSPACE; 59 60 static Typeface[] sDefaults; 61 private static final LongSparseArray<SparseArray<Typeface>> sTypefaceCache = 62 new LongSparseArray<SparseArray<Typeface>>(3); 63 64 static Typeface sDefaultTypeface; 65 static Map<String, Typeface> sSystemFontMap; 66 static FontFamily[] sFallbackFonts; 67 68 static final String FONTS_CONFIG = "fonts.xml"; 69 70 /** 71 * @hide 72 */ 73 public long native_instance; 74 75 // Style 76 public static final int NORMAL = 0; 77 public static final int BOLD = 1; 78 public static final int ITALIC = 2; 79 public static final int BOLD_ITALIC = 3; 80 81 private int mStyle = 0; 82 83 private static void setDefault(Typeface t) { 84 sDefaultTypeface = t; 85 nativeSetDefault(t.native_instance); 86 } 87 88 /** Returns the typeface's intrinsic style attributes */ 89 public int getStyle() { 90 return mStyle; 91 } 92 93 /** Returns true if getStyle() has the BOLD bit set. */ 94 public final boolean isBold() { 95 return (mStyle & BOLD) != 0; 96 } 97 98 /** Returns true if getStyle() has the ITALIC bit set. */ 99 public final boolean isItalic() { 100 return (mStyle & ITALIC) != 0; 101 } 102 103 /** 104 * Create a typeface object given a family name, and option style information. 105 * If null is passed for the name, then the "default" font will be chosen. 106 * The resulting typeface object can be queried (getStyle()) to discover what 107 * its "real" style characteristics are. 108 * 109 * @param familyName May be null. The name of the font family. 110 * @param style The style (normal, bold, italic) of the typeface. 111 * e.g. NORMAL, BOLD, ITALIC, BOLD_ITALIC 112 * @return The best matching typeface. 113 */ 114 public static Typeface create(String familyName, int style) { 115 if (sSystemFontMap != null) { 116 return create(sSystemFontMap.get(familyName), style); 117 } 118 return null; 119 } 120 121 /** 122 * Create a typeface object that best matches the specified existing 123 * typeface and the specified Style. Use this call if you want to pick a new 124 * style from the same family of an existing typeface object. If family is 125 * null, this selects from the default font's family. 126 * 127 * @param family May be null. The name of the existing type face. 128 * @param style The style (normal, bold, italic) of the typeface. 129 * e.g. NORMAL, BOLD, ITALIC, BOLD_ITALIC 130 * @return The best matching typeface. 131 */ 132 public static Typeface create(Typeface family, int style) { 133 if (style < 0 || style > 3) { 134 style = 0; 135 } 136 long ni = 0; 137 if (family != null) { 138 // Return early if we're asked for the same face/style 139 if (family.mStyle == style) { 140 return family; 141 } 142 143 ni = family.native_instance; 144 } 145 146 Typeface typeface; 147 SparseArray<Typeface> styles = sTypefaceCache.get(ni); 148 149 if (styles != null) { 150 typeface = styles.get(style); 151 if (typeface != null) { 152 return typeface; 153 } 154 } 155 156 typeface = new Typeface(nativeCreateFromTypeface(ni, style)); 157 if (styles == null) { 158 styles = new SparseArray<Typeface>(4); 159 sTypefaceCache.put(ni, styles); 160 } 161 styles.put(style, typeface); 162 163 return typeface; 164 } 165 166 /** 167 * Returns one of the default typeface objects, based on the specified style 168 * 169 * @return the default typeface that corresponds to the style 170 */ 171 public static Typeface defaultFromStyle(int style) { 172 return sDefaults[style]; 173 } 174 175 /** 176 * Create a new typeface from the specified font data. 177 * @param mgr The application's asset manager 178 * @param path The file name of the font data in the assets directory 179 * @return The new typeface. 180 */ 181 public static Typeface createFromAsset(AssetManager mgr, String path) { 182 if (sFallbackFonts != null) { 183 FontFamily fontFamily = new FontFamily(); 184 if (fontFamily.addFontFromAsset(mgr, path)) { 185 FontFamily[] families = { fontFamily }; 186 return createFromFamiliesWithDefault(families); 187 } 188 } 189 throw new RuntimeException("Font asset not found " + path); 190 } 191 192 /** 193 * Create a new typeface from the specified font file. 194 * 195 * @param path The path to the font data. 196 * @return The new typeface. 197 */ 198 public static Typeface createFromFile(File path) { 199 return createFromFile(path.getAbsolutePath()); 200 } 201 202 /** 203 * Create a new typeface from the specified font file. 204 * 205 * @param path The full path to the font data. 206 * @return The new typeface. 207 */ 208 public static Typeface createFromFile(String path) { 209 if (sFallbackFonts != null) { 210 FontFamily fontFamily = new FontFamily(); 211 if (fontFamily.addFont(path, 0 /* ttcIndex */)) { 212 FontFamily[] families = { fontFamily }; 213 return createFromFamiliesWithDefault(families); 214 } 215 } 216 throw new RuntimeException("Font not found " + path); 217 } 218 219 /** 220 * Create a new typeface from an array of font families. 221 * 222 * @param families array of font families 223 * @hide 224 */ 225 public static Typeface createFromFamilies(FontFamily[] families) { 226 long[] ptrArray = new long[families.length]; 227 for (int i = 0; i < families.length; i++) { 228 ptrArray[i] = families[i].mNativePtr; 229 } 230 return new Typeface(nativeCreateFromArray(ptrArray)); 231 } 232 233 /** 234 * Create a new typeface from an array of font families, including 235 * also the font families in the fallback list. 236 * 237 * @param families array of font families 238 * @hide 239 */ 240 public static Typeface createFromFamiliesWithDefault(FontFamily[] families) { 241 long[] ptrArray = new long[families.length + sFallbackFonts.length]; 242 for (int i = 0; i < families.length; i++) { 243 ptrArray[i] = families[i].mNativePtr; 244 } 245 for (int i = 0; i < sFallbackFonts.length; i++) { 246 ptrArray[i + families.length] = sFallbackFonts[i].mNativePtr; 247 } 248 return new Typeface(nativeCreateFromArray(ptrArray)); 249 } 250 251 // don't allow clients to call this directly 252 private Typeface(long ni) { 253 if (ni == 0) { 254 throw new RuntimeException("native typeface cannot be made"); 255 } 256 257 native_instance = ni; 258 mStyle = nativeGetStyle(ni); 259 } 260 261 private static FontFamily makeFamilyFromParsed(FontListParser.Family family) { 262 FontFamily fontFamily = new FontFamily(family.lang, family.variant); 263 for (FontListParser.Font font : family.fonts) { 264 fontFamily.addFontWeightStyle(font.fontName, font.ttcIndex, font.axes, 265 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 */ 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 FontListParser.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 FontListParser.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, e); 328 } catch (IOException e) { 329 Log.e(TAG, "Error reading " + configFilename, e); 330 } catch (XmlPullParserException e) { 331 Log.e(TAG, "XML parse exception for " + configFilename, e); 332 } 333 } 334 335 static { 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 353 private static File getSystemFontConfigLocation() { 354 return new File("/system/etc/"); 355 } 356 357 @Override 358 protected void finalize() throws Throwable { 359 try { 360 nativeUnref(native_instance); 361 native_instance = 0; // Other finalizers can still call us. 362 } finally { 363 super.finalize(); 364 } 365 } 366 367 @Override 368 public boolean equals(Object o) { 369 if (this == o) return true; 370 if (o == null || getClass() != o.getClass()) return false; 371 372 Typeface typeface = (Typeface) o; 373 374 return mStyle == typeface.mStyle && native_instance == typeface.native_instance; 375 } 376 377 @Override 378 public int hashCode() { 379 /* 380 * Modified method for hashCode with long native_instance derived from 381 * http://developer.android.com/reference/java/lang/Object.html 382 */ 383 int result = 17; 384 result = 31 * result + (int) (native_instance ^ (native_instance >>> 32)); 385 result = 31 * result + mStyle; 386 return result; 387 } 388 389 private static native long nativeCreateFromTypeface(long native_instance, int style); 390 private static native long nativeCreateWeightAlias(long native_instance, int weight); 391 private static native void nativeUnref(long native_instance); 392 private static native int nativeGetStyle(long native_instance); 393 private static native long nativeCreateFromArray(long[] familyArray); 394 private static native void nativeSetDefault(long native_instance); 395} 396