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(TypefaceRoundTrip,reporter)103 DEF_TEST(TypefaceRoundTrip, reporter) {
104 sk_sp<SkTypeface> typeface(MakeResourceAsTypeface("fonts/7630.otf"));
105 if (!typeface) {
106 // Not all SkFontMgr can MakeFromStream().
107 return;
108 }
109
110 int fontIndex;
111 std::unique_ptr<SkStreamAsset> stream = typeface->openStream(&fontIndex);
112
113 sk_sp<SkFontMgr> fm = SkFontMgr::RefDefault();
114 sk_sp<SkTypeface> typeface2 = fm->makeFromStream(std::move(stream), fontIndex);
115 REPORTER_ASSERT(reporter, typeface2);
116 }
117
DEF_TEST(FontDescriptorNegativeVariationSerialize,reporter)118 DEF_TEST(FontDescriptorNegativeVariationSerialize, reporter) {
119 SkFontDescriptor desc;
120 SkFixed axis = -SK_Fixed1;
121 auto font = std::make_unique<SkMemoryStream>("a", 1, false);
122 desc.setFontData(std::make_unique<SkFontData>(std::move(font), 0, &axis, 1));
123
124 SkDynamicMemoryWStream stream;
125 desc.serialize(&stream);
126 SkFontDescriptor descD;
127 SkFontDescriptor::Deserialize(stream.detachAsStream().get(), &descD);
128 std::unique_ptr<SkFontData> fontData = descD.detachFontData();
129 if (!fontData) {
130 REPORT_FAILURE(reporter, "fontData", SkString());
131 return;
132 }
133
134 if (fontData->getAxisCount() != 1) {
135 REPORT_FAILURE(reporter, "fontData->getAxisCount() != 1", SkString());
136 return;
137 }
138
139 REPORTER_ASSERT(reporter, fontData->getAxis()[0] == -SK_Fixed1);
140 };
141
DEF_TEST(TypefaceAxes,reporter)142 DEF_TEST(TypefaceAxes, reporter) {
143 std::unique_ptr<SkStreamAsset> distortable(GetResourceAsStream("fonts/Distortable.ttf"));
144 if (!distortable) {
145 REPORT_FAILURE(reporter, "distortable", SkString());
146 return;
147 }
148 constexpr int numberOfAxesInDistortable = 1;
149
150 sk_sp<SkFontMgr> fm = SkFontMgr::RefDefault();
151 // The position may be over specified. If there are multiple values for a given axis,
152 // ensure the last one since that's what css-fonts-4 requires.
153 const SkFontArguments::VariationPosition::Coordinate position[] = {
154 { SkSetFourByteTag('w','g','h','t'), 1.618033988749895f },
155 { SkSetFourByteTag('w','g','h','t'), SK_ScalarSqrt2 },
156 };
157 SkFontArguments params;
158 params.setVariationDesignPosition({position, SK_ARRAY_COUNT(position)});
159 // TODO: if axes are set and the back-end doesn't support them, should we create the typeface?
160 sk_sp<SkTypeface> typeface = fm->makeFromStream(std::move(distortable), params);
161
162 if (!typeface) {
163 return; // Not all SkFontMgr can makeFromStream().
164 }
165
166 int count = typeface->getVariationDesignPosition(nullptr, 0);
167 if (count == -1) {
168 return; // The number of axes is unknown.
169 }
170 REPORTER_ASSERT(reporter, count == numberOfAxesInDistortable);
171
172 SkFontArguments::VariationPosition::Coordinate positionRead[numberOfAxesInDistortable];
173 count = typeface->getVariationDesignPosition(positionRead, SK_ARRAY_COUNT(positionRead));
174 if (count == -1) {
175 return; // The position cannot be determined.
176 }
177 REPORTER_ASSERT(reporter, count == SK_ARRAY_COUNT(positionRead));
178
179 REPORTER_ASSERT(reporter, positionRead[0].axis == position[1].axis);
180
181 // Convert to fixed for "almost equal".
182 SkFixed fixedRead = SkScalarToFixed(positionRead[0].value);
183 SkFixed fixedOriginal = SkScalarToFixed(position[1].value);
184 REPORTER_ASSERT(reporter, SkTAbs(fixedRead - fixedOriginal) < 2 || // variation set correctly
185 SkTAbs(fixedRead - SK_Fixed1 ) < 2); // variation remained default
186 }
187
DEF_TEST(TypefaceVariationIndex,reporter)188 DEF_TEST(TypefaceVariationIndex, reporter) {
189 std::unique_ptr<SkStreamAsset> distortable(GetResourceAsStream("fonts/Distortable.ttf"));
190 if (!distortable) {
191 REPORT_FAILURE(reporter, "distortable", SkString());
192 return;
193 }
194
195 sk_sp<SkFontMgr> fm = SkFontMgr::RefDefault();
196 SkFontArguments params;
197 // The first named variation position in Distortable is 'Thin'.
198 params.setCollectionIndex(0x00010000);
199 sk_sp<SkTypeface> typeface = fm->makeFromStream(std::move(distortable), params);
200 if (!typeface) {
201 // FreeType is the only weird thing that supports this, Skia just needs to make sure if it
202 // gets one of these things make sense.
203 return;
204 }
205
206 int count = typeface->getVariationDesignPosition(nullptr, 0);
207 if (!(count == 1)) {
208 REPORT_FAILURE(reporter, "count == 1", SkString());
209 return;
210 }
211
212 SkFontArguments::VariationPosition::Coordinate positionRead[1];
213 count = typeface->getVariationDesignPosition(positionRead, SK_ARRAY_COUNT(positionRead));
214 if (count == -1) {
215 return;
216 }
217 if (!(count == 1)) {
218 REPORT_FAILURE(reporter, "count == 1", SkString());
219 return;
220 }
221 REPORTER_ASSERT(reporter, positionRead[0].axis == SkSetFourByteTag('w','g','h','t'));
222 REPORTER_ASSERT(reporter, positionRead[0].value == 0.5);
223 }
224
DEF_TEST(Typeface,reporter)225 DEF_TEST(Typeface, reporter) {
226
227 sk_sp<SkTypeface> t1(SkTypeface::MakeFromName(nullptr, SkFontStyle()));
228 sk_sp<SkTypeface> t2(SkTypeface::MakeDefault());
229
230 REPORTER_ASSERT(reporter, SkTypeface::Equal(t1.get(), t2.get()));
231 REPORTER_ASSERT(reporter, SkTypeface::Equal(nullptr, t1.get()));
232 REPORTER_ASSERT(reporter, SkTypeface::Equal(nullptr, t2.get()));
233 REPORTER_ASSERT(reporter, SkTypeface::Equal(t1.get(), nullptr));
234 REPORTER_ASSERT(reporter, SkTypeface::Equal(t2.get(), nullptr));
235 }
236
DEF_TEST(TypefaceAxesParameters,reporter)237 DEF_TEST(TypefaceAxesParameters, reporter) {
238 std::unique_ptr<SkStreamAsset> distortable(GetResourceAsStream("fonts/Distortable.ttf"));
239 if (!distortable) {
240 REPORT_FAILURE(reporter, "distortable", SkString());
241 return;
242 }
243 constexpr int numberOfAxesInDistortable = 1;
244 constexpr SkScalar minAxisInDistortable = 0.5;
245 constexpr SkScalar defAxisInDistortable = 1;
246 constexpr SkScalar maxAxisInDistortable = 2;
247 constexpr bool axisIsHiddenInDistortable = true;
248
249 sk_sp<SkFontMgr> fm = SkFontMgr::RefDefault();
250
251 SkFontArguments params;
252 sk_sp<SkTypeface> typeface = fm->makeFromStream(std::move(distortable), params);
253
254 if (!typeface) {
255 return; // Not all SkFontMgr can makeFromStream().
256 }
257
258 SkFontParameters::Variation::Axis parameter[numberOfAxesInDistortable];
259 int count = typeface->getVariationDesignParameters(parameter, SK_ARRAY_COUNT(parameter));
260 if (count == -1) {
261 return;
262 }
263
264 REPORTER_ASSERT(reporter, count == SK_ARRAY_COUNT(parameter));
265 REPORTER_ASSERT(reporter, parameter[0].min == minAxisInDistortable);
266 REPORTER_ASSERT(reporter, parameter[0].def == defAxisInDistortable);
267 REPORTER_ASSERT(reporter, parameter[0].max == maxAxisInDistortable);
268 REPORTER_ASSERT(reporter, parameter[0].tag == SkSetFourByteTag('w','g','h','t'));
269 // This seems silly, but allows MSAN to ensure that isHidden is initialized.
270 // With GDI or before macOS 10.12, Win10, or FreeType 2.8.1 the API for hidden is missing.
271 REPORTER_ASSERT(reporter, parameter[0].isHidden() == axisIsHiddenInDistortable ||
272 parameter[0].isHidden() == false);
273 }
274
count_proc(SkTypeface * face,void * ctx)275 static bool count_proc(SkTypeface* face, void* ctx) {
276 int* count = static_cast<int*>(ctx);
277 *count = *count + 1;
278 return false;
279 }
count(skiatest::Reporter * reporter,const SkTypefaceCache & cache)280 static int count(skiatest::Reporter* reporter, const SkTypefaceCache& cache) {
281 int count = 0;
282 sk_sp<SkTypeface> none = cache.findByProcAndRef(count_proc, &count);
283 REPORTER_ASSERT(reporter, none == nullptr);
284 return count;
285 }
286
DEF_TEST(TypefaceCache,reporter)287 DEF_TEST(TypefaceCache, reporter) {
288 sk_sp<SkTypeface> t1(TestEmptyTypeface::Make());
289 {
290 SkTypefaceCache cache;
291 REPORTER_ASSERT(reporter, count(reporter, cache) == 0);
292 {
293 sk_sp<SkTypeface> t0(TestEmptyTypeface::Make());
294 cache.add(t0);
295 REPORTER_ASSERT(reporter, count(reporter, cache) == 1);
296 cache.add(t1);
297 REPORTER_ASSERT(reporter, count(reporter, cache) == 2);
298 cache.purgeAll();
299 REPORTER_ASSERT(reporter, count(reporter, cache) == 2);
300 }
301 REPORTER_ASSERT(reporter, count(reporter, cache) == 2);
302 cache.purgeAll();
303 REPORTER_ASSERT(reporter, count(reporter, cache) == 1);
304 }
305 REPORTER_ASSERT(reporter, t1->unique());
306 }
307
check_serialize_behaviors(sk_sp<SkTypeface> tf,bool isLocalData,skiatest::Reporter * reporter)308 static void check_serialize_behaviors(sk_sp<SkTypeface> tf, bool isLocalData,
309 skiatest::Reporter* reporter) {
310 if (!tf) {
311 return;
312 }
313 auto data0 = tf->serialize(SkTypeface::SerializeBehavior::kDoIncludeData);
314 auto data1 = tf->serialize(SkTypeface::SerializeBehavior::kDontIncludeData);
315 auto data2 = tf->serialize(SkTypeface::SerializeBehavior::kIncludeDataIfLocal);
316
317 REPORTER_ASSERT(reporter, data0->size() >= data1->size());
318
319 if (isLocalData) {
320 REPORTER_ASSERT(reporter, data0->equals(data2.get()));
321 } else {
322 REPORTER_ASSERT(reporter, data1->equals(data2.get()));
323 }
324 }
325
DEF_TEST(Typeface_serialize,reporter)326 DEF_TEST(Typeface_serialize, reporter) {
327 check_serialize_behaviors(SkTypeface::MakeDefault(), false, reporter);
328 check_serialize_behaviors(SkTypeface::MakeFromStream(
329 GetResourceAsStream("fonts/Distortable.ttf")),
330 true, reporter);
331
332 }
333
DEF_TEST(Typeface_glyph_to_char,reporter)334 DEF_TEST(Typeface_glyph_to_char, reporter) {
335 SkFont font(ToolUtils::emoji_typeface(), 12);
336 SkASSERT(font.getTypeface());
337 char const * text = ToolUtils::emoji_sample_text();
338 size_t const textLen = strlen(text);
339 size_t const codepointCount = SkUTF::CountUTF8(text, textLen);
340 char const * const textEnd = text + textLen;
341 std::unique_ptr<SkUnichar[]> originalCodepoints(new SkUnichar[codepointCount]);
342 for (size_t i = 0; i < codepointCount; ++i) {
343 originalCodepoints[i] = SkUTF::NextUTF8(&text, textEnd);
344 }
345 std::unique_ptr<SkGlyphID[]> glyphs(new SkGlyphID[codepointCount]);
346 font.unicharsToGlyphs(originalCodepoints.get(), codepointCount, glyphs.get());
347
348 std::unique_ptr<SkUnichar[]> newCodepoints(new SkUnichar[codepointCount]);
349 SkFontPriv::GlyphsToUnichars(font, glyphs.get(), codepointCount, newCodepoints.get());
350
351 SkString familyName;
352 font.getTypeface()->getFamilyName(&familyName);
353 for (size_t i = 0; i < codepointCount; ++i) {
354 #if defined(SK_BUILD_FOR_WIN)
355 // GDI does not support character to glyph mapping outside BMP.
356 if (gSkFontMgr_DefaultFactory == &SkFontMgr_New_GDI &&
357 0xFFFF < originalCodepoints[i] && newCodepoints[i] == 0)
358 {
359 continue;
360 }
361 #endif
362 // If two codepoints map to the same glyph then this assert is not valid.
363 // However, the emoji test font should never have multiple characters map to the same glyph.
364 REPORTER_ASSERT(reporter, originalCodepoints[i] == newCodepoints[i],
365 "name:%s i:%d original:%d new:%d glyph:%d", familyName.c_str(), i,
366 originalCodepoints[i], newCodepoints[i], glyphs[i]);
367 }
368 }
369
370 // This test makes sure the legacy typeface creation does not lose its specified
371 // style. See https://bugs.chromium.org/p/skia/issues/detail?id=8447 for more
372 // context.
DEF_TEST(LegacyMakeTypeface,reporter)373 DEF_TEST(LegacyMakeTypeface, reporter) {
374 sk_sp<SkFontMgr> fm = SkFontMgr::RefDefault();
375 sk_sp<SkTypeface> typeface1 = fm->legacyMakeTypeface(nullptr, SkFontStyle::Italic());
376 sk_sp<SkTypeface> typeface2 = fm->legacyMakeTypeface(nullptr, SkFontStyle::Bold());
377 sk_sp<SkTypeface> typeface3 = fm->legacyMakeTypeface(nullptr, SkFontStyle::BoldItalic());
378
379 REPORTER_ASSERT(reporter, typeface1->isItalic());
380 REPORTER_ASSERT(reporter, !typeface1->isBold());
381 REPORTER_ASSERT(reporter, !typeface2->isItalic());
382 REPORTER_ASSERT(reporter, typeface2->isBold());
383 REPORTER_ASSERT(reporter, typeface3->isItalic());
384 REPORTER_ASSERT(reporter, typeface3->isBold());
385 }
386