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