Home | History | Annotate | Download | only in graphics
      1 /*
      2  * Copyright (C) 2010 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 com.android.ide.common.rendering.api.LayoutLog;
     20 import com.android.layoutlib.bridge.Bridge;
     21 import com.android.layoutlib.bridge.impl.DelegateManager;
     22 import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
     23 
     24 import android.annotation.NonNull;
     25 import android.graphics.FontFamily_Delegate.FontVariant;
     26 import android.graphics.fonts.FontVariationAxis;
     27 import android.text.FontConfig;
     28 
     29 import java.awt.Font;
     30 import java.io.File;
     31 import java.nio.ByteBuffer;
     32 import java.util.ArrayList;
     33 import java.util.List;
     34 import java.util.Map;
     35 
     36 import static android.graphics.FontFamily_Delegate.getFontLocation;
     37 
     38 /**
     39  * Delegate implementing the native methods of android.graphics.Typeface
     40  *
     41  * Through the layoutlib_create tool, the original native methods of Typeface have been replaced
     42  * by calls to methods of the same name in this delegate class.
     43  *
     44  * This class behaves like the original native implementation, but in Java, keeping previously
     45  * native data into its own objects and mapping them to int that are sent back and forth between
     46  * it and the original Typeface class.
     47  *
     48  * @see DelegateManager
     49  *
     50  */
     51 public final class Typeface_Delegate {
     52 
     53     public static final String SYSTEM_FONTS = "/system/fonts/";
     54 
     55     // ---- delegate manager ----
     56     private static final DelegateManager<Typeface_Delegate> sManager =
     57             new DelegateManager<Typeface_Delegate>(Typeface_Delegate.class);
     58 
     59 
     60     // ---- delegate data ----
     61 
     62     @NonNull
     63     private final FontFamily_Delegate[] mFontFamilies;  // the reference to FontFamily_Delegate.
     64     /** @see Font#getStyle() */
     65     private final int mStyle;
     66     private final int mWeight;
     67 
     68     private static long sDefaultTypeface;
     69 
     70 
     71     // ---- Public Helper methods ----
     72 
     73     public static Typeface_Delegate getDelegate(long nativeTypeface) {
     74         return sManager.getDelegate(nativeTypeface);
     75     }
     76 
     77     /**
     78      * Return a list of fonts that match the style and variant. The list is ordered according to
     79      * preference of fonts.
     80      *
     81      * The list may contain null when the font failed to load. If null is reached when trying to
     82      * render with this list of fonts, then a warning should be logged letting the user know that
     83      * some font failed to load.
     84      *
     85      * @param variant The variant preferred. Can only be {@link FontVariant#COMPACT} or
     86      *                {@link FontVariant#ELEGANT}
     87      */
     88     @NonNull
     89     public List<Font> getFonts(FontVariant variant) {
     90         assert variant != FontVariant.NONE;
     91 
     92         // Calculate the required weight based on style and weight of this typeface.
     93         int weight = mWeight + 50 +
     94                 ((mStyle & Font.BOLD) == 0 ? 0 : FontFamily_Delegate.BOLD_FONT_WEIGHT_DELTA);
     95         if (weight > 1000) {
     96             weight = 1000;
     97         } else if (weight < 100) {
     98             weight = 100;
     99         }
    100         final boolean isItalic = (mStyle & Font.ITALIC) != 0;
    101         List<Font> fonts = new ArrayList<Font>(mFontFamilies.length);
    102         for (int i = 0; i < mFontFamilies.length; i++) {
    103             FontFamily_Delegate ffd = mFontFamilies[i];
    104             if (ffd != null && ffd.isValid()) {
    105                 Font font = ffd.getFont(weight, isItalic);
    106                 if (font != null) {
    107                     FontVariant ffdVariant = ffd.getVariant();
    108                     if (ffdVariant == FontVariant.NONE) {
    109                         fonts.add(font);
    110                         continue;
    111                     }
    112                     // We cannot open each font and get locales supported, etc to match the fonts.
    113                     // As a workaround, we hardcode certain assumptions like Elegant and Compact
    114                     // always appear in pairs.
    115                     assert i < mFontFamilies.length - 1;
    116                     FontFamily_Delegate ffd2 = mFontFamilies[++i];
    117                     assert ffd2 != null;
    118                     FontVariant ffd2Variant = ffd2.getVariant();
    119                     Font font2 = ffd2.getFont(weight, isItalic);
    120                     assert ffd2Variant != FontVariant.NONE && ffd2Variant != ffdVariant
    121                             && font2 != null;
    122                     // Add the font with the matching variant to the list.
    123                     if (variant == ffd.getVariant()) {
    124                         fonts.add(font);
    125                     } else {
    126                         fonts.add(font2);
    127                     }
    128                 } else {
    129                     // The FontFamily is valid but doesn't contain any matching font. This means
    130                     // that the font failed to load. We add null to the list of fonts. Don't throw
    131                     // the warning just yet. If this is a non-english font, we don't want to warn
    132                     // users who are trying to render only english text.
    133                     fonts.add(null);
    134                 }
    135             }
    136         }
    137         return fonts;
    138     }
    139 
    140     /**
    141      * Clear the default typefaces when disposing bridge.
    142      */
    143     public static void resetDefaults() {
    144         // Sometimes this is called before the Bridge is initialized. In that case, we don't want to
    145         // initialize Typeface because the SDK fonts location hasn't been set.
    146         if (FontFamily_Delegate.getFontLocation() != null) {
    147             Typeface.sDefaults = null;
    148         }
    149     }
    150 
    151 
    152     // ---- native methods ----
    153 
    154     @LayoutlibDelegate
    155     /*package*/ static synchronized long nativeCreateFromTypeface(long native_instance, int style) {
    156         Typeface_Delegate delegate = sManager.getDelegate(native_instance);
    157         if (delegate == null) {
    158             delegate = sManager.getDelegate(sDefaultTypeface);
    159         }
    160         if (delegate == null) {
    161             return 0;
    162         }
    163 
    164         return sManager.addNewDelegate(new Typeface_Delegate(delegate.mFontFamilies, style,
    165                 delegate.mWeight));
    166     }
    167 
    168     @LayoutlibDelegate
    169     /*package*/ static long nativeCreateFromTypefaceWithExactStyle(long native_instance,
    170             int weight, boolean italic) {
    171         Typeface_Delegate delegate = sManager.getDelegate(native_instance);
    172         if (delegate == null) {
    173             delegate = sManager.getDelegate(sDefaultTypeface);
    174         }
    175         if (delegate == null) {
    176             return 0;
    177         }
    178 
    179         int style = weight >= 600 ? (italic ? Typeface.BOLD_ITALIC : Typeface.BOLD) :
    180                 (italic ? Typeface.ITALIC : Typeface.NORMAL);
    181         return sManager.addNewDelegate(new Typeface_Delegate(delegate.mFontFamilies, style, weight));
    182     }
    183 
    184     @LayoutlibDelegate
    185     /*package*/ static synchronized long nativeCreateFromTypefaceWithVariation(long native_instance,
    186             List<FontVariationAxis> axes) {
    187         long newInstance = nativeCreateFromTypeface(native_instance, 0);
    188 
    189         if (newInstance != 0) {
    190             Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED,
    191                     "nativeCreateFromTypefaceWithVariation is not supported", null, null);
    192         }
    193         return newInstance;
    194     }
    195 
    196     @LayoutlibDelegate
    197     /*package*/ static synchronized int[] nativeGetSupportedAxes(long native_instance) {
    198         // nativeCreateFromTypefaceWithVariation is not supported so we do not keep the axes
    199         return null;
    200     }
    201 
    202     @LayoutlibDelegate
    203     /*package*/ static long nativeCreateWeightAlias(long native_instance, int weight) {
    204         Typeface_Delegate delegate = sManager.getDelegate(native_instance);
    205         if (delegate == null) {
    206             delegate = sManager.getDelegate(sDefaultTypeface);
    207         }
    208         if (delegate == null) {
    209             return 0;
    210         }
    211         Typeface_Delegate weightAlias =
    212                 new Typeface_Delegate(delegate.mFontFamilies, delegate.mStyle, weight);
    213         return sManager.addNewDelegate(weightAlias);
    214     }
    215 
    216     @LayoutlibDelegate
    217     /*package*/ static synchronized long nativeCreateFromArray(long[] familyArray, int weight,
    218             int italic) {
    219         FontFamily_Delegate[] fontFamilies = new FontFamily_Delegate[familyArray.length];
    220         for (int i = 0; i < familyArray.length; i++) {
    221             fontFamilies[i] = FontFamily_Delegate.getDelegate(familyArray[i]);
    222         }
    223         if (weight == Typeface.RESOLVE_BY_FONT_TABLE) {
    224             weight = 400;
    225         }
    226         if (italic == Typeface.RESOLVE_BY_FONT_TABLE) {
    227             italic = 0;
    228         }
    229         int style = weight >= 600 ? (italic == 1 ? Typeface.BOLD_ITALIC : Typeface.BOLD) :
    230                 (italic == 1 ? Typeface.ITALIC : Typeface.NORMAL);
    231         Typeface_Delegate delegate = new Typeface_Delegate(fontFamilies, style, weight);
    232         return sManager.addNewDelegate(delegate);
    233     }
    234 
    235     @LayoutlibDelegate
    236     /*package*/ static void nativeUnref(long native_instance) {
    237         sManager.removeJavaReferenceFor(native_instance);
    238     }
    239 
    240     @LayoutlibDelegate
    241     /*package*/ static int nativeGetStyle(long native_instance) {
    242         Typeface_Delegate delegate = sManager.getDelegate(native_instance);
    243         if (delegate == null) {
    244             return 0;
    245         }
    246 
    247         return delegate.mStyle;
    248     }
    249 
    250     @LayoutlibDelegate
    251     /*package*/ static void nativeSetDefault(long native_instance) {
    252         sDefaultTypeface = native_instance;
    253     }
    254 
    255     @LayoutlibDelegate
    256     /*package*/ static int nativeGetWeight(long native_instance) {
    257         Typeface_Delegate delegate = sManager.getDelegate(native_instance);
    258         if (delegate == null) {
    259             return 0;
    260         }
    261         return delegate.mWeight;
    262     }
    263 
    264     @LayoutlibDelegate
    265     /*package*/ static File getSystemFontConfigLocation() {
    266         return new File(getFontLocation());
    267     }
    268 
    269     @LayoutlibDelegate
    270     /*package*/ static FontFamily makeFamilyFromParsed(FontConfig.Family family,
    271             Map<String, ByteBuffer> bufferForPath) {
    272         FontFamily fontFamily = new FontFamily(family.getLanguage(), family.getVariant());
    273         for (FontConfig.Font font : family.getFonts()) {
    274             String fullPathName = "/system/fonts/" + font.getFontName();
    275             FontFamily_Delegate.addFont(fontFamily.mBuilderPtr, fullPathName,
    276                     font.getWeight(), font.isItalic());
    277         }
    278         fontFamily.freeze();
    279         return fontFamily;
    280     }
    281 
    282     // ---- Private delegate/helper methods ----
    283 
    284     public Typeface_Delegate(@NonNull FontFamily_Delegate[] fontFamilies, int style, int weight) {
    285         mFontFamilies = fontFamilies;
    286         mStyle = style;
    287         mWeight = weight;
    288     }
    289 }
    290