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