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/SkFontMgr.h"
9 #include "include/core/SkFontStyle.h"
10 #include "include/core/SkRefCnt.h"
11 #include "include/core/SkString.h"
12 #include "include/core/SkTypeface.h"
13 #include "include/core/SkTypes.h"
14 #include "include/private/base/SkDebug.h"
15 #include "include/private/base/SkTemplates.h"
16 #include "src/base/SkEndian.h"
17 #include "src/sfnt/SkOTTable_name.h"
18 #include "tests/Test.h"
19 #include "tools/flags/CommandLineFlags.h"
20 #include "tools/fonts/FontToolUtils.h"
21
22 #include <algorithm>
23 #include <cstddef>
24 #include <cstdint>
25 #include <cstring>
26
27 using namespace skia_private;
28
29 namespace {
30
31 template <size_t R, size_t D> struct Format0NameTable {
32 SkOTTableName header;
33 SkOTTableName::Record nameRecord[R];
34 char data[D];
35 };
36
37 template <size_t R, size_t L, size_t D> struct Format1NameTable {
38 SkOTTableName header;
39 SkOTTableName::Record nameRecord[R];
40 struct {
41 SkOTTableName::Format1Ext header;
42 SkOTTableName::Format1Ext::LangTagRecord langTagRecord[L];
43 } format1ext;
44 char data[D];
45 };
46
47 typedef Format0NameTable<1, 9> SimpleFormat0NameTable;
48 constexpr SimpleFormat0NameTable simpleFormat0NameTable = {
49 /*header*/ {
50 /*format*/ SkOTTableName::format_0,
51 /*count*/ SkTEndianSwap16<1>::value,
52 /*stringOffset*/ SkTEndianSwap16<offsetof(SimpleFormat0NameTable, data)>::value,
53 },
54 /*nameRecord[]*/ {
55 /*Record*/ {
56 /*platformID*/ { SkOTTableName::Record::PlatformID::Windows },
57 /*encodingID*/ { SkOTTableName::Record::EncodingID::Windows::UnicodeBMPUCS2 },
58 /*languageID*/ { SkOTTableName::Record::LanguageID::Windows::English_UnitedStates },
59 /*nameID*/ { SkOTTableName::Record::NameID::Predefined::FontFamilyName },
60 /*length*/ SkTEndianSwap16<8>::value,
61 /*offset*/ SkTEndianSwap16<0>::value,
62 }
63 },
64 /*data*/ "\x0" "T" "\x0" "e" "\x0" "s" "\x0" "t",
65 };
66
67 typedef Format1NameTable<1, 1, 19> SimpleFormat1NameTable;
68 constexpr SimpleFormat1NameTable simpleFormat1NameTable = {
69 /*header*/ {
70 /*format*/ SkOTTableName::format_1,
71 /*count*/ SkTEndianSwap16<1>::value,
72 /*stringOffset*/ SkTEndianSwap16<offsetof(SimpleFormat1NameTable, data)>::value,
73 },
74 /*nameRecord[]*/ {
75 /*Record*/ {
76 /*platformID*/ { SkOTTableName::Record::PlatformID::Windows },
77 /*encodingID*/ { SkOTTableName::Record::EncodingID::Windows::UnicodeBMPUCS2 },
78 /*languageID*/ { SkTEndianSwap16<0x8000 + 0>::value },
79 /*nameID*/ { SkOTTableName::Record::NameID::Predefined::FontFamilyName },
80 /*length*/ SkTEndianSwap16<8>::value,
81 /*offset*/ SkTEndianSwap16<0>::value,
82 }
83 },
84 /*format1ext*/ {
85 /*header*/ {
86 /*langTagCount*/ SkTEndianSwap16<1>::value,
87 },
88 /*langTagRecord[]*/ {
89 /*LangTagRecord*/ {
90 /*length*/ SkTEndianSwap16<10>::value,
91 /*offset*/ SkTEndianSwap16<8>::value,
92 },
93 },
94 },
95 /*data*/ "\x0" "T" "\x0" "e" "\x0" "s" "\x0" "t"
96 "\x0" "e" "\x0" "n" "\x0" "-" "\x0" "U" "\x0" "S",
97 };
98
99 struct FontNamesTest {
100 const uint8_t* data;
101 size_t size;
102 SkOTTableName::Record::NameID nameID;
103 size_t nameCount;
104 struct {
105 const char* name;
106 const char* language;
107 } names[10];
108
109 } tests[] = {
110 {
111 reinterpret_cast<const uint8_t*>(&simpleFormat0NameTable),
112 sizeof(simpleFormat0NameTable),
113 { SkOTTableName::Record::NameID::Predefined::FontFamilyName },
114 1,
115 {
116 { "Test", "en-US" },
117 },
118 },
119 {
120 reinterpret_cast<const uint8_t*>(&simpleFormat1NameTable),
121 sizeof(simpleFormat1NameTable),
122 { SkOTTableName::Record::NameID::Predefined::FontFamilyName },
123 1,
124 {
125 { "Test", "en-US" },
126 },
127 },
128 };
129
test_synthetic(skiatest::Reporter * reporter,bool verbose)130 static void test_synthetic(skiatest::Reporter* reporter, bool verbose) {
131 for (const auto& test : tests) {
132 SkOTTableName::Iterator iter(test.data, test.size, test.nameID.predefined.value);
133 SkOTTableName::Iterator::Record record;
134 size_t nameIndex = 0;
135 while (nameIndex < test.nameCount && iter.next(record)) {
136 REPORTER_ASSERT(reporter,
137 strcmp(test.names[nameIndex].name, record.name.c_str()) == 0,
138 "Name did not match.");
139
140 REPORTER_ASSERT(reporter,
141 strcmp(test.names[nameIndex].language, record.language.c_str()) == 0,
142 "Language did not match.");
143
144 if (verbose) {
145 SkDebugf("%s <%s>\n", record.name.c_str(), record.language.c_str());
146 }
147
148 ++nameIndex;
149 }
150
151 REPORTER_ASSERT(reporter, nameIndex == test.nameCount, "Fewer names than expected.");
152
153 REPORTER_ASSERT(reporter, !iter.next(record), "More names than expected.");
154 }
155 }
156
157 #define MAX_FAMILIES 1000
test_systemfonts(skiatest::Reporter * reporter,bool verbose)158 static void test_systemfonts(skiatest::Reporter* reporter, bool verbose) {
159 static const SkFontTableTag nameTag = SkSetFourByteTag('n','a','m','e');
160
161 sk_sp<SkFontMgr> fm(ToolUtils::TestFontMgr());
162 SkASSERT_RELEASE(fm);
163 int count = std::min(fm->countFamilies(), MAX_FAMILIES);
164 for (int i = 0; i < count; ++i) {
165 SkString fname;
166 fm->getFamilyName(i, &fname);
167
168 sk_sp<SkFontStyleSet> set(fm->createStyleSet(i));
169 for (int j = 0; j < set->count(); ++j) {
170 SkString sname;
171 SkFontStyle fs;
172 set->getStyle(j, &fs, &sname);
173
174 sk_sp<SkTypeface> typeface(set->createTypeface(j));
175 if (!typeface) {
176 REPORTER_ASSERT(reporter, typeface.get(),
177 "Could not create %s %s.", fname.c_str(), sname.c_str());
178 continue;
179 }
180
181 SkString familyName;
182 typeface->getFamilyName(&familyName);
183 if (verbose) {
184 SkDebugf("[%s]\n", familyName.c_str());
185 }
186
187 sk_sp<SkTypeface::LocalizedStrings> familyNamesIter(
188 typeface->createFamilyNameIterator());
189 SkTypeface::LocalizedString familyNameLocalized;
190 while (familyNamesIter->next(&familyNameLocalized)) {
191 if (verbose) {
192 SkDebugf("(%s) <%s>\n", familyNameLocalized.fString.c_str(),
193 familyNameLocalized.fLanguage.c_str());
194 }
195 }
196
197 size_t nameTableSize = typeface->getTableSize(nameTag);
198 if (0 == nameTableSize) {
199 continue;
200 }
201 AutoTMalloc<uint8_t> nameTableData(nameTableSize);
202 size_t copied = typeface->getTableData(nameTag, 0, nameTableSize, nameTableData.get());
203 if (copied != nameTableSize) {
204 continue;
205 }
206
207 SkOTTableName::Iterator::Record record;
208 SkOTTableName::Iterator familyNameIter(nameTableData.get(), nameTableSize,
209 SkOTTableName::Record::NameID::Predefined::FontFamilyName);
210 while (familyNameIter.next(record)) {
211 REPORTER_ASSERT(
212 reporter,
213 SkOTTableName::Record::NameID::Predefined::FontFamilyName == record.type,
214 "Requested family name, got something else.");
215 if (verbose) {
216 SkDebugf("{%s} <%s>\n", record.name.c_str(), record.language.c_str());
217 }
218 }
219
220 SkOTTableName::Iterator styleNameIter(nameTableData.get(), nameTableSize,
221 SkOTTableName::Record::NameID::Predefined::FontSubfamilyName);
222 while (styleNameIter.next(record)) {
223 REPORTER_ASSERT(
224 reporter,
225 SkOTTableName::Record::NameID::Predefined::FontSubfamilyName == record.type,
226 "Requested subfamily name, got something else.");
227 if (verbose) {
228 SkDebugf("{{%s}} <%s>\n", record.name.c_str(), record.language.c_str());
229 }
230 }
231
232 if (verbose) {
233 SkDebugf("\n");
234 }
235 }
236 }
237 }
238
239 } // namespace
240
241 static DEFINE_bool(verboseFontNames, false, "verbose FontNames test.");
242
DEF_TEST(FontNames,reporter)243 DEF_TEST(FontNames, reporter) {
244 test_synthetic(reporter, FLAGS_verboseFontNames);
245 test_systemfonts(reporter, FLAGS_verboseFontNames);
246 }
247