1/*
2 * Copyright 2015 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 "SkFontDescriptor.h"
9#include "SkOpts.h"
10#include "SkStream.h"
11#include "SkString.h"
12#include "SkTypeface.h"
13#include "SkUtils.h"
14#include "../sfnt/SkOTUtils.h"
15
16#include "SkWhitelistChecksums.inc"
17
18#define WHITELIST_DEBUG 0
19
20extern void WhitelistSerializeTypeface(const SkTypeface*, SkWStream* );
21sk_sp<SkTypeface> WhitelistDeserializeTypeface(SkStream* );
22extern bool CheckChecksums();
23extern bool GenerateChecksums();
24
25#if WHITELIST_DEBUG
26static bool timesNewRomanSerializedNameOnly = false;
27#endif
28
29#define SUBNAME_PREFIX "sk_"
30
31static bool font_name_is_local(const char* fontName, SkFontStyle style) {
32    if (!strcmp(fontName, "DejaVu Sans")) {
33        return true;
34    }
35    sk_sp<SkTypeface> defaultFace(SkTypeface::MakeFromName(nullptr, style));
36    sk_sp<SkTypeface> foundFace(SkTypeface::MakeFromName(fontName, style));
37    return defaultFace != foundFace;
38}
39
40static int whitelist_name_index(const SkTypeface* tf) {
41
42    SkString fontNameStr;
43    sk_sp<SkTypeface::LocalizedStrings> nameIter(
44        SkOTUtils::LocalizedStrings_NameTable::CreateForFamilyNames(*tf));
45    SkTypeface::LocalizedString familyNameLocalized;
46    while (nameIter->next(&familyNameLocalized)) {
47        fontNameStr = familyNameLocalized.fString;
48        // check against permissible list of names
49        for (int i = 0; i < whitelistCount; ++i) {
50            if (fontNameStr.equals(whitelist[i].fFontName)) {
51                return i;
52            }
53        }
54    }
55#if WHITELIST_DEBUG
56    sk_sp<SkTypeface::LocalizedStrings> debugIter(
57        SkOTUtils::LocalizedStrings_NameTable::CreateForFamilyNames(*tf));
58    while (debugIter->next(&familyNameLocalized)) {
59        SkDebugf("no match fontName=\"%s\"\n", familyNameLocalized.fString.c_str());
60    }
61#endif
62    return -1;
63}
64
65static uint32_t compute_checksum(const SkTypeface* tf) {
66    std::unique_ptr<SkFontData> fontData = tf->makeFontData();
67    if (!fontData) {
68        return 0;
69    }
70    SkStreamAsset* fontStream = fontData->getStream();
71    if (!fontStream) {
72        return 0;
73    }
74    SkTDArray<char> data;
75    size_t length = fontStream->getLength();
76    if (!length) {
77        return 0;
78    }
79    data.setCount((int) length);
80    if (!fontStream->peek(data.begin(), length)) {
81        return 0;
82    }
83    return SkOpts::hash(data.begin(), length);
84}
85
86static void serialize_sub(const char* fontName, SkFontStyle style, SkWStream* wstream) {
87    SkFontDescriptor desc;
88    SkString subName(SUBNAME_PREFIX);
89    subName.append(fontName);
90    const char* familyName = subName.c_str();
91    desc.setFamilyName(familyName);
92    desc.setStyle(style);
93    desc.serialize(wstream);
94#if WHITELIST_DEBUG
95    for (int i = 0; i < whitelistCount; ++i) {
96        if (!strcmp(fontName, whitelist[i].fFontName)) {
97            if (!whitelist[i].fSerializedSub) {
98                whitelist[i].fSerializedSub = true;
99                SkDebugf("%s %s\n", __FUNCTION__, familyName);
100            }
101            break;
102        }
103    }
104#endif
105}
106
107static bool is_local(const SkTypeface* tf) {
108    bool isLocal = false;
109    SkFontDescriptor desc;
110    tf->getFontDescriptor(&desc, &isLocal);
111    return isLocal;
112}
113
114static void serialize_full(const SkTypeface* tf, SkWStream* wstream) {
115    bool isLocal = false;
116    SkFontDescriptor desc;
117    tf->getFontDescriptor(&desc, &isLocal);
118
119    // Embed font data if it's a local font.
120    if (isLocal && !desc.hasFontData()) {
121        desc.setFontData(tf->makeFontData());
122    }
123    desc.serialize(wstream);
124}
125
126static void serialize_name_only(const SkTypeface* tf, SkWStream* wstream) {
127    bool isLocal = false;
128    SkFontDescriptor desc;
129    tf->getFontDescriptor(&desc, &isLocal);
130    SkASSERT(!isLocal);
131#if WHITELIST_DEBUG
132    const char* familyName = desc.getFamilyName();
133    if (familyName) {
134        if (!strcmp(familyName, "Times New Roman")) {
135            if (!timesNewRomanSerializedNameOnly) {
136                timesNewRomanSerializedNameOnly = true;
137                SkDebugf("%s %s\n", __FUNCTION__, familyName);
138            }
139        } else {
140            for (int i = 0; i < whitelistCount; ++i) {
141                if (!strcmp(familyName, whitelist[i].fFontName)) {
142                    if (!whitelist[i].fSerializedNameOnly) {
143                        whitelist[i].fSerializedNameOnly = true;
144                        SkDebugf("%s %s\n", __FUNCTION__, familyName);
145                    }
146                    break;
147                }
148            }
149        }
150    }
151#endif
152    desc.serialize(wstream);
153}
154
155void WhitelistSerializeTypeface(const SkTypeface* tf, SkWStream* wstream) {
156    if (!is_local(tf)) {
157        serialize_name_only(tf, wstream);
158        return;
159    }
160    int whitelistIndex = whitelist_name_index(tf);
161    if (whitelistIndex < 0) {
162        serialize_full(tf, wstream);
163        return;
164    }
165    const char* fontName = whitelist[whitelistIndex].fFontName;
166    if (!font_name_is_local(fontName, tf->fontStyle())) {
167#if WHITELIST_DEBUG
168        SkDebugf("name not found locally \"%s\" style=%d\n", fontName, tf->style());
169#endif
170        serialize_full(tf, wstream);
171        return;
172    }
173    uint32_t checksum = compute_checksum(tf);
174    if (whitelist[whitelistIndex].fChecksum != checksum) {
175#if WHITELIST_DEBUG
176        if (whitelist[whitelistIndex].fChecksum) {
177            SkDebugf("!!! checksum changed !!!\n");
178        }
179        SkDebugf("checksum updated\n");
180        SkDebugf("    { \"%s\", 0x%08x },\n", fontName, checksum);
181#endif
182        whitelist[whitelistIndex].fChecksum = checksum;
183    }
184    serialize_sub(fontName, tf->fontStyle(), wstream);
185}
186
187sk_sp<SkTypeface> WhitelistDeserializeTypeface(SkStream* stream) {
188    SkFontDescriptor desc;
189    if (!SkFontDescriptor::Deserialize(stream, &desc)) {
190        return nullptr;
191    }
192
193    std::unique_ptr<SkFontData> data = desc.detachFontData();
194    if (data) {
195        sk_sp<SkTypeface> typeface(SkTypeface::MakeFromFontData(std::move(data)));
196        if (typeface) {
197            return typeface;
198        }
199    }
200    const char* familyName = desc.getFamilyName();
201    if (!strncmp(SUBNAME_PREFIX, familyName, sizeof(SUBNAME_PREFIX) - 1)) {
202        familyName += sizeof(SUBNAME_PREFIX) - 1;
203    }
204    return SkTypeface::MakeFromName(familyName, desc.getStyle());
205}
206
207bool CheckChecksums() {
208    for (int i = 0; i < whitelistCount; ++i) {
209        const char* fontName = whitelist[i].fFontName;
210        sk_sp<SkTypeface> tf(SkTypeface::MakeFromName(fontName, SkFontStyle()));
211        uint32_t checksum = compute_checksum(tf.get());
212        if (whitelist[i].fChecksum != checksum) {
213            return false;
214        }
215    }
216    return true;
217}
218
219const char checksumFileName[] = "SkWhitelistChecksums.inc";
220
221const char checksumHeader[] =
222"/*"                                                                        "\n"
223" * Copyright 2015 Google Inc."                                             "\n"
224" *"                                                                        "\n"
225" * Use of this source code is governed by a BSD-style license that can be" "\n"
226" * found in the LICENSE file."                                             "\n"
227" *"                                                                        "\n"
228" * %s() in %s generated %s."                                               "\n"
229" * Run 'whitelist_typefaces --generate' to create anew."                   "\n"
230" */"                                                                       "\n"
231""                                                                          "\n"
232"#include \"SkTDArray.h\""                                                  "\n"
233""                                                                          "\n"
234"struct Whitelist {"                                                        "\n"
235"    const char* fFontName;"                                                "\n"
236"    uint32_t fChecksum;"                                                   "\n"
237"    bool fSerializedNameOnly;"                                             "\n"
238"    bool fSerializedSub;"                                                  "\n"
239"};"                                                                        "\n"
240""                                                                          "\n"
241"static Whitelist whitelist[] = {"                                          "\n";
242
243const char checksumEntry[] =
244"    { \"%s\", 0x%08x, false, false },"                                     "\n";
245
246const char checksumTrailer[] =
247"};"                                                                        "\n"
248""                                                                          "\n"
249"static const int whitelistCount = (int) SK_ARRAY_COUNT(whitelist);"        "\n";
250
251
252#include "SkOSFile.h"
253
254bool GenerateChecksums() {
255    FILE* file = sk_fopen(checksumFileName, kWrite_SkFILE_Flag);
256    if (!file) {
257        SkDebugf("Can't open %s for writing.\n", checksumFileName);
258        return false;
259    }
260    SkString line;
261    line.printf(checksumHeader, __FUNCTION__, __FILE__, checksumFileName);
262    sk_fwrite(line.c_str(), line.size(), file);
263    for (int i = 0; i < whitelistCount; ++i) {
264        const char* fontName = whitelist[i].fFontName;
265        sk_sp<SkTypeface> tf(SkTypeface::MakeFromName(fontName, SkFontStyle()));
266        uint32_t checksum = compute_checksum(tf.get());
267        line.printf(checksumEntry, fontName, checksum);
268        sk_fwrite(line.c_str(), line.size(), file);
269    }
270    sk_fwrite(checksumTrailer, sizeof(checksumTrailer) - 1, file);
271    sk_fclose(file);
272    return true;
273}
274