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