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