blob: 4248afd8b4a4bc84788af8e6423f5b6d9d55ea4e [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
Hal Canary209e4b12017-05-04 14:23:55 -04008#include "SkAdvancedTypefaceMetrics.h"
bungeman6e45bda2016-07-25 15:11:49 -07009#include "SkData.h"
Ben Wagnerfc497342017-02-24 11:15:26 -050010#include "SkFixed.h"
Ben Wagner255ab8d2016-10-07 15:50:53 -040011#include "SkFontDescriptor.h"
Ben Wagnerfc497342017-02-24 11:15:26 -050012#include "SkFontMgr.h"
13#include "SkMakeUnique.h"
bungeman6e45bda2016-07-25 15:11:49 -070014#include "SkOTTable_OS_2.h"
15#include "SkSFNTHeader.h"
16#include "SkStream.h"
bungeman82a455f2016-04-14 08:04:45 -070017#include "SkRefCnt.h"
Herb Derby33ac15e2018-08-29 12:36:22 -040018#include "SkTestEmptyTypeface.h"
djsollen@google.com1f584ed2013-09-19 12:08:40 +000019#include "SkTypeface.h"
bungeman82a455f2016-04-14 08:04:45 -070020#include "SkTypefaceCache.h"
bungeman6e45bda2016-07-25 15:11:49 -070021#include "Resources.h"
tfarina@chromium.org8f6884a2014-01-24 20:56:26 +000022#include "Test.h"
djsollen@google.com1f584ed2013-09-19 12:08:40 +000023
bungeman6e45bda2016-07-25 15:11:49 -070024#include <memory>
25
26static void TypefaceStyle_test(skiatest::Reporter* reporter,
27 uint16_t weight, uint16_t width, SkData* data)
28{
29 sk_sp<SkData> dataCopy;
reed42943c82016-09-12 12:01:44 -070030 if (!data->unique()) {
bungeman6e45bda2016-07-25 15:11:49 -070031 dataCopy = SkData::MakeWithCopy(data->data(), data->size());
reed42943c82016-09-12 12:01:44 -070032 data = dataCopy.get();
bungeman6e45bda2016-07-25 15:11:49 -070033 }
reed42943c82016-09-12 12:01:44 -070034 SkSFNTHeader* sfntHeader = static_cast<SkSFNTHeader*>(data->writable_data());
bungeman6e45bda2016-07-25 15:11:49 -070035
36 SkSFNTHeader::TableDirectoryEntry* tableEntry =
37 SkTAfter<SkSFNTHeader::TableDirectoryEntry>(sfntHeader);
38 SkSFNTHeader::TableDirectoryEntry* os2TableEntry = nullptr;
39 int numTables = SkEndian_SwapBE16(sfntHeader->numTables);
40 for (int tableEntryIndex = 0; tableEntryIndex < numTables; ++tableEntryIndex) {
41 if (SkOTTableOS2::TAG == tableEntry[tableEntryIndex].tag) {
42 os2TableEntry = tableEntry + tableEntryIndex;
43 break;
44 }
45 }
46 SkASSERT_RELEASE(os2TableEntry);
47
48 size_t os2TableOffset = SkEndian_SwapBE32(os2TableEntry->offset);
49 SkOTTableOS2_V0* os2Table = SkTAddOffset<SkOTTableOS2_V0>(sfntHeader, os2TableOffset);
50 os2Table->usWeightClass.value = SkEndian_SwapBE16(weight);
51 using WidthType = SkOTTableOS2_V0::WidthClass::Value;
52 os2Table->usWidthClass.value = static_cast<WidthType>(SkEndian_SwapBE16(width));
53
Mike Reed271d1d92018-09-03 21:10:10 -040054 sk_sp<SkTypeface> newTypeface(SkTypeface::MakeFromData(sk_ref_sp(data)));
Mike Kleincb6940b2017-11-09 13:45:10 -050055 if (!newTypeface) {
56 // Not all SkFontMgr can MakeFromStream().
57 return;
58 }
bungeman6e45bda2016-07-25 15:11:49 -070059
60 SkFontStyle newStyle = newTypeface->fontStyle();
61
62 //printf("%d, %f\n", weight, (newStyle.weight() - (float)0x7FFF) / (float)0x7FFF);
63 //printf("%d, %f\n", width , (newStyle.width() - (float)0x7F) / (float)0x7F);
64 //printf("%d, %d\n", weight, newStyle.weight());
65 //printf("%d, %d\n", width , newStyle.width());
66
67 // Some back-ends (CG, GDI, DW) support OS/2 version A which uses 0 - 10 (but all differently).
68 REPORTER_ASSERT(reporter,
69 newStyle.weight() == weight ||
70 (weight <= 10 && newStyle.weight() == 100 * weight) ||
71 (weight == 4 && newStyle.weight() == 350) || // GDI weirdness
72 (weight == 5 && newStyle.weight() == 400) || // GDI weirdness
73 (weight == 0 && newStyle.weight() == 1) || // DW weirdness
74 (weight == 1000 && newStyle.weight() == 999) // DW weirdness
75 );
76
77 // Some back-ends (GDI) don't support width, ensure these always report 'medium'.
78 REPORTER_ASSERT(reporter,
79 newStyle.width() == width ||
80 newStyle.width() == 5);
81}
82DEF_TEST(TypefaceStyle, reporter) {
Hal Canary53e5e7d2017-12-08 14:25:14 -050083 std::unique_ptr<SkStreamAsset> stream(GetResourceAsStream("fonts/Em.ttf"));
bungeman6e45bda2016-07-25 15:11:49 -070084 if (!stream) {
Hal Canary53e5e7d2017-12-08 14:25:14 -050085 REPORT_FAILURE(reporter, "fonts/Em.ttf", SkString("Cannot load resource"));
bungeman6e45bda2016-07-25 15:11:49 -070086 return;
87 }
88 sk_sp<SkData> data(SkData::MakeFromStream(stream.get(), stream->getLength()));
89
90 using SkFS = SkFontStyle;
91 for (int weight = SkFS::kInvisible_Weight; weight <= SkFS::kExtraBlack_Weight; ++weight) {
92 TypefaceStyle_test(reporter, weight, 5, data.get());
93 }
bungemand783e082016-08-01 12:37:13 -070094 for (int width = SkFS::kUltraCondensed_Width; width <= SkFS::kUltraExpanded_Width; ++width) {
bungeman6e45bda2016-07-25 15:11:49 -070095 TypefaceStyle_test(reporter, 400, width, data.get());
96 }
97}
98
Ben Wagner03cd6e62018-03-08 16:02:55 -050099DEF_TEST(TypefaceRoundTrip, reporter) {
100 sk_sp<SkTypeface> typeface(MakeResourceAsTypeface("fonts/7630.otf"));
101 if (!typeface) {
102 // Not all SkFontMgr can MakeFromStream().
103 return;
104 }
105
106 int fontIndex;
107 std::unique_ptr<SkStreamAsset> stream(typeface->openStream(&fontIndex));
108
109 sk_sp<SkFontMgr> fm = SkFontMgr::RefDefault();
110 sk_sp<SkTypeface> typeface2 = fm->makeFromStream(std::move(stream), fontIndex);
111 REPORTER_ASSERT(reporter, typeface2);
112}
113
Ben Wagner255ab8d2016-10-07 15:50:53 -0400114DEF_TEST(FontDescriptorNegativeVariationSerialize, reporter) {
115 SkFontDescriptor desc;
116 SkFixed axis = -SK_Fixed1;
117 auto font = skstd::make_unique<SkMemoryStream>("a", 1, false);
118 desc.setFontData(skstd::make_unique<SkFontData>(std::move(font), 0, &axis, 1));
119
120 SkDynamicMemoryWStream stream;
121 desc.serialize(&stream);
122 SkFontDescriptor descD;
123 SkFontDescriptor::Deserialize(stream.detachAsStream().get(), &descD);
124 std::unique_ptr<SkFontData> fontData = descD.detachFontData();
125 if (!fontData) {
126 REPORT_FAILURE(reporter, "fontData", SkString());
127 return;
128 }
129
130 if (fontData->getAxisCount() != 1) {
131 REPORT_FAILURE(reporter, "fontData->getAxisCount() != 1", SkString());
132 return;
133 }
134
135 REPORTER_ASSERT(reporter, fontData->getAxis()[0] == -SK_Fixed1);
136};
137
Ben Wagnerfc497342017-02-24 11:15:26 -0500138DEF_TEST(TypefaceAxes, reporter) {
Hal Canary53e5e7d2017-12-08 14:25:14 -0500139 std::unique_ptr<SkStreamAsset> distortable(GetResourceAsStream("fonts/Distortable.ttf"));
Ben Wagnerfc497342017-02-24 11:15:26 -0500140 if (!distortable) {
141 REPORT_FAILURE(reporter, "distortable", SkString());
142 return;
143 }
bungeman9aec8942017-03-29 13:38:53 -0400144 constexpr int numberOfAxesInDistortable = 1;
Ben Wagnerfc497342017-02-24 11:15:26 -0500145
146 sk_sp<SkFontMgr> fm = SkFontMgr::RefDefault();
bungeman9aec8942017-03-29 13:38:53 -0400147 // The position may be over specified. If there are multiple values for a given axis,
148 // ensure the last one since that's what css-fonts-4 requires.
Ben Wagnerfc497342017-02-24 11:15:26 -0500149 const SkFontArguments::VariationPosition::Coordinate position[] = {
bungeman9aec8942017-03-29 13:38:53 -0400150 { SkSetFourByteTag('w','g','h','t'), 1.618033988749895f },
151 { SkSetFourByteTag('w','g','h','t'), SK_ScalarSqrt2 },
Ben Wagnerfc497342017-02-24 11:15:26 -0500152 };
153 SkFontArguments params;
154 params.setVariationDesignPosition({position, SK_ARRAY_COUNT(position)});
155 // 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 -0400156 sk_sp<SkTypeface> typeface = fm->makeFromStream(std::move(distortable), params);
Ben Wagnerfc497342017-02-24 11:15:26 -0500157
Mike Kleincb6940b2017-11-09 13:45:10 -0500158 if (!typeface) {
159 // Not all SkFontMgr can makeFromStream().
160 return;
161 }
162
Ben Wagnerfc497342017-02-24 11:15:26 -0500163 int count = typeface->getVariationDesignPosition(nullptr, 0);
164 if (count == -1) {
165 return;
166 }
bungeman9aec8942017-03-29 13:38:53 -0400167 REPORTER_ASSERT(reporter, count == numberOfAxesInDistortable);
Ben Wagnerfc497342017-02-24 11:15:26 -0500168
bungeman9aec8942017-03-29 13:38:53 -0400169 SkFontArguments::VariationPosition::Coordinate positionRead[numberOfAxesInDistortable];
Ben Wagnerfc497342017-02-24 11:15:26 -0500170 count = typeface->getVariationDesignPosition(positionRead, SK_ARRAY_COUNT(positionRead));
bungeman9aec8942017-03-29 13:38:53 -0400171 REPORTER_ASSERT(reporter, count == SK_ARRAY_COUNT(positionRead));
Ben Wagnerfc497342017-02-24 11:15:26 -0500172
bungeman9aec8942017-03-29 13:38:53 -0400173 REPORTER_ASSERT(reporter, positionRead[0].axis == position[1].axis);
Ben Wagnerfc497342017-02-24 11:15:26 -0500174
175 // Convert to fixed for "almost equal".
176 SkFixed fixedRead = SkScalarToFixed(positionRead[0].value);
bungeman9aec8942017-03-29 13:38:53 -0400177 SkFixed fixedOriginal = SkScalarToFixed(position[1].value);
Bruce Wang75e64902018-06-12 14:44:22 -0400178 REPORTER_ASSERT(reporter, SkTAbs(fixedRead - fixedOriginal) < 2);
Ben Wagnerfc497342017-02-24 11:15:26 -0500179}
180
181DEF_TEST(TypefaceVariationIndex, reporter) {
Hal Canary53e5e7d2017-12-08 14:25:14 -0500182 std::unique_ptr<SkStreamAsset> distortable(GetResourceAsStream("fonts/Distortable.ttf"));
Ben Wagnerfc497342017-02-24 11:15:26 -0500183 if (!distortable) {
184 REPORT_FAILURE(reporter, "distortable", SkString());
185 return;
186 }
187
188 sk_sp<SkFontMgr> fm = SkFontMgr::RefDefault();
189 SkFontArguments params;
190 // The first named variation position in Distortable is 'Thin'.
191 params.setCollectionIndex(0x00010000);
Mike Reed59227392017-09-26 09:46:08 -0400192 sk_sp<SkTypeface> typeface = fm->makeFromStream(std::move(distortable), params);
Ben Wagnerfc497342017-02-24 11:15:26 -0500193 if (!typeface) {
194 // FreeType is the only weird thing that supports this, Skia just needs to make sure if it
195 // gets one of these things make sense.
196 return;
197 }
198
199 int count = typeface->getVariationDesignPosition(nullptr, 0);
200 if (!(count == 1)) {
201 REPORT_FAILURE(reporter, "count == 1", SkString());
202 return;
203 }
204
205 SkFontArguments::VariationPosition::Coordinate positionRead[1];
206 count = typeface->getVariationDesignPosition(positionRead, SK_ARRAY_COUNT(positionRead));
207 if (count == -1) {
208 return;
209 }
210 if (!(count == 1)) {
211 REPORT_FAILURE(reporter, "count == 1", SkString());
212 return;
213 }
214 REPORTER_ASSERT(reporter, positionRead[0].axis == SkSetFourByteTag('w','g','h','t'));
215 REPORTER_ASSERT(reporter, positionRead[0].value == 0.5);
216}
217
tfarina@chromium.orge4fafb12013-12-12 21:11:12 +0000218DEF_TEST(Typeface, reporter) {
djsollen@google.com1f584ed2013-09-19 12:08:40 +0000219
mbocee6a9912016-05-31 11:42:36 -0700220 sk_sp<SkTypeface> t1(SkTypeface::MakeFromName(nullptr, SkFontStyle()));
Ben Wagner67ef5d72017-10-06 16:05:39 -0400221 sk_sp<SkTypeface> t2(SkTypeface::MakeDefault());
djsollen@google.com1f584ed2013-09-19 12:08:40 +0000222
223 REPORTER_ASSERT(reporter, SkTypeface::Equal(t1.get(), t2.get()));
Ben Wagnera93a14a2017-08-28 10:34:05 -0400224 REPORTER_ASSERT(reporter, SkTypeface::Equal(nullptr, t1.get()));
225 REPORTER_ASSERT(reporter, SkTypeface::Equal(nullptr, t2.get()));
226 REPORTER_ASSERT(reporter, SkTypeface::Equal(t1.get(), nullptr));
227 REPORTER_ASSERT(reporter, SkTypeface::Equal(t2.get(), nullptr));
djsollen@google.com1f584ed2013-09-19 12:08:40 +0000228}
bungeman82a455f2016-04-14 08:04:45 -0700229
Ben Wagnere346b1e2018-06-26 11:22:37 -0400230DEF_TEST(TypefaceAxesParameters, reporter) {
231 std::unique_ptr<SkStreamAsset> distortable(GetResourceAsStream("fonts/Distortable.ttf"));
232 if (!distortable) {
233 REPORT_FAILURE(reporter, "distortable", SkString());
234 return;
235 }
236 constexpr int numberOfAxesInDistortable = 1;
237 constexpr SkScalar minAxisInDistortable = 0.5;
238 constexpr SkScalar defAxisInDistortable = 1;
239 constexpr SkScalar maxAxisInDistortable = 2;
240 constexpr bool axisIsHiddenInDistortable = false;
241
242 sk_sp<SkFontMgr> fm = SkFontMgr::RefDefault();
243
244 SkFontArguments params;
245 sk_sp<SkTypeface> typeface = fm->makeFromStream(std::move(distortable), params);
246
247 if (!typeface) {
248 // Not all SkFontMgr can makeFromStream().
249 return;
250 }
251
252 SkFontParameters::Variation::Axis parameter[numberOfAxesInDistortable];
253 int count = typeface->getVariationDesignParameters(parameter, SK_ARRAY_COUNT(parameter));
254 if (count == -1) {
255 return;
256 }
257
258 REPORTER_ASSERT(reporter, count == SK_ARRAY_COUNT(parameter));
259 REPORTER_ASSERT(reporter, parameter[0].min == minAxisInDistortable);
260 REPORTER_ASSERT(reporter, parameter[0].def == defAxisInDistortable);
261 REPORTER_ASSERT(reporter, parameter[0].max == maxAxisInDistortable);
262 REPORTER_ASSERT(reporter, parameter[0].tag == SkSetFourByteTag('w','g','h','t'));
263 REPORTER_ASSERT(reporter, parameter[0].isHidden() == axisIsHiddenInDistortable);
264
265}
266
bungeman82a455f2016-04-14 08:04:45 -0700267static bool count_proc(SkTypeface* face, void* ctx) {
268 int* count = static_cast<int*>(ctx);
269 *count = *count + 1;
270 return false;
271}
272static int count(skiatest::Reporter* reporter, const SkTypefaceCache& cache) {
273 int count = 0;
274 SkTypeface* none = cache.findByProcAndRef(count_proc, &count);
275 REPORTER_ASSERT(reporter, none == nullptr);
276 return count;
277}
278
279DEF_TEST(TypefaceCache, reporter) {
Herb Derby33ac15e2018-08-29 12:36:22 -0400280 sk_sp<SkTypeface> t1(SkTestEmptyTypeface::Make());
bungeman82a455f2016-04-14 08:04:45 -0700281 {
282 SkTypefaceCache cache;
283 REPORTER_ASSERT(reporter, count(reporter, cache) == 0);
284 {
Herb Derby33ac15e2018-08-29 12:36:22 -0400285 sk_sp<SkTypeface> t0(SkTestEmptyTypeface::Make());
bungeman82a455f2016-04-14 08:04:45 -0700286 cache.add(t0.get());
287 REPORTER_ASSERT(reporter, count(reporter, cache) == 1);
288 cache.add(t1.get());
289 REPORTER_ASSERT(reporter, count(reporter, cache) == 2);
290 cache.purgeAll();
291 REPORTER_ASSERT(reporter, count(reporter, cache) == 2);
292 }
293 REPORTER_ASSERT(reporter, count(reporter, cache) == 2);
294 cache.purgeAll();
295 REPORTER_ASSERT(reporter, count(reporter, cache) == 1);
296 }
297 REPORTER_ASSERT(reporter, t1->unique());
298}
Mike Reedbf677412018-09-03 12:34:54 -0400299
300static void check_serialize_behaviors(sk_sp<SkTypeface> tf, bool isLocalData,
301 skiatest::Reporter* reporter) {
302 if (!tf) {
303 return;
304 }
305 auto data0 = tf->serialize(SkTypeface::SerializeBehavior::kDoIncludeData);
306 auto data1 = tf->serialize(SkTypeface::SerializeBehavior::kDontIncludeData);
307 auto data2 = tf->serialize(SkTypeface::SerializeBehavior::kIncludeDataIfLocal);
308
309 REPORTER_ASSERT(reporter, data0->size() >= data1->size());
310
311 if (isLocalData) {
312 REPORTER_ASSERT(reporter, data0->equals(data2.get()));
313 } else {
314 REPORTER_ASSERT(reporter, data1->equals(data2.get()));
315 }
316}
317
318DEF_TEST(Typeface_serialize, reporter) {
319 check_serialize_behaviors(SkTypeface::MakeDefault(), false, reporter);
320 check_serialize_behaviors(SkTypeface::MakeFromStream(
Mike Reed271d1d92018-09-03 21:10:10 -0400321 GetResourceAsStream("fonts/Distortable.ttf")),
Mike Reedbf677412018-09-03 12:34:54 -0400322 true, reporter);
323
324}
325