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 "include/core/SkStream.h"
9#include "include/core/SkString.h"
10#include "include/core/SkTypeface.h"
11#include "src/core/SkFontDescriptor.h"
12#include "src/core/SkOpts.h"
13#include "src/sfnt/SkOTUtils.h"
14#include "src/utils/SkUTF.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::MakeForFamilyNames(*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::MakeForFamilyNames(*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 "src/core/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