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 static com.google.common.truth.Truth.assertWithMessage;
20 
21 import static org.junit.Assert.assertEquals;
22 import static org.junit.Assert.assertFalse;
23 import static org.junit.Assert.assertNotEquals;
24 import static org.junit.Assert.assertNotNull;
25 import static org.junit.Assert.assertNull;
26 import static org.junit.Assert.assertTrue;
27 
28 import android.content.Context;
29 import android.content.res.AssetManager;
30 import android.content.res.Resources;
31 import android.content.res.Resources.NotFoundException;
32 import android.graphics.Paint;
33 import android.graphics.RectF;
34 import android.graphics.Typeface;
35 import android.graphics.cts.R;
36 import android.graphics.text.PositionedGlyphs;
37 import android.graphics.text.TextRunShaper;
38 import android.os.ParcelFileDescriptor;
39 import android.util.Log;
40 import android.util.Pair;
41 
42 import androidx.test.InstrumentationRegistry;
43 import androidx.test.filters.SmallTest;
44 import androidx.test.runner.AndroidJUnit4;
45 
46 import org.junit.Test;
47 import org.junit.runner.RunWith;
48 
49 import java.io.File;
50 import java.io.FileInputStream;
51 import java.io.FileOutputStream;
52 import java.io.IOException;
53 import java.io.InputStream;
54 import java.nio.ByteBuffer;
55 import java.nio.channels.FileChannel;
56 import java.util.ArrayList;
57 
58 @SmallTest
59 @RunWith(AndroidJUnit4.class)
60 public class FontTest {
61     private static final String TAG = "FontFileUtilTest";
62     private static final String CACHE_FILE_PREFIX = ".font";
63 
64     /**
65      * Create new temporary file.
66      *
67      * Caller must delete the file after used.
68      */
getTempFile()69     private static File getTempFile() {
70         Context ctx = InstrumentationRegistry.getTargetContext();
71         final String prefix = CACHE_FILE_PREFIX;
72         for (int i = 0; i < 100; ++i) {
73             final File file = new File(ctx.getCacheDir(), prefix + i);
74             try {
75                 if (file.createNewFile()) {
76                     return file;
77                 }
78             } catch (IOException e) {
79                 // ignore. Try next file.
80             }
81         }
82         return null;
83     }
84 
mmap(AssetManager am, String path)85     private static ByteBuffer mmap(AssetManager am, String path) {
86         File file = getTempFile();
87         try (InputStream is = am.open(path)) {
88             if (!copyToFile(file, is)) {
89                 return null;
90             }
91             return mmap(file);
92         } catch (IOException e) {
93             Log.e(TAG, "Failed to open assets");
94             return null;
95         } finally {
96             file.delete();
97         }
98     }
99 
mmap(File file)100     private static ByteBuffer mmap(File file) {
101         try (FileInputStream fis = new FileInputStream(file)) {
102             FileChannel channel = fis.getChannel();
103             return channel.map(FileChannel.MapMode.READ_ONLY, 0, channel.size());
104         } catch (IOException e) {
105             return null;
106         }
107     }
108 
copyToFile(File file, InputStream is)109     private static boolean copyToFile(File file, InputStream is) {
110         return copyToFile(file, is, null, null);
111     }
112 
copyToFile(File file, InputStream is, byte[] prepend, byte[] append)113     private static boolean copyToFile(File file, InputStream is, byte[] prepend, byte[] append) {
114         try (FileOutputStream os = new FileOutputStream(file, false)) {
115             byte[] buffer = new byte[1024];
116             int readLen;
117             if (prepend != null) {
118                 os.write(prepend, 0, prepend.length);
119             }
120             while ((readLen = is.read(buffer)) != -1) {
121                 os.write(buffer, 0, readLen);
122             }
123             if (append != null) {
124                 os.write(append, 0, append.length);
125             }
126             return true;
127         } catch (IOException e) {
128             Log.e(TAG, "Error copying resource contents to temp file: " + e.getMessage());
129             return false;
130         }
131     }
132 
assertAxesEquals(String msg, FontVariationAxis[] left, FontVariationAxis[] right)133     private void assertAxesEquals(String msg, FontVariationAxis[] left, FontVariationAxis[] right) {
134         if (left == right) {
135             return;
136         }
137 
138         if (left == null) {
139             assertWithMessage(msg).that(right).isEmpty();
140         } else if (right == null) {
141             assertWithMessage(msg).that(left).isEmpty();
142         } else {
143             assertWithMessage(msg).that(left).isEqualTo(right);
144         }
145     }
146 
assertNullOrEmpty(String msg, FontVariationAxis[] actual)147     private void assertNullOrEmpty(String msg, FontVariationAxis[] actual) {
148         assertWithMessage(msg).that(actual == null || actual.length == 0).isTrue();
149     }
150 
151     @Test
testBuilder_buffer()152     public void testBuilder_buffer() throws IOException {
153         AssetManager am = InstrumentationRegistry.getTargetContext().getAssets();
154         for (Pair<Integer, Boolean> style : FontTestUtil.getAllStyles()) {
155             int weight = style.first.intValue();
156             boolean italic = style.second.booleanValue();
157             final int slant = italic ? FontStyle.FONT_SLANT_ITALIC : FontStyle.FONT_SLANT_UPRIGHT;
158             String path = FontTestUtil.getFontPathFromStyle(weight, italic);
159 
160             ByteBuffer buffer = mmap(am, path);
161 
162             Font font = new Font.Builder(buffer).build();
163             assertEquals(path, weight, font.getStyle().getWeight());
164             assertEquals(path, slant, font.getStyle().getSlant());
165             assertEquals(path, 0, font.getTtcIndex());
166             assertNullOrEmpty(path, font.getAxes());
167             assertNotNull(font.getBuffer());
168             assertNull(font.getFile());
169         }
170     }
171 
172     @Test
testBuilder_buffer_ttc()173     public void testBuilder_buffer_ttc() throws IOException {
174         AssetManager am = InstrumentationRegistry.getTargetContext().getAssets();
175         for (Pair<Integer, Boolean> style : FontTestUtil.getAllStyles()) {
176             int weight = style.first.intValue();
177             boolean italic = style.second.booleanValue();
178             final int slant = italic ? FontStyle.FONT_SLANT_ITALIC : FontStyle.FONT_SLANT_UPRIGHT;
179             String path = FontTestUtil.getTtcFontFileInAsset();
180 
181             ByteBuffer buffer = mmap(am, path);
182             int ttcIndex = FontTestUtil.getTtcIndexFromStyle(weight, italic);
183 
184             Font font = new Font.Builder(buffer).setTtcIndex(ttcIndex).build();
185             assertEquals(path, weight, font.getStyle().getWeight());
186             assertEquals(path, slant, font.getStyle().getSlant());
187             assertEquals(path, ttcIndex, font.getTtcIndex());
188             assertNullOrEmpty(path, font.getAxes());
189             assertNotNull(font.getBuffer());
190             assertNull(font.getFile());
191         }
192     }
193 
194     @Test
testBuilder_buffer_vf()195     public void testBuilder_buffer_vf() throws IOException {
196         AssetManager am = InstrumentationRegistry.getTargetContext().getAssets();
197         for (Pair<Integer, Boolean> style : FontTestUtil.getAllStyles()) {
198             int weight = style.first.intValue();
199             boolean italic = style.second.booleanValue();
200             String path = FontTestUtil.getVFFontInAsset();
201             final int slant = italic ? FontStyle.FONT_SLANT_ITALIC : FontStyle.FONT_SLANT_UPRIGHT;
202 
203             ByteBuffer buffer = mmap(am, path);
204             FontVariationAxis[] axes = FontVariationAxis.fromFontVariationSettings(
205                     FontTestUtil.getVarSettingsFromStyle(weight, italic));
206 
207             Font font = new Font.Builder(buffer).setFontVariationSettings(axes).build();
208             assertEquals(path, weight, font.getStyle().getWeight());
209             assertEquals(path, slant, font.getStyle().getSlant());
210             assertEquals(path, 0, font.getTtcIndex());
211             assertAxesEquals(path, axes, font.getAxes());
212             assertNotNull(font.getBuffer());
213             assertNull(font.getFile());
214         }
215     }
216 
217     @Test
testBuilder_buffer_override()218     public void testBuilder_buffer_override() throws IOException {
219         int customWeight = 350;
220         AssetManager am = InstrumentationRegistry.getTargetContext().getAssets();
221         for (Pair<Integer, Boolean> style : FontTestUtil.getAllStyles()) {
222             int weight = style.first.intValue();
223             boolean italic = style.second.booleanValue();
224             String path = FontTestUtil.getFontPathFromStyle(weight, italic);
225             final int slant = italic ? FontStyle.FONT_SLANT_ITALIC : FontStyle.FONT_SLANT_UPRIGHT;
226 
227             ByteBuffer buffer = mmap(am, path);
228 
229             Font font = new Font.Builder(buffer).setWeight(customWeight).build();
230             assertEquals(path, customWeight, font.getStyle().getWeight());
231             assertEquals(path, slant, font.getStyle().getSlant());
232             assertEquals(path, 0, font.getTtcIndex());
233             assertNullOrEmpty(path, font.getAxes());
234             assertNotNull(font.getBuffer());
235             assertNull(font.getFile());
236         }
237 
238         for (Pair<Integer, Boolean> style : FontTestUtil.getAllStyles()) {
239             int weight = style.first.intValue();
240             boolean italic = style.second.booleanValue();
241             String path = FontTestUtil.getFontPathFromStyle(weight, italic);
242 
243             ByteBuffer buffer = mmap(am, path);
244 
245             Font font = new Font.Builder(buffer).setSlant(FontStyle.FONT_SLANT_ITALIC).build();
246             assertEquals(path, weight, font.getStyle().getWeight());
247             assertEquals(path, FontStyle.FONT_SLANT_ITALIC, font.getStyle().getSlant());
248             assertEquals(path, 0, font.getTtcIndex());
249             assertNullOrEmpty(path, font.getAxes());
250             assertNotNull(font.getBuffer());
251             assertNull(font.getFile());
252         }
253     }
254 
255     @Test(expected = NullPointerException.class)
testBuilder_buffer_invalid_null()256     public void testBuilder_buffer_invalid_null() throws IOException {
257         ByteBuffer buf = null;
258         new Font.Builder(buf);
259     }
260 
261     @Test(expected = IllegalArgumentException.class)
testBuilder_buffer_invalid_not_direct()262     public void testBuilder_buffer_invalid_not_direct() throws IOException {
263         ByteBuffer buf = ByteBuffer.allocate(1024);
264         new Font.Builder(buf);
265     }
266 
267     @Test
testBuilder_file()268     public void testBuilder_file() throws IOException {
269         AssetManager am = InstrumentationRegistry.getTargetContext().getAssets();
270         for (Pair<Integer, Boolean> style : FontTestUtil.getAllStyles()) {
271             int weight = style.first.intValue();
272             boolean italic = style.second.booleanValue();
273             String path = FontTestUtil.getFontPathFromStyle(weight, italic);
274             final int slant = italic ? FontStyle.FONT_SLANT_ITALIC : FontStyle.FONT_SLANT_UPRIGHT;
275 
276             File file = getTempFile();
277             try (InputStream is = am.open(path)) {
278                 assertTrue(copyToFile(file, is));
279 
280                 Font font = new Font.Builder(file).build();
281                 assertEquals(path, weight, font.getStyle().getWeight());
282                 assertEquals(path, slant, font.getStyle().getSlant());
283                 assertEquals(path, 0, font.getTtcIndex());
284                 assertNullOrEmpty(path, font.getAxes());
285                 assertNotNull(font.getBuffer());
286                 assertNotNull(font.getFile());
287             } finally {
288                 file.delete();
289             }
290         }
291     }
292 
293     @Test
testBuilder_file_ttc()294     public void testBuilder_file_ttc() throws IOException {
295         AssetManager am = InstrumentationRegistry.getTargetContext().getAssets();
296         for (Pair<Integer, Boolean> style : FontTestUtil.getAllStyles()) {
297             int weight = style.first.intValue();
298             boolean italic = style.second.booleanValue();
299             String path = FontTestUtil.getTtcFontFileInAsset();
300             int ttcIndex = FontTestUtil.getTtcIndexFromStyle(weight, italic);
301             final int slant = italic ? FontStyle.FONT_SLANT_ITALIC : FontStyle.FONT_SLANT_UPRIGHT;
302 
303             File file = getTempFile();
304             try (InputStream is = am.open(path)) {
305                 assertTrue(copyToFile(file, is));
306 
307                 Font font = new Font.Builder(file).setTtcIndex(ttcIndex).build();
308                 assertEquals(path, weight, font.getStyle().getWeight());
309                 assertEquals(path, slant, font.getStyle().getSlant());
310                 assertEquals(path, ttcIndex, font.getTtcIndex());
311                 assertNullOrEmpty(path, font.getAxes());
312                 assertNotNull(font.getBuffer());
313                 assertNotNull(font.getFile());
314             } finally {
315                 file.delete();
316             }
317         }
318     }
319 
320     @Test
testBuilder_file_vf()321     public void testBuilder_file_vf() throws IOException {
322         AssetManager am = InstrumentationRegistry.getTargetContext().getAssets();
323         for (Pair<Integer, Boolean> style : FontTestUtil.getAllStyles()) {
324             int weight = style.first.intValue();
325             boolean italic = style.second.booleanValue();
326             String path = FontTestUtil.getVFFontInAsset();
327             FontVariationAxis[] axes = FontVariationAxis.fromFontVariationSettings(
328                     FontTestUtil.getVarSettingsFromStyle(weight, italic));
329             final int slant = italic ? FontStyle.FONT_SLANT_ITALIC : FontStyle.FONT_SLANT_UPRIGHT;
330 
331             File file = getTempFile();
332             try (InputStream is = am.open(path)) {
333                 assertTrue(copyToFile(file, is));
334 
335                 Font font = new Font.Builder(file).setFontVariationSettings(axes).build();
336                 assertEquals(path, weight, font.getStyle().getWeight());
337                 assertEquals(path, slant, font.getStyle().getSlant());
338                 assertEquals(path, 0, font.getTtcIndex());
339                 assertAxesEquals(path, axes, font.getAxes());
340                 assertNotNull(font.getBuffer());
341                 assertNotNull(font.getFile());
342             } finally {
343                 file.delete();
344             }
345         }
346     }
347 
348     @Test
testBuilder_file_override()349     public void testBuilder_file_override() throws IOException {
350         int customWeight = 350;
351         AssetManager am = InstrumentationRegistry.getTargetContext().getAssets();
352         for (Pair<Integer, Boolean> style : FontTestUtil.getAllStyles()) {
353             int weight = style.first.intValue();
354             boolean italic = style.second.booleanValue();
355             String path = FontTestUtil.getFontPathFromStyle(weight, italic);
356             final int slant = italic ? FontStyle.FONT_SLANT_ITALIC : FontStyle.FONT_SLANT_UPRIGHT;
357 
358             File file = getTempFile();
359             try (InputStream is = am.open(path)) {
360                 assertTrue(copyToFile(file, is));
361 
362                 Font font = new Font.Builder(file).setWeight(customWeight).build();
363                 assertEquals(path, customWeight, font.getStyle().getWeight());
364                 assertEquals(path, slant, font.getStyle().getSlant());
365                 assertEquals(path, 0, font.getTtcIndex());
366                 assertNullOrEmpty(path, font.getAxes());
367                 assertNotNull(font.getBuffer());
368                 assertNotNull(font.getFile());
369             } finally {
370                 file.delete();
371             }
372         }
373         for (Pair<Integer, Boolean> style : FontTestUtil.getAllStyles()) {
374             int weight = style.first.intValue();
375             boolean italic = style.second.booleanValue();
376             String path = FontTestUtil.getFontPathFromStyle(weight, italic);
377 
378             File file = getTempFile();
379             try (InputStream is = am.open(path)) {
380                 assertTrue(copyToFile(file, is));
381 
382                 Font font = new Font.Builder(file).setSlant(FontStyle.FONT_SLANT_ITALIC).build();
383                 assertEquals(path, weight, font.getStyle().getWeight());
384                 assertEquals(path, FontStyle.FONT_SLANT_ITALIC, font.getStyle().getSlant());
385                 assertEquals(path, 0, font.getTtcIndex());
386                 assertNullOrEmpty(path, font.getAxes());
387                 assertNotNull(font.getBuffer());
388                 assertNotNull(font.getFile());
389             } finally {
390                 file.delete();
391             }
392         }
393     }
394 
395     @Test(expected = NullPointerException.class)
testBuilder_file_invalid_null_file()396     public void testBuilder_file_invalid_null_file() throws IOException {
397         File file = null;
398         new Font.Builder(file);
399     }
400 
401     @Test(expected = IOException.class)
testBuilder_file_invalid_not_found_file()402     public void testBuilder_file_invalid_not_found_file() throws IOException {
403         File file = new File("/no/such/file");
404         new Font.Builder(file).build();
405     }
406 
407     @Test
testBuilder_fd()408     public void testBuilder_fd() throws IOException {
409         AssetManager am = InstrumentationRegistry.getTargetContext().getAssets();
410         for (Pair<Integer, Boolean> style : FontTestUtil.getAllStyles()) {
411             int weight = style.first.intValue();
412             boolean italic = style.second.booleanValue();
413             String path = FontTestUtil.getFontPathFromStyle(weight, italic);
414             final int slant = italic ? FontStyle.FONT_SLANT_ITALIC : FontStyle.FONT_SLANT_UPRIGHT;
415 
416             File file = getTempFile();
417             try (InputStream is = am.open(path)) {
418                 assertTrue(copyToFile(file, is));
419 
420                 try (ParcelFileDescriptor fd = ParcelFileDescriptor.open(file,
421                         ParcelFileDescriptor.MODE_READ_ONLY)) {
422                     Font font = new Font.Builder(fd).build();
423                     assertEquals(path, weight, font.getStyle().getWeight());
424                     assertEquals(path, slant, font.getStyle().getSlant());
425                     assertEquals(path, 0, font.getTtcIndex());
426                     assertNullOrEmpty(path, font.getAxes());
427                     assertNotNull(font.getBuffer());
428                     assertNull(font.getFile());
429                 }
430             } finally {
431                 file.delete();
432             }
433         }
434     }
435 
436     @Test
testBuilder_fd_ttc()437     public void testBuilder_fd_ttc() throws IOException {
438         AssetManager am = InstrumentationRegistry.getTargetContext().getAssets();
439         for (Pair<Integer, Boolean> style : FontTestUtil.getAllStyles()) {
440             int weight = style.first.intValue();
441             boolean italic = style.second.booleanValue();
442             String path = FontTestUtil.getTtcFontFileInAsset();
443             int ttcIndex = FontTestUtil.getTtcIndexFromStyle(weight, italic);
444             final int slant = italic ? FontStyle.FONT_SLANT_ITALIC : FontStyle.FONT_SLANT_UPRIGHT;
445 
446             File file = getTempFile();
447             try (InputStream is = am.open(path)) {
448                 assertTrue(copyToFile(file, is));
449 
450                 try (ParcelFileDescriptor fd = ParcelFileDescriptor.open(file,
451                         ParcelFileDescriptor.MODE_READ_ONLY)) {
452                     Font font = new Font.Builder(fd).setTtcIndex(ttcIndex).build();
453                     assertEquals(path, weight, font.getStyle().getWeight());
454                     assertEquals(path, slant, font.getStyle().getSlant());
455                     assertEquals(path, ttcIndex, font.getTtcIndex());
456                     assertNullOrEmpty(path, font.getAxes());
457                     assertNotNull(font.getBuffer());
458                     assertNull(font.getFile());
459                 }
460             } finally {
461                 file.delete();
462             }
463         }
464     }
465 
466     @Test
testBuilder_fd_vf()467     public void testBuilder_fd_vf() throws IOException {
468         AssetManager am = InstrumentationRegistry.getTargetContext().getAssets();
469         for (Pair<Integer, Boolean> style : FontTestUtil.getAllStyles()) {
470             int weight = style.first.intValue();
471             boolean italic = style.second.booleanValue();
472             String path = FontTestUtil.getVFFontInAsset();
473             FontVariationAxis[] axes = FontVariationAxis.fromFontVariationSettings(
474                     FontTestUtil.getVarSettingsFromStyle(weight, italic));
475             final int slant = italic ? FontStyle.FONT_SLANT_ITALIC : FontStyle.FONT_SLANT_UPRIGHT;
476 
477             File file = getTempFile();
478             try (InputStream is = am.open(path)) {
479                 assertTrue(copyToFile(file, is));
480 
481                 try (ParcelFileDescriptor fd = ParcelFileDescriptor.open(file,
482                         ParcelFileDescriptor.MODE_READ_ONLY)) {
483                     Font font = new Font.Builder(fd).setFontVariationSettings(axes)
484                             .build();
485                     assertEquals(path, weight, font.getStyle().getWeight());
486                     assertEquals(path, slant, font.getStyle().getSlant());
487                     assertEquals(path, 0, font.getTtcIndex());
488                     assertAxesEquals(path, axes, font.getAxes());
489                     assertNotNull(font.getBuffer());
490                     assertNull(font.getFile());
491                 }
492             } finally {
493                 file.delete();
494             }
495         }
496     }
497 
498     @Test
testBuilder_fd_override()499     public void testBuilder_fd_override() throws IOException {
500         int customWeight = 350;
501         AssetManager am = InstrumentationRegistry.getTargetContext().getAssets();
502         for (Pair<Integer, Boolean> style : FontTestUtil.getAllStyles()) {
503             int weight = style.first.intValue();
504             boolean italic = style.second.booleanValue();
505             String path = FontTestUtil.getFontPathFromStyle(weight, italic);
506             final int slant = italic ? FontStyle.FONT_SLANT_ITALIC : FontStyle.FONT_SLANT_UPRIGHT;
507 
508             File file = getTempFile();
509             try (InputStream is = am.open(path)) {
510                 assertTrue(copyToFile(file, is));
511 
512                 try (ParcelFileDescriptor fd = ParcelFileDescriptor.open(file,
513                         ParcelFileDescriptor.MODE_READ_ONLY)) {
514                     Font font = new Font.Builder(fd).setWeight(customWeight).build();
515                     assertEquals(path, customWeight, font.getStyle().getWeight());
516                     assertEquals(path, slant, font.getStyle().getSlant());
517                     assertEquals(path, 0, font.getTtcIndex());
518                     assertNullOrEmpty(path, font.getAxes());
519                     assertNotNull(font.getBuffer());
520                     assertNull(font.getFile());
521                 }
522             } finally {
523                 file.delete();
524             }
525         }
526         for (Pair<Integer, Boolean> style : FontTestUtil.getAllStyles()) {
527             int weight = style.first.intValue();
528             boolean italic = style.second.booleanValue();
529             String path = FontTestUtil.getFontPathFromStyle(weight, italic);
530             final int slant = italic ? FontStyle.FONT_SLANT_ITALIC : FontStyle.FONT_SLANT_UPRIGHT;
531 
532             File file = getTempFile();
533             try (InputStream is = am.open(path)) {
534                 assertTrue(copyToFile(file, is));
535 
536                 try (ParcelFileDescriptor fd = ParcelFileDescriptor.open(file,
537                         ParcelFileDescriptor.MODE_READ_ONLY)) {
538                     Font font = new Font.Builder(fd).setSlant(
539                             FontStyle.FONT_SLANT_ITALIC).build();
540                     assertEquals(path, weight, font.getStyle().getWeight());
541                     assertEquals(path, FontStyle.FONT_SLANT_ITALIC, font.getStyle().getSlant());
542                     assertEquals(path, 0, font.getTtcIndex());
543                     assertNullOrEmpty(path, font.getAxes());
544                     assertNotNull(font.getBuffer());
545                     assertNull(font.getFile());
546                 }
547             } finally {
548                 file.delete();
549             }
550         }
551     }
552 
553     @Test
testBuilder_fd_subdata()554     public void testBuilder_fd_subdata() throws IOException {
555         byte[] placeHolderData = { (byte) 0xde, (byte) 0xad, (byte) 0xbe, (byte) 0xef };
556         AssetManager am = InstrumentationRegistry.getTargetContext().getAssets();
557         for (Pair<Integer, Boolean> style : FontTestUtil.getAllStyles()) {
558             int weight = style.first.intValue();
559             boolean italic = style.second.booleanValue();
560             String path = FontTestUtil.getFontPathFromStyle(weight, italic);
561             final int slant = italic ? FontStyle.FONT_SLANT_ITALIC : FontStyle.FONT_SLANT_UPRIGHT;
562 
563             File file = getTempFile();
564             try (InputStream is = am.open(path)) {
565                 assertTrue(copyToFile(file, is, placeHolderData, placeHolderData));
566 
567                 try (ParcelFileDescriptor fd = ParcelFileDescriptor.open(file,
568                         ParcelFileDescriptor.MODE_READ_ONLY)) {
569                     Font font = new Font.Builder(fd, placeHolderData.length,
570                             file.length() - placeHolderData.length * 2).build();
571                     assertEquals(path, weight, font.getStyle().getWeight());
572                     assertEquals(path, slant, font.getStyle().getSlant());
573                     assertEquals(path, 0, font.getTtcIndex());
574                     assertNullOrEmpty(path, font.getAxes());
575                     assertNotNull(font.getBuffer());
576                     assertNull(font.getFile());
577                 }
578             } finally {
579                 file.delete();
580             }
581         }
582     }
583 
584     @Test
testBuilder_fd_subdata_ttc()585     public void testBuilder_fd_subdata_ttc() throws IOException {
586         byte[] placeHolderData = { (byte) 0xde, (byte) 0xad, (byte) 0xbe, (byte) 0xef };
587         AssetManager am = InstrumentationRegistry.getTargetContext().getAssets();
588         for (Pair<Integer, Boolean> style : FontTestUtil.getAllStyles()) {
589             int weight = style.first.intValue();
590             boolean italic = style.second.booleanValue();
591             String path = FontTestUtil.getTtcFontFileInAsset();
592             int ttcIndex = FontTestUtil.getTtcIndexFromStyle(weight, italic);
593             final int slant = italic ? FontStyle.FONT_SLANT_ITALIC : FontStyle.FONT_SLANT_UPRIGHT;
594 
595             File file = getTempFile();
596             try (InputStream is = am.open(path)) {
597                 assertTrue(copyToFile(file, is, placeHolderData, placeHolderData));
598 
599                 try (ParcelFileDescriptor fd = ParcelFileDescriptor.open(file,
600                         ParcelFileDescriptor.MODE_READ_ONLY)) {
601                     Font font = new Font.Builder(
602                             fd, placeHolderData.length, file.length() - placeHolderData.length * 2)
603                             .setTtcIndex(ttcIndex).build();
604                     assertEquals(path, weight, font.getStyle().getWeight());
605                     assertEquals(path, slant, font.getStyle().getSlant());
606                     assertEquals(path, ttcIndex, font.getTtcIndex());
607                     assertNullOrEmpty(path, font.getAxes());
608                     assertNotNull(font.getBuffer());
609                     assertNull(font.getFile());
610                 }
611             } finally {
612                 file.delete();
613             }
614         }
615     }
616 
617     @Test
testBuilder_fd_subdata_vf()618     public void testBuilder_fd_subdata_vf() throws IOException {
619         byte[] placeHolderData = { (byte) 0xde, (byte) 0xad, (byte) 0xbe, (byte) 0xef };
620         AssetManager am = InstrumentationRegistry.getTargetContext().getAssets();
621         for (Pair<Integer, Boolean> style : FontTestUtil.getAllStyles()) {
622             int weight = style.first.intValue();
623             boolean italic = style.second.booleanValue();
624             String path = FontTestUtil.getVFFontInAsset();
625             FontVariationAxis[] axes = FontVariationAxis.fromFontVariationSettings(
626                     FontTestUtil.getVarSettingsFromStyle(weight, italic));
627             final int slant = italic ? FontStyle.FONT_SLANT_ITALIC : FontStyle.FONT_SLANT_UPRIGHT;
628 
629             File file = getTempFile();
630             try (InputStream is = am.open(path)) {
631                 assertTrue(copyToFile(file, is, placeHolderData, placeHolderData));
632 
633                 try (ParcelFileDescriptor fd = ParcelFileDescriptor.open(file,
634                         ParcelFileDescriptor.MODE_READ_ONLY)) {
635                     Font font = new Font.Builder(
636                             fd, placeHolderData.length, file.length() - placeHolderData.length * 2)
637                             .setFontVariationSettings(axes).build();
638                     assertEquals(path, weight, font.getStyle().getWeight());
639                     assertEquals(path, slant, font.getStyle().getSlant());
640                     assertEquals(path, 0, font.getTtcIndex());
641                     assertAxesEquals(path, axes, font.getAxes());
642                     assertNotNull(font.getBuffer());
643                     assertNull(font.getFile());
644                 }
645             } finally {
646                 file.delete();
647             }
648         }
649     }
650 
651     @Test
testBuilder_fd_subdata_override()652     public void testBuilder_fd_subdata_override() throws IOException {
653         int customWeight = 350;
654         byte[] placeHolderData = { (byte) 0xde, (byte) 0xad, (byte) 0xbe, (byte) 0xef };
655         AssetManager am = InstrumentationRegistry.getTargetContext().getAssets();
656         for (Pair<Integer, Boolean> style : FontTestUtil.getAllStyles()) {
657             int weight = style.first.intValue();
658             boolean italic = style.second.booleanValue();
659             String path = FontTestUtil.getFontPathFromStyle(weight, italic);
660             final int slant = italic ? FontStyle.FONT_SLANT_ITALIC : FontStyle.FONT_SLANT_UPRIGHT;
661 
662             File file = getTempFile();
663             try (InputStream is = am.open(path)) {
664                 assertTrue(copyToFile(file, is, placeHolderData, placeHolderData));
665 
666                 try (ParcelFileDescriptor fd = ParcelFileDescriptor.open(file,
667                         ParcelFileDescriptor.MODE_READ_ONLY)) {
668                     Font font = new Font.Builder(
669                             fd, placeHolderData.length, file.length() - placeHolderData.length * 2)
670                             .setWeight(customWeight).build();
671                     assertEquals(path, customWeight, font.getStyle().getWeight());
672                     assertEquals(path, slant, font.getStyle().getSlant());
673                     assertEquals(path, 0, font.getTtcIndex());
674                     assertNullOrEmpty(path, font.getAxes());
675                     assertNotNull(font.getBuffer());
676                     assertNull(font.getFile());
677                 }
678             } finally {
679                 file.delete();
680             }
681         }
682 
683         for (Pair<Integer, Boolean> style : FontTestUtil.getAllStyles()) {
684             int weight = style.first.intValue();
685             boolean italic = style.second.booleanValue();
686             String path = FontTestUtil.getFontPathFromStyle(weight, italic);
687 
688             File file = getTempFile();
689             try (InputStream is = am.open(path)) {
690                 assertTrue(copyToFile(file, is, placeHolderData, placeHolderData));
691 
692                 try (ParcelFileDescriptor fd = ParcelFileDescriptor.open(file,
693                         ParcelFileDescriptor.MODE_READ_ONLY)) {
694                     Font font = new Font.Builder(
695                             fd, placeHolderData.length, file.length() - placeHolderData.length * 2)
696                             .setSlant(FontStyle.FONT_SLANT_ITALIC).build();
697                     assertEquals(path, weight, font.getStyle().getWeight());
698                     assertEquals(path, FontStyle.FONT_SLANT_ITALIC, font.getStyle().getSlant());
699                     assertEquals(path, 0, font.getTtcIndex());
700                     assertNullOrEmpty(path, font.getAxes());
701                     assertNotNull(font.getBuffer());
702                     assertNull(font.getFile());
703                 }
704             } finally {
705                 file.delete();
706             }
707         }
708     }
709 
710     @Test(expected = NullPointerException.class)
testBuilder_fd_invalid_null()711     public void testBuilder_fd_invalid_null() throws IOException {
712         ParcelFileDescriptor fd = null;
713         new Font.Builder(fd);
714     }
715 
716     @Test(expected = NullPointerException.class)
testBuilder_fd_subadata_invalid_null()717     public void testBuilder_fd_subadata_invalid_null() throws IOException {
718         ParcelFileDescriptor fd = null;
719         new Font.Builder(fd, 0, -1);
720     }
721 
722     @Test(expected = IOException.class)
testBuilder_fd_subadata_invalid_invalid_size()723     public void testBuilder_fd_subadata_invalid_invalid_size() throws IOException {
724         AssetManager am = InstrumentationRegistry.getTargetContext().getAssets();
725         String path = FontTestUtil.getFontPathFromStyle(400, false);
726 
727         File file = getTempFile();
728         try (InputStream is = am.open(path)) {
729             assertTrue(copyToFile(file, is));
730             try (ParcelFileDescriptor fd = ParcelFileDescriptor.open(file,
731                     ParcelFileDescriptor.MODE_READ_ONLY)) {
732                 new Font.Builder(fd, 0, Integer.MAX_VALUE).build();
733             }
734         }
735     }
736 
737     @Test(expected = IOException.class)
testBuilder_fd_subadata_invalid_invalid_offset()738     public void testBuilder_fd_subadata_invalid_invalid_offset() throws IOException {
739         AssetManager am = InstrumentationRegistry.getTargetContext().getAssets();
740         String path = FontTestUtil.getFontPathFromStyle(400, false);
741 
742         File file = getTempFile();
743         try (InputStream is = am.open(path)) {
744             assertTrue(copyToFile(file, is));
745             try (ParcelFileDescriptor fd = ParcelFileDescriptor.open(file,
746                     ParcelFileDescriptor.MODE_READ_ONLY)) {
747                 new Font.Builder(fd, Integer.MAX_VALUE, file.length()).build();
748             }
749         }
750     }
751 
752     @Test
testBuilder_asset()753     public void testBuilder_asset() throws IOException {
754         AssetManager am = InstrumentationRegistry.getTargetContext().getAssets();
755         for (Pair<Integer, Boolean> style : FontTestUtil.getAllStyles()) {
756             int weight = style.first.intValue();
757             boolean italic = style.second.booleanValue();
758             String path = FontTestUtil.getFontPathFromStyle(weight, italic);
759             final int slant = italic ? FontStyle.FONT_SLANT_ITALIC : FontStyle.FONT_SLANT_UPRIGHT;
760 
761             Font font = new Font.Builder(am, path).build();
762             assertEquals(path, weight, font.getStyle().getWeight());
763             assertEquals(path, slant, font.getStyle().getSlant());
764             assertEquals(path, 0, font.getTtcIndex());
765             assertNullOrEmpty(path, font.getAxes());
766             assertNotNull(font.getBuffer());
767             assertNull(font.getFile());
768         }
769     }
770 
771     @Test
testBuilder_asset_ttc()772     public void testBuilder_asset_ttc() throws IOException {
773         AssetManager am = InstrumentationRegistry.getTargetContext().getAssets();
774         for (Pair<Integer, Boolean> style : FontTestUtil.getAllStyles()) {
775             int weight = style.first.intValue();
776             boolean italic = style.second.booleanValue();
777             String path = FontTestUtil.getTtcFontFileInAsset();
778             int ttcIndex = FontTestUtil.getTtcIndexFromStyle(weight, italic);
779             final int slant = italic ? FontStyle.FONT_SLANT_ITALIC : FontStyle.FONT_SLANT_UPRIGHT;
780 
781             Font font = new Font.Builder(am, path).setTtcIndex(ttcIndex).build();
782             assertEquals(path, weight, font.getStyle().getWeight());
783             assertEquals(path, slant, font.getStyle().getSlant());
784             assertEquals(path, ttcIndex, font.getTtcIndex());
785             assertNullOrEmpty(path, font.getAxes());
786             assertNotNull(font.getBuffer());
787             assertNull(font.getFile());
788         }
789     }
790 
791     @Test
testBuilder_asset_vf()792     public void testBuilder_asset_vf() throws IOException {
793         AssetManager am = InstrumentationRegistry.getTargetContext().getAssets();
794         for (Pair<Integer, Boolean> style : FontTestUtil.getAllStyles()) {
795             int weight = style.first.intValue();
796             boolean italic = style.second.booleanValue();
797             String path = FontTestUtil.getVFFontInAsset();
798             FontVariationAxis[] axes = FontVariationAxis.fromFontVariationSettings(
799                     FontTestUtil.getVarSettingsFromStyle(weight, italic));
800             final int slant = italic ? FontStyle.FONT_SLANT_ITALIC : FontStyle.FONT_SLANT_UPRIGHT;
801 
802             Font font = new Font.Builder(am, path).setFontVariationSettings(axes).build();
803             assertEquals(path, weight, font.getStyle().getWeight());
804             assertEquals(path, slant, font.getStyle().getSlant());
805             assertEquals(path, 0, font.getTtcIndex());
806             assertAxesEquals(path, axes, font.getAxes());
807             assertNotNull(font.getBuffer());
808             assertNull(font.getFile());
809         }
810     }
811 
812     @Test
testBuilder_asset_override()813     public void testBuilder_asset_override() throws IOException {
814         int customWeight = 350;
815         AssetManager am = InstrumentationRegistry.getTargetContext().getAssets();
816         for (Pair<Integer, Boolean> style : FontTestUtil.getAllStyles()) {
817             int weight = style.first.intValue();
818             boolean italic = style.second.booleanValue();
819             String path = FontTestUtil.getFontPathFromStyle(weight, italic);
820             final int slant = italic ? FontStyle.FONT_SLANT_ITALIC : FontStyle.FONT_SLANT_UPRIGHT;
821 
822             Font font = new Font.Builder(am, path).setWeight(customWeight).build();
823             assertEquals(path, customWeight, font.getStyle().getWeight());
824             assertEquals(path, slant, font.getStyle().getSlant());
825             assertEquals(path, 0, font.getTtcIndex());
826             assertNullOrEmpty(path, font.getAxes());
827             assertNotNull(font.getBuffer());
828             assertNull(font.getFile());
829         }
830         for (Pair<Integer, Boolean> style : FontTestUtil.getAllStyles()) {
831             int weight = style.first.intValue();
832             boolean italic = style.second.booleanValue();
833             String path = FontTestUtil.getFontPathFromStyle(weight, italic);
834 
835             Font font = new Font.Builder(am, path).setSlant(FontStyle.FONT_SLANT_ITALIC).build();
836             assertEquals(path, weight, font.getStyle().getWeight());
837             assertEquals(path, FontStyle.FONT_SLANT_ITALIC, font.getStyle().getSlant());
838             assertEquals(path, 0, font.getTtcIndex());
839             assertNullOrEmpty(path, font.getAxes());
840             assertNotNull(font.getBuffer());
841             assertNull(font.getFile());
842         }
843     }
844 
845     @Test(expected = NullPointerException.class)
testBuilder_asset_invalid_null_asset()846     public void testBuilder_asset_invalid_null_asset() throws IOException {
847         AssetManager am = null;
848         new Font.Builder(am, "/some/path");
849     }
850 
851     @Test(expected = IOException.class)
testBuilder_asset_invalid_not_found()852     public void testBuilder_asset_invalid_not_found() throws IOException {
853         AssetManager am = InstrumentationRegistry.getTargetContext().getAssets();
854         new Font.Builder(am, "/no/such/file").build();
855     }
856 
857     @Test
testBuilder_resource()858     public void testBuilder_resource() throws IOException {
859         Resources res = InstrumentationRegistry.getTargetContext().getResources();
860         for (Pair<Integer, Boolean> style : FontTestUtil.getAllStyles()) {
861             int weight = style.first.intValue();
862             boolean italic = style.second.booleanValue();
863             int resId = FontTestUtil.getFontResourceIdFromStyle(weight, italic);
864             final int slant = italic ? FontStyle.FONT_SLANT_ITALIC : FontStyle.FONT_SLANT_UPRIGHT;
865 
866             Font font = new Font.Builder(res, resId).build();
867             assertEquals("ResId=#" + resId, weight, font.getStyle().getWeight());
868             assertEquals("ResId=#" + resId, slant, font.getStyle().getSlant());
869             assertEquals("ResId=#" + resId, 0, font.getTtcIndex());
870             assertNullOrEmpty("ResId=#" + resId, font.getAxes());
871             assertNotNull("ResId=#" + resId, font.getBuffer());
872             assertNull("ResId=#" + resId, font.getFile());
873         }
874     }
875 
876     @Test
testBuilder_resource_ttc()877     public void testBuilder_resource_ttc() throws IOException {
878         Resources res = InstrumentationRegistry.getTargetContext().getResources();
879         for (Pair<Integer, Boolean> style : FontTestUtil.getAllStyles()) {
880             int weight = style.first.intValue();
881             boolean italic = style.second.booleanValue();
882             int resId = FontTestUtil.getTtcFontFileResourceId();
883             int ttcIndex = FontTestUtil.getTtcIndexFromStyle(weight, italic);
884             final int slant = italic ? FontStyle.FONT_SLANT_ITALIC : FontStyle.FONT_SLANT_UPRIGHT;
885 
886             Font font = new Font.Builder(res, resId).setTtcIndex(ttcIndex).build();
887             assertEquals("ResId=#" + resId, weight, font.getStyle().getWeight());
888             assertEquals("ResId=#" + resId, slant, font.getStyle().getSlant());
889             assertEquals("ResId=#" + resId, ttcIndex, font.getTtcIndex());
890             assertNullOrEmpty("ResId=#" + resId, font.getAxes());
891             assertNotNull("ResId=#" + resId, font.getBuffer());
892             assertNull("ResId=#" + resId, font.getFile());
893         }
894     }
895 
896     @Test
testBuilder_resource_vf()897     public void testBuilder_resource_vf() throws IOException {
898         Resources res = InstrumentationRegistry.getTargetContext().getResources();
899         for (Pair<Integer, Boolean> style : FontTestUtil.getAllStyles()) {
900             int weight = style.first.intValue();
901             boolean italic = style.second.booleanValue();
902             int resId = FontTestUtil.getVFFontResourceId();
903             FontVariationAxis[] axes = FontVariationAxis.fromFontVariationSettings(
904                     FontTestUtil.getVarSettingsFromStyle(weight, italic));
905             final int slant = italic ? FontStyle.FONT_SLANT_ITALIC : FontStyle.FONT_SLANT_UPRIGHT;
906 
907             Font font = new Font.Builder(res, resId).setFontVariationSettings(axes).build();
908             assertEquals("ResId=#" + resId, weight, font.getStyle().getWeight());
909             assertEquals("ResId=#" + resId, slant, font.getStyle().getSlant());
910             assertEquals("ResId=#" + resId, 0, font.getTtcIndex());
911             assertAxesEquals("ResId=#" + resId, axes, font.getAxes());
912             assertNotNull("ResId=#" + font.getBuffer());
913             assertNull("ResId=#" + resId, font.getFile());
914         }
915     }
916 
917     @Test
testBuilder_resource_override()918     public void testBuilder_resource_override() throws IOException {
919         int customWeight = 350;
920         Resources res = InstrumentationRegistry.getTargetContext().getResources();
921         for (Pair<Integer, Boolean> style : FontTestUtil.getAllStyles()) {
922             int weight = style.first.intValue();
923             boolean italic = style.second.booleanValue();
924             int resId = FontTestUtil.getFontResourceIdFromStyle(weight, italic);
925             final int slant = italic ? FontStyle.FONT_SLANT_ITALIC : FontStyle.FONT_SLANT_UPRIGHT;
926 
927             Font font = new Font.Builder(res, resId).setWeight(customWeight).build();
928             assertEquals("ResId=#" + resId, customWeight, font.getStyle().getWeight());
929             assertEquals("ResId=#" + resId, slant, font.getStyle().getSlant());
930             assertEquals("ResId=#" + resId, 0, font.getTtcIndex());
931             assertNullOrEmpty("ResId=#" + resId, font.getAxes());
932             assertNotNull("ResId=#" + resId, font.getBuffer());
933             assertNull("ResId=#" + resId, font.getFile());
934         }
935 
936         for (Pair<Integer, Boolean> style : FontTestUtil.getAllStyles()) {
937             int weight = style.first.intValue();
938             boolean italic = style.second.booleanValue();
939             int resId = FontTestUtil.getFontResourceIdFromStyle(weight, italic);
940 
941             Font font = new Font.Builder(res, resId).setSlant(FontStyle.FONT_SLANT_ITALIC).build();
942             assertEquals("ResId=#" + resId, weight, font.getStyle().getWeight());
943             assertEquals("ResId=#" + resId, FontStyle.FONT_SLANT_ITALIC,
944                     font.getStyle().getSlant());
945             assertEquals("ResId=#" + resId, 0, font.getTtcIndex());
946             assertNullOrEmpty("ResId=#" + resId, font.getAxes());
947             assertNotNull("ResId=#" + resId, font.getBuffer());
948             assertNull("ResId=#" + resId, font.getFile());
949         }
950     }
951 
952     @Test(expected = NullPointerException.class)
testBuilder_resource_invalid_null_resource()953     public void testBuilder_resource_invalid_null_resource() throws IOException {
954         Resources res = null;
955         new Font.Builder(res, R.font.ascii);
956     }
957 
958     @Test(expected = NotFoundException.class)
testBuilder_resource_invalid_res_id()959     public void testBuilder_resource_invalid_res_id() throws IOException {
960         Resources res = InstrumentationRegistry.getTargetContext().getResources();
961         new Font.Builder(res, -1).build();
962     }
963 
964     @Test(expected = IOException.class)
testBuilder_asset_invalid_xml_font()965     public void testBuilder_asset_invalid_xml_font() throws IOException {
966         Resources res = InstrumentationRegistry.getTargetContext().getResources();
967         new Font.Builder(res, R.font.multiweight_family /* XML font */).build();
968     }
969 
970     @Test
testEquals()971     public void testEquals() throws IOException {
972         Resources res = InstrumentationRegistry.getTargetContext().getResources();
973         ArrayList<Font> fonts1 = new ArrayList<>();
974         ArrayList<Font> fonts2 = new ArrayList<>();
975         for (Pair<Integer, Boolean> style : FontTestUtil.getAllStyles()) {
976             int weight = style.first.intValue();
977             boolean italic = style.second.booleanValue();
978             int resId = FontTestUtil.getFontResourceIdFromStyle(weight, italic);
979 
980             fonts1.add(new Font.Builder(res, resId).build());
981             fonts2.add(new Font.Builder(res, resId).build());
982         }
983 
984         for (int i = 0; i < fonts1.size(); ++i) {
985             assertTrue(fonts1.get(i).equals(fonts1.get(i)));
986             assertTrue(fonts1.get(i).equals(fonts2.get(i)));
987             assertTrue(fonts2.get(i).equals(fonts1.get(i)));
988         }
989 
990         for (int i = 0; i < fonts1.size(); ++i) {
991             for (int j = i + 1; j < fonts1.size(); ++j) {
992                 assertFalse(fonts1.get(i).equals(fonts1.get(j)));
993                 assertFalse(fonts1.get(j).equals(fonts1.get(i)));
994             }
995         }
996     }
997 
998     @Test
testHashCode()999     public void testHashCode() throws IOException {
1000         Resources res = InstrumentationRegistry.getTargetContext().getResources();
1001         ArrayList<Font> fonts1 = new ArrayList<>();
1002         ArrayList<Font> fonts2 = new ArrayList<>();
1003         for (Pair<Integer, Boolean> style : FontTestUtil.getAllStyles()) {
1004             int weight = style.first.intValue();
1005             boolean italic = style.second.booleanValue();
1006             int resId = FontTestUtil.getFontResourceIdFromStyle(weight, italic);
1007 
1008             fonts1.add(new Font.Builder(res, resId).build());
1009             fonts2.add(new Font.Builder(res, resId).build());
1010         }
1011 
1012         for (int i = 0; i < fonts1.size(); ++i) {
1013             assertEquals(fonts1.get(i).hashCode(), fonts1.get(i).hashCode());
1014             assertEquals(fonts1.get(i).hashCode(), fonts2.get(i).hashCode());
1015         }
1016     }
1017 
1018     @Test(expected = IllegalArgumentException.class)
testMaxFontWeight()1019     public void testMaxFontWeight() throws IOException {
1020         final Resources res = InstrumentationRegistry.getTargetContext().getResources();
1021         new Font.Builder(res, R.font.ascii).setWeight(FontStyle.FONT_WEIGHT_MAX + 1).build();
1022     }
1023 
1024     @Test(expected = IllegalArgumentException.class)
testMinFontWeight()1025     public void testMinFontWeight() throws IOException {
1026         final Resources res = InstrumentationRegistry.getTargetContext().getResources();
1027         new Font.Builder(res, R.font.ascii).setWeight(FontStyle.FONT_WEIGHT_MIN - 1).build();
1028     }
1029 
1030     @Test
builder_with_font_with_axis()1031     public void builder_with_font_with_axis() throws IOException {
1032         AssetManager assets = InstrumentationRegistry.getTargetContext().getAssets();
1033 
1034         // WeightEqualsEmVariableFont adjust glyph advance as follows
1035         //  glyph advance = 'wght' value / 1000
1036         // Thus, by setting text size to 1000px, the glyph advance will equals to passed wght value.
1037         Font baseFont = new Font.Builder(assets, "fonts/var_fonts/WeightEqualsEmVariableFont.ttf")
1038                 .build();
1039 
1040         FontStyle style = new FontStyle(123, FontStyle.FONT_SLANT_ITALIC);
1041 
1042         for (int weight = 50; weight < 1000; weight += 50) {
1043             Font clonedFont = new Font.Builder(baseFont)
1044                     .setWeight(style.getWeight())
1045                     .setSlant(style.getSlant())
1046                     .setFontVariationSettings("'wght' " + weight)
1047                     .build();
1048 
1049             // New font should have the same style passed.
1050             assertEquals(style.getWeight(), clonedFont.getStyle().getWeight());
1051             assertEquals(style.getSlant(), clonedFont.getStyle().getSlant());
1052 
1053             Paint p = new Paint();
1054             p.setTextSize(1000);  // make 1em = 1000px = weight
1055             p.setTypeface(new Typeface.CustomFallbackBuilder(
1056                     new FontFamily.Builder(clonedFont).build()
1057             ).build());
1058             assertEquals(weight, p.measureText("a"), 0);
1059 
1060         }
1061     }
1062 
1063     @Test
builder_with_explicit_style()1064     public void builder_with_explicit_style() throws IOException {
1065         AssetManager assets = InstrumentationRegistry.getTargetContext().getAssets();
1066 
1067         Font baseFont = new Font.Builder(assets, "fonts/others/samplefont.ttf").build();
1068         FontStyle style = new FontStyle(123, FontStyle.FONT_SLANT_ITALIC);
1069         Font clonedFont = new Font.Builder(baseFont)
1070                 .setWeight(style.getWeight())
1071                 .setSlant(style.getSlant())
1072                 .build();
1073 
1074         assertEquals(style.getWeight(), clonedFont.getStyle().getWeight());
1075         assertEquals(style.getSlant(), clonedFont.getStyle().getSlant());
1076     }
1077 
1078     @Test
builder_style_resolve_default()1079     public void builder_style_resolve_default() throws IOException {
1080         AssetManager assets = InstrumentationRegistry.getTargetContext().getAssets();
1081 
1082         Font baseFont = new Font.Builder(assets,
1083                 "fonts/family_selection/ttf/ascii_l3em_weight600_italic.ttf").build();
1084         Font clonedFont = new Font.Builder(baseFont).build();
1085 
1086         assertEquals(600, clonedFont.getStyle().getWeight());
1087         assertEquals(FontStyle.FONT_SLANT_ITALIC, clonedFont.getStyle().getSlant());
1088     }
1089 
1090     @Test
getBoundingBox()1091     public void getBoundingBox() throws IOException {
1092         AssetManager assets = InstrumentationRegistry.getTargetContext().getAssets();
1093 
1094         Font font = new Font.Builder(assets, "fonts/measurement/a3em.ttf").build();
1095         Paint paint = new Paint();
1096         paint.setTextSize(100);  // make 1em = 100px
1097 
1098         int glyphID = 1;  // See a3em.ttx file for the Glyph ID.
1099 
1100         RectF rect = new RectF();
1101         float advance = font.getGlyphBounds(glyphID, paint, rect);
1102 
1103         assertEquals(100f, advance, 0f);
1104         // Glyph bbox is 0.1em shifted to right. See lsb value in hmtx in ttx file.
1105         assertEquals(rect.left, 10f, 0f);
1106         assertEquals(rect.top, -100f, 0f);
1107         assertEquals(rect.right, 110f, 0f);
1108         assertEquals(rect.bottom, 0f, 0f);
1109     }
1110 
1111     @Test
getFontMetrics()1112     public void getFontMetrics() throws IOException {
1113         AssetManager assets = InstrumentationRegistry.getTargetContext().getAssets();
1114 
1115         Font font = new Font.Builder(assets, "fonts/measurement/a3em.ttf").build();
1116         Paint paint = new Paint();
1117         paint.setTextSize(100);  // make 1em = 100px
1118 
1119         Paint.FontMetrics metrics = new Paint.FontMetrics();
1120         font.getMetrics(paint, metrics);
1121 
1122         assertEquals(-100f, metrics.ascent, 0f);
1123         assertEquals(20f, metrics.descent, 0f);
1124         // This refers head.yMax which is not explicitly visible in ttx file.
1125         assertEquals(-300f, metrics.top, 0f);
1126         // This refers head.yMin which is not explicitly visible in ttx file.
1127         assertEquals(0f, metrics.bottom, 0f);
1128     }
1129 
1130     @Test
byteBufferEquality()1131     public void byteBufferEquality() throws IOException {
1132         AssetManager assets = InstrumentationRegistry.getTargetContext().getAssets();
1133 
1134         Font aFont = new Font.Builder(assets, "fonts/others/samplefont.ttf").build();
1135         // Copied font must be equals to original one.
1136         Font bFont = new Font.Builder(aFont).build();
1137         assertEquals(aFont, bFont);
1138         assertEquals(bFont, aFont);
1139 
1140         // Same source font must be equal.
1141         Font cFont = new Font.Builder(assets, "fonts/others/samplefont.ttf").build();
1142         assertEquals(aFont, cFont);
1143         assertEquals(cFont, aFont);
1144 
1145         // Created font from duplicated buffers must be equal.
1146         Font dFont = new Font.Builder(aFont.getBuffer().duplicate()).build();
1147         Font eFont = new Font.Builder(aFont.getBuffer().duplicate()).build();
1148         assertEquals(dFont, eFont);
1149         assertEquals(eFont, dFont);
1150 
1151         // Different parameter should be unequal but sameSource returns true.
1152         Font fFont = new Font.Builder(aFont.getBuffer().duplicate())
1153                 .setFontVariationSettings("'wght' 400").build();
1154         assertNotEquals(aFont, fFont);
1155         assertNotEquals(fFont, aFont);
1156 
1157         // Different source must be not equals.
1158         Font gFont = new Font.Builder(assets, "fonts/others/samplefont2.ttf").build();
1159         assertNotEquals(aFont, gFont);
1160     }
1161 
1162     @Test
fontIdentifier()1163     public void fontIdentifier() throws IOException {
1164         AssetManager assets = InstrumentationRegistry.getTargetContext().getAssets();
1165 
1166         Font aFont = new Font.Builder(assets, "fonts/others/samplefont.ttf").build();
1167         // Copied font must be equals to original one.
1168         Font bFont = new Font.Builder(aFont).build();
1169         assertEquals(aFont.getSourceIdentifier(), bFont.getSourceIdentifier());
1170 
1171         // Different parameter should be unequal but sameSource returns true.
1172         Font dFont = new Font.Builder(aFont)
1173                 .setFontVariationSettings("'wght' 400")
1174                 .setWeight(123)
1175                 .build();
1176         assertEquals(aFont.getSourceIdentifier(), dFont.getSourceIdentifier());
1177 
1178         // Different source must be not equals.
1179         Font gFont = new Font.Builder(assets, "fonts/others/samplefont2.ttf").build();
1180         assertNotEquals(aFont.getSourceIdentifier(), gFont.getSourceIdentifier());
1181 
1182         Typeface typeface = new Typeface.CustomFallbackBuilder(
1183                 new FontFamily.Builder(
1184                         aFont
1185                 ).build()
1186         ).build();
1187 
1188         Paint paint = new Paint();
1189         paint.setTypeface(typeface);
1190         PositionedGlyphs glyphs = TextRunShaper.shapeTextRun("a", 0, 1, 0, 1, 0f, 0f, false, paint);
1191         assertEquals(aFont, glyphs.getFont(0));
1192         assertEquals(aFont.getSourceIdentifier(), glyphs.getFont(0).getSourceIdentifier());
1193     }
1194 
1195     @Test
byteBufferSameHash()1196     public void byteBufferSameHash() throws IOException {
1197         AssetManager assets = InstrumentationRegistry.getTargetContext().getAssets();
1198 
1199         Font aFont = new Font.Builder(assets, "fonts/others/samplefont.ttf").build();
1200         // Copied font must be equals to original one.
1201         assertEquals(new Font.Builder(aFont).build().hashCode(), aFont.hashCode());
1202 
1203         // Same source font must be equal.
1204         assertEquals(new Font.Builder(assets, "fonts/others/samplefont.ttf").build().hashCode(),
1205                 aFont.hashCode());
1206 
1207         // Created font from duplicated buffers must be equal.
1208         int cFontHash = new Font.Builder(aFont.getBuffer().duplicate()).build().hashCode();
1209         int dFontHash = new Font.Builder(aFont.getBuffer().duplicate()).build().hashCode();
1210         assertEquals(cFontHash, dFontHash);
1211     }
1212 }
1213