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