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 17package android.graphics; 18 19import com.android.ide.common.rendering.api.LayoutLog; 20import com.android.layoutlib.bridge.Bridge; 21import com.android.layoutlib.bridge.impl.DelegateManager; 22import com.android.tools.layoutlib.annotations.LayoutlibDelegate; 23 24import android.annotation.NonNull; 25import android.graphics.FontFamily_Delegate.FontVariant; 26import android.graphics.fonts.FontVariationAxis; 27import android.text.FontConfig; 28 29import java.awt.Font; 30import java.io.File; 31import java.nio.ByteBuffer; 32import java.util.ArrayList; 33import java.util.List; 34import java.util.Map; 35 36import 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 */ 51public 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 + ((mStyle & Font.BOLD) == 0 ? 0 : FontFamily_Delegate.BOLD_FONT_WEIGHT_DELTA); 94 if (weight > 900) { 95 weight = 900; 96 } 97 final boolean isItalic = (mStyle & Font.ITALIC) != 0; 98 List<Font> fonts = new ArrayList<Font>(mFontFamilies.length); 99 for (int i = 0; i < mFontFamilies.length; i++) { 100 FontFamily_Delegate ffd = mFontFamilies[i]; 101 if (ffd != null && ffd.isValid()) { 102 Font font = ffd.getFont(weight, isItalic); 103 if (font != null) { 104 FontVariant ffdVariant = ffd.getVariant(); 105 if (ffdVariant == FontVariant.NONE) { 106 fonts.add(font); 107 continue; 108 } 109 // We cannot open each font and get locales supported, etc to match the fonts. 110 // As a workaround, we hardcode certain assumptions like Elegant and Compact 111 // always appear in pairs. 112 assert i < mFontFamilies.length - 1; 113 FontFamily_Delegate ffd2 = mFontFamilies[++i]; 114 assert ffd2 != null; 115 FontVariant ffd2Variant = ffd2.getVariant(); 116 Font font2 = ffd2.getFont(weight, isItalic); 117 assert ffd2Variant != FontVariant.NONE && ffd2Variant != ffdVariant 118 && font2 != null; 119 // Add the font with the matching variant to the list. 120 if (variant == ffd.getVariant()) { 121 fonts.add(font); 122 } else { 123 fonts.add(font2); 124 } 125 } else { 126 // The FontFamily is valid but doesn't contain any matching font. This means 127 // that the font failed to load. We add null to the list of fonts. Don't throw 128 // the warning just yet. If this is a non-english font, we don't want to warn 129 // users who are trying to render only english text. 130 fonts.add(null); 131 } 132 } 133 } 134 return fonts; 135 } 136 137 /** 138 * Clear the default typefaces when disposing bridge. 139 */ 140 public static void resetDefaults() { 141 // Sometimes this is called before the Bridge is initialized. In that case, we don't want to 142 // initialize Typeface because the SDK fonts location hasn't been set. 143 if (FontFamily_Delegate.getFontLocation() != null) { 144 Typeface.sDefaults = null; 145 } 146 } 147 148 149 // ---- native methods ---- 150 151 @LayoutlibDelegate 152 /*package*/ static synchronized long nativeCreateFromTypeface(long native_instance, int style) { 153 Typeface_Delegate delegate = sManager.getDelegate(native_instance); 154 if (delegate == null) { 155 delegate = sManager.getDelegate(sDefaultTypeface); 156 } 157 if (delegate == null) { 158 return 0; 159 } 160 161 return sManager.addNewDelegate(new Typeface_Delegate(delegate.mFontFamilies, style, 162 delegate.mWeight)); 163 } 164 165 @LayoutlibDelegate 166 /*package*/ static synchronized long nativeCreateFromTypefaceWithVariation(long native_instance, 167 List<FontVariationAxis> axes) { 168 long newInstance = nativeCreateFromTypeface(native_instance, 0); 169 170 if (newInstance != 0) { 171 Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED, 172 "nativeCreateFromTypefaceWithVariation is not supported", null, null); 173 } 174 return newInstance; 175 } 176 177 @LayoutlibDelegate 178 /*package*/ static synchronized int[] nativeGetSupportedAxes(long native_instance) { 179 // nativeCreateFromTypefaceWithVariation is not supported so we do not keep the axes 180 return null; 181 } 182 183 @LayoutlibDelegate 184 /*package*/ static long nativeCreateWeightAlias(long native_instance, int weight) { 185 Typeface_Delegate delegate = sManager.getDelegate(native_instance); 186 if (delegate == null) { 187 delegate = sManager.getDelegate(sDefaultTypeface); 188 } 189 if (delegate == null) { 190 return 0; 191 } 192 Typeface_Delegate weightAlias = 193 new Typeface_Delegate(delegate.mFontFamilies, delegate.mStyle, weight); 194 return sManager.addNewDelegate(weightAlias); 195 } 196 197 @LayoutlibDelegate 198 /*package*/ static synchronized long nativeCreateFromArray(long[] familyArray) { 199 FontFamily_Delegate[] fontFamilies = new FontFamily_Delegate[familyArray.length]; 200 for (int i = 0; i < familyArray.length; i++) { 201 fontFamilies[i] = FontFamily_Delegate.getDelegate(familyArray[i]); 202 } 203 Typeface_Delegate delegate = new Typeface_Delegate(fontFamilies, Typeface.NORMAL); 204 return sManager.addNewDelegate(delegate); 205 } 206 207 @LayoutlibDelegate 208 /*package*/ static void nativeUnref(long native_instance) { 209 sManager.removeJavaReferenceFor(native_instance); 210 } 211 212 @LayoutlibDelegate 213 /*package*/ static int nativeGetStyle(long native_instance) { 214 Typeface_Delegate delegate = sManager.getDelegate(native_instance); 215 if (delegate == null) { 216 return 0; 217 } 218 219 return delegate.mStyle; 220 } 221 222 @LayoutlibDelegate 223 /*package*/ static void nativeSetDefault(long native_instance) { 224 sDefaultTypeface = native_instance; 225 } 226 227 @LayoutlibDelegate 228 /*package*/ static File getSystemFontConfigLocation() { 229 return new File(getFontLocation()); 230 } 231 232 @LayoutlibDelegate 233 /*package*/ static FontFamily makeFamilyFromParsed(FontConfig.Family family, 234 Map<String, ByteBuffer> bufferForPath) { 235 FontFamily fontFamily = new FontFamily(family.getLanguage(), family.getVariant()); 236 for (FontConfig.Font font : family.getFonts()) { 237 String fullPathName = "/system/fonts/" + font.getFontName(); 238 FontFamily_Delegate.addFont(fontFamily.mBuilderPtr, fullPathName, 239 font.getWeight(), font.isItalic()); 240 } 241 fontFamily.freeze(); 242 return fontFamily; 243 } 244 245 // ---- Private delegate/helper methods ---- 246 247 private Typeface_Delegate(@NonNull FontFamily_Delegate[] fontFamilies, int style) { 248 this(fontFamilies, style, FontFamily_Delegate.DEFAULT_FONT_WEIGHT); 249 } 250 251 public Typeface_Delegate(@NonNull FontFamily_Delegate[] fontFamilies, int style, int weight) { 252 mFontFamilies = fontFamilies; 253 mStyle = style; 254 mWeight = weight; 255 } 256} 257