blob: 1da94bb8a6d08e4271463c5ef14791bb7b410692 [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"
11#include "SkFontMgr.h"
12#include "SkMakeUnique.h"
bungeman6e45bda2016-07-25 15:11:49 -070013#include "SkOTTable_OS_2.h"
14#include "SkSFNTHeader.h"
15#include "SkStream.h"
bungeman82a455f2016-04-14 08:04:45 -070016#include "SkRefCnt.h"
djsollen@google.com1f584ed2013-09-19 12:08:40 +000017#include "SkTypeface.h"
bungeman82a455f2016-04-14 08:04:45 -070018#include "SkTypefaceCache.h"
bungeman6e45bda2016-07-25 15:11:49 -070019#include "Resources.h"
tfarina@chromium.org8f6884a2014-01-24 20:56:26 +000020#include "Test.h"
djsollen@google.com1f584ed2013-09-19 12:08:40 +000021
bungeman6e45bda2016-07-25 15:11:49 -070022#include <memory>
23
24static void TypefaceStyle_test(skiatest::Reporter* reporter,
25 uint16_t weight, uint16_t width, SkData* data)
26{
27 sk_sp<SkData> dataCopy;
reed42943c82016-09-12 12:01:44 -070028 if (!data->unique()) {
bungeman6e45bda2016-07-25 15:11:49 -070029 dataCopy = SkData::MakeWithCopy(data->data(), data->size());
reed42943c82016-09-12 12:01:44 -070030 data = dataCopy.get();
bungeman6e45bda2016-07-25 15:11:49 -070031 }
reed42943c82016-09-12 12:01:44 -070032 SkSFNTHeader* sfntHeader = static_cast<SkSFNTHeader*>(data->writable_data());
bungeman6e45bda2016-07-25 15:11:49 -070033
34 SkSFNTHeader::TableDirectoryEntry* tableEntry =
35 SkTAfter<SkSFNTHeader::TableDirectoryEntry>(sfntHeader);
36 SkSFNTHeader::TableDirectoryEntry* os2TableEntry = nullptr;
37 int numTables = SkEndian_SwapBE16(sfntHeader->numTables);
38 for (int tableEntryIndex = 0; tableEntryIndex < numTables; ++tableEntryIndex) {
39 if (SkOTTableOS2::TAG == tableEntry[tableEntryIndex].tag) {
40 os2TableEntry = tableEntry + tableEntryIndex;
41 break;
42 }
43 }
44 SkASSERT_RELEASE(os2TableEntry);
45
46 size_t os2TableOffset = SkEndian_SwapBE32(os2TableEntry->offset);
47 SkOTTableOS2_V0* os2Table = SkTAddOffset<SkOTTableOS2_V0>(sfntHeader, os2TableOffset);
48 os2Table->usWeightClass.value = SkEndian_SwapBE16(weight);
49 using WidthType = SkOTTableOS2_V0::WidthClass::Value;
50 os2Table->usWidthClass.value = static_cast<WidthType>(SkEndian_SwapBE16(width));
51
reed42943c82016-09-12 12:01:44 -070052 sk_sp<SkTypeface> newTypeface(SkTypeface::MakeFromStream(new SkMemoryStream(sk_ref_sp(data))));
bungeman6e45bda2016-07-25 15:11:49 -070053 SkASSERT_RELEASE(newTypeface);
54
55 SkFontStyle newStyle = newTypeface->fontStyle();
56
57 //printf("%d, %f\n", weight, (newStyle.weight() - (float)0x7FFF) / (float)0x7FFF);
58 //printf("%d, %f\n", width , (newStyle.width() - (float)0x7F) / (float)0x7F);
59 //printf("%d, %d\n", weight, newStyle.weight());
60 //printf("%d, %d\n", width , newStyle.width());
61
62 // Some back-ends (CG, GDI, DW) support OS/2 version A which uses 0 - 10 (but all differently).
63 REPORTER_ASSERT(reporter,
64 newStyle.weight() == weight ||
65 (weight <= 10 && newStyle.weight() == 100 * weight) ||
66 (weight == 4 && newStyle.weight() == 350) || // GDI weirdness
67 (weight == 5 && newStyle.weight() == 400) || // GDI weirdness
68 (weight == 0 && newStyle.weight() == 1) || // DW weirdness
69 (weight == 1000 && newStyle.weight() == 999) // DW weirdness
70 );
71
72 // Some back-ends (GDI) don't support width, ensure these always report 'medium'.
73 REPORTER_ASSERT(reporter,
74 newStyle.width() == width ||
75 newStyle.width() == 5);
76}
77DEF_TEST(TypefaceStyle, reporter) {
78 std::unique_ptr<SkStreamAsset> stream(GetResourceAsStream("/fonts/Em.ttf"));
79 if (!stream) {
80 REPORT_FAILURE(reporter, "/fonts/Em.ttf", SkString("Cannot load resource"));
81 return;
82 }
83 sk_sp<SkData> data(SkData::MakeFromStream(stream.get(), stream->getLength()));
84
85 using SkFS = SkFontStyle;
86 for (int weight = SkFS::kInvisible_Weight; weight <= SkFS::kExtraBlack_Weight; ++weight) {
87 TypefaceStyle_test(reporter, weight, 5, data.get());
88 }
bungemand783e082016-08-01 12:37:13 -070089 for (int width = SkFS::kUltraCondensed_Width; width <= SkFS::kUltraExpanded_Width; ++width) {
bungeman6e45bda2016-07-25 15:11:49 -070090 TypefaceStyle_test(reporter, 400, width, data.get());
91 }
92}
93
Ben Wagnerfc497342017-02-24 11:15:26 -050094DEF_TEST(TypefaceAxes, reporter) {
95 std::unique_ptr<SkStreamAsset> distortable(GetResourceAsStream("/fonts/Distortable.ttf"));
96 if (!distortable) {
97 REPORT_FAILURE(reporter, "distortable", SkString());
98 return;
99 }
bungeman9aec8942017-03-29 13:38:53 -0400100 constexpr int numberOfAxesInDistortable = 1;
Ben Wagnerfc497342017-02-24 11:15:26 -0500101
102 sk_sp<SkFontMgr> fm = SkFontMgr::RefDefault();
bungeman9aec8942017-03-29 13:38:53 -0400103 // The position may be over specified. If there are multiple values for a given axis,
104 // ensure the last one since that's what css-fonts-4 requires.
Ben Wagnerfc497342017-02-24 11:15:26 -0500105 const SkFontArguments::VariationPosition::Coordinate position[] = {
bungeman9aec8942017-03-29 13:38:53 -0400106 { SkSetFourByteTag('w','g','h','t'), 1.618033988749895f },
107 { SkSetFourByteTag('w','g','h','t'), SK_ScalarSqrt2 },
Ben Wagnerfc497342017-02-24 11:15:26 -0500108 };
109 SkFontArguments params;
110 params.setVariationDesignPosition({position, SK_ARRAY_COUNT(position)});
111 // TODO: if axes are set and the back-end doesn't support them, should we create the typeface?
112 sk_sp<SkTypeface> typeface(fm->createFromStream(distortable.release(), params));
113
114 int count = typeface->getVariationDesignPosition(nullptr, 0);
115 if (count == -1) {
116 return;
117 }
bungeman9aec8942017-03-29 13:38:53 -0400118 REPORTER_ASSERT(reporter, count == numberOfAxesInDistortable);
Ben Wagnerfc497342017-02-24 11:15:26 -0500119
bungeman9aec8942017-03-29 13:38:53 -0400120 SkFontArguments::VariationPosition::Coordinate positionRead[numberOfAxesInDistortable];
Ben Wagnerfc497342017-02-24 11:15:26 -0500121 count = typeface->getVariationDesignPosition(positionRead, SK_ARRAY_COUNT(positionRead));
bungeman9aec8942017-03-29 13:38:53 -0400122 REPORTER_ASSERT(reporter, count == SK_ARRAY_COUNT(positionRead));
Ben Wagnerfc497342017-02-24 11:15:26 -0500123
bungeman9aec8942017-03-29 13:38:53 -0400124 REPORTER_ASSERT(reporter, positionRead[0].axis == position[1].axis);
Ben Wagnerfc497342017-02-24 11:15:26 -0500125
126 // Convert to fixed for "almost equal".
127 SkFixed fixedRead = SkScalarToFixed(positionRead[0].value);
bungeman9aec8942017-03-29 13:38:53 -0400128 SkFixed fixedOriginal = SkScalarToFixed(position[1].value);
Ben Wagnerfc497342017-02-24 11:15:26 -0500129 REPORTER_ASSERT(reporter, fixedRead == fixedOriginal);
130}
131
132DEF_TEST(TypefaceVariationIndex, reporter) {
133 std::unique_ptr<SkStreamAsset> distortable(GetResourceAsStream("/fonts/Distortable.ttf"));
134 if (!distortable) {
135 REPORT_FAILURE(reporter, "distortable", SkString());
136 return;
137 }
138
139 sk_sp<SkFontMgr> fm = SkFontMgr::RefDefault();
140 SkFontArguments params;
141 // The first named variation position in Distortable is 'Thin'.
142 params.setCollectionIndex(0x00010000);
143 sk_sp<SkTypeface> typeface(fm->createFromStream(distortable.release(), params));
144 if (!typeface) {
145 // FreeType is the only weird thing that supports this, Skia just needs to make sure if it
146 // gets one of these things make sense.
147 return;
148 }
149
150 int count = typeface->getVariationDesignPosition(nullptr, 0);
151 if (!(count == 1)) {
152 REPORT_FAILURE(reporter, "count == 1", SkString());
153 return;
154 }
155
156 SkFontArguments::VariationPosition::Coordinate positionRead[1];
157 count = typeface->getVariationDesignPosition(positionRead, SK_ARRAY_COUNT(positionRead));
158 if (count == -1) {
159 return;
160 }
161 if (!(count == 1)) {
162 REPORT_FAILURE(reporter, "count == 1", SkString());
163 return;
164 }
165 REPORTER_ASSERT(reporter, positionRead[0].axis == SkSetFourByteTag('w','g','h','t'));
166 REPORTER_ASSERT(reporter, positionRead[0].value == 0.5);
167}
168
tfarina@chromium.orge4fafb12013-12-12 21:11:12 +0000169DEF_TEST(Typeface, reporter) {
djsollen@google.com1f584ed2013-09-19 12:08:40 +0000170
mbocee6a9912016-05-31 11:42:36 -0700171 sk_sp<SkTypeface> t1(SkTypeface::MakeFromName(nullptr, SkFontStyle()));
bungeman13b9c952016-05-12 10:09:30 -0700172 sk_sp<SkTypeface> t2(SkTypeface::MakeDefault(SkTypeface::kNormal));
djsollen@google.com1f584ed2013-09-19 12:08:40 +0000173
174 REPORTER_ASSERT(reporter, SkTypeface::Equal(t1.get(), t2.get()));
175 REPORTER_ASSERT(reporter, SkTypeface::Equal(0, t1.get()));
176 REPORTER_ASSERT(reporter, SkTypeface::Equal(0, t2.get()));
177 REPORTER_ASSERT(reporter, SkTypeface::Equal(t1.get(), 0));
178 REPORTER_ASSERT(reporter, SkTypeface::Equal(t2.get(), 0));
179
180#ifdef SK_BUILD_FOR_ANDROID
mbocee6a9912016-05-31 11:42:36 -0700181 sk_sp<SkTypeface> t3(SkTypeface::MakeFromName("non-existent-font", SkFontStyle()));
bungeman13b9c952016-05-12 10:09:30 -0700182 REPORTER_ASSERT(reporter, nullptr == t3);
djsollen@google.com1f584ed2013-09-19 12:08:40 +0000183#endif
184}
bungeman82a455f2016-04-14 08:04:45 -0700185
bungemana821af832016-04-14 09:44:34 -0700186namespace {
187
bungeman82a455f2016-04-14 08:04:45 -0700188class SkEmptyTypeface : public SkTypeface {
189public:
bungemane3aea102016-07-13 05:16:58 -0700190 static sk_sp<SkTypeface> Create() { return sk_sp<SkTypeface>(new SkEmptyTypeface()); }
bungeman82a455f2016-04-14 08:04:45 -0700191protected:
bungemane3aea102016-07-13 05:16:58 -0700192 SkEmptyTypeface() : SkTypeface(SkFontStyle(), true) { }
bungeman82a455f2016-04-14 08:04:45 -0700193
194 SkStreamAsset* onOpenStream(int* ttcIndex) const override { return nullptr; }
195 SkScalerContext* onCreateScalerContext(const SkScalerContextEffects&,
196 const SkDescriptor*) const override {
197 return nullptr;
198 }
199 void onFilterRec(SkScalerContextRec*) const override { }
Hal Canary209e4b12017-05-04 14:23:55 -0400200 std::unique_ptr<SkAdvancedTypefaceMetrics> onGetAdvancedMetrics() const override {
201 return nullptr;
202 }
bungeman82a455f2016-04-14 08:04:45 -0700203 void onGetFontDescriptor(SkFontDescriptor*, bool*) const override { }
204 virtual int onCharsToGlyphs(const void* chars, Encoding encoding,
205 uint16_t glyphs[], int glyphCount) const override {
206 SK_ABORT("unimplemented");
207 return 0;
208 }
Mike Kleinfc6c37b2016-09-27 09:34:10 -0400209 int onCountGlyphs() const override { return 0; }
210 int onGetUPEM() const override { return 0; }
bungeman82a455f2016-04-14 08:04:45 -0700211 void onGetFamilyName(SkString* familyName) const override { familyName->reset(); }
212 SkTypeface::LocalizedStrings* onCreateFamilyNameIterator() const override {
213 SK_ABORT("unimplemented");
214 return nullptr;
Mike Kleinfc6c37b2016-09-27 09:34:10 -0400215 }
Ben Wagnerfc497342017-02-24 11:15:26 -0500216 int onGetVariationDesignPosition(SkFontArguments::VariationPosition::Coordinate coordinates[],
217 int coordinateCount) const override
218 {
219 return 0;
220 }
bungeman82a455f2016-04-14 08:04:45 -0700221 int onGetTableTags(SkFontTableTag tags[]) const override { return 0; }
222 size_t onGetTableData(SkFontTableTag, size_t, size_t, void*) const override { return 0; }
223};
224
bungemana821af832016-04-14 09:44:34 -0700225}
226
bungeman82a455f2016-04-14 08:04:45 -0700227static bool count_proc(SkTypeface* face, void* ctx) {
228 int* count = static_cast<int*>(ctx);
229 *count = *count + 1;
230 return false;
231}
232static int count(skiatest::Reporter* reporter, const SkTypefaceCache& cache) {
233 int count = 0;
234 SkTypeface* none = cache.findByProcAndRef(count_proc, &count);
235 REPORTER_ASSERT(reporter, none == nullptr);
236 return count;
237}
238
239DEF_TEST(TypefaceCache, reporter) {
bungemane3aea102016-07-13 05:16:58 -0700240 sk_sp<SkTypeface> t1(SkEmptyTypeface::Create());
bungeman82a455f2016-04-14 08:04:45 -0700241 {
242 SkTypefaceCache cache;
243 REPORTER_ASSERT(reporter, count(reporter, cache) == 0);
244 {
bungemane3aea102016-07-13 05:16:58 -0700245 sk_sp<SkTypeface> t0(SkEmptyTypeface::Create());
bungeman82a455f2016-04-14 08:04:45 -0700246 cache.add(t0.get());
247 REPORTER_ASSERT(reporter, count(reporter, cache) == 1);
248 cache.add(t1.get());
249 REPORTER_ASSERT(reporter, count(reporter, cache) == 2);
250 cache.purgeAll();
251 REPORTER_ASSERT(reporter, count(reporter, cache) == 2);
252 }
253 REPORTER_ASSERT(reporter, count(reporter, cache) == 2);
254 cache.purgeAll();
255 REPORTER_ASSERT(reporter, count(reporter, cache) == 1);
256 }
257 REPORTER_ASSERT(reporter, t1->unique());
258}