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