Home | History | Annotate | Download | only in graphics
      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 
     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 */
     90     public int getStyle() {
     91         return mStyle;
     92     }
     93 
     94     /** Returns true if getStyle() has the BOLD bit set. */
     95     public final boolean isBold() {
     96         return (mStyle & BOLD) != 0;
     97     }
     98 
     99     /** Returns true if getStyle() has the ITALIC bit set. */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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
    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 
    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      */
    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 {
    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         } finally {
    362             super.finalize();
    363         }
    364     }
    365 
    366     @Override
    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
    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 
    388     private static native long nativeCreateFromTypeface(long native_instance, int style);
    389     private static native long nativeCreateWeightAlias(long native_instance, int weight);
    390     private static native void nativeUnref(long native_instance);
    391     private static native int  nativeGetStyle(long native_instance);
    392     private static native long nativeCreateFromArray(long[] familyArray);
    393     private static native void nativeSetDefault(long native_instance);
    394 }
    395