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.annotations.NonNull; 20 import com.android.layoutlib.bridge.impl.DelegateManager; 21 import com.android.tools.layoutlib.annotations.LayoutlibDelegate; 22 23 import android.graphics.FontFamily_Delegate.FontVariant; 24 25 import java.awt.Font; 26 import java.io.File; 27 import java.util.ArrayList; 28 import java.util.List; 29 30 import static android.graphics.FontFamily_Delegate.getFontLocation; 31 32 /** 33 * Delegate implementing the native methods of android.graphics.Typeface 34 * 35 * Through the layoutlib_create tool, the original native methods of Typeface have been replaced 36 * by calls to methods of the same name in this delegate class. 37 * 38 * This class behaves like the original native implementation, but in Java, keeping previously 39 * native data into its own objects and mapping them to int that are sent back and forth between 40 * it and the original Typeface class. 41 * 42 * @see DelegateManager 43 * 44 */ 45 public final class Typeface_Delegate { 46 47 public static final String SYSTEM_FONTS = "/system/fonts/"; 48 49 // ---- delegate manager ---- 50 private static final DelegateManager<Typeface_Delegate> sManager = 51 new DelegateManager<Typeface_Delegate>(Typeface_Delegate.class); 52 53 54 // ---- delegate data ---- 55 56 @NonNull 57 private final FontFamily_Delegate[] mFontFamilies; // the reference to FontFamily_Delegate. 58 /** @see Font#getStyle() */ 59 private final int mStyle; 60 private final int mWeight; 61 62 private static long sDefaultTypeface; 63 64 65 // ---- Public Helper methods ---- 66 getDelegate(long nativeTypeface)67 public static Typeface_Delegate getDelegate(long nativeTypeface) { 68 return sManager.getDelegate(nativeTypeface); 69 } 70 71 /** 72 * Return a list of fonts that match the style and variant. The list is ordered according to 73 * preference of fonts. 74 * 75 * The list may contain null when the font failed to load. If null is reached when trying to 76 * render with this list of fonts, then a warning should be logged letting the user know that 77 * some font failed to load. 78 * 79 * @param variant The variant preferred. Can only be {@link FontVariant#COMPACT} or 80 * {@link FontVariant#ELEGANT} 81 */ 82 @NonNull getFonts(FontVariant variant)83 public List<Font> getFonts(FontVariant variant) { 84 assert variant != FontVariant.NONE; 85 86 // Calculate the required weight based on style and weight of this typeface. 87 int weight = mWeight + ((mStyle & Font.BOLD) == 0 ? 0 : FontFamily_Delegate.BOLD_FONT_WEIGHT_DELTA); 88 if (weight > 900) { 89 weight = 900; 90 } 91 final boolean isItalic = (mStyle & Font.ITALIC) != 0; 92 List<Font> fonts = new ArrayList<Font>(mFontFamilies.length); 93 for (int i = 0; i < mFontFamilies.length; i++) { 94 FontFamily_Delegate ffd = mFontFamilies[i]; 95 if (ffd != null && ffd.isValid()) { 96 Font font = ffd.getFont(weight, isItalic); 97 if (font != null) { 98 FontVariant ffdVariant = ffd.getVariant(); 99 if (ffdVariant == FontVariant.NONE) { 100 fonts.add(font); 101 continue; 102 } 103 // We cannot open each font and get locales supported, etc to match the fonts. 104 // As a workaround, we hardcode certain assumptions like Elegant and Compact 105 // always appear in pairs. 106 assert i < mFontFamilies.length - 1; 107 FontFamily_Delegate ffd2 = mFontFamilies[++i]; 108 assert ffd2 != null; 109 FontVariant ffd2Variant = ffd2.getVariant(); 110 Font font2 = ffd2.getFont(weight, isItalic); 111 assert ffd2Variant != FontVariant.NONE && ffd2Variant != ffdVariant 112 && font2 != null; 113 // Add the font with the matching variant to the list. 114 if (variant == ffd.getVariant()) { 115 fonts.add(font); 116 } else { 117 fonts.add(font2); 118 } 119 } else { 120 // The FontFamily is valid but doesn't contain any matching font. This means 121 // that the font failed to load. We add null to the list of fonts. Don't throw 122 // the warning just yet. If this is a non-english font, we don't want to warn 123 // users who are trying to render only english text. 124 fonts.add(null); 125 } 126 } 127 } 128 return fonts; 129 } 130 131 /** 132 * Clear the default typefaces when disposing bridge. 133 */ 134 public static void resetDefaults() { 135 // Sometimes this is called before the Bridge is initialized. In that case, we don't want to 136 // initialize Typeface because the SDK fonts location hasn't been set. 137 if (FontFamily_Delegate.getFontLocation() != null) { 138 Typeface.sDefaults = null; 139 } 140 } 141 142 143 // ---- native methods ---- 144 145 @LayoutlibDelegate 146 /*package*/ static synchronized long nativeCreateFromTypeface(long native_instance, int style) { 147 Typeface_Delegate delegate = sManager.getDelegate(native_instance); 148 if (delegate == null) { 149 delegate = sManager.getDelegate(sDefaultTypeface); 150 } 151 if (delegate == null) { 152 return 0; 153 } 154 155 return sManager.addNewDelegate(new Typeface_Delegate(delegate.mFontFamilies, style, 156 delegate.mWeight)); 157 } 158 159 @LayoutlibDelegate 160 /*package*/ static long nativeCreateWeightAlias(long native_instance, int weight) { 161 Typeface_Delegate delegate = sManager.getDelegate(native_instance); 162 if (delegate == null) { 163 delegate = sManager.getDelegate(sDefaultTypeface); 164 } 165 if (delegate == null) { 166 return 0; 167 } 168 Typeface_Delegate weightAlias = 169 new Typeface_Delegate(delegate.mFontFamilies, delegate.mStyle, weight); 170 return sManager.addNewDelegate(weightAlias); 171 } 172 173 @LayoutlibDelegate 174 /*package*/ static synchronized long nativeCreateFromArray(long[] familyArray) { 175 FontFamily_Delegate[] fontFamilies = new FontFamily_Delegate[familyArray.length]; 176 for (int i = 0; i < familyArray.length; i++) { 177 fontFamilies[i] = FontFamily_Delegate.getDelegate(familyArray[i]); 178 } 179 Typeface_Delegate delegate = new Typeface_Delegate(fontFamilies, Typeface.NORMAL); 180 return sManager.addNewDelegate(delegate); 181 } 182 183 @LayoutlibDelegate 184 /*package*/ static void nativeUnref(long native_instance) { 185 sManager.removeJavaReferenceFor(native_instance); 186 } 187 188 @LayoutlibDelegate 189 /*package*/ static int nativeGetStyle(long native_instance) { 190 Typeface_Delegate delegate = sManager.getDelegate(native_instance); 191 if (delegate == null) { 192 return 0; 193 } 194 195 return delegate.mStyle; 196 } 197 198 @LayoutlibDelegate 199 /*package*/ static void nativeSetDefault(long native_instance) { 200 sDefaultTypeface = native_instance; 201 } 202 203 @LayoutlibDelegate 204 /*package*/ static File getSystemFontConfigLocation() { 205 return new File(getFontLocation()); 206 } 207 208 // ---- Private delegate/helper methods ---- 209 210 private Typeface_Delegate(@NonNull FontFamily_Delegate[] fontFamilies, int style) { 211 this(fontFamilies, style, FontFamily_Delegate.DEFAULT_FONT_WEIGHT); 212 } 213 214 public Typeface_Delegate(@NonNull FontFamily_Delegate[] fontFamilies, int style, int weight) { 215 mFontFamilies = fontFamilies; 216 mStyle = style; 217 mWeight = weight; 218 } 219 } 220