FontFamily_Delegate.java revision b0d767dcf17dd393646b8fc3e8c9f46dfe12e230
1/* 2 * Copyright (C) 2014 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.content.res.AssetManager; 25 26import java.awt.Font; 27import java.awt.FontFormatException; 28import java.io.File; 29import java.util.ArrayList; 30import java.util.List; 31 32import static android.graphics.Typeface_Delegate.SYSTEM_FONTS; 33 34/** 35 * Delegate implementing the native methods of android.graphics.FontFamily 36 * 37 * Through the layoutlib_create tool, the original native methods of FontFamily 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 FontFamily class. 43 * 44 * @see DelegateManager 45 */ 46public class FontFamily_Delegate { 47 48 // FONT_SUFFIX_ITALIC will always match FONT_SUFFIX_BOLDITALIC and hence it must be checked 49 // separately. 50 private static final String FONT_SUFFIX_BOLDITALIC = "BoldItalic.ttf"; 51 private static final String FONT_SUFFIX_BOLD = "Bold.ttf"; 52 private static final String FONT_SUFFIX_ITALIC = "Italic.ttf"; 53 54 /** 55 * A class associating {@link Font} with its metadata. 56 */ 57 private static final class FontInfo { 58 Font mFont; 59 /** Regular, Bold, Italic, or BoldItalic. */ 60 int mStyle; 61 } 62 63 // ---- delegate manager ---- 64 private static final DelegateManager<FontFamily_Delegate> sManager = 65 new DelegateManager<FontFamily_Delegate>(FontFamily_Delegate.class); 66 67 // ---- delegate helper data ---- 68 private static String sFontLocation; 69 private static final List<FontFamily_Delegate> sPostInitDelegate = new 70 ArrayList<FontFamily_Delegate>(); 71 72 73 // ---- delegate data ---- 74 private List<FontInfo> mFonts = new ArrayList<FontInfo>(); 75 /** 76 * The variant of the Font Family - compact or elegant. 77 * 0 is unspecified, 1 is compact and 2 is elegant. This needs to be kept in sync with values in 78 * android.graphics.FontFamily 79 * 80 * @see Paint#setElegantTextHeight(boolean) 81 */ 82 private FontVariant mVariant; 83 // Path of fonts that haven't been created since sFontLoader hasn't been initialized. 84 private List<String> mPath = new ArrayList<String>(); 85 86 87 // ---- Public helper class ---- 88 89 public enum FontVariant { 90 // The order needs to be kept in sync with android.graphics.FontFamily. 91 NONE, COMPACT, ELEGANT 92 } 93 94 // ---- Public Helper methods ---- 95 96 public static FontFamily_Delegate getDelegate(long nativeFontFamily) { 97 return sManager.getDelegate(nativeFontFamily); 98 } 99 100 public static synchronized void setFontLocation(String fontLocation) { 101 sFontLocation = fontLocation; 102 for (FontFamily_Delegate fontFamily : sPostInitDelegate) { 103 fontFamily.init(); 104 } 105 sPostInitDelegate.clear(); 106 } 107 108 public Font getFont(int style) { 109 FontInfo plainFont = null; 110 for (FontInfo font : mFonts) { 111 if (font.mStyle == style) { 112 return font.mFont; 113 } 114 if (font.mStyle == Font.PLAIN && plainFont == null) { 115 plainFont = font; 116 } 117 } 118 119 // No font with the mentioned style is found. Try to derive one. 120 if (plainFont != null && style > 0 && style < 4) { 121 FontInfo styledFont = new FontInfo(); 122 styledFont.mFont = plainFont.mFont.deriveFont(style); 123 styledFont.mStyle = style; 124 // Add the font to the list of fonts so that we don't have to derive it the next time. 125 mFonts.add(styledFont); 126 return styledFont.mFont; 127 } 128 return null; 129 } 130 131 public FontVariant getVariant() { 132 return mVariant; 133 } 134 135 /*package*/ static int getFontStyle(String path) { 136 int style = Font.PLAIN; 137 String fontName = path.substring(path.lastIndexOf('/')); 138 if (fontName.endsWith(FONT_SUFFIX_BOLDITALIC)) { 139 style = Font.BOLD | Font.ITALIC; 140 } else if (fontName.endsWith(FONT_SUFFIX_BOLD)) { 141 style = Font.BOLD; 142 } else if (fontName.endsWith(FONT_SUFFIX_ITALIC)) { 143 style = Font.ITALIC; 144 } 145 return style; 146 } 147 148 /*package*/ static Font loadFont(String path) { 149 if (path.startsWith(SYSTEM_FONTS) ) { 150 String relativePath = path.substring(SYSTEM_FONTS.length()); 151 File f = new File(sFontLocation, relativePath); 152 153 try { 154 return Font.createFont(Font.TRUETYPE_FONT, f); 155 } catch (Exception e) { 156 if (path.endsWith(".otf") && e instanceof FontFormatException) { 157 // If we aren't able to load an Open Type font, don't log a warning just yet. 158 // We wait for a case where font is being used. Only then we try to log the 159 // warning. 160 return null; 161 } 162 Bridge.getLog().fidelityWarning(LayoutLog.TAG_BROKEN, 163 String.format("Unable to load font %1$s", relativePath), 164 e, null); 165 } 166 } else { 167 Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED, 168 "Only platform fonts located in " + SYSTEM_FONTS + "can be loaded.", 169 null, null); 170 } 171 172 return null; 173 } 174 175 176 // ---- native methods ---- 177 178 @LayoutlibDelegate 179 /*package*/ static long nCreateFamily(String lang, int variant) { 180 // TODO: support lang. This is required for japanese locale. 181 FontFamily_Delegate delegate = new FontFamily_Delegate(); 182 // variant can be 0, 1 or 2. 183 assert variant < 3; 184 delegate.mVariant = FontVariant.values()[variant]; 185 if (sFontLocation != null) { 186 delegate.init(); 187 } else { 188 sPostInitDelegate.add(delegate); 189 } 190 return sManager.addNewDelegate(delegate); 191 } 192 193 @LayoutlibDelegate 194 /*package*/ static void nUnrefFamily(long nativePtr) { 195 // Removing the java reference for the object doesn't mean that it's freed for garbage 196 // collection. Typeface_Delegate may still hold a reference for it. 197 sManager.removeJavaReferenceFor(nativePtr); 198 } 199 200 @LayoutlibDelegate 201 /*package*/ static boolean nAddFont(long nativeFamily, String path) { 202 FontFamily_Delegate delegate = getDelegate(nativeFamily); 203 if (delegate != null) { 204 if (sFontLocation == null) { 205 delegate.mPath.add(path); 206 return true; 207 } 208 return delegate.addFont(path); 209 } 210 return false; 211 } 212 213 @LayoutlibDelegate 214 /*package*/ static boolean nAddFontFromAsset(long nativeFamily, AssetManager mgr, String path) { 215 Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED, 216 "FontFamily.addFontFromAsset is not supported.", null, null); 217 return false; 218 } 219 220 221 // ---- private helper methods ---- 222 223 private void init() { 224 for (String path : mPath) { 225 addFont(path); 226 } 227 mPath = null; 228 } 229 230 private boolean addFont(String path) { 231 Font font = loadFont(path); 232 if (font == null) { 233 return false; 234 } 235 FontInfo fontInfo = new FontInfo(); 236 fontInfo.mFont = font; 237 fontInfo.mStyle = getFontStyle(path); 238 // TODO ensure that mFonts doesn't have the font with this style already. 239 mFonts.add(fontInfo); 240 return true; 241 } 242} 243