/* * 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 "SkFontDescriptor.h" #include "SkOpts.h" #include "SkStream.h" #include "SkString.h" #include "SkTypeface.h" #include "SkUtils.h" #include "../sfnt/SkOTUtils.h" #include "SkWhitelistChecksums.inc" #define WHITELIST_DEBUG 0 extern void WhitelistSerializeTypeface(const SkTypeface*, SkWStream* ); sk_sp WhitelistDeserializeTypeface(SkStream* ); extern bool CheckChecksums(); extern bool GenerateChecksums(); #if WHITELIST_DEBUG static bool timesNewRomanSerializedNameOnly = false; #endif #define SUBNAME_PREFIX "sk_" static bool font_name_is_local(const char* fontName, SkFontStyle style) { if (!strcmp(fontName, "DejaVu Sans")) { return true; } sk_sp defaultFace(SkTypeface::MakeFromName(nullptr, style)); sk_sp foundFace(SkTypeface::MakeFromName(fontName, style)); return defaultFace != foundFace; } static int whitelist_name_index(const SkTypeface* tf) { SkString fontNameStr; sk_sp nameIter( SkOTUtils::LocalizedStrings_NameTable::CreateForFamilyNames(*tf)); SkTypeface::LocalizedString familyNameLocalized; while (nameIter->next(&familyNameLocalized)) { fontNameStr = familyNameLocalized.fString; // check against permissible list of names for (int i = 0; i < whitelistCount; ++i) { if (fontNameStr.equals(whitelist[i].fFontName)) { return i; } } } #if WHITELIST_DEBUG sk_sp debugIter( SkOTUtils::LocalizedStrings_NameTable::CreateForFamilyNames(*tf)); while (debugIter->next(&familyNameLocalized)) { SkDebugf("no match fontName=\"%s\"\n", familyNameLocalized.fString.c_str()); } #endif return -1; } static uint32_t compute_checksum(const SkTypeface* tf) { std::unique_ptr fontData = tf->makeFontData(); if (!fontData) { return 0; } SkStreamAsset* fontStream = fontData->getStream(); if (!fontStream) { return 0; } SkTDArray data; size_t length = fontStream->getLength(); if (!length) { return 0; } data.setCount((int) length); if (!fontStream->peek(data.begin(), length)) { return 0; } return SkOpts::hash(data.begin(), length); } static void serialize_sub(const char* fontName, SkFontStyle style, SkWStream* wstream) { SkFontDescriptor desc; SkString subName(SUBNAME_PREFIX); subName.append(fontName); const char* familyName = subName.c_str(); desc.setFamilyName(familyName); desc.setStyle(style); 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->getFontDescriptor(&desc, &isLocal); return isLocal; } static void serialize_full(const SkTypeface* tf, SkWStream* wstream) { bool isLocal = false; SkFontDescriptor desc; tf->getFontDescriptor(&desc, &isLocal); // Embed font data if it's a local font. if (isLocal && !desc.hasFontData()) { desc.setFontData(tf->makeFontData()); } desc.serialize(wstream); } static void serialize_name_only(const SkTypeface* tf, SkWStream* wstream) { bool isLocal = false; SkFontDescriptor desc; 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->fontStyle())) { #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->fontStyle(), wstream); } sk_sp WhitelistDeserializeTypeface(SkStream* stream) { SkFontDescriptor desc; if (!SkFontDescriptor::Deserialize(stream, &desc)) { return nullptr; } std::unique_ptr data = desc.detachFontData(); if (data) { sk_sp typeface(SkTypeface::MakeFromFontData(std::move(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::MakeFromName(familyName, desc.getStyle()); } bool CheckChecksums() { for (int i = 0; i < whitelistCount; ++i) { const char* fontName = whitelist[i].fFontName; sk_sp tf(SkTypeface::MakeFromName(fontName, SkFontStyle())); uint32_t checksum = compute_checksum(tf.get()); if (whitelist[i].fChecksum != checksum) { return false; } } return true; } const char checksumFileName[] = "SkWhitelistChecksums.inc"; 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() { FILE* 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; sk_sp tf(SkTypeface::MakeFromName(fontName, SkFontStyle())); uint32_t checksum = compute_checksum(tf.get()); 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; }