1baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Gupta/* 2baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Gupta * Copyright (C) 2014 The Android Open Source Project 3baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Gupta * 4baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Gupta * Licensed under the Apache License, Version 2.0 (the "License"); 5baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Gupta * you may not use this file except in compliance with the License. 6baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Gupta * You may obtain a copy of the License at 7baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Gupta * 8baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Gupta * http://www.apache.org/licenses/LICENSE-2.0 9baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Gupta * 10baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Gupta * Unless required by applicable law or agreed to in writing, software 11baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Gupta * distributed under the License is distributed on an "AS IS" BASIS, 12baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Gupta * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Gupta * See the License for the specific language governing permissions and 14baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Gupta * limitations under the License. 15baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Gupta */ 16baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Gupta 17baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Guptapackage android.graphics; 18baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Gupta 19145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Guptaimport com.android.annotations.NonNull; 20145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Guptaimport com.android.annotations.Nullable; 21baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Guptaimport com.android.ide.common.rendering.api.LayoutLog; 22baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Guptaimport com.android.layoutlib.bridge.Bridge; 23baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Guptaimport com.android.layoutlib.bridge.impl.DelegateManager; 24baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Guptaimport com.android.tools.layoutlib.annotations.LayoutlibDelegate; 25baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Gupta 26ad69aee5ed503d9592c10a0f6d1a5b617d99e6c5Deepanshu Guptaimport android.content.res.AssetManager; 27ad69aee5ed503d9592c10a0f6d1a5b617d99e6c5Deepanshu Gupta 28baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Guptaimport java.awt.Font; 29b0d767dcf17dd393646b8fc3e8c9f46dfe12e230Deepanshu Guptaimport java.awt.FontFormatException; 30baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Guptaimport java.io.File; 310e4be2540984235a0a7b84ea0466ef3c92d27b07Deepanshu Guptaimport java.io.FileNotFoundException; 32baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Guptaimport java.util.ArrayList; 33e644ff8d92ba040d11636be0fb6c433b52bcc6c2Deepanshu Guptaimport java.util.Collections; 34e644ff8d92ba040d11636be0fb6c433b52bcc6c2Deepanshu Guptaimport java.util.HashSet; 35baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Guptaimport java.util.List; 360e4be2540984235a0a7b84ea0466ef3c92d27b07Deepanshu Guptaimport java.util.Scanner; 37e644ff8d92ba040d11636be0fb6c433b52bcc6c2Deepanshu Guptaimport java.util.Set; 38baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Gupta 39baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Guptaimport static android.graphics.Typeface_Delegate.SYSTEM_FONTS; 40baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Gupta 41baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Gupta/** 42baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Gupta * Delegate implementing the native methods of android.graphics.FontFamily 43baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Gupta * 44baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Gupta * Through the layoutlib_create tool, the original native methods of FontFamily have been replaced 45baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Gupta * by calls to methods of the same name in this delegate class. 46baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Gupta * 47baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Gupta * This class behaves like the original native implementation, but in Java, keeping previously 48baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Gupta * native data into its own objects and mapping them to int that are sent back and forth between 49baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Gupta * it and the original FontFamily class. 50baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Gupta * 51baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Gupta * @see DelegateManager 52baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Gupta */ 53baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Guptapublic class FontFamily_Delegate { 54baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Gupta 55145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta public static final int DEFAULT_FONT_WEIGHT = 400; 56145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta public static final int BOLD_FONT_WEIGHT_DELTA = 300; 57145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta public static final int BOLD_FONT_WEIGHT = 700; 58145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta 59baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Gupta // FONT_SUFFIX_ITALIC will always match FONT_SUFFIX_BOLDITALIC and hence it must be checked 60baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Gupta // separately. 61baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Gupta private static final String FONT_SUFFIX_ITALIC = "Italic.ttf"; 620e4be2540984235a0a7b84ea0466ef3c92d27b07Deepanshu Gupta private static final String FN_ALL_FONTS_LIST = "fontsInSdk.txt"; 63e644ff8d92ba040d11636be0fb6c433b52bcc6c2Deepanshu Gupta 64baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Gupta /** 65baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Gupta * A class associating {@link Font} with its metadata. 66baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Gupta */ 67baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Gupta private static final class FontInfo { 68145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta @Nullable 69baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Gupta Font mFont; 70145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta int mWeight; 71145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta boolean mIsItalic; 72baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Gupta } 73baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Gupta 74baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Gupta // ---- delegate manager ---- 75baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Gupta private static final DelegateManager<FontFamily_Delegate> sManager = 76baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Gupta new DelegateManager<FontFamily_Delegate>(FontFamily_Delegate.class); 77baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Gupta 78baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Gupta // ---- delegate helper data ---- 79baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Gupta private static String sFontLocation; 80baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Gupta private static final List<FontFamily_Delegate> sPostInitDelegate = new 81baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Gupta ArrayList<FontFamily_Delegate>(); 820e4be2540984235a0a7b84ea0466ef3c92d27b07Deepanshu Gupta private static Set<String> SDK_FONTS; 83baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Gupta 84baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Gupta 85baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Gupta // ---- delegate data ---- 86baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Gupta private List<FontInfo> mFonts = new ArrayList<FontInfo>(); 87145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta 88ad69aee5ed503d9592c10a0f6d1a5b617d99e6c5Deepanshu Gupta /** 89ad69aee5ed503d9592c10a0f6d1a5b617d99e6c5Deepanshu Gupta * The variant of the Font Family - compact or elegant. 90145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta * <p/> 91ad69aee5ed503d9592c10a0f6d1a5b617d99e6c5Deepanshu Gupta * 0 is unspecified, 1 is compact and 2 is elegant. This needs to be kept in sync with values in 92ad69aee5ed503d9592c10a0f6d1a5b617d99e6c5Deepanshu Gupta * android.graphics.FontFamily 93ad69aee5ed503d9592c10a0f6d1a5b617d99e6c5Deepanshu Gupta * 94ad69aee5ed503d9592c10a0f6d1a5b617d99e6c5Deepanshu Gupta * @see Paint#setElegantTextHeight(boolean) 95ad69aee5ed503d9592c10a0f6d1a5b617d99e6c5Deepanshu Gupta */ 96ad69aee5ed503d9592c10a0f6d1a5b617d99e6c5Deepanshu Gupta private FontVariant mVariant; 97145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta // List of runnables to process fonts after sFontLoader is initialized. 98145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta private List<Runnable> mPostInitRunnables = new ArrayList<Runnable>(); 99e644ff8d92ba040d11636be0fb6c433b52bcc6c2Deepanshu Gupta /** @see #isValid() */ 100e644ff8d92ba040d11636be0fb6c433b52bcc6c2Deepanshu Gupta private boolean mValid = false; 101baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Gupta 102baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Gupta 1033c937cf5c730519e750cdee4d5fa61e2a593e33aDeepanshu Gupta // ---- Public helper class ---- 1043c937cf5c730519e750cdee4d5fa61e2a593e33aDeepanshu Gupta 1053c937cf5c730519e750cdee4d5fa61e2a593e33aDeepanshu Gupta public enum FontVariant { 1063c937cf5c730519e750cdee4d5fa61e2a593e33aDeepanshu Gupta // The order needs to be kept in sync with android.graphics.FontFamily. 1073c937cf5c730519e750cdee4d5fa61e2a593e33aDeepanshu Gupta NONE, COMPACT, ELEGANT 1083c937cf5c730519e750cdee4d5fa61e2a593e33aDeepanshu Gupta } 1093c937cf5c730519e750cdee4d5fa61e2a593e33aDeepanshu Gupta 110baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Gupta // ---- Public Helper methods ---- 111baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Gupta 112baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Gupta public static FontFamily_Delegate getDelegate(long nativeFontFamily) { 113baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Gupta return sManager.getDelegate(nativeFontFamily); 114baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Gupta } 115baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Gupta 116baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Gupta public static synchronized void setFontLocation(String fontLocation) { 117baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Gupta sFontLocation = fontLocation; 1180e4be2540984235a0a7b84ea0466ef3c92d27b07Deepanshu Gupta // init list of bundled fonts. 1190e4be2540984235a0a7b84ea0466ef3c92d27b07Deepanshu Gupta File allFonts = new File(fontLocation, FN_ALL_FONTS_LIST); 1200e4be2540984235a0a7b84ea0466ef3c92d27b07Deepanshu Gupta // Current number of fonts is 103. Use the next round number to leave scope for more fonts 1210e4be2540984235a0a7b84ea0466ef3c92d27b07Deepanshu Gupta // in the future. 1220e4be2540984235a0a7b84ea0466ef3c92d27b07Deepanshu Gupta Set<String> allFontsList = new HashSet<String>(128); 1230e4be2540984235a0a7b84ea0466ef3c92d27b07Deepanshu Gupta Scanner scanner = null; 1240e4be2540984235a0a7b84ea0466ef3c92d27b07Deepanshu Gupta try { 1250e4be2540984235a0a7b84ea0466ef3c92d27b07Deepanshu Gupta scanner = new Scanner(allFonts); 1260e4be2540984235a0a7b84ea0466ef3c92d27b07Deepanshu Gupta while (scanner.hasNext()) { 1270e4be2540984235a0a7b84ea0466ef3c92d27b07Deepanshu Gupta String name = scanner.next(); 1280e4be2540984235a0a7b84ea0466ef3c92d27b07Deepanshu Gupta // Skip font configuration files. 1290e4be2540984235a0a7b84ea0466ef3c92d27b07Deepanshu Gupta if (!name.endsWith(".xml")) { 1300e4be2540984235a0a7b84ea0466ef3c92d27b07Deepanshu Gupta allFontsList.add(name); 1310e4be2540984235a0a7b84ea0466ef3c92d27b07Deepanshu Gupta } 1320e4be2540984235a0a7b84ea0466ef3c92d27b07Deepanshu Gupta } 1330e4be2540984235a0a7b84ea0466ef3c92d27b07Deepanshu Gupta } catch (FileNotFoundException e) { 1340e4be2540984235a0a7b84ea0466ef3c92d27b07Deepanshu Gupta Bridge.getLog().error(LayoutLog.TAG_BROKEN, 1350e4be2540984235a0a7b84ea0466ef3c92d27b07Deepanshu Gupta "Unable to load the list of fonts. Try re-installing the SDK Platform from the SDK Manager.", 1360e4be2540984235a0a7b84ea0466ef3c92d27b07Deepanshu Gupta e, null); 1370e4be2540984235a0a7b84ea0466ef3c92d27b07Deepanshu Gupta } finally { 1380e4be2540984235a0a7b84ea0466ef3c92d27b07Deepanshu Gupta if (scanner != null) { 1390e4be2540984235a0a7b84ea0466ef3c92d27b07Deepanshu Gupta scanner.close(); 1400e4be2540984235a0a7b84ea0466ef3c92d27b07Deepanshu Gupta } 1410e4be2540984235a0a7b84ea0466ef3c92d27b07Deepanshu Gupta } 1420e4be2540984235a0a7b84ea0466ef3c92d27b07Deepanshu Gupta SDK_FONTS = Collections.unmodifiableSet(allFontsList); 143baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Gupta for (FontFamily_Delegate fontFamily : sPostInitDelegate) { 144baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Gupta fontFamily.init(); 145baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Gupta } 146baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Gupta sPostInitDelegate.clear(); 147baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Gupta } 148baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Gupta 149145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta @Nullable 150145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta public Font getFont(int desiredWeight, boolean isItalic) { 151145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta FontInfo desiredStyle = new FontInfo(); 152145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta desiredStyle.mWeight = desiredWeight; 153145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta desiredStyle.mIsItalic = isItalic; 154145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta FontInfo bestFont = null; 155145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta int bestMatch = Integer.MAX_VALUE; 156baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Gupta for (FontInfo font : mFonts) { 157145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta int match = computeMatch(font, desiredStyle); 158145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta if (match < bestMatch) { 159145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta bestMatch = match; 160145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta bestFont = font; 161baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Gupta } 162baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Gupta } 163145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta if (bestFont == null) { 164145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta return null; 165baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Gupta } 166145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta if (bestMatch == 0) { 167145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta return bestFont.mFont; 168145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta } 169145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta // Derive the font as required and add it to the list of Fonts. 170145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta deriveFont(bestFont, desiredStyle); 171145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta addFont(desiredStyle); 172145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta return desiredStyle.mFont; 173baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Gupta } 174baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Gupta 175ad69aee5ed503d9592c10a0f6d1a5b617d99e6c5Deepanshu Gupta public FontVariant getVariant() { 176ad69aee5ed503d9592c10a0f6d1a5b617d99e6c5Deepanshu Gupta return mVariant; 177ad69aee5ed503d9592c10a0f6d1a5b617d99e6c5Deepanshu Gupta } 178ad69aee5ed503d9592c10a0f6d1a5b617d99e6c5Deepanshu Gupta 179e644ff8d92ba040d11636be0fb6c433b52bcc6c2Deepanshu Gupta /** 180e644ff8d92ba040d11636be0fb6c433b52bcc6c2Deepanshu Gupta * Returns if the FontFamily should contain any fonts. If this returns true and 181145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta * {@link #getFont(int, boolean)} returns an empty list, it means that an error occurred while 182145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta * loading the fonts. However, some fonts are deliberately skipped, for example they are not 183145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta * bundled with the SDK. In such a case, this method returns false. 184e644ff8d92ba040d11636be0fb6c433b52bcc6c2Deepanshu Gupta */ 185e644ff8d92ba040d11636be0fb6c433b52bcc6c2Deepanshu Gupta public boolean isValid() { 186e644ff8d92ba040d11636be0fb6c433b52bcc6c2Deepanshu Gupta return mValid; 187e644ff8d92ba040d11636be0fb6c433b52bcc6c2Deepanshu Gupta } 188e644ff8d92ba040d11636be0fb6c433b52bcc6c2Deepanshu Gupta 1893c937cf5c730519e750cdee4d5fa61e2a593e33aDeepanshu Gupta /*package*/ static Font loadFont(String path) { 1903c937cf5c730519e750cdee4d5fa61e2a593e33aDeepanshu Gupta if (path.startsWith(SYSTEM_FONTS) ) { 1913c937cf5c730519e750cdee4d5fa61e2a593e33aDeepanshu Gupta String relativePath = path.substring(SYSTEM_FONTS.length()); 1923c937cf5c730519e750cdee4d5fa61e2a593e33aDeepanshu Gupta File f = new File(sFontLocation, relativePath); 1933c937cf5c730519e750cdee4d5fa61e2a593e33aDeepanshu Gupta 1943c937cf5c730519e750cdee4d5fa61e2a593e33aDeepanshu Gupta try { 1953c937cf5c730519e750cdee4d5fa61e2a593e33aDeepanshu Gupta return Font.createFont(Font.TRUETYPE_FONT, f); 1963c937cf5c730519e750cdee4d5fa61e2a593e33aDeepanshu Gupta } catch (Exception e) { 197b0d767dcf17dd393646b8fc3e8c9f46dfe12e230Deepanshu Gupta if (path.endsWith(".otf") && e instanceof FontFormatException) { 198b0d767dcf17dd393646b8fc3e8c9f46dfe12e230Deepanshu Gupta // If we aren't able to load an Open Type font, don't log a warning just yet. 199b0d767dcf17dd393646b8fc3e8c9f46dfe12e230Deepanshu Gupta // We wait for a case where font is being used. Only then we try to log the 200b0d767dcf17dd393646b8fc3e8c9f46dfe12e230Deepanshu Gupta // warning. 201b0d767dcf17dd393646b8fc3e8c9f46dfe12e230Deepanshu Gupta return null; 202b0d767dcf17dd393646b8fc3e8c9f46dfe12e230Deepanshu Gupta } 2033c937cf5c730519e750cdee4d5fa61e2a593e33aDeepanshu Gupta Bridge.getLog().fidelityWarning(LayoutLog.TAG_BROKEN, 2043c937cf5c730519e750cdee4d5fa61e2a593e33aDeepanshu Gupta String.format("Unable to load font %1$s", relativePath), 205b0d767dcf17dd393646b8fc3e8c9f46dfe12e230Deepanshu Gupta e, null); 2063c937cf5c730519e750cdee4d5fa61e2a593e33aDeepanshu Gupta } 2073c937cf5c730519e750cdee4d5fa61e2a593e33aDeepanshu Gupta } else { 2083c937cf5c730519e750cdee4d5fa61e2a593e33aDeepanshu Gupta Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED, 2093c937cf5c730519e750cdee4d5fa61e2a593e33aDeepanshu Gupta "Only platform fonts located in " + SYSTEM_FONTS + "can be loaded.", 210b0d767dcf17dd393646b8fc3e8c9f46dfe12e230Deepanshu Gupta null, null); 2113c937cf5c730519e750cdee4d5fa61e2a593e33aDeepanshu Gupta } 2123c937cf5c730519e750cdee4d5fa61e2a593e33aDeepanshu Gupta 2133c937cf5c730519e750cdee4d5fa61e2a593e33aDeepanshu Gupta return null; 2143c937cf5c730519e750cdee4d5fa61e2a593e33aDeepanshu Gupta } 2153c937cf5c730519e750cdee4d5fa61e2a593e33aDeepanshu Gupta 216ad69aee5ed503d9592c10a0f6d1a5b617d99e6c5Deepanshu Gupta 217baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Gupta // ---- native methods ---- 218baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Gupta 219baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Gupta @LayoutlibDelegate 220ad69aee5ed503d9592c10a0f6d1a5b617d99e6c5Deepanshu Gupta /*package*/ static long nCreateFamily(String lang, int variant) { 221ad69aee5ed503d9592c10a0f6d1a5b617d99e6c5Deepanshu Gupta // TODO: support lang. This is required for japanese locale. 222baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Gupta FontFamily_Delegate delegate = new FontFamily_Delegate(); 223ad69aee5ed503d9592c10a0f6d1a5b617d99e6c5Deepanshu Gupta // variant can be 0, 1 or 2. 224ad69aee5ed503d9592c10a0f6d1a5b617d99e6c5Deepanshu Gupta assert variant < 3; 225ad69aee5ed503d9592c10a0f6d1a5b617d99e6c5Deepanshu Gupta delegate.mVariant = FontVariant.values()[variant]; 226baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Gupta if (sFontLocation != null) { 227baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Gupta delegate.init(); 228baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Gupta } else { 229baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Gupta sPostInitDelegate.add(delegate); 230baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Gupta } 231baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Gupta return sManager.addNewDelegate(delegate); 232baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Gupta } 233baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Gupta 234baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Gupta @LayoutlibDelegate 235baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Gupta /*package*/ static void nUnrefFamily(long nativePtr) { 2369113968f9570b0c8ada2dec34fa6cf893da7c022Deepanshu Gupta // Removing the java reference for the object doesn't mean that it's freed for garbage 2379113968f9570b0c8ada2dec34fa6cf893da7c022Deepanshu Gupta // collection. Typeface_Delegate may still hold a reference for it. 238baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Gupta sManager.removeJavaReferenceFor(nativePtr); 239baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Gupta } 240baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Gupta 241baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Gupta @LayoutlibDelegate 242145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta /*package*/ static boolean nAddFont(long nativeFamily, final String path) { 243145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta final FontFamily_Delegate delegate = getDelegate(nativeFamily); 244baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Gupta if (delegate != null) { 245baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Gupta if (sFontLocation == null) { 246145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta delegate.mPostInitRunnables.add(new Runnable() { 247145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta @Override 248145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta public void run() { 249145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta delegate.addFont(path); 250145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta } 251145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta }); 252baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Gupta return true; 253baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Gupta } 254baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Gupta return delegate.addFont(path); 255baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Gupta } 256baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Gupta return false; 257baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Gupta } 258baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Gupta 259ad69aee5ed503d9592c10a0f6d1a5b617d99e6c5Deepanshu Gupta @LayoutlibDelegate 260145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta /*package*/ static boolean nAddFontWeightStyle(long nativeFamily, final String path, 261145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta final int weight, final boolean isItalic) { 262145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta final FontFamily_Delegate delegate = getDelegate(nativeFamily); 263145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta if (delegate != null) { 264145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta if (sFontLocation == null) { 265145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta delegate.mPostInitRunnables.add(new Runnable() { 266145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta @Override 267145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta public void run() { 268145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta delegate.addFont(path, weight, isItalic); 269145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta } 270145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta }); 271145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta return true; 272145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta } 273145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta return delegate.addFont(path, weight, isItalic); 274145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta } 275145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta return false; 276145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta } 277145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta 278145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta @LayoutlibDelegate 279ad69aee5ed503d9592c10a0f6d1a5b617d99e6c5Deepanshu Gupta /*package*/ static boolean nAddFontFromAsset(long nativeFamily, AssetManager mgr, String path) { 280ad69aee5ed503d9592c10a0f6d1a5b617d99e6c5Deepanshu Gupta Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED, 281b0d767dcf17dd393646b8fc3e8c9f46dfe12e230Deepanshu Gupta "FontFamily.addFontFromAsset is not supported.", null, null); 282ad69aee5ed503d9592c10a0f6d1a5b617d99e6c5Deepanshu Gupta return false; 283ad69aee5ed503d9592c10a0f6d1a5b617d99e6c5Deepanshu Gupta } 284ad69aee5ed503d9592c10a0f6d1a5b617d99e6c5Deepanshu Gupta 2853c937cf5c730519e750cdee4d5fa61e2a593e33aDeepanshu Gupta 2863c937cf5c730519e750cdee4d5fa61e2a593e33aDeepanshu Gupta // ---- private helper methods ---- 2873c937cf5c730519e750cdee4d5fa61e2a593e33aDeepanshu Gupta 288baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Gupta private void init() { 289145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta for (Runnable postInitRunnable : mPostInitRunnables) { 290145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta postInitRunnable.run(); 291baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Gupta } 292145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta mPostInitRunnables = null; 293baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Gupta } 294baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Gupta 295145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta private boolean addFont(@NonNull String path) { 296145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta return addFont(path, DEFAULT_FONT_WEIGHT, path.endsWith(FONT_SUFFIX_ITALIC)); 297145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta } 298145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta 299145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta private boolean addFont(@NonNull String path, int weight, boolean isItalic) { 3000e4be2540984235a0a7b84ea0466ef3c92d27b07Deepanshu Gupta if (path.startsWith(SYSTEM_FONTS) && 3010e4be2540984235a0a7b84ea0466ef3c92d27b07Deepanshu Gupta !SDK_FONTS.contains(path.substring(SYSTEM_FONTS.length()))) { 3020e4be2540984235a0a7b84ea0466ef3c92d27b07Deepanshu Gupta return mValid = false; 3030e4be2540984235a0a7b84ea0466ef3c92d27b07Deepanshu Gupta } 3040e4be2540984235a0a7b84ea0466ef3c92d27b07Deepanshu Gupta // Set valid to true, even if the font fails to load. 3050e4be2540984235a0a7b84ea0466ef3c92d27b07Deepanshu Gupta mValid = true; 306baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Gupta Font font = loadFont(path); 307baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Gupta if (font == null) { 308baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Gupta return false; 309baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Gupta } 310baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Gupta FontInfo fontInfo = new FontInfo(); 311baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Gupta fontInfo.mFont = font; 312145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta fontInfo.mWeight = weight; 313145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta fontInfo.mIsItalic = isItalic; 314145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta addFont(fontInfo); 315145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta return true; 316145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta } 317145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta 318145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta private boolean addFont(@NonNull FontInfo fontInfo) { 319145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta int weight = fontInfo.mWeight; 320145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta boolean isItalic = fontInfo.mIsItalic; 321145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta // The list is usually just two fonts big. So iterating over all isn't as bad as it looks. 322145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta // It's biggest for roboto where the size is 12. 323145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta for (FontInfo font : mFonts) { 324145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta if (font.mWeight == weight && font.mIsItalic == isItalic) { 325145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta return false; 326145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta } 327145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta } 328baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Gupta mFonts.add(fontInfo); 329baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Gupta return true; 330baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Gupta } 331145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta 332145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta /** 333145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta * Compute matching metric between two styles - 0 is an exact match. 334145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta */ 335145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta private static int computeMatch(@NonNull FontInfo font1, @NonNull FontInfo font2) { 336145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta int score = Math.abs(font1.mWeight - font2.mWeight); 337145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta if (font1.mIsItalic != font2.mIsItalic) { 338145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta score += 200; 339145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta } 340145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta return score; 341145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta } 342145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta 343145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta /** 344145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta * Try to derive a font from {@code srcFont} for the style in {@code outFont}. 345145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta * <p/> 346145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta * {@code outFont} is updated to reflect the style of the derived font. 347145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta * @param srcFont the source font 348145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta * @param outFont contains the desired font style. Updated to contain the derived font and 349145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta * its style 350145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta * @return outFont 351145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta */ 352145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta @NonNull 353145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta private FontInfo deriveFont(@NonNull FontInfo srcFont, @NonNull FontInfo outFont) { 354145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta int desiredWeight = outFont.mWeight; 355145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta int srcWeight = srcFont.mWeight; 356145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta Font derivedFont = srcFont.mFont; 357145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta // Embolden the font if required. 358145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta if (desiredWeight >= BOLD_FONT_WEIGHT && desiredWeight - srcWeight > BOLD_FONT_WEIGHT_DELTA / 2) { 359145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta derivedFont = derivedFont.deriveFont(Font.BOLD); 360145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta srcWeight += BOLD_FONT_WEIGHT_DELTA; 361145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta } 362145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta // Italicize the font if required. 363145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta if (outFont.mIsItalic && !srcFont.mIsItalic) { 364145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta derivedFont = derivedFont.deriveFont(Font.ITALIC); 365145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta } else if (outFont.mIsItalic != srcFont.mIsItalic) { 366145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta // The desired font is plain, but the src font is italics. We can't convert it back. So 367145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta // we update the value to reflect the true style of the font we're deriving. 368145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta outFont.mIsItalic = srcFont.mIsItalic; 369145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta } 370145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta outFont.mFont = derivedFont; 371145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta outFont.mWeight = srcWeight; 372145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta // No need to update mIsItalics, as it's already been handled above. 373145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta return outFont; 374145bc2d067faa3fb49b71e9e8c8c70b40564061aDeepanshu Gupta } 375baef8c1ffe5c900fb0da9512654bf249b5fc9269Deepanshu Gupta} 376