1 /* 2 * Copyright (C) 2018 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.fonts; 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.annotation.Nullable; 26 import android.graphics.FontFamily_Delegate.FontInfo; 27 import android.graphics.FontFamily_Delegate.FontVariant; 28 import android.graphics.Paint; 29 30 import java.awt.Font; 31 import java.io.ByteArrayInputStream; 32 import java.io.File; 33 import java.nio.ByteBuffer; 34 import java.util.LinkedHashMap; 35 import java.util.Map; 36 37 import libcore.util.NativeAllocationRegistry_Delegate; 38 39 import static android.graphics.FontFamily_Delegate.computeMatch; 40 import static android.graphics.FontFamily_Delegate.deriveFont; 41 42 /** 43 * Delegate implementing the native methods of android.graphics.fonts.FontFamily$Builder 44 * <p> 45 * Through the layoutlib_create tool, the original native methods of FontFamily$Builder have been 46 * replaced by calls to methods of the same name in this delegate class. 47 * <p> 48 * This class behaves like the original native implementation, but in Java, keeping previously 49 * native data into its own objects and mapping them to int that are sent back and forth between it 50 * and the original FontFamily$Builder class. 51 * 52 * @see DelegateManager 53 */ 54 public class FontFamily_Builder_Delegate { 55 private static final DelegateManager<FontFamily_Builder_Delegate> sBuilderManager = 56 new DelegateManager<>(FontFamily_Builder_Delegate.class); 57 58 private static long sFontFamilyFinalizer = -1; 59 60 // Order does not really matter but we use a LinkedHashMap to get reproducible results across 61 // render calls 62 private Map<FontInfo, Font> mFonts = new LinkedHashMap<>(); 63 /** 64 * The variant of the Font Family - compact or elegant. 65 * <p/> 66 * 0 is unspecified, 1 is compact and 2 is elegant. This needs to be kept in sync with values in 67 * android.graphics.FontFamily 68 * 69 * @see Paint#setElegantTextHeight(boolean) 70 */ 71 private FontVariant mVariant; 72 private boolean mIsCustomFallback; 73 74 @LayoutlibDelegate nInitBuilder()75 /*package*/ static long nInitBuilder() { 76 return sBuilderManager.addNewDelegate(new FontFamily_Builder_Delegate()); 77 } 78 79 @LayoutlibDelegate nAddFont(long builderPtr, long fontPtr)80 /*package*/ static void nAddFont(long builderPtr, long fontPtr) { 81 FontFamily_Builder_Delegate familyBuilder = sBuilderManager.getDelegate(builderPtr); 82 Font_Builder_Delegate fontBuilder = Font_Builder_Delegate.sBuilderManager.getDelegate(fontPtr); 83 if (familyBuilder == null || fontBuilder == null) { 84 return; 85 } 86 Font font; 87 if (fontBuilder.filePath.equals("")) { 88 font = loadFontBuffer(fontBuilder.mBuffer); 89 90 } else { 91 font = loadFontPath(fontBuilder.filePath); 92 } 93 if (font != null) { 94 familyBuilder.addFont(font, fontBuilder.mWeight, fontBuilder.mItalic); 95 } 96 } 97 98 @LayoutlibDelegate nBuild(long builderPtr, String langTags, int variant, boolean isCustomFallback)99 /*package*/ static long nBuild(long builderPtr, String langTags, int variant, 100 boolean isCustomFallback) { 101 FontFamily_Builder_Delegate builder = sBuilderManager.getDelegate(builderPtr); 102 if (builder != null) { 103 assert variant < 3; 104 builder.mVariant = FontVariant.values()[variant]; 105 builder.mIsCustomFallback = isCustomFallback; 106 } 107 return builderPtr; 108 } 109 110 @LayoutlibDelegate 111 /*package*/ static long nGetReleaseNativeFamily() { 112 synchronized (Font_Builder_Delegate.class) { 113 if (sFontFamilyFinalizer == -1) { 114 sFontFamilyFinalizer = NativeAllocationRegistry_Delegate.createFinalizer( 115 sBuilderManager::removeJavaReferenceFor); 116 } 117 } 118 return sFontFamilyFinalizer; 119 } 120 121 public static FontFamily_Builder_Delegate getDelegate(long nativeFontFamily) { 122 return sBuilderManager.getDelegate(nativeFontFamily); 123 } 124 125 @Nullable 126 public Font getFont(int desiredWeight, boolean isItalic) { 127 FontInfo desiredStyle = new FontInfo(); 128 desiredStyle.mWeight = desiredWeight; 129 desiredStyle.mIsItalic = isItalic; 130 131 Font cachedFont = mFonts.get(desiredStyle); 132 if (cachedFont != null) { 133 return cachedFont; 134 } 135 136 FontInfo bestFont = null; 137 138 if (mFonts.size() == 1) { 139 // No need to compute the match since we only have one candidate 140 bestFont = mFonts.keySet().iterator().next(); 141 } else { 142 int bestMatch = Integer.MAX_VALUE; 143 144 for (FontInfo font : mFonts.keySet()) { 145 int match = computeMatch(font, desiredStyle); 146 if (match < bestMatch) { 147 bestMatch = match; 148 bestFont = font; 149 if (bestMatch == 0) { 150 break; 151 } 152 } 153 } 154 } 155 156 if (bestFont == null) { 157 return null; 158 } 159 160 161 // Derive the font as required and add it to the list of Fonts. 162 deriveFont(bestFont, desiredStyle); 163 addFont(desiredStyle); 164 return desiredStyle.mFont; 165 } 166 167 public FontVariant getVariant() { 168 return mVariant; 169 } 170 171 // ---- private helper methods ---- 172 173 private void addFont(@NonNull Font font, int weight, boolean italic) { 174 FontInfo fontInfo = new FontInfo(); 175 fontInfo.mFont = font; 176 fontInfo.mWeight = weight; 177 fontInfo.mIsItalic = italic; 178 addFont(fontInfo); 179 } 180 181 private void addFont(@NonNull FontInfo fontInfo) { 182 mFonts.putIfAbsent(fontInfo, fontInfo.mFont); 183 } 184 185 private static Font loadFontBuffer(@NonNull ByteBuffer buffer) { 186 try { 187 byte[] byteArray = new byte[buffer.limit()]; 188 buffer.rewind(); 189 buffer.get(byteArray); 190 return Font.createFont(Font.TRUETYPE_FONT, new ByteArrayInputStream(byteArray)); 191 } catch (Exception e) { 192 Bridge.getLog().fidelityWarning(LayoutLog.TAG_BROKEN, "Unable to load font", 193 e, null, null); 194 } 195 196 return null; 197 } 198 199 private static Font loadFontPath(@NonNull String path) { 200 try { 201 File file = new File(path); 202 return Font.createFont(Font.TRUETYPE_FONT, file); 203 } catch (Exception e) { 204 Bridge.getLog().fidelityWarning(LayoutLog.TAG_BROKEN, "Unable to load font", 205 e, null, null); 206 } 207 208 return null; 209 } 210 } 211