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