blob: 84adbe10b4cbd1928e3273176c3236a0efb0a135 [file] [log] [blame]
djsollen@google.com1f584ed2013-09-19 12:08:40 +00001/*
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
Mike Kleinc0bd9f92019-04-23 12:05:21 -05008#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"
Ben Wagnera541db4d2019-05-22 17:33:07 -040013#include "include/ports/SkTypeface_win.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050014#include "include/private/SkFixed.h"
15#include "src/core/SkAdvancedTypefaceMetrics.h"
16#include "src/core/SkFontDescriptor.h"
Ben Wagnera541db4d2019-05-22 17:33:07 -040017#include "src/core/SkFontMgrPriv.h"
18#include "src/core/SkFontPriv.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050019#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"
Ben Wagnera541db4d2019-05-22 17:33:07 -040023#include "src/utils/SkUTF.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050024#include "tests/Test.h"
25#include "tools/Resources.h"
Ben Wagnera541db4d2019-05-22 17:33:07 -040026#include "tools/ToolUtils.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050027#include "tools/fonts/TestEmptyTypeface.h"
djsollen@google.com1f584ed2013-09-19 12:08:40 +000028
bungeman6e45bda2016-07-25 15:11:49 -070029#include <memory>
30
31static void TypefaceStyle_test(skiatest::Reporter* reporter,
32 uint16_t weight, uint16_t width, SkData* data)
33{
34 sk_sp<SkData> dataCopy;
reed42943c82016-09-12 12:01:44 -070035 if (!data->unique()) {
bungeman6e45bda2016-07-25 15:11:49 -070036 dataCopy = SkData::MakeWithCopy(data->data(), data->size());
reed42943c82016-09-12 12:01:44 -070037 data = dataCopy.get();
bungeman6e45bda2016-07-25 15:11:49 -070038 }
reed42943c82016-09-12 12:01:44 -070039 SkSFNTHeader* sfntHeader = static_cast<SkSFNTHeader*>(data->writable_data());
bungeman6e45bda2016-07-25 15:11:49 -070040
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
Mike Reed271d1d92018-09-03 21:10:10 -040059 sk_sp<SkTypeface> newTypeface(SkTypeface::MakeFromData(sk_ref_sp(data)));
Mike Kleincb6940b2017-11-09 13:45:10 -050060 if (!newTypeface) {
61 // Not all SkFontMgr can MakeFromStream().
62 return;
63 }
bungeman6e45bda2016-07-25 15:11:49 -070064
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}
87DEF_TEST(TypefaceStyle, reporter) {
Hal Canary53e5e7d2017-12-08 14:25:14 -050088 std::unique_ptr<SkStreamAsset> stream(GetResourceAsStream("fonts/Em.ttf"));
bungeman6e45bda2016-07-25 15:11:49 -070089 if (!stream) {
Hal Canary53e5e7d2017-12-08 14:25:14 -050090 REPORT_FAILURE(reporter, "fonts/Em.ttf", SkString("Cannot load resource"));
bungeman6e45bda2016-07-25 15:11:49 -070091 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 }
bungemand783e082016-08-01 12:37:13 -070099 for (int width = SkFS::kUltraCondensed_Width; width <= SkFS::kUltraExpanded_Width; ++width) {
bungeman6e45bda2016-07-25 15:11:49 -0700100 TypefaceStyle_test(reporter, 400, width, data.get());
101 }
102}
103
Ben Wagner03cd6e62018-03-08 16:02:55 -0500104DEF_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;
Ben Wagnerff84d8a2019-02-26 15:39:41 -0500112 std::unique_ptr<SkStreamAsset> stream = typeface->openStream(&fontIndex);
Ben Wagner03cd6e62018-03-08 16:02:55 -0500113
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
Ben Wagner255ab8d2016-10-07 15:50:53 -0400119DEF_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
Ben Wagnerfc497342017-02-24 11:15:26 -0500143DEF_TEST(TypefaceAxes, reporter) {
Hal Canary53e5e7d2017-12-08 14:25:14 -0500144 std::unique_ptr<SkStreamAsset> distortable(GetResourceAsStream("fonts/Distortable.ttf"));
Ben Wagnerfc497342017-02-24 11:15:26 -0500145 if (!distortable) {
146 REPORT_FAILURE(reporter, "distortable", SkString());
147 return;
148 }
bungeman9aec8942017-03-29 13:38:53 -0400149 constexpr int numberOfAxesInDistortable = 1;
Ben Wagnerfc497342017-02-24 11:15:26 -0500150
151 sk_sp<SkFontMgr> fm = SkFontMgr::RefDefault();
bungeman9aec8942017-03-29 13:38:53 -0400152 // 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.
Ben Wagnerfc497342017-02-24 11:15:26 -0500154 const SkFontArguments::VariationPosition::Coordinate position[] = {
bungeman9aec8942017-03-29 13:38:53 -0400155 { SkSetFourByteTag('w','g','h','t'), 1.618033988749895f },
156 { SkSetFourByteTag('w','g','h','t'), SK_ScalarSqrt2 },
Ben Wagnerfc497342017-02-24 11:15:26 -0500157 };
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?
Mike Reed59227392017-09-26 09:46:08 -0400161 sk_sp<SkTypeface> typeface = fm->makeFromStream(std::move(distortable), params);
Ben Wagnerfc497342017-02-24 11:15:26 -0500162
Mike Kleincb6940b2017-11-09 13:45:10 -0500163 if (!typeface) {
164 // Not all SkFontMgr can makeFromStream().
165 return;
166 }
167
Ben Wagnerfc497342017-02-24 11:15:26 -0500168 int count = typeface->getVariationDesignPosition(nullptr, 0);
169 if (count == -1) {
170 return;
171 }
bungeman9aec8942017-03-29 13:38:53 -0400172 REPORTER_ASSERT(reporter, count == numberOfAxesInDistortable);
Ben Wagnerfc497342017-02-24 11:15:26 -0500173
bungeman9aec8942017-03-29 13:38:53 -0400174 SkFontArguments::VariationPosition::Coordinate positionRead[numberOfAxesInDistortable];
Ben Wagnerfc497342017-02-24 11:15:26 -0500175 count = typeface->getVariationDesignPosition(positionRead, SK_ARRAY_COUNT(positionRead));
bungeman9aec8942017-03-29 13:38:53 -0400176 REPORTER_ASSERT(reporter, count == SK_ARRAY_COUNT(positionRead));
Ben Wagnerfc497342017-02-24 11:15:26 -0500177
bungeman9aec8942017-03-29 13:38:53 -0400178 REPORTER_ASSERT(reporter, positionRead[0].axis == position[1].axis);
Ben Wagnerfc497342017-02-24 11:15:26 -0500179
180 // Convert to fixed for "almost equal".
181 SkFixed fixedRead = SkScalarToFixed(positionRead[0].value);
bungeman9aec8942017-03-29 13:38:53 -0400182 SkFixed fixedOriginal = SkScalarToFixed(position[1].value);
Bruce Wang75e64902018-06-12 14:44:22 -0400183 REPORTER_ASSERT(reporter, SkTAbs(fixedRead - fixedOriginal) < 2);
Ben Wagnerfc497342017-02-24 11:15:26 -0500184}
185
186DEF_TEST(TypefaceVariationIndex, reporter) {
Hal Canary53e5e7d2017-12-08 14:25:14 -0500187 std::unique_ptr<SkStreamAsset> distortable(GetResourceAsStream("fonts/Distortable.ttf"));
Ben Wagnerfc497342017-02-24 11:15:26 -0500188 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);
Mike Reed59227392017-09-26 09:46:08 -0400197 sk_sp<SkTypeface> typeface = fm->makeFromStream(std::move(distortable), params);
Ben Wagnerfc497342017-02-24 11:15:26 -0500198 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
tfarina@chromium.orge4fafb12013-12-12 21:11:12 +0000223DEF_TEST(Typeface, reporter) {
djsollen@google.com1f584ed2013-09-19 12:08:40 +0000224
mbocee6a9912016-05-31 11:42:36 -0700225 sk_sp<SkTypeface> t1(SkTypeface::MakeFromName(nullptr, SkFontStyle()));
Ben Wagner67ef5d72017-10-06 16:05:39 -0400226 sk_sp<SkTypeface> t2(SkTypeface::MakeDefault());
djsollen@google.com1f584ed2013-09-19 12:08:40 +0000227
228 REPORTER_ASSERT(reporter, SkTypeface::Equal(t1.get(), t2.get()));
Ben Wagnera93a14a2017-08-28 10:34:05 -0400229 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));
djsollen@google.com1f584ed2013-09-19 12:08:40 +0000233}
bungeman82a455f2016-04-14 08:04:45 -0700234
Ben Wagnere346b1e2018-06-26 11:22:37 -0400235DEF_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
bungeman82a455f2016-04-14 08:04:45 -0700272static bool count_proc(SkTypeface* face, void* ctx) {
273 int* count = static_cast<int*>(ctx);
274 *count = *count + 1;
275 return false;
276}
277static int count(skiatest::Reporter* reporter, const SkTypefaceCache& cache) {
278 int count = 0;
Ben Wagner68efb212019-03-04 16:15:40 -0500279 sk_sp<SkTypeface> none = cache.findByProcAndRef(count_proc, &count);
bungeman82a455f2016-04-14 08:04:45 -0700280 REPORTER_ASSERT(reporter, none == nullptr);
281 return count;
282}
283
284DEF_TEST(TypefaceCache, reporter) {
Mike Klein0cffcbf92019-03-20 11:08:46 -0500285 sk_sp<SkTypeface> t1(TestEmptyTypeface::Make());
bungeman82a455f2016-04-14 08:04:45 -0700286 {
287 SkTypefaceCache cache;
288 REPORTER_ASSERT(reporter, count(reporter, cache) == 0);
289 {
Mike Klein0cffcbf92019-03-20 11:08:46 -0500290 sk_sp<SkTypeface> t0(TestEmptyTypeface::Make());
Ben Wagner68efb212019-03-04 16:15:40 -0500291 cache.add(t0);
bungeman82a455f2016-04-14 08:04:45 -0700292 REPORTER_ASSERT(reporter, count(reporter, cache) == 1);
Ben Wagner68efb212019-03-04 16:15:40 -0500293 cache.add(t1);
bungeman82a455f2016-04-14 08:04:45 -0700294 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}
Mike Reedbf677412018-09-03 12:34:54 -0400304
305static 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
323DEF_TEST(Typeface_serialize, reporter) {
324 check_serialize_behaviors(SkTypeface::MakeDefault(), false, reporter);
325 check_serialize_behaviors(SkTypeface::MakeFromStream(
Mike Reed271d1d92018-09-03 21:10:10 -0400326 GetResourceAsStream("fonts/Distortable.ttf")),
Mike Reedbf677412018-09-03 12:34:54 -0400327 true, reporter);
328
329}
330
Ben Wagnera541db4d2019-05-22 17:33:07 -0400331DEF_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}
Mike Reed40d9c512019-06-10 11:34:23 -0400366
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.
370DEF_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}