• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2013 Google Inc.
3  *
4  * Use of this source code is governed by a BSD-style license that can be
5  * found in the LICENSE file.
6  */
7 
8 #include "include/core/SkData.h"
9 #include "include/core/SkFont.h"
10 #include "include/core/SkFontArguments.h"
11 #include "include/core/SkFontMgr.h"
12 #include "include/core/SkFontParameters.h"
13 #include "include/core/SkFontStyle.h"
14 #include "include/core/SkRect.h"
15 #include "include/core/SkRefCnt.h"
16 #include "include/core/SkScalar.h"
17 #include "include/core/SkStream.h"
18 #include "include/core/SkString.h"
19 #include "include/core/SkTypeface.h"
20 #include "include/core/SkTypes.h"
21 #include "include/private/base/SkFixed.h"
22 #include "include/private/base/SkTemplates.h"
23 #include "src/base/SkUTF.h"
24 #include "src/core/SkEndian.h"
25 #include "src/core/SkFontDescriptor.h"
26 #include "src/core/SkFontPriv.h"
27 #include "src/core/SkTypefaceCache.h"
28 #include "src/sfnt/SkOTTable_OS_2.h"
29 #include "src/sfnt/SkOTTable_OS_2_V0.h"
30 #include "src/sfnt/SkSFNTHeader.h"
31 #include "tests/Test.h"
32 #include "tools/Resources.h"
33 #include "tools/ToolUtils.h"
34 #include "tools/fonts/TestEmptyTypeface.h"
35 
36 #include <algorithm>
37 #include <cstddef>
38 #include <cstdint>
39 #include <memory>
40 #include <string>
41 #include <utility>
42 
43 #if defined(SK_BUILD_FOR_WIN)
44 #include "include/ports/SkTypeface_win.h"
45 #include "src/core/SkFontMgrPriv.h"
46 #endif
47 
TypefaceStyle_test(skiatest::Reporter * reporter,uint16_t weight,uint16_t width,SkData * data)48 static void TypefaceStyle_test(skiatest::Reporter* reporter,
49                                uint16_t weight, uint16_t width, SkData* data)
50 {
51     sk_sp<SkData> dataCopy;
52     if (!data->unique()) {
53         dataCopy = SkData::MakeWithCopy(data->data(), data->size());
54         data = dataCopy.get();
55     }
56     SkSFNTHeader* sfntHeader = static_cast<SkSFNTHeader*>(data->writable_data());
57 
58     SkSFNTHeader::TableDirectoryEntry* tableEntry =
59         SkTAfter<SkSFNTHeader::TableDirectoryEntry>(sfntHeader);
60     SkSFNTHeader::TableDirectoryEntry* os2TableEntry = nullptr;
61     int numTables = SkEndian_SwapBE16(sfntHeader->numTables);
62     for (int tableEntryIndex = 0; tableEntryIndex < numTables; ++tableEntryIndex) {
63         if (SkOTTableOS2::TAG == tableEntry[tableEntryIndex].tag) {
64             os2TableEntry = tableEntry + tableEntryIndex;
65             break;
66         }
67     }
68     SkASSERT_RELEASE(os2TableEntry);
69 
70     size_t os2TableOffset = SkEndian_SwapBE32(os2TableEntry->offset);
71     SkOTTableOS2_V0* os2Table = SkTAddOffset<SkOTTableOS2_V0>(sfntHeader, os2TableOffset);
72     os2Table->usWeightClass.value = SkEndian_SwapBE16(weight);
73     using WidthType = SkOTTableOS2_V0::WidthClass::Value;
74     os2Table->usWidthClass.value = static_cast<WidthType>(SkEndian_SwapBE16(width));
75 
76     sk_sp<SkTypeface> newTypeface(SkTypeface::MakeFromData(sk_ref_sp(data)));
77     if (!newTypeface) {
78         // Not all SkFontMgr can MakeFromStream().
79         return;
80     }
81 
82     SkFontStyle newStyle = newTypeface->fontStyle();
83 
84     //printf("%d, %f\n", weight, (newStyle.weight() - (float)0x7FFF) / (float)0x7FFF);
85     //printf("%d, %f\n", width , (newStyle.width()  - (float)0x7F)   / (float)0x7F);
86     //printf("%d, %d\n", weight, newStyle.weight());
87     //printf("%d, %d\n", width , newStyle.width());
88 
89     // Some back-ends (CG, GDI, DW) support OS/2 version A which uses 0 - 10 (but all differently).
90     REPORTER_ASSERT(reporter,
91                     newStyle.weight() == weight ||
92                     (weight <=   10 && newStyle.weight() == 100 * weight) ||
93                     (weight ==    4 && newStyle.weight() == 350) ||  // GDI weirdness
94                     (weight ==    5 && newStyle.weight() == 400) ||  // GDI weirdness
95                     (weight ==    0 && newStyle.weight() ==   1) ||  // DW weirdness
96                     (weight == 1000 && newStyle.weight() == 999)     // DW weirdness
97     );
98 
99     // Some back-ends (GDI) don't support width, ensure these always report 'medium'.
100     REPORTER_ASSERT(reporter,
101                     newStyle.width() == width ||
102                     newStyle.width() == 5);
103 }
DEF_TEST(TypefaceStyle,reporter)104 DEF_TEST(TypefaceStyle, reporter) {
105     std::unique_ptr<SkStreamAsset> stream(GetResourceAsStream("fonts/Em.ttf"));
106     if (!stream) {
107         REPORT_FAILURE(reporter, "fonts/Em.ttf", SkString("Cannot load resource"));
108         return;
109     }
110     sk_sp<SkData> data(SkData::MakeFromStream(stream.get(), stream->getLength()));
111 
112     using SkFS = SkFontStyle;
113     for (int weight = SkFS::kInvisible_Weight; weight <= SkFS::kExtraBlack_Weight; ++weight) {
114         TypefaceStyle_test(reporter, weight, 5, data.get());
115     }
116     for (int width = SkFS::kUltraCondensed_Width; width <= SkFS::kUltraExpanded_Width; ++width) {
117         TypefaceStyle_test(reporter, 400, width, data.get());
118     }
119 }
120 
DEF_TEST(TypefacePostScriptName,reporter)121 DEF_TEST(TypefacePostScriptName, reporter) {
122     sk_sp<SkTypeface> typeface(MakeResourceAsTypeface("fonts/Em.ttf"));
123     if (!typeface) {
124         // Not all SkFontMgr can MakeFromStream().
125         return;
126     }
127 
128     SkString postScriptName;
129     bool hasName = typeface->getPostScriptName(&postScriptName);
130     bool hasName2 = typeface->getPostScriptName(nullptr);
131     REPORTER_ASSERT(reporter, hasName == hasName2);
132     if (hasName) {
133         REPORTER_ASSERT(reporter, postScriptName == SkString("Em"));
134     }
135 }
136 
DEF_TEST(TypefaceRoundTrip,reporter)137 DEF_TEST(TypefaceRoundTrip, reporter) {
138     sk_sp<SkTypeface> typeface(MakeResourceAsTypeface("fonts/7630.otf"));
139     if (!typeface) {
140         // Not all SkFontMgr can MakeFromStream().
141         return;
142     }
143 
144     int fontIndex;
145     std::unique_ptr<SkStreamAsset> stream = typeface->openStream(&fontIndex);
146 
147     sk_sp<SkTypeface> typeface2 = SkTypeface::MakeFromStream(std::move(stream), fontIndex);
148     REPORTER_ASSERT(reporter, typeface2);
149 }
150 
DEF_TEST(FontDescriptorNegativeVariationSerialize,reporter)151 DEF_TEST(FontDescriptorNegativeVariationSerialize, reporter) {
152     SkFontDescriptor desc;
153     SkFontStyle style(2, 9, SkFontStyle::kOblique_Slant);
154     desc.setStyle(style);
155     const char postscriptName[] = "postscript";
156     desc.setPostscriptName(postscriptName);
157     SkFontArguments::VariationPosition::Coordinate* variation = desc.setVariationCoordinates(1);
158     variation[0] = { 0, -1.0f };
159 
160     SkDynamicMemoryWStream stream;
161     desc.serialize(&stream);
162     SkFontDescriptor descD;
163     SkFontDescriptor::Deserialize(stream.detachAsStream().get(), &descD);
164 
165     REPORTER_ASSERT(reporter, descD.getStyle() == style);
166     REPORTER_ASSERT(reporter, 0 == strcmp(desc.getPostscriptName(), postscriptName));
167     if (descD.getVariationCoordinateCount() != 1) {
168         REPORT_FAILURE(reporter, "descD.getVariationCoordinateCount() != 1", SkString());
169         return;
170     }
171 
172     REPORTER_ASSERT(reporter, descD.getVariation()[0].value == -1.0f);
173 }
174 
DEF_TEST(TypefaceAxes,reporter)175 DEF_TEST(TypefaceAxes, reporter) {
176     using Variation = SkFontArguments::VariationPosition;
177     // In DWrite in at least up to 1901 18363.1198 IDWriteFontFace5::GetFontAxisValues and
178     // GetFontAxisValueCount along with IDWriteFontResource::GetFontAxisAttributes and
179     // GetFontAxisCount (and related) seem to incorrectly collapse multiple axes with the same tag.
180     // Since this is a limitation of the underlying implementation, for now allow the test to pass
181     // with the axis tag count (as opposed to the axis count). Eventually all implementations should
182     // pass this test without 'alsoAcceptedAxisTagCount'.
183     auto test = [&](SkTypeface* typeface, const Variation& expected, int alsoAcceptedAxisTagCount) {
184         if (!typeface) {
185             return;  // Not all SkFontMgr can makeFromStream().
186         }
187 
188         int actualCount = typeface->getVariationDesignPosition(nullptr, 0);
189         if (actualCount == -1) {
190             return;  // The number of axes is unknown.
191         }
192         REPORTER_ASSERT(reporter, actualCount == expected.coordinateCount ||
193                                   actualCount == alsoAcceptedAxisTagCount);
194 
195         // Variable font conservative bounds don't vary, so ensure they aren't reported.
196         REPORTER_ASSERT(reporter, typeface->getBounds().isEmpty());
197 
198         std::unique_ptr<Variation::Coordinate[]> actual(new Variation::Coordinate[actualCount]);
199         actualCount = typeface->getVariationDesignPosition(actual.get(), actualCount);
200         if (actualCount == -1) {
201             return;  // The position cannot be determined.
202         }
203         REPORTER_ASSERT(reporter, actualCount == expected.coordinateCount ||
204                                   actualCount == alsoAcceptedAxisTagCount);
205 
206         // Every actual must be expected.
207         std::unique_ptr<bool[]> expectedUsed(new bool[expected.coordinateCount]());
208         for (int actualIdx = 0; actualIdx < actualCount; ++actualIdx) {
209             bool actualFound = false;
210             for (int expectedIdx = 0; expectedIdx < expected.coordinateCount; ++expectedIdx) {
211                 if (expectedUsed[expectedIdx]) {
212                     continue;
213                 }
214 
215                 if (actual[actualIdx].axis != expected.coordinates[expectedIdx].axis) {
216                     continue;
217                 }
218 
219                 // Convert to fixed for "almost equal".
220                 SkFixed fixedRead = SkScalarToFixed(actual[actualIdx].value);
221                 SkFixed fixedOriginal = SkScalarToFixed(expected.coordinates[expectedIdx].value);
222                 if (!(SkTAbs(fixedRead - fixedOriginal) < 2)) {
223                     continue;
224                 }
225 
226                 // This actual matched an unused expected.
227                 actualFound = true;
228                 expectedUsed[expectedIdx] = true;
229                 break;
230             }
231             REPORTER_ASSERT(reporter, actualFound,
232                 "Actual axis '%c%c%c%c' with value '%f' not expected",
233                 (actual[actualIdx].axis >> 24) & 0xFF,
234                 (actual[actualIdx].axis >> 16) & 0xFF,
235                 (actual[actualIdx].axis >>  8) & 0xFF,
236                 (actual[actualIdx].axis      ) & 0xFF,
237                 SkScalarToDouble(actual[actualIdx].value));
238         }
239     };
240 
241     sk_sp<SkFontMgr> fm = SkFontMgr::RefDefault();
242 
243     // Not specifying a position should produce the default.
244     {
245         std::unique_ptr<SkStreamAsset> variable(GetResourceAsStream("fonts/Variable.ttf"));
246         if (!variable) {
247             REPORT_FAILURE(reporter, "variable", SkString());
248             return;
249         }
250         const Variation::Coordinate defaultPosition[] = {
251             { SkSetFourByteTag('w','g','h','t'), 400.0f },
252             { SkSetFourByteTag('w','d','t','h'), 100.0f },
253         };
254         sk_sp<SkTypeface> typeface = fm->makeFromStream(std::move(variable), 0);
255         test(typeface.get(), Variation{&defaultPosition[0], 2}, -1);
256     }
257 
258     // Multiple axes with the same tag (and min, max, default) works.
259     {
260         std::unique_ptr<SkStreamAsset> dupTags(GetResourceAsStream("fonts/VaryAlongQuads.ttf"));
261         if (!dupTags) {
262             REPORT_FAILURE(reporter, "dupTags", SkString());
263             return;
264         }
265 
266         // The position may be over specified. If there are multiple values for a given axis,
267         // ensure the last one since that's what css-fonts-4 requires.
268         const Variation::Coordinate position[] = {
269             { SkSetFourByteTag('w','g','h','t'), 700.0f },
270             { SkSetFourByteTag('w','g','h','t'), 600.0f },
271             { SkSetFourByteTag('w','g','h','t'), 600.0f },
272         };
273         SkFontArguments params;
274         params.setVariationDesignPosition({position, std::size(position)});
275         sk_sp<SkTypeface> typeface = fm->makeFromStream(std::move(dupTags), params);
276         test(typeface.get(), Variation{&position[1], 2}, 1);
277     }
278 
279     // Overspecifying an axis tag value applies the last one in the list.
280     {
281         std::unique_ptr<SkStreamAsset> distortable(GetResourceAsStream("fonts/Distortable.ttf"));
282         if (!distortable) {
283             REPORT_FAILURE(reporter, "distortable", SkString());
284             return;
285         }
286 
287         // The position may be over specified. If there are multiple values for a given axis,
288         // ensure the last one since that's what css-fonts-4 requires.
289         const Variation::Coordinate position[] = {
290             { SkSetFourByteTag('w','g','h','t'), 1.618033988749895f },
291             { SkSetFourByteTag('w','g','h','t'), SK_ScalarSqrt2 },
292         };
293         SkFontArguments params;
294         params.setVariationDesignPosition({position, std::size(position)});
295         sk_sp<SkTypeface> typeface = fm->makeFromStream(std::move(distortable), params);
296         test(typeface.get(), Variation{&position[1], 1}, -1);
297 
298         if (typeface) {
299             // Cloning without specifying any parameters should produce an equivalent variation.
300             sk_sp<SkTypeface> clone = typeface->makeClone(SkFontArguments());
301             test(clone.get(), Variation{&position[1], 1}, -1);
302         }
303     }
304 }
305 
DEF_TEST(TypefaceVariationIndex,reporter)306 DEF_TEST(TypefaceVariationIndex, reporter) {
307     std::unique_ptr<SkStreamAsset> distortable(GetResourceAsStream("fonts/Distortable.ttf"));
308     if (!distortable) {
309         REPORT_FAILURE(reporter, "distortable", SkString());
310         return;
311     }
312 
313     sk_sp<SkFontMgr> fm = SkFontMgr::RefDefault();
314     SkFontArguments params;
315     // The first named variation position in Distortable is 'Thin'.
316     params.setCollectionIndex(0x00010000);
317     sk_sp<SkTypeface> typeface = fm->makeFromStream(std::move(distortable), params);
318     if (!typeface) {
319         // FreeType is the only weird thing that supports this, Skia just needs to make sure if it
320         // gets one of these things make sense.
321         return;
322     }
323 
324     int count = typeface->getVariationDesignPosition(nullptr, 0);
325     if (!(count == 1)) {
326         REPORT_FAILURE(reporter, "count == 1", SkString());
327         return;
328     }
329 
330     SkFontArguments::VariationPosition::Coordinate positionRead[1];
331     count = typeface->getVariationDesignPosition(positionRead, std::size(positionRead));
332     if (count == -1) {
333         return;
334     }
335     if (!(count == 1)) {
336         REPORT_FAILURE(reporter, "count == 1", SkString());
337         return;
338     }
339     REPORTER_ASSERT(reporter, positionRead[0].axis == SkSetFourByteTag('w','g','h','t'));
340     REPORTER_ASSERT(reporter, positionRead[0].value == 0.5);
341 }
342 
DEF_TEST(Typeface,reporter)343 DEF_TEST(Typeface, reporter) {
344 
345     sk_sp<SkTypeface> t1(SkTypeface::MakeFromName(nullptr, SkFontStyle()));
346     sk_sp<SkTypeface> t2(SkTypeface::MakeDefault());
347 
348     REPORTER_ASSERT(reporter, SkTypeface::Equal(t1.get(), t2.get()));
349     REPORTER_ASSERT(reporter, SkTypeface::Equal(nullptr, t1.get()));
350     REPORTER_ASSERT(reporter, SkTypeface::Equal(nullptr, t2.get()));
351     REPORTER_ASSERT(reporter, SkTypeface::Equal(t1.get(), nullptr));
352     REPORTER_ASSERT(reporter, SkTypeface::Equal(t2.get(), nullptr));
353 }
354 
DEF_TEST(TypefaceAxesParameters,reporter)355 DEF_TEST(TypefaceAxesParameters, reporter) {
356     using Axis = SkFontParameters::Variation::Axis;
357 
358     // In DWrite in at least up to 1901 18363.1198 IDWriteFontFace5::GetFontAxisValues and
359     // GetFontAxisValueCount along with IDWriteFontResource::GetFontAxisAttributes and
360     // GetFontAxisCount (and related) seem to incorrectly collapse multiple axes with the same tag.
361     // Since this is a limitation of the underlying implementation, for now allow the test to pass
362     // with the axis tag count (as opposed to the axis count). Eventually all implementations should
363     // pass this test without 'alsoAcceptedAxisTagCount'.
364     auto test = [&](SkTypeface* typeface, const Axis* expected, int expectedCount,
365                     int alsoAcceptedAxisTagCount)
366     {
367         if (!typeface) {
368             return;  // Not all SkFontMgr can makeFromStream().
369         }
370 
371         int actualCount = typeface->getVariationDesignParameters(nullptr, 0);
372         if (actualCount == -1) {
373             return;  // The number of axes is unknown.
374         }
375         REPORTER_ASSERT(reporter, actualCount == expectedCount ||
376                                   actualCount == alsoAcceptedAxisTagCount);
377 
378         std::unique_ptr<Axis[]> actual(new Axis[actualCount]);
379         actualCount = typeface->getVariationDesignParameters(actual.get(), actualCount);
380         if (actualCount == -1) {
381             return;  // The position cannot be determined.
382         }
383         REPORTER_ASSERT(reporter, actualCount == expectedCount ||
384                                   actualCount == alsoAcceptedAxisTagCount);
385 
386         // Every actual must be expected.
387         std::unique_ptr<bool[]> expectedUsed(new bool[expectedCount]());
388         for (int actualIdx = 0; actualIdx < actualCount; ++actualIdx) {
389             bool actualFound = false;
390             for (int expectedIdx = 0; expectedIdx < expectedCount; ++expectedIdx) {
391                 if (expectedUsed[expectedIdx]) {
392                     continue;
393                 }
394 
395                 if (actual[actualIdx].tag != expected[expectedIdx].tag) {
396                     continue;
397                 }
398 
399                 // Convert to fixed for "almost equal".
400                 SkFixed fixedActualMin = SkScalarToFixed(actual[actualIdx].min);
401                 SkFixed fixedExpectedMin = SkScalarToFixed(expected[expectedIdx].min);
402                 if (!(SkTAbs(fixedActualMin - fixedExpectedMin) < 2)) {
403                     continue;
404                 }
405 
406                 SkFixed fixedActualMax = SkScalarToFixed(actual[actualIdx].max);
407                 SkFixed fixedExpectedMax = SkScalarToFixed(expected[expectedIdx].max);
408                 if (!(SkTAbs(fixedActualMax - fixedExpectedMax) < 2)) {
409                     continue;
410                 }
411 
412                 SkFixed fixedActualDefault = SkScalarToFixed(actual[actualIdx].def);
413                 SkFixed fixedExpectedDefault = SkScalarToFixed(expected[expectedIdx].def);
414                 if (!(SkTAbs(fixedActualDefault - fixedExpectedDefault) < 2)) {
415                     continue;
416                 }
417 
418                 // This seems silly, but allows MSAN to ensure that isHidden is initialized.
419                 // In GDI or before macOS 10.12, Win10, or FreeType 2.8.1 API for hidden is missing.
420                 if (actual[actualIdx].isHidden() &&
421                     actual[actualIdx].isHidden() != expected[expectedIdx].isHidden())
422                 {
423                     continue;
424                 }
425 
426                 // This actual matched an unused expected.
427                 actualFound = true;
428                 expectedUsed[expectedIdx] = true;
429                 break;
430             }
431             REPORTER_ASSERT(reporter, actualFound,
432                 "Actual axis '%c%c%c%c' with min %f max %f default %f hidden %s not expected",
433                 (actual[actualIdx].tag >> 24) & 0xFF,
434                 (actual[actualIdx].tag >> 16) & 0xFF,
435                 (actual[actualIdx].tag >>  8) & 0xFF,
436                 (actual[actualIdx].tag      ) & 0xFF,
437                 actual[actualIdx].min,
438                 actual[actualIdx].def,
439                 actual[actualIdx].max,
440                 actual[actualIdx].isHidden() ? "true" : "false");
441         }
442     };
443 
444     sk_sp<SkFontMgr> fm = SkFontMgr::RefDefault();
445 
446     // Two axis OpenType variable font.
447     {
448         std::unique_ptr<SkStreamAsset> variable(GetResourceAsStream("fonts/Variable.ttf"));
449         if (!variable) {
450             REPORT_FAILURE(reporter, "variable", SkString());
451             return;
452         }
453         constexpr Axis expected[] = {
454             Axis(SkSetFourByteTag('w','g','h','t'), 100.0f, 400.0f, 900.0f, true ),
455             Axis(SkSetFourByteTag('w','d','t','h'),  50.0f, 100.0f, 200.0f, false),
456         };
457         sk_sp<SkTypeface> typeface = fm->makeFromStream(std::move(variable), 0);
458         test(typeface.get(), &expected[0], std::size(expected), -1);
459     }
460 
461     // Multiple axes with the same tag (and min, max, default) works.
462     {
463         std::unique_ptr<SkStreamAsset> dupTags(GetResourceAsStream("fonts/VaryAlongQuads.ttf"));
464         if (!dupTags) {
465             REPORT_FAILURE(reporter, "dupTags", SkString());
466             return;
467         }
468 
469         // The position may be over specified. If there are multiple values for a given axis,
470         // ensure the last one since that's what css-fonts-4 requires.
471         constexpr Axis expected[] = {
472             Axis(SkSetFourByteTag('w','g','h','t'), 100.0f, 400.0f, 900.0f, false),
473             Axis(SkSetFourByteTag('w','g','h','t'), 100.0f, 400.0f, 900.0f, false),
474         };
475         sk_sp<SkTypeface> typeface = fm->makeFromStream(std::move(dupTags), 0);
476         test(typeface.get(), &expected[0], std::size(expected), 1);
477     }
478 
479     // Simple single axis GX variable font.
480     {
481         std::unique_ptr<SkStreamAsset> distortable(GetResourceAsStream("fonts/Distortable.ttf"));
482         if (!distortable) {
483             REPORT_FAILURE(reporter, "distortable", SkString());
484             return;
485         }
486         constexpr Axis expected[] = {
487             Axis(SkSetFourByteTag('w','g','h','t'), 0.5f, 1.0f, 2.0f, true),
488         };
489         sk_sp<SkTypeface> typeface = fm->makeFromStream(std::move(distortable), 0);
490         test(typeface.get(), &expected[0], std::size(expected), -1);
491     }
492 }
493 
count_proc(SkTypeface * face,void * ctx)494 static bool count_proc(SkTypeface* face, void* ctx) {
495     int* count = static_cast<int*>(ctx);
496     *count = *count + 1;
497     return false;
498 }
count(skiatest::Reporter * reporter,const SkTypefaceCache & cache)499 static int count(skiatest::Reporter* reporter, const SkTypefaceCache& cache) {
500     int count = 0;
501     sk_sp<SkTypeface> none = cache.findByProcAndRef(count_proc, &count);
502     REPORTER_ASSERT(reporter, none == nullptr);
503     return count;
504 }
505 
DEF_TEST(TypefaceCache,reporter)506 DEF_TEST(TypefaceCache, reporter) {
507     sk_sp<SkTypeface> t1(TestEmptyTypeface::Make());
508     {
509         SkTypefaceCache cache;
510         REPORTER_ASSERT(reporter, count(reporter, cache) == 0);
511         {
512             sk_sp<SkTypeface> t0(TestEmptyTypeface::Make());
513             cache.add(t0);
514             REPORTER_ASSERT(reporter, count(reporter, cache) == 1);
515             cache.add(t1);
516             REPORTER_ASSERT(reporter, count(reporter, cache) == 2);
517             cache.purgeAll();
518             REPORTER_ASSERT(reporter, count(reporter, cache) == 2);
519         }
520         REPORTER_ASSERT(reporter, count(reporter, cache) == 2);
521         cache.purgeAll();
522         REPORTER_ASSERT(reporter, count(reporter, cache) == 1);
523     }
524     REPORTER_ASSERT(reporter, t1->unique());
525 }
526 
check_serialize_behaviors(sk_sp<SkTypeface> tf,skiatest::Reporter * reporter)527 static void check_serialize_behaviors(sk_sp<SkTypeface> tf, skiatest::Reporter* reporter) {
528     if (!tf) {
529         return;
530     }
531 
532     SkFontDescriptor desc;
533     bool serialize;
534     tf->getFontDescriptor(&desc, &serialize);
535 
536     auto data0 = tf->serialize(SkTypeface::SerializeBehavior::kDoIncludeData);
537     auto data1 = tf->serialize(SkTypeface::SerializeBehavior::kDontIncludeData);
538     auto data2 = tf->serialize(SkTypeface::SerializeBehavior::kIncludeDataIfLocal);
539 
540     REPORTER_ASSERT(reporter, data0->size() >= data1->size());
541 
542     if (serialize) {
543         REPORTER_ASSERT(reporter, data0->equals(data2.get()));
544     } else {
545         REPORTER_ASSERT(reporter, data1->equals(data2.get()));
546     }
547 }
548 
DEF_TEST(Typeface_serialize,reporter)549 DEF_TEST(Typeface_serialize, reporter) {
550     check_serialize_behaviors(SkTypeface::MakeDefault(), reporter);
551     check_serialize_behaviors(
552         SkTypeface::MakeFromStream(GetResourceAsStream("fonts/Distortable.ttf")), reporter);
553 
554 }
555 
DEF_TEST(Typeface_glyph_to_char,reporter)556 DEF_TEST(Typeface_glyph_to_char, reporter) {
557     SkFont font(ToolUtils::emoji_typeface(), 12);
558     SkASSERT(font.getTypeface());
559     char const * text = ToolUtils::emoji_sample_text();
560     size_t const textLen = strlen(text);
561     SkString familyName;
562     font.getTypeface()->getFamilyName(&familyName);
563 
564     size_t const codepointCount = SkUTF::CountUTF8(text, textLen);
565     char const * const textEnd = text + textLen;
566     std::unique_ptr<SkUnichar[]> originalCodepoints(new SkUnichar[codepointCount]);
567     for (size_t i = 0; i < codepointCount; ++i) {
568         originalCodepoints[i] = SkUTF::NextUTF8(&text, textEnd);
569     }
570     std::unique_ptr<SkGlyphID[]> glyphs(new SkGlyphID[codepointCount]);
571     font.unicharsToGlyphs(originalCodepoints.get(), codepointCount, glyphs.get());
572     if (std::any_of(glyphs.get(), glyphs.get()+codepointCount, [](SkGlyphID g){ return g == 0;})) {
573         ERRORF(reporter, "Unexpected typeface \"%s\". Expected full support for emoji_sample_text.",
574                familyName.c_str());
575         return;
576     }
577 
578     std::unique_ptr<SkUnichar[]> newCodepoints(new SkUnichar[codepointCount]);
579     SkFontPriv::GlyphsToUnichars(font, glyphs.get(), codepointCount, newCodepoints.get());
580 
581     for (size_t i = 0; i < codepointCount; ++i) {
582 #if defined(SK_BUILD_FOR_WIN)
583         // GDI does not support character to glyph mapping outside BMP.
584         if (gSkFontMgr_DefaultFactory == &SkFontMgr_New_GDI &&
585             0xFFFF < originalCodepoints[i] && newCodepoints[i] == 0)
586         {
587             continue;
588         }
589 #endif
590         // If two codepoints map to the same glyph then this assert is not valid.
591         // However, the emoji test font should never have multiple characters map to the same glyph.
592         REPORTER_ASSERT(reporter, originalCodepoints[i] == newCodepoints[i],
593                         "name:%s i:%zu original:%d new:%d glyph:%d", familyName.c_str(), i,
594                         originalCodepoints[i], newCodepoints[i], glyphs[i]);
595     }
596 }
597 
598 // This test makes sure the legacy typeface creation does not lose its specified
599 // style. See https://bugs.chromium.org/p/skia/issues/detail?id=8447 for more
600 // context.
DEF_TEST(LegacyMakeTypeface,reporter)601 DEF_TEST(LegacyMakeTypeface, reporter) {
602     sk_sp<SkFontMgr> fm = SkFontMgr::RefDefault();
603     sk_sp<SkTypeface> typeface1 = fm->legacyMakeTypeface(nullptr, SkFontStyle::Italic());
604     sk_sp<SkTypeface> typeface2 = fm->legacyMakeTypeface(nullptr, SkFontStyle::Bold());
605     sk_sp<SkTypeface> typeface3 = fm->legacyMakeTypeface(nullptr, SkFontStyle::BoldItalic());
606 
607     REPORTER_ASSERT(reporter, typeface1->isItalic());
608     REPORTER_ASSERT(reporter, !typeface1->isBold());
609     REPORTER_ASSERT(reporter, !typeface2->isItalic());
610     REPORTER_ASSERT(reporter, typeface2->isBold());
611     REPORTER_ASSERT(reporter, typeface3->isItalic());
612     REPORTER_ASSERT(reporter, typeface3->isBold());
613 }
614