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 "SkAdvancedTypefaceMetrics.h"
9 #include "SkData.h"
10 #include "SkFixed.h"
11 #include "SkFontDescriptor.h"
12 #include "SkFontMgr.h"
13 #include "SkMakeUnique.h"
14 #include "SkOTTable_OS_2.h"
15 #include "SkSFNTHeader.h"
16 #include "SkStream.h"
17 #include "SkRefCnt.h"
18 #include "SkTestEmptyTypeface.h"
19 #include "SkTypeface.h"
20 #include "SkTypefaceCache.h"
21 #include "Resources.h"
22 #include "Test.h"
23
24 #include <memory>
25
TypefaceStyle_test(skiatest::Reporter * reporter,uint16_t weight,uint16_t width,SkData * data)26 static void TypefaceStyle_test(skiatest::Reporter* reporter,
27 uint16_t weight, uint16_t width, SkData* data)
28 {
29 sk_sp<SkData> dataCopy;
30 if (!data->unique()) {
31 dataCopy = SkData::MakeWithCopy(data->data(), data->size());
32 data = dataCopy.get();
33 }
34 SkSFNTHeader* sfntHeader = static_cast<SkSFNTHeader*>(data->writable_data());
35
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
54 sk_sp<SkTypeface> newTypeface(SkTypeface::MakeFromData(sk_ref_sp(data)));
55 if (!newTypeface) {
56 // Not all SkFontMgr can MakeFromStream().
57 return;
58 }
59
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 }
DEF_TEST(TypefaceStyle,reporter)82 DEF_TEST(TypefaceStyle, reporter) {
83 std::unique_ptr<SkStreamAsset> stream(GetResourceAsStream("fonts/Em.ttf"));
84 if (!stream) {
85 REPORT_FAILURE(reporter, "fonts/Em.ttf", SkString("Cannot load resource"));
86 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 }
94 for (int width = SkFS::kUltraCondensed_Width; width <= SkFS::kUltraExpanded_Width; ++width) {
95 TypefaceStyle_test(reporter, 400, width, data.get());
96 }
97 }
98
DEF_TEST(TypefaceRoundTrip,reporter)99 DEF_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
DEF_TEST(FontDescriptorNegativeVariationSerialize,reporter)114 DEF_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
DEF_TEST(TypefaceAxes,reporter)138 DEF_TEST(TypefaceAxes, reporter) {
139 std::unique_ptr<SkStreamAsset> distortable(GetResourceAsStream("fonts/Distortable.ttf"));
140 if (!distortable) {
141 REPORT_FAILURE(reporter, "distortable", SkString());
142 return;
143 }
144 constexpr int numberOfAxesInDistortable = 1;
145
146 sk_sp<SkFontMgr> fm = SkFontMgr::RefDefault();
147 // 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.
149 const SkFontArguments::VariationPosition::Coordinate position[] = {
150 { SkSetFourByteTag('w','g','h','t'), 1.618033988749895f },
151 { SkSetFourByteTag('w','g','h','t'), SK_ScalarSqrt2 },
152 };
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?
156 sk_sp<SkTypeface> typeface = fm->makeFromStream(std::move(distortable), params);
157
158 if (!typeface) {
159 // Not all SkFontMgr can makeFromStream().
160 return;
161 }
162
163 int count = typeface->getVariationDesignPosition(nullptr, 0);
164 if (count == -1) {
165 return;
166 }
167 REPORTER_ASSERT(reporter, count == numberOfAxesInDistortable);
168
169 SkFontArguments::VariationPosition::Coordinate positionRead[numberOfAxesInDistortable];
170 count = typeface->getVariationDesignPosition(positionRead, SK_ARRAY_COUNT(positionRead));
171 REPORTER_ASSERT(reporter, count == SK_ARRAY_COUNT(positionRead));
172
173 REPORTER_ASSERT(reporter, positionRead[0].axis == position[1].axis);
174
175 // Convert to fixed for "almost equal".
176 SkFixed fixedRead = SkScalarToFixed(positionRead[0].value);
177 SkFixed fixedOriginal = SkScalarToFixed(position[1].value);
178 REPORTER_ASSERT(reporter, SkTAbs(fixedRead - fixedOriginal) < 2);
179 }
180
DEF_TEST(TypefaceVariationIndex,reporter)181 DEF_TEST(TypefaceVariationIndex, reporter) {
182 std::unique_ptr<SkStreamAsset> distortable(GetResourceAsStream("fonts/Distortable.ttf"));
183 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);
192 sk_sp<SkTypeface> typeface = fm->makeFromStream(std::move(distortable), params);
193 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
DEF_TEST(Typeface,reporter)218 DEF_TEST(Typeface, reporter) {
219
220 sk_sp<SkTypeface> t1(SkTypeface::MakeFromName(nullptr, SkFontStyle()));
221 sk_sp<SkTypeface> t2(SkTypeface::MakeDefault());
222
223 REPORTER_ASSERT(reporter, SkTypeface::Equal(t1.get(), t2.get()));
224 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));
228 }
229
DEF_TEST(TypefaceAxesParameters,reporter)230 DEF_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
count_proc(SkTypeface * face,void * ctx)267 static bool count_proc(SkTypeface* face, void* ctx) {
268 int* count = static_cast<int*>(ctx);
269 *count = *count + 1;
270 return false;
271 }
count(skiatest::Reporter * reporter,const SkTypefaceCache & cache)272 static 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
DEF_TEST(TypefaceCache,reporter)279 DEF_TEST(TypefaceCache, reporter) {
280 sk_sp<SkTypeface> t1(SkTestEmptyTypeface::Make());
281 {
282 SkTypefaceCache cache;
283 REPORTER_ASSERT(reporter, count(reporter, cache) == 0);
284 {
285 sk_sp<SkTypeface> t0(SkTestEmptyTypeface::Make());
286 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 }
299
check_serialize_behaviors(sk_sp<SkTypeface> tf,bool isLocalData,skiatest::Reporter * reporter)300 static 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
DEF_TEST(Typeface_serialize,reporter)318 DEF_TEST(Typeface_serialize, reporter) {
319 check_serialize_behaviors(SkTypeface::MakeDefault(), false, reporter);
320 check_serialize_behaviors(SkTypeface::MakeFromStream(
321 GetResourceAsStream("fonts/Distortable.ttf")),
322 true, reporter);
323
324 }
325
326