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