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