Index: src/utils/SkWhitelistTypefaces.cpp |
diff --git a/src/utils/SkWhitelistTypefaces.cpp b/src/utils/SkWhitelistTypefaces.cpp |
new file mode 100644 |
index 0000000000000000000000000000000000000000..99f3644a97f6ef6376739ae761e039fe9ef4c5b6 |
--- /dev/null |
+++ b/src/utils/SkWhitelistTypefaces.cpp |
@@ -0,0 +1,335 @@ |
+/* |
+ * Copyright 2015 Google Inc. |
+ * |
+ * Use of this source code is governed by a BSD-style license that can be |
+ * found in the LICENSE file. |
+ */ |
+ |
+#include "SkChecksum.h" |
+#include "SkFontDescriptor.h" |
+#include "SkStream.h" |
+#include "SkString.h" |
+#include "SkTypeface.h" |
+#include "SkUtils.h" |
+ |
+#include "SkWhitelistChecksums.cpp" |
+ |
+#define WHITELIST_DEBUG 0 |
+ |
+extern void WhitelistSerializeTypeface(const SkTypeface*, SkWStream* ); |
+extern SkTypeface* WhitelistDeserializeTypeface(SkStream* ); |
+extern bool CheckChecksums(); |
+extern bool GenerateChecksums(); |
+ |
+#if WHITELIST_DEBUG |
+static bool timesNewRomanSerializedNameOnly = false; |
+#endif |
+ |
+struct NameRecord { |
+ unsigned short fPlatformID; |
+ unsigned short fEncodingID; |
+ unsigned short fLanguageID; |
+ unsigned short fNameID; |
+ unsigned short fLength; |
+ unsigned short fOffset; |
+}; |
+ |
+struct NameTable { |
bungeman-skia
2015/08/31 15:59:32
Why not SkOTTableName?
|
+ unsigned short fFormat; |
+ unsigned short fCount; |
+ unsigned short fStringOffset; |
+ NameRecord fRecord[1]; |
+}; |
+ |
+#define SUBNAME_PREFIX "sk_" |
+ |
+static unsigned short swizzle(unsigned short x) { |
+ return x << 8 | (x >> 8 & 0xff); |
+} |
+ |
+static bool font_name_is_local(const char* fontName, SkTypeface::Style style) { |
+ if (!strcmp(fontName, "DejaVu Sans")) { |
+ return true; |
+ } |
+ SkTypeface* defaultFace = SkTypeface::CreateFromName(nullptr, style); |
+ SkTypeface* foundFace = SkTypeface::CreateFromName(fontName, style); |
+ return defaultFace != foundFace; |
+} |
+ |
+static int name_table(const NameTable* nameTable, int tableIndex, const char** stringLocPtr) { |
+ int nameTableCount = swizzle(nameTable->fCount); |
+ for (int i = 0; i < nameTableCount; ++i) { |
+ const NameRecord* nameRecord = &nameTable->fRecord[i]; |
+ int recordNameID = swizzle(nameRecord->fNameID); |
+ if (recordNameID != tableIndex) { |
+ continue; |
+ } |
+ int stringLen = swizzle(nameRecord->fLength); |
+ if (!stringLen) { |
+ break; |
+ } |
+ int recordOffset = swizzle(nameRecord->fOffset); |
+ const char* stringLoc = (const char* ) nameTable + swizzle(nameTable->fStringOffset); |
+ stringLoc += recordOffset; |
+ *stringLocPtr = stringLoc; |
+ return stringLen; |
+ } |
+ return -1; |
+} |
+ |
+static int whitelist_name_index(const SkTypeface* tf) { |
+ static const SkFontTableTag nameTag = SkSetFourByteTag('n', 'a', 'm', 'e'); |
+ size_t nameSize = tf->getTableSize(nameTag); |
+ if (!nameSize) { |
+ return -1; |
+ } |
+ SkTDArray<char> name; |
+ name.setCount((int) nameSize); |
+ tf->getTableData(nameTag, 0, nameSize, name.begin()); |
+ const NameTable* nameTable = (const NameTable* ) name.begin(); |
+ const char* stringLoc; |
+ int stringLen = name_table(nameTable, 1, &stringLoc); |
bungeman-skia
2015/08/31 15:59:32
Any reason not to use SkOTTableName::Iterator or L
caryclark
2015/09/01 12:17:37
I'll switch over to LocalizedStrings_NameTable in
|
+ if (stringLen < 0) { |
+ stringLen = name_table(nameTable, 16, &stringLoc); |
+ } |
+ if (stringLen < 0) { |
+ stringLen = name_table(nameTable, 21, &stringLoc); |
+ } |
+ if (stringLen < 0) { |
+ return -1; |
+ } |
+ SkString fontNameStr; |
+ if (!*stringLoc) { |
+ stringLen /= 2; |
+ for (int i = 0; i < stringLen; ++i) { |
+ fontNameStr.appendUnichar(swizzle(((const uint16_t*) stringLoc)[i])); |
+ } |
+ } else { |
+ fontNameStr.resize(stringLen); |
+ strncpy(fontNameStr.writable_str(), stringLoc, stringLen); |
+ } |
+ // check against permissible list of names |
+ for (int i = 0; i < whitelistCount; ++i) { |
+ if (fontNameStr.equals(whitelist[i].fFontName)) { |
+ return i; |
+ } |
+ } |
+ for (int i = 0; i < whitelistCount; ++i) { |
+ if (fontNameStr.startsWith(whitelist[i].fFontName)) { |
+#if WHITELIST_DEBUG |
+ SkDebugf("partial match whitelist=\"%s\" fontName=\"%s\"\n", whitelist[i].fFontName, |
+ fontNameStr.c_str()); |
+#endif |
+ return -1; |
+ } |
+ } |
+#if WHITELIST_DEBUG |
+ SkDebugf("no match fontName=\"%s\"\n", fontNameStr.c_str()); |
+#endif |
+ return -1; |
+} |
+ |
+static uint32_t compute_checksum(const SkTypeface* tf) { |
+ SkFontData* fontData = tf->createFontData(); |
+ if (!fontData) { |
+ return 0; |
+ } |
+ SkStreamAsset* fontStream = fontData->getStream(); |
+ if (!fontStream) { |
+ return 0; |
+ } |
+ SkTDArray<char> data; |
+ size_t length = fontStream->getLength(); |
+ if (!length) { |
+ return 0; |
+ } |
+ data.setCount((int) length); |
+ if (!fontStream->peek(data.begin(), length)) { |
+ return 0; |
+ } |
+ return SkChecksum::Murmur3(data.begin(), length); |
+} |
+ |
+static void serialize_sub(const char* fontName, SkTypeface::Style style, SkWStream* wstream) { |
+ SkFontDescriptor desc(style); |
+ SkString subName(SUBNAME_PREFIX); |
+ subName.append(fontName); |
+ const char* familyName = subName.c_str(); |
+ desc.setFamilyName(familyName); |
+ desc.serialize(wstream); |
+#if WHITELIST_DEBUG |
+ for (int i = 0; i < whitelistCount; ++i) { |
+ if (!strcmp(fontName, whitelist[i].fFontName)) { |
+ if (!whitelist[i].fSerializedSub) { |
+ whitelist[i].fSerializedSub = true; |
+ SkDebugf("%s %s\n", __FUNCTION__, familyName); |
+ } |
+ break; |
+ } |
+ } |
+#endif |
+} |
+ |
+static bool is_local(const SkTypeface* tf) { |
+ bool isLocal = false; |
+ SkFontDescriptor desc(tf->style()); |
+ tf->getFontDescriptor(&desc, &isLocal); |
+ return isLocal; |
+} |
+ |
+static void serialize_full(const SkTypeface* tf, SkWStream* wstream) { |
+ bool isLocal = false; |
+ SkFontDescriptor desc(tf->style()); |
+ tf->getFontDescriptor(&desc, &isLocal); |
+ |
+ // Embed font data if it's a local font. |
+ if (isLocal && !desc.hasFontData()) { |
+ desc.setFontData(tf->createFontData()); |
+ } |
+ desc.serialize(wstream); |
+} |
+ |
+static void serialize_name_only(const SkTypeface* tf, SkWStream* wstream) { |
+ bool isLocal = false; |
+ SkFontDescriptor desc(tf->style()); |
+ tf->getFontDescriptor(&desc, &isLocal); |
+ SkASSERT(!isLocal); |
+#if WHITELIST_DEBUG |
+ const char* familyName = desc.getFamilyName(); |
+ if (familyName) { |
+ if (!strcmp(familyName, "Times New Roman")) { |
+ if (!timesNewRomanSerializedNameOnly) { |
+ timesNewRomanSerializedNameOnly = true; |
+ SkDebugf("%s %s\n", __FUNCTION__, familyName); |
+ } |
+ } else { |
+ for (int i = 0; i < whitelistCount; ++i) { |
+ if (!strcmp(familyName, whitelist[i].fFontName)) { |
+ if (!whitelist[i].fSerializedNameOnly) { |
+ whitelist[i].fSerializedNameOnly = true; |
+ SkDebugf("%s %s\n", __FUNCTION__, familyName); |
+ } |
+ break; |
+ } |
+ } |
+ } |
+ } |
+#endif |
+ desc.serialize(wstream); |
+} |
+ |
+void WhitelistSerializeTypeface(const SkTypeface* tf, SkWStream* wstream) { |
+ if (!is_local(tf)) { |
+ serialize_name_only(tf, wstream); |
+ return; |
+ } |
+ int whitelistIndex = whitelist_name_index(tf); |
+ if (whitelistIndex < 0) { |
+ serialize_full(tf, wstream); |
+ return; |
+ } |
+ const char* fontName = whitelist[whitelistIndex].fFontName; |
+ if (!font_name_is_local(fontName, tf->style())) { |
+#if WHITELIST_DEBUG |
+ SkDebugf("name not found locally \"%s\" style=%d\n", fontName, tf->style()); |
+#endif |
+ serialize_full(tf, wstream); |
+ return; |
+ } |
+ uint32_t checksum = compute_checksum(tf); |
+ if (whitelist[whitelistIndex].fChecksum != checksum) { |
+#if WHITELIST_DEBUG |
+ if (whitelist[whitelistIndex].fChecksum) { |
+ SkDebugf("!!! checksum changed !!!\n"); |
+ } |
+ SkDebugf("checksum updated\n"); |
+ SkDebugf(" { \"%s\", 0x%08x },\n", fontName, checksum); |
+#endif |
+ whitelist[whitelistIndex].fChecksum = checksum; |
+ } |
+ serialize_sub(fontName, tf->style(), wstream); |
+} |
+ |
+SkTypeface* WhitelistDeserializeTypeface(SkStream* stream) { |
+ SkFontDescriptor desc(stream); |
+ SkFontData* data = desc.detachFontData(); |
+ if (data) { |
+ SkTypeface* typeface = SkTypeface::CreateFromFontData(data); |
+ if (typeface) { |
+ return typeface; |
+ } |
+ } |
+ const char* familyName = desc.getFamilyName(); |
+ if (!strncmp(SUBNAME_PREFIX, familyName, sizeof(SUBNAME_PREFIX) - 1)) { |
+ familyName += sizeof(SUBNAME_PREFIX) - 1; |
+ } |
+ return SkTypeface::CreateFromName(desc.getFamilyName(), desc.getStyle()); |
+} |
+ |
+bool CheckChecksums() { |
+ for (int i = 0; i < whitelistCount; ++i) { |
+ const char* fontName = whitelist[i].fFontName; |
+ SkTypeface* tf = SkTypeface::CreateFromName(fontName, SkTypeface::kNormal); |
+ uint32_t checksum = compute_checksum(tf); |
+ if (whitelist[i].fChecksum != checksum) { |
+ return false; |
+ } |
+ } |
+ return true; |
+} |
+ |
+const char checksumFileName[] = "SkWhitelistChecksums.cpp"; |
+ |
+const char checksumHeader[] = |
+"/*" "\n" |
+" * Copyright 2015 Google Inc." "\n" |
+" *" "\n" |
+" * Use of this source code is governed by a BSD-style license that can be" "\n" |
+" * found in the LICENSE file." "\n" |
+" *" "\n" |
+" * %s() in %s generated %s." "\n" |
+" * Run 'whitelist_typefaces --generate' to create anew." "\n" |
+" */" "\n" |
+"" "\n" |
+"#include \"SkTDArray.h\"" "\n" |
+"" "\n" |
+"struct Whitelist {" "\n" |
+" const char* fFontName;" "\n" |
+" uint32_t fChecksum;" "\n" |
+" bool fSerializedNameOnly;" "\n" |
+" bool fSerializedSub;" "\n" |
+"};" "\n" |
+"" "\n" |
+"static Whitelist whitelist[] = {" "\n"; |
+ |
+const char checksumEntry[] = |
+" { \"%s\", 0x%08x, false, false }," "\n"; |
+ |
+const char checksumTrailer[] = |
+"};" "\n" |
+"" "\n" |
+"static const int whitelistCount = (int) SK_ARRAY_COUNT(whitelist);" "\n"; |
+ |
+ |
+#include "SkOSFile.h" |
+ |
+bool GenerateChecksums() { |
+ SkFILE* file = sk_fopen(checksumFileName, kWrite_SkFILE_Flag); |
+ if (!file) { |
+ SkDebugf("Can't open %s for writing.\n", checksumFileName); |
+ return false; |
+ } |
+ SkString line; |
+ line.printf(checksumHeader, __FUNCTION__, __FILE__, checksumFileName); |
+ sk_fwrite(line.c_str(), line.size(), file); |
+ for (int i = 0; i < whitelistCount; ++i) { |
+ const char* fontName = whitelist[i].fFontName; |
+ SkTypeface* tf = SkTypeface::CreateFromName(fontName, SkTypeface::kNormal); |
+ uint32_t checksum = compute_checksum(tf); |
+ line.printf(checksumEntry, fontName, checksum); |
+ sk_fwrite(line.c_str(), line.size(), file); |
+ } |
+ sk_fwrite(checksumTrailer, sizeof(checksumTrailer) - 1, file); |
+ sk_fclose(file); |
+ return true; |
+} |