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