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