xref: /aosp_15_r20/external/skia/tests/TypefaceTest.cpp (revision c8dee2aa9b3f27cf6c858bd81872bdeb2c07ed17)
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/SkFontMetrics.h"
12 #include "include/core/SkFontMgr.h"
13 #include "include/core/SkFontParameters.h"
14 #include "include/core/SkFontStyle.h"
15 #include "include/core/SkRect.h"
16 #include "include/core/SkRefCnt.h"
17 #include "include/core/SkScalar.h"
18 #include "include/core/SkStream.h"
19 #include "include/core/SkString.h"
20 #include "include/core/SkTypeface.h"
21 #include "include/core/SkTypes.h"
22 #include "include/private/base/SkFixed.h"
23 #include "include/private/base/SkTemplates.h"
24 #include "include/utils/SkCustomTypeface.h"
25 #include "src/base/SkEndian.h"
26 #include "src/base/SkUTF.h"
27 #include "src/core/SkFontDescriptor.h"
28 #include "src/core/SkFontPriv.h"
29 #include "src/core/SkTypefaceCache.h"
30 #include "src/sfnt/SkOTTable_OS_2.h"
31 #include "src/sfnt/SkOTTable_OS_2_V0.h"
32 #include "src/sfnt/SkSFNTHeader.h"
33 #include "tests/Test.h"
34 #include "tools/Resources.h"
35 #include "tools/ToolUtils.h"
36 #include "tools/fonts/FontToolUtils.h"
37 #include "tools/fonts/TestEmptyTypeface.h"
38 
39 #include <algorithm>
40 #include <array>
41 #include <cinttypes>
42 #include <cstddef>
43 #include <cstdint>
44 #include <cstring>
45 #include <memory>
46 #include <utility>
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(ToolUtils::TestFontMgr()->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 'normal'.
100     REPORTER_ASSERT(
101             reporter,
102             newStyle.width() == width || newStyle.width() == SkFontStyle::Width::kNormal_Width,
103             "newStyle.width(): %d width: %" PRIu16, newStyle.width(), width);
104 }
DEF_TEST(TypefaceStyle,reporter)105 DEF_TEST(TypefaceStyle, reporter) {
106     std::unique_ptr<SkStreamAsset> stream(GetResourceAsStream("fonts/Em.ttf"));
107     if (!stream) {
108         REPORT_FAILURE(reporter, "fonts/Em.ttf", SkString("Cannot load resource"));
109         return;
110     }
111     sk_sp<SkData> data(SkData::MakeFromStream(stream.get(), stream->getLength()));
112 
113     using SkFS = SkFontStyle;
114     for (int weight = SkFS::kInvisible_Weight; weight <= SkFS::kExtraBlack_Weight; ++weight) {
115         TypefaceStyle_test(reporter, weight, 5, data.get());
116     }
117     for (int width = SkFS::kUltraCondensed_Width; width <= SkFS::kUltraExpanded_Width; ++width) {
118         TypefaceStyle_test(reporter, 400, width, data.get());
119     }
120 }
121 
DEF_TEST(TypefaceStyleVariable,reporter)122 DEF_TEST(TypefaceStyleVariable, reporter) {
123     using Variation = SkFontArguments::VariationPosition;
124     sk_sp<SkFontMgr> fm = ToolUtils::TestFontMgr();
125 
126     std::unique_ptr<SkStreamAsset> stream(GetResourceAsStream("fonts/Variable.ttf"));
127     if (!stream) {
128         REPORT_FAILURE(reporter, "fonts/Variable.ttf", SkString("Cannot load resource"));
129         return;
130     }
131     sk_sp<SkTypeface> typeface(ToolUtils::TestFontMgr()->makeFromStream(stream->duplicate()));
132     if (!typeface) {
133         // Not all SkFontMgr can MakeFromStream().
134         return;
135     }
136 
137     // Creating Variable.ttf without any extra parameters should have a normal font style.
138     SkFontStyle fs = typeface->fontStyle();
139     REPORTER_ASSERT(reporter, fs == SkFontStyle::Normal(),
140                     "fs: %d %d %d", fs.weight(), fs.width(), fs.slant());
141 
142     // Ensure that the font supports variable stuff
143     Variation::Coordinate varPos[2];
144     int numAxes = typeface->getVariationDesignPosition(varPos, std::size(varPos));
145     if (numAxes <= 0) {
146         // Not all SkTypeface can get the variation.
147         return;
148     }
149     if (numAxes != 2) {
150         // Variable.ttf has two axes.
151         REPORTER_ASSERT(reporter, numAxes == 2);
152         return;
153     }
154 
155     // If a fontmgr or typeface can do variations, ensure the variation affects the reported style.
156     struct TestCase {
157         std::vector<Variation::Coordinate> position;
158         SkFontStyle expected;
159 
160         // On Mac10.15 and earlier, the wdth affected the style using the old gx ranges.
161         // On macOS 11 and later, the wdth affects the style using the new OpenType ranges.
162         // Allow old CoreText to report the wrong width values.
163         SkFontStyle mac1015expected;
164     } testCases[] = {
165       // In range but non-default
166       { {{ SkSetFourByteTag('w','g','h','t'), 200.0f },
167          { SkSetFourByteTag('w','d','t','h'), 75.0f  }},
168         {200, 3, SkFontStyle::kUpright_Slant},
169         {200, 9, SkFontStyle::kUpright_Slant}},
170 
171       // Out of range low, should clamp
172       { {{ SkSetFourByteTag('w','g','h','t'), 0.0f },
173          { SkSetFourByteTag('w','d','t','h'), 75.0f  }},
174         {100, 3, SkFontStyle::kUpright_Slant},
175         {100, 9, SkFontStyle::kUpright_Slant}},
176 
177       // Out of range high, should clamp
178       { {{ SkSetFourByteTag('w','g','h','t'), 10000.0f },
179          { SkSetFourByteTag('w','d','t','h'), 75.0f  }},
180         {900, 3, SkFontStyle::kUpright_Slant},
181         {900, 9, SkFontStyle::kUpright_Slant}},
182     };
183 
184     auto runTest = [&fm, &typeface, &stream, &reporter](TestCase& test){
185         static const constexpr bool isMac =
186 #if defined(SK_BUILD_FOR_MAC)
187             true;
188 #else
189             false;
190 #endif
191         SkFontArguments args;
192         args.setVariationDesignPosition(Variation{test.position.data(), (int)test.position.size()});
193 
194         sk_sp<SkTypeface> nonDefaultTypeface = fm->makeFromStream(stream->duplicate(), args);
195         SkFontStyle ndfs = nonDefaultTypeface->fontStyle();
196         REPORTER_ASSERT(reporter, ndfs == test.expected || (isMac && ndfs == test.mac1015expected),
197                         "ndfs: %d %d %d", ndfs.weight(), ndfs.width(), ndfs.slant());
198 
199         sk_sp<SkTypeface> cloneTypeface = typeface->makeClone(args);
200         SkFontStyle cfs = cloneTypeface->fontStyle();
201         REPORTER_ASSERT(reporter, cfs == test.expected || (isMac && cfs == test.mac1015expected),
202                         "cfs: %d %d %d", cfs.weight(), cfs.width(), cfs.slant());
203 
204     };
205 
206     for (auto&& testCase : testCases) {
207         runTest(testCase);
208     }
209 }
210 
DEF_TEST(TypefacePostScriptName,reporter)211 DEF_TEST(TypefacePostScriptName, reporter) {
212     sk_sp<SkTypeface> typeface(ToolUtils::CreateTypefaceFromResource("fonts/Em.ttf"));
213     if (!typeface) {
214         // Not all SkFontMgr can MakeFromStream().
215         return;
216     }
217 
218     SkString postScriptName;
219     bool hasName = typeface->getPostScriptName(&postScriptName);
220     bool hasName2 = typeface->getPostScriptName(nullptr);
221     REPORTER_ASSERT(reporter, hasName == hasName2);
222     if (hasName) {
223         REPORTER_ASSERT(reporter, postScriptName == SkString("Em"));
224     }
225 }
226 
DEF_TEST(TypefaceRoundTrip,reporter)227 DEF_TEST(TypefaceRoundTrip, reporter) {
228     sk_sp<SkTypeface> typeface(ToolUtils::CreateTypefaceFromResource("fonts/7630.otf"));
229     if (!typeface) {
230         // Not all SkFontMgr can MakeFromStream().
231         return;
232     }
233 
234     int fontIndex;
235     std::unique_ptr<SkStreamAsset> stream = typeface->openStream(&fontIndex);
236 
237     sk_sp<SkTypeface> typeface2 =
238             ToolUtils::TestFontMgr()->makeFromStream(std::move(stream), fontIndex);
239     REPORTER_ASSERT(reporter, typeface2);
240 }
241 
DEF_TEST(FontDescriptorNegativeVariationSerialize,reporter)242 DEF_TEST(FontDescriptorNegativeVariationSerialize, reporter) {
243     SkFontDescriptor desc;
244     SkFontStyle style(2, 9, SkFontStyle::kOblique_Slant);
245     desc.setStyle(style);
246     const char postscriptName[] = "postscript";
247     desc.setPostscriptName(postscriptName);
248     SkFontArguments::VariationPosition::Coordinate* variation = desc.setVariationCoordinates(1);
249     variation[0] = { 0, -1.0f };
250 
251     SkDynamicMemoryWStream stream;
252     desc.serialize(&stream);
253     SkFontDescriptor descD;
254     SkFontDescriptor::Deserialize(stream.detachAsStream().get(), &descD);
255 
256     REPORTER_ASSERT(reporter, descD.getStyle() == style);
257     REPORTER_ASSERT(reporter, 0 == strcmp(desc.getPostscriptName(), postscriptName));
258     if (descD.getVariationCoordinateCount() != 1) {
259         REPORT_FAILURE(reporter, "descD.getVariationCoordinateCount() != 1", SkString());
260         return;
261     }
262 
263     REPORTER_ASSERT(reporter, descD.getVariation()[0].value == -1.0f);
264 }
265 
DEF_TEST(TypefaceAxes,reporter)266 DEF_TEST(TypefaceAxes, reporter) {
267     using Variation = SkFontArguments::VariationPosition;
268     // In DWrite in at least up to 1901 18363.1198 IDWriteFontFace5::GetFontAxisValues and
269     // GetFontAxisValueCount along with IDWriteFontResource::GetFontAxisAttributes and
270     // GetFontAxisCount (and related) seem to incorrectly collapse multiple axes with the same tag.
271     // Since this is a limitation of the underlying implementation, for now allow the test to pass
272     // with the axis tag count (as opposed to the axis count). Eventually all implementations should
273     // pass this test without 'alsoAcceptedAxisTagCount'.
274     auto test = [&](SkTypeface* typeface, const Variation& expected, int alsoAcceptedAxisTagCount) {
275         if (!typeface) {
276             return;  // Not all SkFontMgr can makeFromStream().
277         }
278 
279         int actualCount = typeface->getVariationDesignPosition(nullptr, 0);
280         if (actualCount == -1) {
281             return;  // The number of axes is unknown.
282         }
283         REPORTER_ASSERT(reporter, actualCount == expected.coordinateCount ||
284                                   actualCount == alsoAcceptedAxisTagCount);
285 
286         // Variable font conservative bounds don't vary, so ensure they aren't reported.
287         REPORTER_ASSERT(reporter, typeface->getBounds().isEmpty());
288 
289         std::unique_ptr<Variation::Coordinate[]> actual(new Variation::Coordinate[actualCount]);
290         actualCount = typeface->getVariationDesignPosition(actual.get(), actualCount);
291         if (actualCount == -1) {
292             return;  // The position cannot be determined.
293         }
294         REPORTER_ASSERT(reporter, actualCount == expected.coordinateCount ||
295                                   actualCount == alsoAcceptedAxisTagCount);
296 
297         // Every actual must be expected.
298         std::unique_ptr<bool[]> expectedUsed(new bool[expected.coordinateCount]());
299         for (int actualIdx = 0; actualIdx < actualCount; ++actualIdx) {
300             bool actualFound = false;
301             for (int expectedIdx = 0; expectedIdx < expected.coordinateCount; ++expectedIdx) {
302                 if (expectedUsed[expectedIdx]) {
303                     continue;
304                 }
305 
306                 if (actual[actualIdx].axis != expected.coordinates[expectedIdx].axis) {
307                     continue;
308                 }
309 
310                 // Convert to fixed for "almost equal".
311                 SkFixed fixedRead = SkScalarToFixed(actual[actualIdx].value);
312                 SkFixed fixedOriginal = SkScalarToFixed(expected.coordinates[expectedIdx].value);
313                 if (!(SkTAbs(fixedRead - fixedOriginal) < 2)) {
314                     continue;
315                 }
316 
317                 // This actual matched an unused expected.
318                 actualFound = true;
319                 expectedUsed[expectedIdx] = true;
320                 break;
321             }
322             REPORTER_ASSERT(reporter, actualFound,
323                 "Actual axis '%c%c%c%c' with value '%f' not expected",
324                 (char)((actual[actualIdx].axis >> 24) & 0xFF),
325                 (char)((actual[actualIdx].axis >> 16) & 0xFF),
326                 (char)((actual[actualIdx].axis >>  8) & 0xFF),
327                 (char)((actual[actualIdx].axis      ) & 0xFF),
328                 SkScalarToDouble(actual[actualIdx].value));
329         }
330     };
331 
332     sk_sp<SkFontMgr> fm = ToolUtils::TestFontMgr();
333 
334     // Not specifying a position should produce the default.
335     {
336         std::unique_ptr<SkStreamAsset> variable(GetResourceAsStream("fonts/Variable.ttf"));
337         if (!variable) {
338             REPORT_FAILURE(reporter, "variable", SkString());
339             return;
340         }
341         const Variation::Coordinate defaultPosition[] = {
342             { SkSetFourByteTag('w','g','h','t'), 400.0f },
343             { SkSetFourByteTag('w','d','t','h'), 100.0f },
344         };
345         sk_sp<SkTypeface> typeface = fm->makeFromStream(std::move(variable), 0);
346         test(typeface.get(), Variation{&defaultPosition[0], 2}, -1);
347     }
348 
349     // Multiple axes with the same tag (and min, max, default) works.
350     {
351         std::unique_ptr<SkStreamAsset> dupTags(GetResourceAsStream("fonts/VaryAlongQuads.ttf"));
352         if (!dupTags) {
353             REPORT_FAILURE(reporter, "dupTags", SkString());
354             return;
355         }
356 
357         // The position may be over specified. If there are multiple values for a given axis,
358         // ensure the last one since that's what css-fonts-4 requires.
359         const Variation::Coordinate position[] = {
360             { SkSetFourByteTag('w','g','h','t'), 700.0f },
361             { SkSetFourByteTag('w','g','h','t'), 600.0f },
362             { SkSetFourByteTag('w','g','h','t'), 600.0f },
363         };
364         SkFontArguments params;
365         params.setVariationDesignPosition({position, std::size(position)});
366         sk_sp<SkTypeface> typeface = fm->makeFromStream(std::move(dupTags), params);
367         test(typeface.get(), Variation{&position[1], 2}, 1);
368     }
369 
370     // Overspecifying an axis tag value applies the last one in the list.
371     {
372         std::unique_ptr<SkStreamAsset> distortable(GetResourceAsStream("fonts/Distortable.ttf"));
373         if (!distortable) {
374             REPORT_FAILURE(reporter, "distortable", SkString());
375             return;
376         }
377 
378         // The position may be over specified. If there are multiple values for a given axis,
379         // ensure the last one since that's what css-fonts-4 requires.
380         const Variation::Coordinate position[] = {
381             { SkSetFourByteTag('w','g','h','t'), 1.618033988749895f },
382             { SkSetFourByteTag('w','g','h','t'), SK_ScalarSqrt2 },
383         };
384         SkFontArguments params;
385         params.setVariationDesignPosition({position, std::size(position)});
386         sk_sp<SkTypeface> typeface = fm->makeFromStream(std::move(distortable), params);
387         test(typeface.get(), Variation{&position[1], 1}, -1);
388 
389         if (typeface) {
390             // Cloning without specifying any parameters should produce an equivalent variation.
391             sk_sp<SkTypeface> clone = typeface->makeClone(SkFontArguments());
392             test(clone.get(), Variation{&position[1], 1}, -1);
393         }
394     }
395 }
396 
DEF_TEST(TypefaceVariationIndex,reporter)397 DEF_TEST(TypefaceVariationIndex, reporter) {
398     std::unique_ptr<SkStreamAsset> distortable(GetResourceAsStream("fonts/Distortable.ttf"));
399     if (!distortable) {
400         REPORT_FAILURE(reporter, "distortable", SkString());
401         return;
402     }
403 
404     sk_sp<SkFontMgr> fm = ToolUtils::TestFontMgr();
405     SkFontArguments params;
406     // The first named variation position in Distortable is 'Thin'.
407     params.setCollectionIndex(0x00010000);
408     sk_sp<SkTypeface> typeface = fm->makeFromStream(std::move(distortable), params);
409     if (!typeface) {
410         // FreeType is the only weird thing that supports this, Skia just needs to make sure if it
411         // gets one of these things make sense.
412         return;
413     }
414 
415     int count = typeface->getVariationDesignPosition(nullptr, 0);
416     if (!(count == 1)) {
417         REPORT_FAILURE(reporter, "count == 1", SkString());
418         return;
419     }
420 
421     SkFontArguments::VariationPosition::Coordinate positionRead[1];
422     count = typeface->getVariationDesignPosition(positionRead, std::size(positionRead));
423     if (count == -1) {
424         return;
425     }
426     if (!(count == 1)) {
427         REPORT_FAILURE(reporter, "count == 1", SkString());
428         return;
429     }
430     REPORTER_ASSERT(reporter, positionRead[0].axis == SkSetFourByteTag('w','g','h','t'));
431     REPORTER_ASSERT(reporter, positionRead[0].value == 0.5,
432                     "positionRead[0].value: %f", positionRead[0].value);
433 }
434 
DEF_TEST(Typeface,reporter)435 DEF_TEST(Typeface, reporter) {
436 
437     sk_sp<SkTypeface> t1(ToolUtils::CreateTestTypeface(nullptr, SkFontStyle()));
438     sk_sp<SkTypeface> t2(ToolUtils::DefaultTypeface());
439 
440     REPORTER_ASSERT(reporter, SkTypeface::Equal(t1.get(), t2.get()));
441     REPORTER_ASSERT(reporter, SkTypeface::Equal(nullptr, nullptr));
442 
443     REPORTER_ASSERT(reporter, !SkTypeface::Equal(nullptr, t1.get()));
444     REPORTER_ASSERT(reporter, !SkTypeface::Equal(nullptr, t2.get()));
445     REPORTER_ASSERT(reporter, !SkTypeface::Equal(t1.get(), nullptr));
446     REPORTER_ASSERT(reporter, !SkTypeface::Equal(t2.get(), nullptr));
447 }
448 
DEF_TEST(TypefaceAxesParameters,reporter)449 DEF_TEST(TypefaceAxesParameters, reporter) {
450     using Axis = SkFontParameters::Variation::Axis;
451 
452     // In DWrite in at least up to 1901 18363.1198 IDWriteFontFace5::GetFontAxisValues and
453     // GetFontAxisValueCount along with IDWriteFontResource::GetFontAxisAttributes and
454     // GetFontAxisCount (and related) seem to incorrectly collapse multiple axes with the same tag.
455     // Since this is a limitation of the underlying implementation, for now allow the test to pass
456     // with the axis tag count (as opposed to the axis count). Eventually all implementations should
457     // pass this test without 'alsoAcceptedAxisTagCount'.
458     auto test = [&](SkTypeface* typeface, const Axis* expected, int expectedCount,
459                     int alsoAcceptedAxisTagCount)
460     {
461         if (!typeface) {
462             return;  // Not all SkFontMgr can makeFromStream().
463         }
464 
465         int actualCount = typeface->getVariationDesignParameters(nullptr, 0);
466         if (actualCount == -1) {
467             return;  // The number of axes is unknown.
468         }
469         REPORTER_ASSERT(reporter, actualCount == expectedCount ||
470                                   actualCount == alsoAcceptedAxisTagCount);
471 
472         std::unique_ptr<Axis[]> actual(new Axis[actualCount]);
473         actualCount = typeface->getVariationDesignParameters(actual.get(), actualCount);
474         if (actualCount == -1) {
475             return;  // The position cannot be determined.
476         }
477         REPORTER_ASSERT(reporter, actualCount == expectedCount ||
478                                   actualCount == alsoAcceptedAxisTagCount);
479 
480         // Every actual must be expected.
481         std::unique_ptr<bool[]> expectedUsed(new bool[expectedCount]());
482         for (int actualIdx = 0; actualIdx < actualCount; ++actualIdx) {
483             bool actualFound = false;
484             for (int expectedIdx = 0; expectedIdx < expectedCount; ++expectedIdx) {
485                 if (expectedUsed[expectedIdx]) {
486                     continue;
487                 }
488 
489                 if (actual[actualIdx].tag != expected[expectedIdx].tag) {
490                     continue;
491                 }
492 
493                 // Convert to fixed for "almost equal".
494                 SkFixed fixedActualMin = SkScalarToFixed(actual[actualIdx].min);
495                 SkFixed fixedExpectedMin = SkScalarToFixed(expected[expectedIdx].min);
496                 if (!(SkTAbs(fixedActualMin - fixedExpectedMin) < 2)) {
497                     continue;
498                 }
499 
500                 SkFixed fixedActualMax = SkScalarToFixed(actual[actualIdx].max);
501                 SkFixed fixedExpectedMax = SkScalarToFixed(expected[expectedIdx].max);
502                 if (!(SkTAbs(fixedActualMax - fixedExpectedMax) < 2)) {
503                     continue;
504                 }
505 
506                 SkFixed fixedActualDefault = SkScalarToFixed(actual[actualIdx].def);
507                 SkFixed fixedExpectedDefault = SkScalarToFixed(expected[expectedIdx].def);
508                 if (!(SkTAbs(fixedActualDefault - fixedExpectedDefault) < 2)) {
509                     continue;
510                 }
511 
512                 // This seems silly, but allows MSAN to ensure that isHidden is initialized.
513                 // In GDI or before macOS 10.12, Win10, or FreeType 2.8.1 API for hidden is missing.
514                 if (actual[actualIdx].isHidden() &&
515                     actual[actualIdx].isHidden() != expected[expectedIdx].isHidden())
516                 {
517                     continue;
518                 }
519 
520                 // This actual matched an unused expected.
521                 actualFound = true;
522                 expectedUsed[expectedIdx] = true;
523                 break;
524             }
525             REPORTER_ASSERT(reporter, actualFound,
526                 "Actual axis '%c%c%c%c' with min %f max %f default %f hidden %s not expected",
527                 (char)((actual[actualIdx].tag >> 24) & 0xFF),
528                 (char)((actual[actualIdx].tag >> 16) & 0xFF),
529                 (char)((actual[actualIdx].tag >>  8) & 0xFF),
530                 (char)((actual[actualIdx].tag      ) & 0xFF),
531                 actual[actualIdx].min,
532                 actual[actualIdx].def,
533                 actual[actualIdx].max,
534                 actual[actualIdx].isHidden() ? "true" : "false");
535         }
536     };
537 
538     sk_sp<SkFontMgr> fm = ToolUtils::TestFontMgr();
539 
540     // Two axis OpenType variable font.
541     {
542         std::unique_ptr<SkStreamAsset> variable(GetResourceAsStream("fonts/Variable.ttf"));
543         if (!variable) {
544             REPORT_FAILURE(reporter, "variable", SkString());
545             return;
546         }
547         constexpr Axis expected[] = {
548             Axis(SkSetFourByteTag('w','g','h','t'), 100.0f, 400.0f, 900.0f, true ),
549             Axis(SkSetFourByteTag('w','d','t','h'),  50.0f, 100.0f, 200.0f, false),
550         };
551         sk_sp<SkTypeface> typeface = fm->makeFromStream(std::move(variable), 0);
552         test(typeface.get(), &expected[0], std::size(expected), -1);
553     }
554 
555     // Multiple axes with the same tag (and min, max, default) works.
556     {
557         std::unique_ptr<SkStreamAsset> dupTags(GetResourceAsStream("fonts/VaryAlongQuads.ttf"));
558         if (!dupTags) {
559             REPORT_FAILURE(reporter, "dupTags", SkString());
560             return;
561         }
562 
563         // The position may be over specified. If there are multiple values for a given axis,
564         // ensure the last one since that's what css-fonts-4 requires.
565         constexpr Axis expected[] = {
566             Axis(SkSetFourByteTag('w','g','h','t'), 100.0f, 400.0f, 900.0f, false),
567             Axis(SkSetFourByteTag('w','g','h','t'), 100.0f, 400.0f, 900.0f, false),
568         };
569         sk_sp<SkTypeface> typeface = fm->makeFromStream(std::move(dupTags), 0);
570         test(typeface.get(), &expected[0], std::size(expected), 1);
571     }
572 
573     // Simple single axis GX variable font.
574     {
575         std::unique_ptr<SkStreamAsset> distortable(GetResourceAsStream("fonts/Distortable.ttf"));
576         if (!distortable) {
577             REPORT_FAILURE(reporter, "distortable", SkString());
578             return;
579         }
580         constexpr Axis expected[] = {
581             Axis(SkSetFourByteTag('w','g','h','t'), 0.5f, 1.0f, 2.0f, true),
582         };
583         sk_sp<SkTypeface> typeface = fm->makeFromStream(std::move(distortable), 0);
584         test(typeface.get(), &expected[0], std::size(expected), -1);
585     }
586 }
587 
count_proc(SkTypeface * face,void * ctx)588 static bool count_proc(SkTypeface* face, void* ctx) {
589     int* count = static_cast<int*>(ctx);
590     *count = *count + 1;
591     return false;
592 }
count(skiatest::Reporter * reporter,const SkTypefaceCache & cache)593 static int count(skiatest::Reporter* reporter, const SkTypefaceCache& cache) {
594     int count = 0;
595     sk_sp<SkTypeface> none = cache.findByProcAndRef(count_proc, &count);
596     REPORTER_ASSERT(reporter, none == nullptr);
597     return count;
598 }
599 
DEF_TEST(TypefaceCache,reporter)600 DEF_TEST(TypefaceCache, reporter) {
601     sk_sp<SkTypeface> t1(TestEmptyTypeface::Make());
602     {
603         SkTypefaceCache cache;
604         REPORTER_ASSERT(reporter, count(reporter, cache) == 0);
605         {
606             sk_sp<SkTypeface> t0(TestEmptyTypeface::Make());
607             cache.add(t0);
608             REPORTER_ASSERT(reporter, count(reporter, cache) == 1);
609             cache.add(t1);
610             REPORTER_ASSERT(reporter, count(reporter, cache) == 2);
611             cache.purgeAll();
612             REPORTER_ASSERT(reporter, count(reporter, cache) == 2);
613         }
614         REPORTER_ASSERT(reporter, count(reporter, cache) == 2);
615         cache.purgeAll();
616         REPORTER_ASSERT(reporter, count(reporter, cache) == 1);
617     }
618     REPORTER_ASSERT(reporter, t1->unique());
619 }
620 
check_serialize_behaviors(sk_sp<SkTypeface> tf,skiatest::Reporter * reporter)621 static void check_serialize_behaviors(sk_sp<SkTypeface> tf, skiatest::Reporter* reporter) {
622     if (!tf) {
623         return;
624     }
625 
626     SkFontDescriptor desc;
627     bool serialize;
628     tf->getFontDescriptor(&desc, &serialize);
629 
630     auto data0 = tf->serialize(SkTypeface::SerializeBehavior::kDoIncludeData);
631     auto data1 = tf->serialize(SkTypeface::SerializeBehavior::kDontIncludeData);
632     auto data2 = tf->serialize(SkTypeface::SerializeBehavior::kIncludeDataIfLocal);
633 
634     REPORTER_ASSERT(reporter, data0->size() >= data1->size());
635 
636     if (serialize) {
637         REPORTER_ASSERT(reporter, data0->equals(data2.get()));
638     } else {
639         REPORTER_ASSERT(reporter, data1->equals(data2.get()));
640     }
641 }
642 
DEF_TEST(Typeface_serialize,reporter)643 DEF_TEST(Typeface_serialize, reporter) {
644     check_serialize_behaviors(ToolUtils::DefaultTypeface(), reporter);
645     check_serialize_behaviors(
646             ToolUtils::TestFontMgr()->makeFromStream(GetResourceAsStream("fonts/Distortable.ttf")),
647             reporter);
648 }
649 
DEF_TEST(Typeface_glyph_to_char,reporter)650 DEF_TEST(Typeface_glyph_to_char, reporter) {
651     ToolUtils::EmojiTestSample emojiSample = ToolUtils::EmojiSample();
652     SkFont font(emojiSample.typeface, 12);
653     SkASSERT(font.getTypeface());
654     char const * text = emojiSample.sampleText;
655     size_t const textLen = strlen(text);
656     SkString familyName;
657     font.getTypeface()->getFamilyName(&familyName);
658 
659     size_t const codepointCount = SkUTF::CountUTF8(text, textLen);
660     char const * const textEnd = text + textLen;
661     std::unique_ptr<SkUnichar[]> originalCodepoints(new SkUnichar[codepointCount]);
662     for (size_t i = 0; i < codepointCount; ++i) {
663         originalCodepoints[i] = SkUTF::NextUTF8(&text, textEnd);
664     }
665     std::unique_ptr<SkGlyphID[]> glyphs(new SkGlyphID[codepointCount]);
666     font.unicharsToGlyphs(originalCodepoints.get(), codepointCount, glyphs.get());
667     if (std::any_of(glyphs.get(), glyphs.get()+codepointCount, [](SkGlyphID g){ return g == 0;})) {
668         ERRORF(reporter, "Unexpected typeface \"%s\". Expected full support for emoji_sample_text.",
669                familyName.c_str());
670         return;
671     }
672 
673     std::unique_ptr<SkUnichar[]> newCodepoints(new SkUnichar[codepointCount]);
674     SkFontPriv::GlyphsToUnichars(font, glyphs.get(), codepointCount, newCodepoints.get());
675 
676     for (size_t i = 0; i < codepointCount; ++i) {
677         // GDI does not support character to glyph mapping outside BMP.
678         if (ToolUtils::FontMgrIsGDI() && 0xFFFF < originalCodepoints[i] && newCodepoints[i] == 0) {
679             continue;
680         }
681         // If two codepoints map to the same glyph then this assert is not valid.
682         // However, the emoji test font should never have multiple characters map to the same glyph.
683         REPORTER_ASSERT(reporter, originalCodepoints[i] == newCodepoints[i],
684                         "name:%s i:%zu original:%d new:%d glyph:%d", familyName.c_str(), i,
685                         originalCodepoints[i], newCodepoints[i], glyphs[i]);
686     }
687 }
688 
689 // This test makes sure the legacy typeface creation does not lose its specified
690 // style. See https://bugs.chromium.org/p/skia/issues/detail?id=8447 for more
691 // context.
DEF_TEST(LegacyMakeTypeface,reporter)692 DEF_TEST(LegacyMakeTypeface, reporter) {
693     sk_sp<SkFontMgr> fm = ToolUtils::TestFontMgr();
694     sk_sp<SkTypeface> typeface1 = fm->legacyMakeTypeface(nullptr, SkFontStyle::Italic());
695     sk_sp<SkTypeface> typeface2 = fm->legacyMakeTypeface(nullptr, SkFontStyle::Bold());
696     sk_sp<SkTypeface> typeface3 = fm->legacyMakeTypeface(nullptr, SkFontStyle::BoldItalic());
697 
698     if (typeface1 || typeface2 || typeface3) {
699         REPORTER_ASSERT(reporter, typeface1 && typeface2 && typeface1);
700     }
701 
702     if (typeface1) {
703         REPORTER_ASSERT(reporter, typeface1->isItalic());
704         REPORTER_ASSERT(reporter, !typeface1->isBold());
705     }
706     if (typeface2) {
707         REPORTER_ASSERT(reporter, !typeface2->isItalic());
708         REPORTER_ASSERT(reporter, typeface2->isBold());
709     }
710     if (typeface3) {
711         REPORTER_ASSERT(reporter, typeface3->isItalic());
712         REPORTER_ASSERT(reporter, typeface3->isBold());
713     }
714 }
715 
DEF_TEST(CustomTypeface_invalid_glyphid,reporter)716 DEF_TEST(CustomTypeface_invalid_glyphid, reporter) {
717     SkPath glyph_path;
718     glyph_path.addRect({10, 20, 30, 40});
719 
720     SkCustomTypefaceBuilder builder;
721     builder.setGlyph(0, 42, glyph_path);
722 
723     SkFont custom_font(builder.detach(), 1);
724 
725     SkGlyphID glyph_ids[] = {0, 1};
726     SkRect bounds[2];
727     custom_font.getBounds(glyph_ids, 2, bounds, nullptr);
728 
729     REPORTER_ASSERT(reporter, bounds[0] == SkRect::MakeLTRB(10, 20, 30, 40));
730     REPORTER_ASSERT(reporter, bounds[1] == SkRect::MakeLTRB(0, 0, 0, 0));
731 }
732