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