1 /*
2  * Copyright 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 static org.junit.Assert.assertEquals;
20 import static org.junit.Assert.assertNotEquals;
21 
22 import android.graphics.Typeface;
23 import android.graphics.cts.R;
24 import android.text.TextPaint;
25 import android.util.Pair;
26 
27 import java.util.ArrayList;
28 import java.util.Collections;
29 import java.util.HashMap;
30 import java.util.List;
31 import java.util.Locale;
32 import java.util.Map;
33 
34 /**
35  * Provides a utility for testing fonts
36  *
37  * For the purpose of testing font selection of families or fallbacks, this class provies following
38  * regular font files.
39  *
40  * - ascii_a3em_weight100_upright.ttf
41  *   'a' has 3em width and others have 1em width. The metadata has weight=100, non-italic value.
42  * - ascii_b3em_weight100_italic.ttf
43  *   'b' has 3em width and others have 1em width. The metadata has weight=100, italic value.
44  * - ascii_c3em_weight200_upright.ttf
45  *   'c' has 3em width and others have 1em width. The metadata has weight=200, non-italic value.
46  * - ascii_d3em_weight200_italic.ttf
47  *   'd' has 3em width and others have 1em width. The metadata has weight=200, italic value.
48  * - ascii_e3em_weight300_upright.ttf
49  *   'e' has 3em width and others have 1em width. The metadata has weight=300, non-italic value.
50  * - ascii_f3em_weight300_italic.ttf
51  *   'f' has 3em width and others have 1em width. The metadata has weight=300, italic value.
52  * - ascii_g3em_weight400_upright.ttf
53  *   'g' has 3em width and others have 1em width. The metadata has weight=400, non-italic value.
54  * - ascii_h3em_weight400_italic.ttf
55  *   'h' has 3em width and others have 1em width. The metadata has weight=400, italic value.
56  * - ascii_i3em_weight500_upright.ttf
57  *   'i' has 3em width and others have 1em width. The metadata has weight=500, non-italic value.
58  * - ascii_j3em_weight500_italic.ttf
59  *   'j' has 3em width and others have 1em width. The metadata has weight=500, italic value.
60  * - ascii_k3em_weight600_upright.ttf
61  *   'k' has 3em width and others have 1em width. The metadata has weight=600, non-italic value.
62  * - ascii_l3em_weight600_italic.ttf
63  *   'l' has 3em width and others have 1em width. The metadata has weight=600, italic value.
64  * - ascii_m3em_weight700_upright.ttf
65  *   'm' has 3em width and others have 1em width. The metadata has weight=700, non-italic value.
66  * - ascii_n3em_weight700_italic.ttf
67  *   'n' has 3em width and others have 1em width. The metadata has weight=700, italic value.
68  * - ascii_o3em_weight800_upright.ttf
69  *   'o' has 3em width and others have 1em width. The metadata has weight=800, non-italic value.
70  * - ascii_p3em_weight800_italic.ttf
71  *   'p' has 3em width and others have 1em width. The metadata has weight=800, italic value.
72  * - ascii_q3em_weight900_upright.ttf
73  *   'q' has 3em width and others have 1em width. The metadata has weight=900, non-italic value.
74  * - ascii_r3em_weight900_italic.ttf
75  *   'r' has 3em width and others have 1em width. The metadata has weight=900, italic value.
76  *
77  * In addition to above font files, this class provides a font collection file and a variable font
78  * file.
79  * - ascii.ttc
80  *   The collection of above 18 fonts with above order.
81  * - ascii_vf.ttf
82  *   This font supports a-z characters and all characters has 1em width. This font supports 'wght',
83  *   'ital' axes but no effect for the glyph width. This font also supports 'Asc[a-z]' 26 axes which
84  *   makes glyph width 3em. For example, 'Asca 1.0' makes a glyph width of 'a' 3em, 'Ascb 1.0' makes
85  *   a glyph width of 'b' 3em. With these axes, above font can be replicated like
86  *   - 'Asca' 1.0, 'wght' 100.0' is equivalent with ascii_a3em_width100_upright.ttf
87  *   - 'Ascb' 1.0, 'wght' 100.0, 'ital' 1.0' is equivalent with ascii_b3em_width100_italic.ttf
88  */
89 public class FontTestUtil {
90     private static final String FAMILY_SELECTION_FONT_PATH_IN_ASSET = "fonts/family_selection";
91     private static final List<Pair<Integer, Boolean>> sStyleList;
92     private static final Map<Pair<Integer, Boolean>, String> sFontMap;
93     private static final Map<Pair<Integer, Boolean>, Integer> sTtcMap;
94     private static final Map<Pair<Integer, Boolean>, String> sVariationSettingsMap;
95     private static final Map<Pair<Integer, Boolean>, Integer> sResourceMap;
96     private static final String[] sFontList = {  // Same order of ascii.ttc
97             FAMILY_SELECTION_FONT_PATH_IN_ASSET + "/ttf/ascii_a3em_weight100_upright.ttf",
98             FAMILY_SELECTION_FONT_PATH_IN_ASSET + "/ttf/ascii_b3em_weight100_italic.ttf",
99             FAMILY_SELECTION_FONT_PATH_IN_ASSET + "/ttf/ascii_c3em_weight200_upright.ttf",
100             FAMILY_SELECTION_FONT_PATH_IN_ASSET + "/ttf/ascii_d3em_weight200_italic.ttf",
101             FAMILY_SELECTION_FONT_PATH_IN_ASSET + "/ttf/ascii_e3em_weight300_upright.ttf",
102             FAMILY_SELECTION_FONT_PATH_IN_ASSET + "/ttf/ascii_f3em_weight300_italic.ttf",
103             FAMILY_SELECTION_FONT_PATH_IN_ASSET + "/ttf/ascii_g3em_weight400_upright.ttf",
104             FAMILY_SELECTION_FONT_PATH_IN_ASSET + "/ttf/ascii_h3em_weight400_italic.ttf",
105             FAMILY_SELECTION_FONT_PATH_IN_ASSET + "/ttf/ascii_i3em_weight500_upright.ttf",
106             FAMILY_SELECTION_FONT_PATH_IN_ASSET + "/ttf/ascii_j3em_weight500_italic.ttf",
107             FAMILY_SELECTION_FONT_PATH_IN_ASSET + "/ttf/ascii_k3em_weight600_upright.ttf",
108             FAMILY_SELECTION_FONT_PATH_IN_ASSET + "/ttf/ascii_l3em_weight600_italic.ttf",
109             FAMILY_SELECTION_FONT_PATH_IN_ASSET + "/ttf/ascii_m3em_weight700_upright.ttf",
110             FAMILY_SELECTION_FONT_PATH_IN_ASSET + "/ttf/ascii_n3em_weight700_italic.ttf",
111             FAMILY_SELECTION_FONT_PATH_IN_ASSET + "/ttf/ascii_o3em_weight800_upright.ttf",
112             FAMILY_SELECTION_FONT_PATH_IN_ASSET + "/ttf/ascii_p3em_weight800_italic.ttf",
113             FAMILY_SELECTION_FONT_PATH_IN_ASSET + "/ttf/ascii_q3em_weight900_upright.ttf",
114             FAMILY_SELECTION_FONT_PATH_IN_ASSET + "/ttf/ascii_r3em_weight900_italic.ttf",
115     };
116 
117     private static final String[] FONT_VARIATION_SETTING_LIST = {
118             "'Asca' 1.0, 'wght' 100.0",
119             "'Ascb' 1.0, 'wght' 100.0, 'ital' 1.0",
120             "'Ascc' 1.0, 'wght' 200.0",
121             "'Ascd' 1.0, 'wght' 200.0, 'ital' 1.0",
122             "'Asce' 1.0, 'wght' 300.0",
123             "'Ascf' 1.0, 'wght' 300.0, 'ital' 1.0",
124             "'Ascg' 1.0, 'wght' 400.0",
125             "'Asch' 1.0, 'wght' 400.0, 'ital' 1.0",
126             "'Asci' 1.0, 'wght' 500.0",
127             "'Ascj' 1.0, 'wght' 500.0, 'ital' 1.0",
128             "'Asck' 1.0, 'wght' 600.0",
129             "'Ascl' 1.0, 'wght' 600.0, 'ital' 1.0",
130             "'Ascm' 1.0, 'wght' 700.0",
131             "'Ascn' 1.0, 'wght' 700.0, 'ital' 1.0",
132             "'Asco' 1.0, 'wght' 800.0",
133             "'Ascp' 1.0, 'wght' 800.0, 'ital' 1.0",
134             "'Ascq' 1.0, 'wght' 900.0",
135             "'Ascr' 1.0, 'wght' 900.0, 'ital' 1.0",
136     };
137 
138     private static final int[] FONT_RESOURCE_ID_LIST = {
139             R.font.ascii_a3em_weight100_upright,
140             R.font.ascii_b3em_weight100_italic,
141             R.font.ascii_c3em_weight200_upright,
142             R.font.ascii_d3em_weight200_italic,
143             R.font.ascii_e3em_weight300_upright,
144             R.font.ascii_f3em_weight300_italic,
145             R.font.ascii_g3em_weight400_upright,
146             R.font.ascii_h3em_weight400_italic,
147             R.font.ascii_i3em_weight500_upright,
148             R.font.ascii_j3em_weight500_italic,
149             R.font.ascii_k3em_weight600_upright,
150             R.font.ascii_l3em_weight600_italic,
151             R.font.ascii_m3em_weight700_upright,
152             R.font.ascii_n3em_weight700_italic,
153             R.font.ascii_o3em_weight800_upright,
154             R.font.ascii_p3em_weight800_italic,
155             R.font.ascii_q3em_weight900_upright,
156             R.font.ascii_r3em_weight900_italic,
157     };
158 
159     private static final char[] CHAR_3EM_WIDTH = {
160             'a',
161             'b',
162             'c',
163             'd',
164             'e',
165             'f',
166             'g',
167             'h',
168             'i',
169             'j',
170             'k',
171             'l',
172             'm',
173             'n',
174             'o',
175             'p',
176             'q',
177             'r',
178     };
179 
180     static {
181         // Style list with the same order of sFontList.
182         ArrayList<Pair<Integer, Boolean>> styles = new ArrayList<>();
styles.add(new Pair<>(100, false))183         styles.add(new Pair<>(100, false));
styles.add(new Pair<>(100, true))184         styles.add(new Pair<>(100, true));
styles.add(new Pair<>(200, false))185         styles.add(new Pair<>(200, false));
styles.add(new Pair<>(200, true))186         styles.add(new Pair<>(200, true));
styles.add(new Pair<>(300, false))187         styles.add(new Pair<>(300, false));
styles.add(new Pair<>(300, true))188         styles.add(new Pair<>(300, true));
styles.add(new Pair<>(400, false))189         styles.add(new Pair<>(400, false));
styles.add(new Pair<>(400, true))190         styles.add(new Pair<>(400, true));
styles.add(new Pair<>(500, false))191         styles.add(new Pair<>(500, false));
styles.add(new Pair<>(500, true))192         styles.add(new Pair<>(500, true));
styles.add(new Pair<>(600, false))193         styles.add(new Pair<>(600, false));
styles.add(new Pair<>(600, true))194         styles.add(new Pair<>(600, true));
styles.add(new Pair<>(700, false))195         styles.add(new Pair<>(700, false));
styles.add(new Pair<>(700, true))196         styles.add(new Pair<>(700, true));
styles.add(new Pair<>(800, false))197         styles.add(new Pair<>(800, false));
styles.add(new Pair<>(800, true))198         styles.add(new Pair<>(800, true));
styles.add(new Pair<>(900, false))199         styles.add(new Pair<>(900, false));
styles.add(new Pair<>(900, true))200         styles.add(new Pair<>(900, true));
201         sStyleList = Collections.unmodifiableList(styles);
202 
203         HashMap<Pair<Integer, Boolean>, String> map = new HashMap<>();
204         HashMap<Pair<Integer, Boolean>, Integer> ttcMap = new HashMap<>();
205         HashMap<Pair<Integer, Boolean>, String> variationMap = new HashMap<>();
206         HashMap<Pair<Integer, Boolean>, Integer> resourceMap = new HashMap<>();
207         HashMap<Character, Pair<Integer, Boolean>> reverseMap = new HashMap<>();
208         for (int i = 0; i < sFontList.length; ++i) {
sStyleList.get(i)209             map.put(sStyleList.get(i), sFontList[i]);
sStyleList.get(i)210             ttcMap.put(sStyleList.get(i), i);
sStyleList.get(i)211             variationMap.put(sStyleList.get(i), FONT_VARIATION_SETTING_LIST[i]);
sStyleList.get(i)212             resourceMap.put(sStyleList.get(i), FONT_RESOURCE_ID_LIST[i]);
213         }
214         sFontMap = Collections.unmodifiableMap(map);
215         sTtcMap = Collections.unmodifiableMap(ttcMap);
216         sVariationSettingsMap = Collections.unmodifiableMap(variationMap);
217         sResourceMap = Collections.unmodifiableMap(resourceMap);
218     }
219 
220     /**
221      * Measure a character with 100px text size
222      */
measureChar(Typeface typeface, char c)223     private static float measureChar(Typeface typeface, char c) {
224         final TextPaint tp = new TextPaint();
225         tp.setTextSize(100);
226         tp.setTypeface(typeface);
227         tp.setTextLocale(Locale.US);
228         return tp.measureText(new char[] { c }, 0, 1);
229     }
230 
231     /**
232      * Returns a path to the font collection file in asset directory.
233      */
getTtcFontFileInAsset()234     public static String getTtcFontFileInAsset() {
235         return FAMILY_SELECTION_FONT_PATH_IN_ASSET + "/ascii.ttc";
236     }
237 
238     /**
239      * Returns a resource id for the font collection file.
240      */
getTtcFontFileResourceId()241     public static int getTtcFontFileResourceId() {
242         return R.font.ascii;
243     }
244 
245     /**
246      * Returns a path to the variable font file in asset directory.
247      */
getVFFontInAsset()248     public static String getVFFontInAsset() {
249         return FAMILY_SELECTION_FONT_PATH_IN_ASSET + "/ascii_vf.ttf";
250     }
251 
252     /**
253      * Returns a resource id for the variable font.
254      */
getVFFontResourceId()255     public static int getVFFontResourceId() {
256         return R.font.ascii_vf;
257     }
258 
259     /**
260      * Returns a ttc index of the specified style.
261      */
getTtcIndexFromStyle(int weight, boolean italic)262     public static int getTtcIndexFromStyle(int weight, boolean italic) {
263         return sTtcMap.get(new Pair<>(weight, italic)).intValue();
264     }
265 
266     /**
267      * Returns a variation settings string of the specified style.
268      */
getVarSettingsFromStyle(int weight, boolean italic)269     public static String getVarSettingsFromStyle(int weight, boolean italic) {
270         return sVariationSettingsMap.get(new Pair<>(weight, italic));
271     }
272 
273     /**
274      * Returns a font resource ID of the specific style.
275      */
getFontResourceIdFromStyle(int weight, boolean italic)276     public static int getFontResourceIdFromStyle(int weight, boolean italic) {
277         return sResourceMap.get(new Pair<>(weight, italic));
278     }
279 
280     /**
281      * Returns a font path from the specified style.
282      */
getFontPathFromStyle(int weight, boolean italic)283     public static String getFontPathFromStyle(int weight, boolean italic) {
284         return sFontMap.get(new Pair<>(weight, italic));
285     }
286 
287     /**
288      * Returns all supported styles.
289      *
290      * @return a pair of weight and style(uplight/italic)
291      */
getAllStyles()292     public static List<Pair<Integer, Boolean>> getAllStyles() {
293         return sStyleList;
294     }
295 
296     /**
297      * Returns selected font index in the sStyleList array.
298      */
getSelectedFontStyle(Typeface typeface)299     private static int getSelectedFontStyle(Typeface typeface) {
300         int indexOf3Em = -1;
301         for (int i = 0; i < CHAR_3EM_WIDTH.length; ++i) {
302             if (measureChar(typeface, CHAR_3EM_WIDTH[i]) == 300.0f) {
303                 assertEquals("A font has two 3em width character. Likely the wrong test setup.",
304                         -1, indexOf3Em);
305                 indexOf3Em = i;
306             }
307         }
308         assertNotEquals("No font has 3em width character. Likely the wrong test setup.",
309                 -1, indexOf3Em);
310         return indexOf3Em;
311     }
312 
313     /**
314      * Returns selected font's style.
315      */
getSelectedStyle(Typeface typeface)316     public static Pair<Integer, Boolean> getSelectedStyle(Typeface typeface) {
317         return sStyleList.get(getSelectedFontStyle(typeface));
318     }
319 
320     /**
321      * Returns selected font's file path.
322      *
323      * Note that this is valid only if the all Font objects in the FontFamily is created with
324      * AssetManager.
325      */
getSelectedFontPathInAsset(Typeface typeface)326     public static String getSelectedFontPathInAsset(Typeface typeface) {
327         return sFontList[getSelectedFontStyle(typeface)];
328     }
329 
330     /**
331      * Returns selected font's ttc index.
332      *
333      * Note that this is valid only if the all Font objects in the FontFamily is created with
334      * TTC font with ttcIndex.
335      */
getSelectedTtcIndex(Typeface typeface)336     public static int getSelectedTtcIndex(Typeface typeface) {
337         return getSelectedFontStyle(typeface);
338     }
339 
340     /**
341      * Returns selected font's variation settings.
342      *
343      * Note that this is valid only if the all Font objects in the FontFamily is created with
344      * variable fonts with font variation settings.
345      */
getSelectedVariationSettings(Typeface typeface)346     public static String getSelectedVariationSettings(Typeface typeface) {
347         return FONT_VARIATION_SETTING_LIST[getSelectedFontStyle(typeface)];
348     }
349 }
350