blob: f0253ef08f3d193ad92d56071e8b8f447249e0bc [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"
djsollen@google.com1f584ed2013-09-19 12:08:40 +000018#include "SkTypeface.h"
bungeman82a455f2016-04-14 08:04:45 -070019#include "SkTypefaceCache.h"
bungeman6e45bda2016-07-25 15:11:49 -070020#include "Resources.h"
tfarina@chromium.org8f6884a2014-01-24 20:56:26 +000021#include "Test.h"
djsollen@google.com1f584ed2013-09-19 12:08:40 +000022
bungeman6e45bda2016-07-25 15:11:49 -070023#include <memory>
24
25static void TypefaceStyle_test(skiatest::Reporter* reporter,
26 uint16_t weight, uint16_t width, SkData* data)
27{
28 sk_sp<SkData> dataCopy;
reed42943c82016-09-12 12:01:44 -070029 if (!data->unique()) {
bungeman6e45bda2016-07-25 15:11:49 -070030 dataCopy = SkData::MakeWithCopy(data->data(), data->size());
reed42943c82016-09-12 12:01:44 -070031 data = dataCopy.get();
bungeman6e45bda2016-07-25 15:11:49 -070032 }
reed42943c82016-09-12 12:01:44 -070033 SkSFNTHeader* sfntHeader = static_cast<SkSFNTHeader*>(data->writable_data());
bungeman6e45bda2016-07-25 15:11:49 -070034
35 SkSFNTHeader::TableDirectoryEntry* tableEntry =
36 SkTAfter<SkSFNTHeader::TableDirectoryEntry>(sfntHeader);
37 SkSFNTHeader::TableDirectoryEntry* os2TableEntry = nullptr;
38 int numTables = SkEndian_SwapBE16(sfntHeader->numTables);
39 for (int tableEntryIndex = 0; tableEntryIndex < numTables; ++tableEntryIndex) {
40 if (SkOTTableOS2::TAG == tableEntry[tableEntryIndex].tag) {
41 os2TableEntry = tableEntry + tableEntryIndex;
42 break;
43 }
44 }
45 SkASSERT_RELEASE(os2TableEntry);
46
47 size_t os2TableOffset = SkEndian_SwapBE32(os2TableEntry->offset);
48 SkOTTableOS2_V0* os2Table = SkTAddOffset<SkOTTableOS2_V0>(sfntHeader, os2TableOffset);
49 os2Table->usWeightClass.value = SkEndian_SwapBE16(weight);
50 using WidthType = SkOTTableOS2_V0::WidthClass::Value;
51 os2Table->usWidthClass.value = static_cast<WidthType>(SkEndian_SwapBE16(width));
52
reed42943c82016-09-12 12:01:44 -070053 sk_sp<SkTypeface> newTypeface(SkTypeface::MakeFromStream(new SkMemoryStream(sk_ref_sp(data))));
Mike Kleincb6940b2017-11-09 13:45:10 -050054 if (!newTypeface) {
55 // Not all SkFontMgr can MakeFromStream().
56 return;
57 }
bungeman6e45bda2016-07-25 15:11:49 -070058
59 SkFontStyle newStyle = newTypeface->fontStyle();
60
61 //printf("%d, %f\n", weight, (newStyle.weight() - (float)0x7FFF) / (float)0x7FFF);
62 //printf("%d, %f\n", width , (newStyle.width() - (float)0x7F) / (float)0x7F);
63 //printf("%d, %d\n", weight, newStyle.weight());
64 //printf("%d, %d\n", width , newStyle.width());
65
66 // Some back-ends (CG, GDI, DW) support OS/2 version A which uses 0 - 10 (but all differently).
67 REPORTER_ASSERT(reporter,
68 newStyle.weight() == weight ||
69 (weight <= 10 && newStyle.weight() == 100 * weight) ||
70 (weight == 4 && newStyle.weight() == 350) || // GDI weirdness
71 (weight == 5 && newStyle.weight() == 400) || // GDI weirdness
72 (weight == 0 && newStyle.weight() == 1) || // DW weirdness
73 (weight == 1000 && newStyle.weight() == 999) // DW weirdness
74 );
75
76 // Some back-ends (GDI) don't support width, ensure these always report 'medium'.
77 REPORTER_ASSERT(reporter,
78 newStyle.width() == width ||
79 newStyle.width() == 5);
80}
81DEF_TEST(TypefaceStyle, reporter) {
Hal Canary53e5e7d2017-12-08 14:25:14 -050082 std::unique_ptr<SkStreamAsset> stream(GetResourceAsStream("fonts/Em.ttf"));
bungeman6e45bda2016-07-25 15:11:49 -070083 if (!stream) {
Hal Canary53e5e7d2017-12-08 14:25:14 -050084 REPORT_FAILURE(reporter, "fonts/Em.ttf", SkString("Cannot load resource"));
bungeman6e45bda2016-07-25 15:11:49 -070085 return;
86 }
87 sk_sp<SkData> data(SkData::MakeFromStream(stream.get(), stream->getLength()));
88
89 using SkFS = SkFontStyle;
90 for (int weight = SkFS::kInvisible_Weight; weight <= SkFS::kExtraBlack_Weight; ++weight) {
91 TypefaceStyle_test(reporter, weight, 5, data.get());
92 }
bungemand783e082016-08-01 12:37:13 -070093 for (int width = SkFS::kUltraCondensed_Width; width <= SkFS::kUltraExpanded_Width; ++width) {
bungeman6e45bda2016-07-25 15:11:49 -070094 TypefaceStyle_test(reporter, 400, width, data.get());
95 }
96}
97
Ben Wagner03cd6e62018-03-08 16:02:55 -050098DEF_TEST(TypefaceRoundTrip, reporter) {
99 sk_sp<SkTypeface> typeface(MakeResourceAsTypeface("fonts/7630.otf"));
100 if (!typeface) {
101 // Not all SkFontMgr can MakeFromStream().
102 return;
103 }
104
105 int fontIndex;
106 std::unique_ptr<SkStreamAsset> stream(typeface->openStream(&fontIndex));
107
108 sk_sp<SkFontMgr> fm = SkFontMgr::RefDefault();
109 sk_sp<SkTypeface> typeface2 = fm->makeFromStream(std::move(stream), fontIndex);
110 REPORTER_ASSERT(reporter, typeface2);
111}
112
Ben Wagner255ab8d2016-10-07 15:50:53 -0400113DEF_TEST(FontDescriptorNegativeVariationSerialize, reporter) {
114 SkFontDescriptor desc;
115 SkFixed axis = -SK_Fixed1;
116 auto font = skstd::make_unique<SkMemoryStream>("a", 1, false);
117 desc.setFontData(skstd::make_unique<SkFontData>(std::move(font), 0, &axis, 1));
118
119 SkDynamicMemoryWStream stream;
120 desc.serialize(&stream);
121 SkFontDescriptor descD;
122 SkFontDescriptor::Deserialize(stream.detachAsStream().get(), &descD);
123 std::unique_ptr<SkFontData> fontData = descD.detachFontData();
124 if (!fontData) {
125 REPORT_FAILURE(reporter, "fontData", SkString());
126 return;
127 }
128
129 if (fontData->getAxisCount() != 1) {
130 REPORT_FAILURE(reporter, "fontData->getAxisCount() != 1", SkString());
131 return;
132 }
133
134 REPORTER_ASSERT(reporter, fontData->getAxis()[0] == -SK_Fixed1);
135};
136
Ben Wagnerfc497342017-02-24 11:15:26 -0500137DEF_TEST(TypefaceAxes, reporter) {
Hal Canary53e5e7d2017-12-08 14:25:14 -0500138 std::unique_ptr<SkStreamAsset> distortable(GetResourceAsStream("fonts/Distortable.ttf"));
Ben Wagnerfc497342017-02-24 11:15:26 -0500139 if (!distortable) {
140 REPORT_FAILURE(reporter, "distortable", SkString());
141 return;
142 }
bungeman9aec8942017-03-29 13:38:53 -0400143 constexpr int numberOfAxesInDistortable = 1;
Ben Wagnerfc497342017-02-24 11:15:26 -0500144
145 sk_sp<SkFontMgr> fm = SkFontMgr::RefDefault();
bungeman9aec8942017-03-29 13:38:53 -0400146 // The position may be over specified. If there are multiple values for a given axis,
147 // ensure the last one since that's what css-fonts-4 requires.
Ben Wagnerfc497342017-02-24 11:15:26 -0500148 const SkFontArguments::VariationPosition::Coordinate position[] = {
bungeman9aec8942017-03-29 13:38:53 -0400149 { SkSetFourByteTag('w','g','h','t'), 1.618033988749895f },
150 { SkSetFourByteTag('w','g','h','t'), SK_ScalarSqrt2 },
Ben Wagnerfc497342017-02-24 11:15:26 -0500151 };
152 SkFontArguments params;
153 params.setVariationDesignPosition({position, SK_ARRAY_COUNT(position)});
154 // 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 -0400155 sk_sp<SkTypeface> typeface = fm->makeFromStream(std::move(distortable), params);
Ben Wagnerfc497342017-02-24 11:15:26 -0500156
Mike Kleincb6940b2017-11-09 13:45:10 -0500157 if (!typeface) {
158 // Not all SkFontMgr can makeFromStream().
159 return;
160 }
161
Ben Wagnerfc497342017-02-24 11:15:26 -0500162 int count = typeface->getVariationDesignPosition(nullptr, 0);
163 if (count == -1) {
164 return;
165 }
bungeman9aec8942017-03-29 13:38:53 -0400166 REPORTER_ASSERT(reporter, count == numberOfAxesInDistortable);
Ben Wagnerfc497342017-02-24 11:15:26 -0500167
bungeman9aec8942017-03-29 13:38:53 -0400168 SkFontArguments::VariationPosition::Coordinate positionRead[numberOfAxesInDistortable];
Ben Wagnerfc497342017-02-24 11:15:26 -0500169 count = typeface->getVariationDesignPosition(positionRead, SK_ARRAY_COUNT(positionRead));
bungeman9aec8942017-03-29 13:38:53 -0400170 REPORTER_ASSERT(reporter, count == SK_ARRAY_COUNT(positionRead));
Ben Wagnerfc497342017-02-24 11:15:26 -0500171
bungeman9aec8942017-03-29 13:38:53 -0400172 REPORTER_ASSERT(reporter, positionRead[0].axis == position[1].axis);
Ben Wagnerfc497342017-02-24 11:15:26 -0500173
174 // Convert to fixed for "almost equal".
175 SkFixed fixedRead = SkScalarToFixed(positionRead[0].value);
bungeman9aec8942017-03-29 13:38:53 -0400176 SkFixed fixedOriginal = SkScalarToFixed(position[1].value);
Bruce Wang75e64902018-06-12 14:44:22 -0400177 REPORTER_ASSERT(reporter, SkTAbs(fixedRead - fixedOriginal) < 2);
Ben Wagnerfc497342017-02-24 11:15:26 -0500178}
179
180DEF_TEST(TypefaceVariationIndex, reporter) {
Hal Canary53e5e7d2017-12-08 14:25:14 -0500181 std::unique_ptr<SkStreamAsset> distortable(GetResourceAsStream("fonts/Distortable.ttf"));
Ben Wagnerfc497342017-02-24 11:15:26 -0500182 if (!distortable) {
183 REPORT_FAILURE(reporter, "distortable", SkString());
184 return;
185 }
186
187 sk_sp<SkFontMgr> fm = SkFontMgr::RefDefault();
188 SkFontArguments params;
189 // The first named variation position in Distortable is 'Thin'.
190 params.setCollectionIndex(0x00010000);
Mike Reed59227392017-09-26 09:46:08 -0400191 sk_sp<SkTypeface> typeface = fm->makeFromStream(std::move(distortable), params);
Ben Wagnerfc497342017-02-24 11:15:26 -0500192 if (!typeface) {
193 // FreeType is the only weird thing that supports this, Skia just needs to make sure if it
194 // gets one of these things make sense.
195 return;
196 }
197
198 int count = typeface->getVariationDesignPosition(nullptr, 0);
199 if (!(count == 1)) {
200 REPORT_FAILURE(reporter, "count == 1", SkString());
201 return;
202 }
203
204 SkFontArguments::VariationPosition::Coordinate positionRead[1];
205 count = typeface->getVariationDesignPosition(positionRead, SK_ARRAY_COUNT(positionRead));
206 if (count == -1) {
207 return;
208 }
209 if (!(count == 1)) {
210 REPORT_FAILURE(reporter, "count == 1", SkString());
211 return;
212 }
213 REPORTER_ASSERT(reporter, positionRead[0].axis == SkSetFourByteTag('w','g','h','t'));
214 REPORTER_ASSERT(reporter, positionRead[0].value == 0.5);
215}
216
tfarina@chromium.orge4fafb12013-12-12 21:11:12 +0000217DEF_TEST(Typeface, reporter) {
djsollen@google.com1f584ed2013-09-19 12:08:40 +0000218
mbocee6a9912016-05-31 11:42:36 -0700219 sk_sp<SkTypeface> t1(SkTypeface::MakeFromName(nullptr, SkFontStyle()));
Ben Wagner67ef5d72017-10-06 16:05:39 -0400220 sk_sp<SkTypeface> t2(SkTypeface::MakeDefault());
djsollen@google.com1f584ed2013-09-19 12:08:40 +0000221
222 REPORTER_ASSERT(reporter, SkTypeface::Equal(t1.get(), t2.get()));
Ben Wagnera93a14a2017-08-28 10:34:05 -0400223 REPORTER_ASSERT(reporter, SkTypeface::Equal(nullptr, t1.get()));
224 REPORTER_ASSERT(reporter, SkTypeface::Equal(nullptr, t2.get()));
225 REPORTER_ASSERT(reporter, SkTypeface::Equal(t1.get(), nullptr));
226 REPORTER_ASSERT(reporter, SkTypeface::Equal(t2.get(), nullptr));
djsollen@google.com1f584ed2013-09-19 12:08:40 +0000227}
bungeman82a455f2016-04-14 08:04:45 -0700228
bungemana821af82016-04-14 09:44:34 -0700229namespace {
230
Hal Canary46cc3da2018-05-09 11:50:34 -0400231class EmptyTypeface : public SkTypeface {
bungeman82a455f2016-04-14 08:04:45 -0700232public:
Hal Canary46cc3da2018-05-09 11:50:34 -0400233 static sk_sp<SkTypeface> Create() { return sk_sp<SkTypeface>(new EmptyTypeface()); }
bungeman82a455f2016-04-14 08:04:45 -0700234protected:
Hal Canary46cc3da2018-05-09 11:50:34 -0400235 EmptyTypeface() : SkTypeface(SkFontStyle(), true) { }
bungeman82a455f2016-04-14 08:04:45 -0700236
237 SkStreamAsset* onOpenStream(int* ttcIndex) const override { return nullptr; }
238 SkScalerContext* onCreateScalerContext(const SkScalerContextEffects&,
239 const SkDescriptor*) const override {
240 return nullptr;
241 }
242 void onFilterRec(SkScalerContextRec*) const override { }
Hal Canary209e4b12017-05-04 14:23:55 -0400243 std::unique_ptr<SkAdvancedTypefaceMetrics> onGetAdvancedMetrics() const override {
244 return nullptr;
245 }
bungeman82a455f2016-04-14 08:04:45 -0700246 void onGetFontDescriptor(SkFontDescriptor*, bool*) const override { }
247 virtual int onCharsToGlyphs(const void* chars, Encoding encoding,
248 uint16_t glyphs[], int glyphCount) const override {
249 SK_ABORT("unimplemented");
250 return 0;
251 }
Mike Kleinfc6c37b2016-09-27 09:34:10 -0400252 int onCountGlyphs() const override { return 0; }
253 int onGetUPEM() const override { return 0; }
bungeman82a455f2016-04-14 08:04:45 -0700254 void onGetFamilyName(SkString* familyName) const override { familyName->reset(); }
255 SkTypeface::LocalizedStrings* onCreateFamilyNameIterator() const override {
256 SK_ABORT("unimplemented");
257 return nullptr;
Mike Kleinfc6c37b2016-09-27 09:34:10 -0400258 }
Ben Wagnerfc497342017-02-24 11:15:26 -0500259 int onGetVariationDesignPosition(SkFontArguments::VariationPosition::Coordinate coordinates[],
260 int coordinateCount) const override
261 {
262 return 0;
263 }
bungeman82a455f2016-04-14 08:04:45 -0700264 int onGetTableTags(SkFontTableTag tags[]) const override { return 0; }
265 size_t onGetTableData(SkFontTableTag, size_t, size_t, void*) const override { return 0; }
266};
267
bungemana821af82016-04-14 09:44:34 -0700268}
269
bungeman82a455f2016-04-14 08:04:45 -0700270static bool count_proc(SkTypeface* face, void* ctx) {
271 int* count = static_cast<int*>(ctx);
272 *count = *count + 1;
273 return false;
274}
275static int count(skiatest::Reporter* reporter, const SkTypefaceCache& cache) {
276 int count = 0;
277 SkTypeface* none = cache.findByProcAndRef(count_proc, &count);
278 REPORTER_ASSERT(reporter, none == nullptr);
279 return count;
280}
281
282DEF_TEST(TypefaceCache, reporter) {
Hal Canary46cc3da2018-05-09 11:50:34 -0400283 sk_sp<SkTypeface> t1(EmptyTypeface::Create());
bungeman82a455f2016-04-14 08:04:45 -0700284 {
285 SkTypefaceCache cache;
286 REPORTER_ASSERT(reporter, count(reporter, cache) == 0);
287 {
Hal Canary46cc3da2018-05-09 11:50:34 -0400288 sk_sp<SkTypeface> t0(EmptyTypeface::Create());
bungeman82a455f2016-04-14 08:04:45 -0700289 cache.add(t0.get());
290 REPORTER_ASSERT(reporter, count(reporter, cache) == 1);
291 cache.add(t1.get());
292 REPORTER_ASSERT(reporter, count(reporter, cache) == 2);
293 cache.purgeAll();
294 REPORTER_ASSERT(reporter, count(reporter, cache) == 2);
295 }
296 REPORTER_ASSERT(reporter, count(reporter, cache) == 2);
297 cache.purgeAll();
298 REPORTER_ASSERT(reporter, count(reporter, cache) == 1);
299 }
300 REPORTER_ASSERT(reporter, t1->unique());
301}