Flutter Engine
The Flutter Engine
Loading...
Searching...
No Matches
TypefaceTest.cpp
Go to the documentation of this file.
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
14#include "include/core/SkRect.h"
23#include "src/base/SkEndian.h"
24#include "src/base/SkUTF.h"
26#include "src/core/SkFontPriv.h"
31#include "tests/Test.h"
32#include "tools/Resources.h"
33#include "tools/ToolUtils.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
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
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).
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'.
100 newStyle.width() == width || newStyle.width() == SkFontStyle::Width::kNormal_Width,
101 "newStyle.width(): %d width: %" PRIu16, newStyle.width(), width);
102}
103DEF_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
120DEF_TEST(TypefaceStyleVariable, reporter) {
121 using Variation = SkFontArguments::VariationPosition;
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();
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 const Variation::Coordinate nonDefaultPosition[] = {
155 { SkSetFourByteTag('w','g','h','t'), 200.0f },
156 { SkSetFourByteTag('w','d','t','h'), 75.0f },
157 };
158 const SkFontStyle expectedStyle(200, 3, SkFontStyle::kUpright_Slant);
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#if defined(SK_BUILD_FOR_MAC) || defined(SK_BUILD_FOR_IOS)
164 SkFontStyle mac1015style(200, 9, SkFontStyle::kUpright_Slant);
165#else
166 SkFontStyle mac1015style = expectedStyle;
167#endif
169 args.setVariationDesignPosition(Variation{nonDefaultPosition, std::size(nonDefaultPosition)});
170
171 sk_sp<SkTypeface> nonDefaultTypeface = fm->makeFromStream(stream->duplicate(), args);
172 SkFontStyle ndfs = nonDefaultTypeface->fontStyle();
173 REPORTER_ASSERT(reporter, ndfs == expectedStyle || ndfs == mac1015style,
174 "ndfs: %d %d %d", ndfs.weight(), ndfs.width(), ndfs.slant());
175
176 sk_sp<SkTypeface> cloneTypeface = typeface->makeClone(args);
177 SkFontStyle cfs = cloneTypeface->fontStyle();
178 REPORTER_ASSERT(reporter, cfs == expectedStyle || cfs == mac1015style,
179 "cfs: %d %d %d", cfs.weight(), cfs.width(), cfs.slant());
180}
181
182DEF_TEST(TypefacePostScriptName, reporter) {
184 if (!typeface) {
185 // Not all SkFontMgr can MakeFromStream().
186 return;
187 }
188
189 SkString postScriptName;
190 bool hasName = typeface->getPostScriptName(&postScriptName);
191 bool hasName2 = typeface->getPostScriptName(nullptr);
192 REPORTER_ASSERT(reporter, hasName == hasName2);
193 if (hasName) {
194 REPORTER_ASSERT(reporter, postScriptName == SkString("Em"));
195 }
196}
197
198DEF_TEST(TypefaceRoundTrip, reporter) {
200 if (!typeface) {
201 // Not all SkFontMgr can MakeFromStream().
202 return;
203 }
204
205 int fontIndex;
206 std::unique_ptr<SkStreamAsset> stream = typeface->openStream(&fontIndex);
207
208 sk_sp<SkTypeface> typeface2 =
209 ToolUtils::TestFontMgr()->makeFromStream(std::move(stream), fontIndex);
210 REPORTER_ASSERT(reporter, typeface2);
211}
212
213DEF_TEST(FontDescriptorNegativeVariationSerialize, reporter) {
214 SkFontDescriptor desc;
216 desc.setStyle(style);
217 const char postscriptName[] = "postscript";
218 desc.setPostscriptName(postscriptName);
219 SkFontArguments::VariationPosition::Coordinate* variation = desc.setVariationCoordinates(1);
220 variation[0] = { 0, -1.0f };
221
223 desc.serialize(&stream);
224 SkFontDescriptor descD;
225 SkFontDescriptor::Deserialize(stream.detachAsStream().get(), &descD);
226
227 REPORTER_ASSERT(reporter, descD.getStyle() == style);
228 REPORTER_ASSERT(reporter, 0 == strcmp(desc.getPostscriptName(), postscriptName));
229 if (descD.getVariationCoordinateCount() != 1) {
230 REPORT_FAILURE(reporter, "descD.getVariationCoordinateCount() != 1", SkString());
231 return;
232 }
233
234 REPORTER_ASSERT(reporter, descD.getVariation()[0].value == -1.0f);
235}
236
237DEF_TEST(TypefaceAxes, reporter) {
238 using Variation = SkFontArguments::VariationPosition;
239 // In DWrite in at least up to 1901 18363.1198 IDWriteFontFace5::GetFontAxisValues and
240 // GetFontAxisValueCount along with IDWriteFontResource::GetFontAxisAttributes and
241 // GetFontAxisCount (and related) seem to incorrectly collapse multiple axes with the same tag.
242 // Since this is a limitation of the underlying implementation, for now allow the test to pass
243 // with the axis tag count (as opposed to the axis count). Eventually all implementations should
244 // pass this test without 'alsoAcceptedAxisTagCount'.
245 auto test = [&](SkTypeface* typeface, const Variation& expected, int alsoAcceptedAxisTagCount) {
246 if (!typeface) {
247 return; // Not all SkFontMgr can makeFromStream().
248 }
249
250 int actualCount = typeface->getVariationDesignPosition(nullptr, 0);
251 if (actualCount == -1) {
252 return; // The number of axes is unknown.
253 }
254 REPORTER_ASSERT(reporter, actualCount == expected.coordinateCount ||
255 actualCount == alsoAcceptedAxisTagCount);
256
257 // Variable font conservative bounds don't vary, so ensure they aren't reported.
259
260 std::unique_ptr<Variation::Coordinate[]> actual(new Variation::Coordinate[actualCount]);
261 actualCount = typeface->getVariationDesignPosition(actual.get(), actualCount);
262 if (actualCount == -1) {
263 return; // The position cannot be determined.
264 }
265 REPORTER_ASSERT(reporter, actualCount == expected.coordinateCount ||
266 actualCount == alsoAcceptedAxisTagCount);
267
268 // Every actual must be expected.
269 std::unique_ptr<bool[]> expectedUsed(new bool[expected.coordinateCount]());
270 for (int actualIdx = 0; actualIdx < actualCount; ++actualIdx) {
271 bool actualFound = false;
272 for (int expectedIdx = 0; expectedIdx < expected.coordinateCount; ++expectedIdx) {
273 if (expectedUsed[expectedIdx]) {
274 continue;
275 }
276
277 if (actual[actualIdx].axis != expected.coordinates[expectedIdx].axis) {
278 continue;
279 }
280
281 // Convert to fixed for "almost equal".
282 SkFixed fixedRead = SkScalarToFixed(actual[actualIdx].value);
283 SkFixed fixedOriginal = SkScalarToFixed(expected.coordinates[expectedIdx].value);
284 if (!(SkTAbs(fixedRead - fixedOriginal) < 2)) {
285 continue;
286 }
287
288 // This actual matched an unused expected.
289 actualFound = true;
290 expectedUsed[expectedIdx] = true;
291 break;
292 }
293 REPORTER_ASSERT(reporter, actualFound,
294 "Actual axis '%c%c%c%c' with value '%f' not expected",
295 (char)((actual[actualIdx].axis >> 24) & 0xFF),
296 (char)((actual[actualIdx].axis >> 16) & 0xFF),
297 (char)((actual[actualIdx].axis >> 8) & 0xFF),
298 (char)((actual[actualIdx].axis ) & 0xFF),
299 SkScalarToDouble(actual[actualIdx].value));
300 }
301 };
302
304
305 // Not specifying a position should produce the default.
306 {
307 std::unique_ptr<SkStreamAsset> variable(GetResourceAsStream("fonts/Variable.ttf"));
308 if (!variable) {
309 REPORT_FAILURE(reporter, "variable", SkString());
310 return;
311 }
312 const Variation::Coordinate defaultPosition[] = {
313 { SkSetFourByteTag('w','g','h','t'), 400.0f },
314 { SkSetFourByteTag('w','d','t','h'), 100.0f },
315 };
316 sk_sp<SkTypeface> typeface = fm->makeFromStream(std::move(variable), 0);
317 test(typeface.get(), Variation{&defaultPosition[0], 2}, -1);
318 }
319
320 // Multiple axes with the same tag (and min, max, default) works.
321 {
322 std::unique_ptr<SkStreamAsset> dupTags(GetResourceAsStream("fonts/VaryAlongQuads.ttf"));
323 if (!dupTags) {
324 REPORT_FAILURE(reporter, "dupTags", SkString());
325 return;
326 }
327
328 // The position may be over specified. If there are multiple values for a given axis,
329 // ensure the last one since that's what css-fonts-4 requires.
330 const Variation::Coordinate position[] = {
331 { SkSetFourByteTag('w','g','h','t'), 700.0f },
332 { SkSetFourByteTag('w','g','h','t'), 600.0f },
333 { SkSetFourByteTag('w','g','h','t'), 600.0f },
334 };
336 params.setVariationDesignPosition({position, std::size(position)});
337 sk_sp<SkTypeface> typeface = fm->makeFromStream(std::move(dupTags), params);
338 test(typeface.get(), Variation{&position[1], 2}, 1);
339 }
340
341 // Overspecifying an axis tag value applies the last one in the list.
342 {
343 std::unique_ptr<SkStreamAsset> distortable(GetResourceAsStream("fonts/Distortable.ttf"));
344 if (!distortable) {
345 REPORT_FAILURE(reporter, "distortable", SkString());
346 return;
347 }
348
349 // The position may be over specified. If there are multiple values for a given axis,
350 // ensure the last one since that's what css-fonts-4 requires.
351 const Variation::Coordinate position[] = {
352 { SkSetFourByteTag('w','g','h','t'), 1.618033988749895f },
353 { SkSetFourByteTag('w','g','h','t'), SK_ScalarSqrt2 },
354 };
356 params.setVariationDesignPosition({position, std::size(position)});
357 sk_sp<SkTypeface> typeface = fm->makeFromStream(std::move(distortable), params);
358 test(typeface.get(), Variation{&position[1], 1}, -1);
359
360 if (typeface) {
361 // Cloning without specifying any parameters should produce an equivalent variation.
362 sk_sp<SkTypeface> clone = typeface->makeClone(SkFontArguments());
363 test(clone.get(), Variation{&position[1], 1}, -1);
364 }
365 }
366}
367
368DEF_TEST(TypefaceVariationIndex, reporter) {
369 std::unique_ptr<SkStreamAsset> distortable(GetResourceAsStream("fonts/Distortable.ttf"));
370 if (!distortable) {
371 REPORT_FAILURE(reporter, "distortable", SkString());
372 return;
373 }
374
377 // The first named variation position in Distortable is 'Thin'.
378 params.setCollectionIndex(0x00010000);
379 sk_sp<SkTypeface> typeface = fm->makeFromStream(std::move(distortable), params);
380 if (!typeface) {
381 // FreeType is the only weird thing that supports this, Skia just needs to make sure if it
382 // gets one of these things make sense.
383 return;
384 }
385
386 int count = typeface->getVariationDesignPosition(nullptr, 0);
387 if (!(count == 1)) {
388 REPORT_FAILURE(reporter, "count == 1", SkString());
389 return;
390 }
391
393 count = typeface->getVariationDesignPosition(positionRead, std::size(positionRead));
394 if (count == -1) {
395 return;
396 }
397 if (!(count == 1)) {
398 REPORT_FAILURE(reporter, "count == 1", SkString());
399 return;
400 }
401 REPORTER_ASSERT(reporter, positionRead[0].axis == SkSetFourByteTag('w','g','h','t'));
402 REPORTER_ASSERT(reporter, positionRead[0].value == 0.5,
403 "positionRead[0].value: %f", positionRead[0].value);
404}
405
419
420DEF_TEST(TypefaceAxesParameters, reporter) {
422
423 // In DWrite in at least up to 1901 18363.1198 IDWriteFontFace5::GetFontAxisValues and
424 // GetFontAxisValueCount along with IDWriteFontResource::GetFontAxisAttributes and
425 // GetFontAxisCount (and related) seem to incorrectly collapse multiple axes with the same tag.
426 // Since this is a limitation of the underlying implementation, for now allow the test to pass
427 // with the axis tag count (as opposed to the axis count). Eventually all implementations should
428 // pass this test without 'alsoAcceptedAxisTagCount'.
429 auto test = [&](SkTypeface* typeface, const Axis* expected, int expectedCount,
430 int alsoAcceptedAxisTagCount)
431 {
432 if (!typeface) {
433 return; // Not all SkFontMgr can makeFromStream().
434 }
435
436 int actualCount = typeface->getVariationDesignParameters(nullptr, 0);
437 if (actualCount == -1) {
438 return; // The number of axes is unknown.
439 }
440 REPORTER_ASSERT(reporter, actualCount == expectedCount ||
441 actualCount == alsoAcceptedAxisTagCount);
442
443 std::unique_ptr<Axis[]> actual(new Axis[actualCount]);
444 actualCount = typeface->getVariationDesignParameters(actual.get(), actualCount);
445 if (actualCount == -1) {
446 return; // The position cannot be determined.
447 }
448 REPORTER_ASSERT(reporter, actualCount == expectedCount ||
449 actualCount == alsoAcceptedAxisTagCount);
450
451 // Every actual must be expected.
452 std::unique_ptr<bool[]> expectedUsed(new bool[expectedCount]());
453 for (int actualIdx = 0; actualIdx < actualCount; ++actualIdx) {
454 bool actualFound = false;
455 for (int expectedIdx = 0; expectedIdx < expectedCount; ++expectedIdx) {
456 if (expectedUsed[expectedIdx]) {
457 continue;
458 }
459
460 if (actual[actualIdx].tag != expected[expectedIdx].tag) {
461 continue;
462 }
463
464 // Convert to fixed for "almost equal".
465 SkFixed fixedActualMin = SkScalarToFixed(actual[actualIdx].min);
466 SkFixed fixedExpectedMin = SkScalarToFixed(expected[expectedIdx].min);
467 if (!(SkTAbs(fixedActualMin - fixedExpectedMin) < 2)) {
468 continue;
469 }
470
471 SkFixed fixedActualMax = SkScalarToFixed(actual[actualIdx].max);
472 SkFixed fixedExpectedMax = SkScalarToFixed(expected[expectedIdx].max);
473 if (!(SkTAbs(fixedActualMax - fixedExpectedMax) < 2)) {
474 continue;
475 }
476
477 SkFixed fixedActualDefault = SkScalarToFixed(actual[actualIdx].def);
478 SkFixed fixedExpectedDefault = SkScalarToFixed(expected[expectedIdx].def);
479 if (!(SkTAbs(fixedActualDefault - fixedExpectedDefault) < 2)) {
480 continue;
481 }
482
483 // This seems silly, but allows MSAN to ensure that isHidden is initialized.
484 // In GDI or before macOS 10.12, Win10, or FreeType 2.8.1 API for hidden is missing.
485 if (actual[actualIdx].isHidden() &&
486 actual[actualIdx].isHidden() != expected[expectedIdx].isHidden())
487 {
488 continue;
489 }
490
491 // This actual matched an unused expected.
492 actualFound = true;
493 expectedUsed[expectedIdx] = true;
494 break;
495 }
496 REPORTER_ASSERT(reporter, actualFound,
497 "Actual axis '%c%c%c%c' with min %f max %f default %f hidden %s not expected",
498 (char)((actual[actualIdx].tag >> 24) & 0xFF),
499 (char)((actual[actualIdx].tag >> 16) & 0xFF),
500 (char)((actual[actualIdx].tag >> 8) & 0xFF),
501 (char)((actual[actualIdx].tag ) & 0xFF),
502 actual[actualIdx].min,
503 actual[actualIdx].def,
504 actual[actualIdx].max,
505 actual[actualIdx].isHidden() ? "true" : "false");
506 }
507 };
508
510
511 // Two axis OpenType variable font.
512 {
513 std::unique_ptr<SkStreamAsset> variable(GetResourceAsStream("fonts/Variable.ttf"));
514 if (!variable) {
515 REPORT_FAILURE(reporter, "variable", SkString());
516 return;
517 }
518 constexpr Axis expected[] = {
519 Axis(SkSetFourByteTag('w','g','h','t'), 100.0f, 400.0f, 900.0f, true ),
520 Axis(SkSetFourByteTag('w','d','t','h'), 50.0f, 100.0f, 200.0f, false),
521 };
522 sk_sp<SkTypeface> typeface = fm->makeFromStream(std::move(variable), 0);
523 test(typeface.get(), &expected[0], std::size(expected), -1);
524 }
525
526 // Multiple axes with the same tag (and min, max, default) works.
527 {
528 std::unique_ptr<SkStreamAsset> dupTags(GetResourceAsStream("fonts/VaryAlongQuads.ttf"));
529 if (!dupTags) {
530 REPORT_FAILURE(reporter, "dupTags", SkString());
531 return;
532 }
533
534 // The position may be over specified. If there are multiple values for a given axis,
535 // ensure the last one since that's what css-fonts-4 requires.
536 constexpr Axis expected[] = {
537 Axis(SkSetFourByteTag('w','g','h','t'), 100.0f, 400.0f, 900.0f, false),
538 Axis(SkSetFourByteTag('w','g','h','t'), 100.0f, 400.0f, 900.0f, false),
539 };
540 sk_sp<SkTypeface> typeface = fm->makeFromStream(std::move(dupTags), 0);
541 test(typeface.get(), &expected[0], std::size(expected), 1);
542 }
543
544 // Simple single axis GX variable font.
545 {
546 std::unique_ptr<SkStreamAsset> distortable(GetResourceAsStream("fonts/Distortable.ttf"));
547 if (!distortable) {
548 REPORT_FAILURE(reporter, "distortable", SkString());
549 return;
550 }
551 constexpr Axis expected[] = {
552 Axis(SkSetFourByteTag('w','g','h','t'), 0.5f, 1.0f, 2.0f, true),
553 };
554 sk_sp<SkTypeface> typeface = fm->makeFromStream(std::move(distortable), 0);
555 test(typeface.get(), &expected[0], std::size(expected), -1);
556 }
557}
558
559static bool count_proc(SkTypeface* face, void* ctx) {
560 int* count = static_cast<int*>(ctx);
561 *count = *count + 1;
562 return false;
563}
565 int count = 0;
566 sk_sp<SkTypeface> none = cache.findByProcAndRef(count_proc, &count);
567 REPORTER_ASSERT(reporter, none == nullptr);
568 return count;
569}
570
571DEF_TEST(TypefaceCache, reporter) {
573 {
574 SkTypefaceCache cache;
575 REPORTER_ASSERT(reporter, count(reporter, cache) == 0);
576 {
578 cache.add(t0);
579 REPORTER_ASSERT(reporter, count(reporter, cache) == 1);
580 cache.add(t1);
581 REPORTER_ASSERT(reporter, count(reporter, cache) == 2);
582 cache.purgeAll();
583 REPORTER_ASSERT(reporter, count(reporter, cache) == 2);
584 }
585 REPORTER_ASSERT(reporter, count(reporter, cache) == 2);
586 cache.purgeAll();
587 REPORTER_ASSERT(reporter, count(reporter, cache) == 1);
588 }
589 REPORTER_ASSERT(reporter, t1->unique());
590}
591
593 if (!tf) {
594 return;
595 }
596
597 SkFontDescriptor desc;
598 bool serialize;
599 tf->getFontDescriptor(&desc, &serialize);
600
601 auto data0 = tf->serialize(SkTypeface::SerializeBehavior::kDoIncludeData);
602 auto data1 = tf->serialize(SkTypeface::SerializeBehavior::kDontIncludeData);
603 auto data2 = tf->serialize(SkTypeface::SerializeBehavior::kIncludeDataIfLocal);
604
605 REPORTER_ASSERT(reporter, data0->size() >= data1->size());
606
607 if (serialize) {
608 REPORTER_ASSERT(reporter, data0->equals(data2.get()));
609 } else {
610 REPORTER_ASSERT(reporter, data1->equals(data2.get()));
611 }
612}
613
614DEF_TEST(Typeface_serialize, reporter) {
617 ToolUtils::TestFontMgr()->makeFromStream(GetResourceAsStream("fonts/Distortable.ttf")),
618 reporter);
619}
620
621DEF_TEST(Typeface_glyph_to_char, reporter) {
623 SkFont font(emojiSample.typeface, 12);
624 SkASSERT(font.getTypeface());
625 char const * text = emojiSample.sampleText;
626 size_t const textLen = strlen(text);
627 SkString familyName;
628 font.getTypeface()->getFamilyName(&familyName);
629
630 size_t const codepointCount = SkUTF::CountUTF8(text, textLen);
631 char const * const textEnd = text + textLen;
632 std::unique_ptr<SkUnichar[]> originalCodepoints(new SkUnichar[codepointCount]);
633 for (size_t i = 0; i < codepointCount; ++i) {
634 originalCodepoints[i] = SkUTF::NextUTF8(&text, textEnd);
635 }
636 std::unique_ptr<SkGlyphID[]> glyphs(new SkGlyphID[codepointCount]);
637 font.unicharsToGlyphs(originalCodepoints.get(), codepointCount, glyphs.get());
638 if (std::any_of(glyphs.get(), glyphs.get()+codepointCount, [](SkGlyphID g){ return g == 0;})) {
639 ERRORF(reporter, "Unexpected typeface \"%s\". Expected full support for emoji_sample_text.",
640 familyName.c_str());
641 return;
642 }
643
644 std::unique_ptr<SkUnichar[]> newCodepoints(new SkUnichar[codepointCount]);
645 SkFontPriv::GlyphsToUnichars(font, glyphs.get(), codepointCount, newCodepoints.get());
646
647 for (size_t i = 0; i < codepointCount; ++i) {
648 // GDI does not support character to glyph mapping outside BMP.
649 if (ToolUtils::FontMgrIsGDI() && 0xFFFF < originalCodepoints[i] && newCodepoints[i] == 0) {
650 continue;
651 }
652 // If two codepoints map to the same glyph then this assert is not valid.
653 // However, the emoji test font should never have multiple characters map to the same glyph.
654 REPORTER_ASSERT(reporter, originalCodepoints[i] == newCodepoints[i],
655 "name:%s i:%zu original:%d new:%d glyph:%d", familyName.c_str(), i,
656 originalCodepoints[i], newCodepoints[i], glyphs[i]);
657 }
658}
659
660// This test makes sure the legacy typeface creation does not lose its specified
661// style. See https://bugs.chromium.org/p/skia/issues/detail?id=8447 for more
662// context.
663DEF_TEST(LegacyMakeTypeface, reporter) {
665 sk_sp<SkTypeface> typeface1 = fm->legacyMakeTypeface(nullptr, SkFontStyle::Italic());
666 sk_sp<SkTypeface> typeface2 = fm->legacyMakeTypeface(nullptr, SkFontStyle::Bold());
667 sk_sp<SkTypeface> typeface3 = fm->legacyMakeTypeface(nullptr, SkFontStyle::BoldItalic());
668
669 if (typeface1 || typeface2 || typeface3) {
670 REPORTER_ASSERT(reporter, typeface1 && typeface2 && typeface1);
671 }
672
673 if (typeface1) {
674 REPORTER_ASSERT(reporter, typeface1->isItalic());
675 REPORTER_ASSERT(reporter, !typeface1->isBold());
676 }
677 if (typeface2) {
678 REPORTER_ASSERT(reporter, !typeface2->isItalic());
679 REPORTER_ASSERT(reporter, typeface2->isBold());
680 }
681 if (typeface3) {
682 REPORTER_ASSERT(reporter, typeface3->isItalic());
683 REPORTER_ASSERT(reporter, typeface3->isBold());
684 }
685}
#define test(name)
reporter
uint16_t glyphs[5]
int count
std::unique_ptr< SkStreamAsset > GetResourceAsStream(const char *resource, bool useFileStream)
Definition Resources.cpp:31
#define SkASSERT_RELEASE(cond)
Definition SkAssert.h:100
#define SkASSERT(cond)
Definition SkAssert.h:116
#define SkEndian_SwapBE32(n)
Definition SkEndian.h:136
#define SkEndian_SwapBE16(n)
Definition SkEndian.h:135
int32_t SkFixed
Definition SkFixed.h:25
#define SkScalarToFixed(x)
Definition SkFixed.h:125
sk_sp< T > sk_ref_sp(T *obj)
Definition SkRefCnt.h:381
#define SkScalarToDouble(x)
Definition SkScalar.h:63
#define SK_ScalarSqrt2
Definition SkScalar.h:20
static T SkTAbs(T value)
Definition SkTemplates.h:43
int32_t SkUnichar
Definition SkTypes.h:175
uint16_t SkGlyphID
Definition SkTypes.h:179
static constexpr SkFourByteTag SkSetFourByteTag(char a, char b, char c, char d)
Definition SkTypes.h:167
#define DEF_TEST(name, reporter)
Definition Test.h:312
#define REPORTER_ASSERT(r, cond,...)
Definition Test.h:286
#define ERRORF(r,...)
Definition Test.h:293
#define REPORT_FAILURE(reporter, cond, message)
Definition Test.h:90
static void check_serialize_behaviors(sk_sp< SkTypeface > tf, skiatest::Reporter *reporter)
static bool count_proc(SkTypeface *face, void *ctx)
static void TypefaceStyle_test(skiatest::Reporter *reporter, uint16_t weight, uint16_t width, SkData *data)
static sk_sp< SkData > MakeWithCopy(const void *data, size_t length)
Definition SkData.cpp:111
static sk_sp< SkData > MakeFromStream(SkStream *, size_t size)
Definition SkData.cpp:208
SkFontStyle getStyle() const
const SkFontArguments::VariationPosition::Coordinate * getVariation() const
static bool Deserialize(SkStream *, SkFontDescriptor *result)
int getVariationCoordinateCount() const
sk_sp< SkTypeface > makeFromStream(std::unique_ptr< SkStreamAsset >, int ttcIndex=0) const
static void GlyphsToUnichars(const SkFont &, const uint16_t glyphs[], int count, SkUnichar[])
Definition SkFont.cpp:396
Slant slant() const
Definition SkFontStyle.h:64
static constexpr SkFontStyle Italic()
Definition SkFontStyle.h:72
int width() const
Definition SkFontStyle.h:63
int weight() const
Definition SkFontStyle.h:62
static constexpr SkFontStyle BoldItalic()
Definition SkFontStyle.h:75
static constexpr SkFontStyle Bold()
Definition SkFontStyle.h:69
static constexpr SkFontStyle Normal()
Definition SkFontStyle.h:66
const char * c_str() const
Definition SkString.h:133
SkRect getBounds() const
int getVariationDesignParameters(SkFontParameters::Variation::Axis parameters[], int parameterCount) const
int getVariationDesignPosition(SkFontArguments::VariationPosition::Coordinate coordinates[], int coordinateCount) const
static bool Equal(const SkTypeface *facea, const SkTypeface *faceb)
static sk_sp< SkTypeface > Make()
T * get() const
Definition SkRefCnt.h:303
const EmbeddedViewParams * params
G_BEGIN_DECLS G_MODULE_EXPORT FlValue * args
uint8_t value
static float max(float r, float g, float b)
Definition hsl.cpp:49
static float min(float r, float g, float b)
Definition hsl.cpp:48
std::u16string text
SK_SPI SkUnichar NextUTF8(const char **ptr, const char *end)
Definition SkUTF.cpp:118
SK_SPI int CountUTF8(const char *utf8, size_t byteLength)
Definition SkUTF.cpp:47
bool FontMgrIsGDI()
sk_sp< SkTypeface > CreateTypefaceFromResource(const char *resource, int ttcIndex)
EmojiTestSample EmojiSample()
sk_sp< SkTypeface > DefaultTypeface()
sk_sp< SkTypeface > CreateTestTypeface(const char *name, SkFontStyle style)
sk_sp< SkFontMgr > TestFontMgr()
int32_t width
SkFontArguments & setCollectionIndex(int collectionIndex)
SkFontArguments & setVariationDesignPosition(VariationPosition position)
enum SkOTTableOS2_V0::WidthClass::Value value
struct SkOTTableOS2_V0::WidthClass usWidthClass
struct SkOTTableOS2_V0::WeightClass usWeightClass
static constexpr SK_OT_ULONG TAG
bool isEmpty() const
Definition SkRect.h:693
SK_SFNT_USHORT numTables
sk_sp< SkTypeface > typeface