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