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 "include/core/SkStream.h"
9 #include "include/core/SkTypeface.h"
10 #include "include/ports/SkTypeface_fontations.h"
11 #include "tests/Test.h"
12 #include "tools/Resources.h"
13
14 #include <memory>
15
16 namespace {
17 const char kFontResource[] = "fonts/ahem.ttf";
18 const char kTtcResource[] = "fonts/test.ttc";
19 const char kVariableResource[] = "fonts/test_glyphs-glyf_colr_1_variable.ttf";
20 constexpr size_t kNumVariableAxes = 44;
21
22 struct AxisExpectation {
23 SkFourByteTag tag;
24 float minValue;
25 float defValue;
26 float maxValue;
27 } axisExpectations[] = {
28 {SkSetFourByteTag('S', 'W', 'P', 'S'), -90.0, 0.0, 90.0},
29 {SkSetFourByteTag('S', 'W', 'P', 'E'), -90.0, 0.0, 90.0},
30 {SkSetFourByteTag('S', 'W', 'C', '1'), -2.0, 0.0, 2.0},
31 {SkSetFourByteTag('S', 'W', 'C', '2'), -2.0, 0.0, 2.0},
32 {SkSetFourByteTag('S', 'W', 'C', '3'), -2.0, 0.0, 2.0},
33 {SkSetFourByteTag('S', 'W', 'C', '4'), -2.0, 0.0, 2.0},
34 {SkSetFourByteTag('S', 'C', 'O', 'X'), -200., 0.0, 200.},
35 {SkSetFourByteTag('S', 'C', 'O', 'Y'), -200., 0.0, 200.},
36 {SkSetFourByteTag('S', 'C', 'S', 'X'), -2.0, 0.0, 1.9999389648437},
37 {SkSetFourByteTag('S', 'C', 'S', 'Y'), -2.0, 0.0, 1.9999389648437},
38 {SkSetFourByteTag('G', 'R', 'X', '0'), -1000, 0.0, 1000},
39 {SkSetFourByteTag('G', 'R', 'Y', '0'), -1000, 0.0, 1000},
40 {SkSetFourByteTag('G', 'R', 'X', '1'), -1000, 0.0, 1000},
41 {SkSetFourByteTag('G', 'R', 'Y', '1'), -1000, 0.0, 1000},
42 {SkSetFourByteTag('G', 'R', 'X', '2'), -1000, 0.0, 1000},
43 {SkSetFourByteTag('G', 'R', 'Y', '2'), -1000, 0.0, 1000},
44 {SkSetFourByteTag('G', 'R', 'R', '0'), -1000, 0.0, 1000},
45 {SkSetFourByteTag('G', 'R', 'R', '1'), -1000, 0.0, 1000},
46 {SkSetFourByteTag('C', 'O', 'L', '1'), -2.0, 0.0, 2.0},
47 {SkSetFourByteTag('C', 'O', 'L', '2'), -2.0, 0.0, 2.0},
48 {SkSetFourByteTag('C', 'O', 'L', '3'), -2.0, 0.0, 2.0},
49 {SkSetFourByteTag('R', 'O', 'T', 'A'), 0.0, 0.0, 539.989013671875},
50 {SkSetFourByteTag('R', 'O', 'T', 'X'), -500.0, 0.0, 500.0},
51 {SkSetFourByteTag('R', 'O', 'T', 'Y'), -500.0, 0.0, 500.0},
52 {SkSetFourByteTag('S', 'K', 'X', 'A'), -90.0, 0.0, 90.0},
53 {SkSetFourByteTag('S', 'K', 'Y', 'A'), -90.0, 0.0, 90.0},
54 {SkSetFourByteTag('S', 'K', 'C', 'X'), -500.0, 0.0, 500.0},
55 {SkSetFourByteTag('S', 'K', 'C', 'Y'), -500.0, 0.0, 500.0},
56 {SkSetFourByteTag('T', 'R', 'X', 'X'), -2.0, 0.0, 2.0},
57 {SkSetFourByteTag('T', 'R', 'Y', 'X'), -2.0, 0.0, 2.0},
58 {SkSetFourByteTag('T', 'R', 'X', 'Y'), -2.0, 0.0, 2.0},
59 {SkSetFourByteTag('T', 'R', 'Y', 'Y'), -2.0, 0.0, 2.0},
60 {SkSetFourByteTag('T', 'R', 'D', 'X'), -500.0, 0.0, 500.0},
61 {SkSetFourByteTag('T', 'R', 'D', 'Y'), -500.0, 0.0, 500.0},
62 {SkSetFourByteTag('T', 'L', 'D', 'X'), -500.0, 0.0, 500.0},
63 {SkSetFourByteTag('T', 'L', 'D', 'Y'), -500.0, 0.0, 500.0},
64 {SkSetFourByteTag('C', 'L', 'X', 'I'), -500.0, 0.0, 500.0},
65 {SkSetFourByteTag('C', 'L', 'Y', 'I'), -500.0, 0.0, 500.0},
66 {SkSetFourByteTag('C', 'L', 'X', 'A'), -500.0, 0.0, 500.0},
67 {SkSetFourByteTag('C', 'L', 'Y', 'A'), -500.0, 0.0, 500.0},
68 {SkSetFourByteTag('C', 'L', 'I', 'O'), -500.0, 0.0, 500.0},
69 {SkSetFourByteTag('A', 'P', 'H', '1'), -1.0, 0.0, 0.0},
70 {SkSetFourByteTag('A', 'P', 'H', '2'), -1.0, 0.0, 0.0},
71 {SkSetFourByteTag('A', 'P', 'H', '3'), -1.0, 0.0, 0.0},
72 };
73 } // namespace
74
DEF_TEST(Fontations_DoNotMakeFromNull,reporter)75 DEF_TEST(Fontations_DoNotMakeFromNull, reporter) {
76 std::unique_ptr<SkStreamAsset> nullStream = SkMemoryStream::MakeDirect(nullptr, 0);
77 sk_sp<SkTypeface> probeTypeface(
78 SkTypeface_Make_Fontations(std::move(nullStream), SkFontArguments()));
79 REPORTER_ASSERT(reporter, !probeTypeface);
80 }
81
DEF_TEST(Fontations_DoNotMakeFromNonSfnt,reporter)82 DEF_TEST(Fontations_DoNotMakeFromNonSfnt, reporter) {
83 char notAnSfnt[] = "I_AM_NOT_AN_SFNT";
84 std::unique_ptr<SkStreamAsset> notSfntStream =
85 SkMemoryStream::MakeDirect(notAnSfnt, std::size(notAnSfnt));
86 sk_sp<SkTypeface> probeTypeface(
87 SkTypeface_Make_Fontations(std::move(notSfntStream), SkFontArguments()));
88 REPORTER_ASSERT(reporter, !probeTypeface);
89 }
90
DEF_TEST(Fontations_MakeFromFont,reporter)91 DEF_TEST(Fontations_MakeFromFont, reporter) {
92 sk_sp<SkTypeface> probeTypeface(
93 SkTypeface_Make_Fontations(GetResourceAsStream(kFontResource), SkFontArguments()));
94 REPORTER_ASSERT(reporter, probeTypeface);
95 }
96
DEF_TEST(Fontations_MakeFromCollection,reporter)97 DEF_TEST(Fontations_MakeFromCollection, reporter) {
98 sk_sp<SkTypeface> probeTypeface(
99 SkTypeface_Make_Fontations(GetResourceAsStream(kTtcResource), SkFontArguments()));
100 REPORTER_ASSERT(reporter, probeTypeface);
101 }
102
DEF_TEST(Fontations_MakeFromCollectionNonNullIndex,reporter)103 DEF_TEST(Fontations_MakeFromCollectionNonNullIndex, reporter) {
104 SkFontArguments args;
105 args.setCollectionIndex(1);
106 sk_sp<SkTypeface> probeTypeface(
107 SkTypeface_Make_Fontations(GetResourceAsStream(kTtcResource), args));
108 REPORTER_ASSERT(reporter, probeTypeface);
109 }
110
DEF_TEST(Fontations_DoNotMakeFromCollection_Invalid_Index,reporter)111 DEF_TEST(Fontations_DoNotMakeFromCollection_Invalid_Index, reporter) {
112 SkFontArguments args;
113 args.setCollectionIndex(1000);
114 sk_sp<SkTypeface> probeTypeface(
115 SkTypeface_Make_Fontations(GetResourceAsStream(kTtcResource), args));
116 REPORTER_ASSERT(reporter, !probeTypeface);
117 }
118
DEF_TEST(Fontations_TableData,reporter)119 DEF_TEST(Fontations_TableData, reporter) {
120 constexpr size_t kNameTableSize = 11310;
121 constexpr size_t kTestOffset = 1310;
122 constexpr size_t kTestLength = 500;
123 char destBuffer[kNameTableSize] = {0};
124 sk_sp<SkTypeface> testTypeface(
125 SkTypeface_Make_Fontations(GetResourceAsStream(kFontResource), SkFontArguments()));
126 SkFourByteTag nameTableTag = SkSetFourByteTag('n', 'a', 'm', 'e');
127 SkFourByteTag nonExistantTag = SkSetFourByteTag('0', 'X', '0', 'X');
128
129 // Getting size without buffer.
130 REPORTER_ASSERT(reporter,
131 testTypeface->getTableData(nameTableTag, 0, kNameTableSize, nullptr) ==
132 kNameTableSize);
133 // Reading full table.
134 REPORTER_ASSERT(reporter,
135 testTypeface->getTableData(nameTableTag, 0, kNameTableSize, destBuffer) ==
136 kNameTableSize);
137 // Reading restricted length.
138 REPORTER_ASSERT(
139 reporter,
140 testTypeface->getTableData(nameTableTag, 0, kTestLength, destBuffer) == kTestLength);
141 REPORTER_ASSERT(reporter,
142 testTypeface->getTableData(
143 nameTableTag, kTestOffset, kTestLength, destBuffer) == kTestLength);
144 // Reading at an offset.
145 REPORTER_ASSERT(
146 reporter,
147 testTypeface->getTableData(nameTableTag, kTestOffset, kNameTableSize, destBuffer) ==
148 kNameTableSize - kTestOffset);
149
150 // Reading from offset past table.
151 REPORTER_ASSERT(reporter,
152 testTypeface->getTableData(
153 nameTableTag, kNameTableSize, kNameTableSize, destBuffer) == 0);
154 REPORTER_ASSERT(reporter,
155 testTypeface->getTableData(nameTableTag, kNameTableSize, 0, nullptr) == 0);
156 // Reading one byte before end of table.
157 REPORTER_ASSERT(reporter,
158 testTypeface->getTableData(
159 nameTableTag, kNameTableSize - 1, kNameTableSize, destBuffer) == 1);
160 // Trying to start reading at an offset past table start.
161 REPORTER_ASSERT(reporter,
162 testTypeface->getTableData(nameTableTag, 0, kNameTableSize + 10, destBuffer) ==
163 kNameTableSize);
164 // Restricting length without target buffer.
165 REPORTER_ASSERT(reporter,
166 testTypeface->getTableData(nameTableTag, 0, kTestLength, nullptr) ==
167 kTestLength);
168
169 // Trying to access non-existant table.
170 REPORTER_ASSERT(reporter,
171 testTypeface->getTableData(nonExistantTag, 0, kNameTableSize, destBuffer) ==
172 0);
173 REPORTER_ASSERT(reporter,
174 testTypeface->getTableData(nonExistantTag, 0, 0, nullptr) ==
175 0);
176 REPORTER_ASSERT(reporter,
177 testTypeface->getTableData(nonExistantTag, kTestOffset, 0, nullptr) == 0);
178 }
179
DEF_TEST(Fontations_TableTags,reporter)180 DEF_TEST(Fontations_TableTags, reporter) {
181 constexpr size_t kNumTags = 11;
182 SkFourByteTag tagsBuffer[kNumTags] = {0};
183 sk_sp<SkTypeface> testTypeface(
184 SkTypeface_Make_Fontations(GetResourceAsStream(kFontResource), SkFontArguments()));
185 SkFourByteTag firstTag = SkSetFourByteTag('O', 'S', '/', '2');
186 SkFourByteTag lastTag = SkSetFourByteTag('p', 'o', 's', 't');
187
188 REPORTER_ASSERT(reporter, testTypeface->getTableTags(nullptr) == kNumTags);
189
190 REPORTER_ASSERT(reporter, testTypeface->getTableTags(tagsBuffer) == kNumTags);
191 REPORTER_ASSERT(reporter, tagsBuffer[0] == firstTag);
192 REPORTER_ASSERT(reporter, tagsBuffer[kNumTags - 1] == lastTag);
193 }
194
DEF_TEST(Fontations_VariationPosition,reporter)195 DEF_TEST(Fontations_VariationPosition, reporter) {
196 sk_sp<SkTypeface> variableTypeface(
197 SkTypeface_Make_Fontations(GetResourceAsStream(kVariableResource), SkFontArguments()));
198 // Everything at default.
199 const int numAxes = variableTypeface->getVariationDesignPosition(nullptr, 0);
200 REPORTER_ASSERT(reporter, numAxes == kNumVariableAxes, "numAxes: %d", numAxes);
201
202 SkFontArguments::VariationPosition::Coordinate kSwpsCoordinateFirst = {SkSetFourByteTag('S', 'W', 'P', 'S'), 25};
203 SkFontArguments::VariationPosition::Coordinate kSwpsCoordinateSecond = {SkSetFourByteTag('S', 'W', 'P', 'S'), 55};
204 SkFontArguments::VariationPosition::Coordinate kSwpeCoordinate = {SkSetFourByteTag('S', 'W', 'P', 'E'), 45};
205 SkFontArguments::VariationPosition::Coordinate kInvalidCoordinate = {SkSetFourByteTag('_', '_', '_', '_'), 0};
206
207 // 'SWPS' and 'SWPE' exist. Second 'SWPS' should override first, invalid tag should be stripped.
208 SkFontArguments::VariationPosition::Coordinate cloneCoordinates[4] = {
209 kSwpsCoordinateFirst, kSwpsCoordinateSecond, kSwpeCoordinate, kInvalidCoordinate};
210
211 SkFontArguments::VariationPosition clonePosition;
212 clonePosition.coordinates = cloneCoordinates;
213 clonePosition.coordinateCount = 4;
214
215 sk_sp<SkTypeface> cloneTypeface = variableTypeface->makeClone(
216 SkFontArguments().setVariationDesignPosition(clonePosition));
217 const int cloneNumAxes = cloneTypeface->getVariationDesignPosition(nullptr, 0);
218 REPORTER_ASSERT(reporter, cloneNumAxes == kNumVariableAxes, "clonedNumAxes: %d", cloneNumAxes);
219
220 SkFontArguments::VariationPosition::Coordinate retrieveCoordinates[kNumVariableAxes] = {};
221
222 // Error when providing too little space.
223 const int badClonedNumAxes = cloneTypeface->getVariationDesignPosition(retrieveCoordinates, 1);
224 REPORTER_ASSERT(reporter, badClonedNumAxes == -1, "badClonedNumAxes: %d", badClonedNumAxes);
225
226 const int retrievedClonedNumAxes =
227 cloneTypeface->getVariationDesignPosition(retrieveCoordinates, kNumVariableAxes);
228 REPORTER_ASSERT(reporter, retrievedClonedNumAxes == kNumVariableAxes,
229 "retrievedClonedNumAxes: %d", retrievedClonedNumAxes);
230 REPORTER_ASSERT(reporter,
231 retrieveCoordinates[0].axis == kSwpsCoordinateSecond.axis &&
232 retrieveCoordinates[0].value == kSwpsCoordinateSecond.value);
233 REPORTER_ASSERT(reporter,
234 retrieveCoordinates[1].axis == kSwpeCoordinate.axis &&
235 retrieveCoordinates[1].value == kSwpeCoordinate.value);
236 }
237
DEF_TEST(Fontations_VariationParameters,reporter)238 DEF_TEST(Fontations_VariationParameters, reporter) {
239 sk_sp<SkTypeface> variableTypeface(
240 SkTypeface_Make_Fontations(GetResourceAsStream(kVariableResource), SkFontArguments()));
241 REPORTER_ASSERT(reporter,
242 variableTypeface->getVariationDesignParameters(nullptr, 0) == kNumVariableAxes);
243
244 SkFontParameters::Variation::Axis axes[kNumVariableAxes] = {};
245 REPORTER_ASSERT(reporter,
246 variableTypeface->getVariationDesignParameters(axes, kNumVariableAxes) ==
247 kNumVariableAxes);
248
249 for (size_t i = 0; i < kNumVariableAxes; ++i) {
250 REPORTER_ASSERT(reporter, axes[i].tag == axisExpectations[i].tag);
251 REPORTER_ASSERT(reporter, axes[i].min == axisExpectations[i].minValue);
252 REPORTER_ASSERT(reporter, axes[i].def == axisExpectations[i].defValue);
253 REPORTER_ASSERT(reporter, axes[i].max == axisExpectations[i].maxValue);
254 }
255 }
256
DEF_TEST(Fontations_VariationParameters_BufferTooSmall,reporter)257 DEF_TEST(Fontations_VariationParameters_BufferTooSmall, reporter) {
258 sk_sp<SkTypeface> variableTypeface(
259 SkTypeface_Make_Fontations(GetResourceAsStream(kVariableResource), SkFontArguments()));
260 REPORTER_ASSERT(reporter,
261 variableTypeface->getVariationDesignParameters(nullptr, 0) == kNumVariableAxes);
262
263 constexpr size_t kArrayTooSmall = 3;
264 SkFontParameters::Variation::Axis axes[kArrayTooSmall] = {};
265 REPORTER_ASSERT(reporter,
266 variableTypeface->getVariationDesignParameters(axes, kArrayTooSmall) == -1);
267 }
268