• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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