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