diff options
-rw-r--r-- | gyp/tools.gyp | 11 | ||||
-rw-r--r-- | gyp/utils.gypi | 1 | ||||
-rw-r--r-- | include/core/SkTypeface.h | 4 | ||||
-rw-r--r-- | src/core/SkPictureData.cpp | 5 | ||||
-rw-r--r-- | src/core/SkTypeface.cpp | 27 | ||||
-rw-r--r-- | src/utils/SkWhitelistChecksums.cpp | 50 | ||||
-rw-r--r-- | src/utils/SkWhitelistTypefaces.cpp | 335 | ||||
-rw-r--r-- | tests/PictureTest.cpp | 13 | ||||
-rw-r--r-- | tools/whitelist_typefaces.cpp | 38 |
9 files changed, 463 insertions, 21 deletions
diff --git a/gyp/tools.gyp b/gyp/tools.gyp index d7b2e131e7..463fa732a7 100644 --- a/gyp/tools.gyp +++ b/gyp/tools.gyp @@ -37,6 +37,7 @@ 'skpmaker', 'test_image_decoder', 'test_public_includes', + 'whitelist_typefaces', ], 'conditions': [ ['skia_shared_lib', @@ -651,6 +652,16 @@ }, }, { + 'target_name': 'whitelist_typefaces', + 'type': 'executable', + 'sources': [ + '../tools/whitelist_typefaces.cpp', + ], + 'dependencies': [ + 'skia_lib.gyp:skia_lib', + ], + }, + { 'target_name': 'test_public_includes', 'type': 'static_library', # Ensure that our public headers don't have unused params so that clients diff --git a/gyp/utils.gypi b/gyp/utils.gypi index 53ea7f0dfc..e2abc03d5a 100644 --- a/gyp/utils.gypi +++ b/gyp/utils.gypi @@ -100,6 +100,7 @@ '<(skia_src_path)/utils/SkThreadUtils_win.cpp', '<(skia_src_path)/utils/SkThreadUtils_win.h', '<(skia_src_path)/utils/SkTFitsIn.h', + '<(skia_src_path)/utils/SkWhitelistTypefaces.cpp', #mac '<(skia_include_path)/utils/mac/SkCGUtils.h', diff --git a/include/core/SkTypeface.h b/include/core/SkTypeface.h index f6f30e66ca..c4242181fb 100644 --- a/include/core/SkTypeface.h +++ b/include/core/SkTypeface.h @@ -144,10 +144,6 @@ public: */ void serialize(SkWStream*) const; - /** Like serialize, but write the whole font (not just a signature) if possible. - */ - void serializeForcingEmbedding(SkWStream*) const; - /** Given the data previously written by serialize(), return a new instance to a typeface referring to the same font. If that font is not available, return null. If an instance is returned, the caller is responsible for diff --git a/src/core/SkPictureData.cpp b/src/core/SkPictureData.cpp index 2cc28410fe..5089e5d8d6 100644 --- a/src/core/SkPictureData.cpp +++ b/src/core/SkPictureData.cpp @@ -193,12 +193,7 @@ void SkPictureData::WriteTypefaces(SkWStream* stream, const SkRefCntSet& rec) { rec.copyToArray((SkRefCnt**)array); for (int i = 0; i < count; i++) { -#ifdef SK_PICTURE_FORCE_FONT_EMBEDDING - array[i]->serializeForcingEmbedding(stream); -#else - // TODO: if (embedFonts) { array[i]->serializeForcingEmbedding(stream) } else array[i]->serialize(stream); -#endif } } diff --git a/src/core/SkTypeface.cpp b/src/core/SkTypeface.cpp index 81daf2f610..3ed2565ee2 100644 --- a/src/core/SkTypeface.cpp +++ b/src/core/SkTypeface.cpp @@ -20,8 +20,16 @@ SkTypeface::SkTypeface(const SkFontStyle& style, SkFontID fontID, bool isFixedPi SkTypeface::~SkTypeface() { } +#ifdef SK_WHITELIST_SERIALIZED_TYPEFACES +extern void WhitelistSerializeTypeface(const SkTypeface*, SkWStream* ); +#define SK_TYPEFACE_DELEGATE WhitelistSerializeTypeface +#else +#define SK_TYPEFACE_DELEGATE nullptr +#endif SkTypeface* (*gCreateTypefaceDelegate)(const char [], SkTypeface::Style ) = nullptr; +void (*gSerializeTypefaceDelegate)(const SkTypeface*, SkWStream* ) = SK_TYPEFACE_DELEGATE; +SkTypeface* (*gDeserializeTypefaceDelegate)(SkStream* ) = nullptr; /////////////////////////////////////////////////////////////////////////////// @@ -164,6 +172,10 @@ SkTypeface* SkTypeface::CreateFromFile(const char path[], int index) { /////////////////////////////////////////////////////////////////////////////// void SkTypeface::serialize(SkWStream* wstream) const { + if (gSerializeTypefaceDelegate) { + (*gSerializeTypefaceDelegate)(this, wstream); + return; + } bool isLocal = false; SkFontDescriptor desc(this->style()); this->onGetFontDescriptor(&desc, &isLocal); @@ -175,19 +187,10 @@ void SkTypeface::serialize(SkWStream* wstream) const { desc.serialize(wstream); } -void SkTypeface::serializeForcingEmbedding(SkWStream* wstream) const { - bool ignoredIsLocal; - SkFontDescriptor desc(this->style()); - this->onGetFontDescriptor(&desc, &ignoredIsLocal); - - // Always embed font data. - if (!desc.hasFontData()) { - desc.setFontData(this->onCreateFontData()); - } - desc.serialize(wstream); -} - SkTypeface* SkTypeface::Deserialize(SkStream* stream) { + if (gDeserializeTypefaceDelegate) { + return (*gDeserializeTypefaceDelegate)(stream); + } SkFontDescriptor desc(stream); SkFontData* data = desc.detachFontData(); if (data) { diff --git a/src/utils/SkWhitelistChecksums.cpp b/src/utils/SkWhitelistChecksums.cpp new file mode 100644 index 0000000000..b223d6c13a --- /dev/null +++ b/src/utils/SkWhitelistChecksums.cpp @@ -0,0 +1,50 @@ +/* + * Copyright 2015 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + * + * GenerateChecksums() in ../../src/utils/SkWhitelistTypefaces.cpp generated SkWhitelistChecksums.cpp. + * Run 'whitelist_typefaces --generate' to create anew. + */ + +#include "SkTDArray.h" + +struct Whitelist { + const char* fFontName; + uint32_t fChecksum; + bool fSerializedNameOnly; + bool fSerializedSub; +}; + +static Whitelist whitelist[] = { + { "Aegean", 0x82ad09a5, false, false }, + { "Analecta", 0xc378d990, false, false }, + { "Arial", 0xbc28cb14, false, false }, + { "DejaVu Sans", 0x91e2fe32, false, false }, + { "DejaVu Sans Mono", 0xddc8af48, false, false }, + { "DejaVu Serif", 0x0077166d, false, false }, + { "FreeMono", 0xb9e602cd, false, false }, + { "FreeSans", 0x6f484994, false, false }, + { "FreeSerif", 0x933ce1c4, false, false }, + { "Khmer OS", 0x917c40aa, false, false }, + { "Kochi Gothic", 0x962132dd, false, false }, + { "Lohit Kannada", 0x7c829df4, false, false }, + { "Lohit Marathi", 0x0eb0a941, false, false }, + { "Lohit Oriya", 0xf3e9d313, false, false }, + { "Lohit Punjabi", 0xfd8b26e0, false, false }, + { "Lohit Tamil", 0x83a29565, false, false }, + { "Lohit Telugu", 0x11424bd0, false, false }, + { "Meera", 0xe3e16220, false, false }, + { "Mukti Narrow", 0x53f7d053, false, false }, + { "NanumBarunGothic", 0x859e77ea, false, false }, + { "NanumGothic", 0x9edbcdb8, false, false }, + { "OpenSymbol", 0xd3d743df, false, false }, + { "Symbola", 0xfa80f2ab, false, false }, + { "TakaoPGothic", 0x068c405a, false, false }, + { "Waree", 0x036e4fa4, false, false }, + { "WenQuanYi Micro Hei", 0xfea8587c, false, false }, + { "padmaa", 0x09eb1865, false, false }, +}; + +static const int whitelistCount = (int) SK_ARRAY_COUNT(whitelist); diff --git a/src/utils/SkWhitelistTypefaces.cpp b/src/utils/SkWhitelistTypefaces.cpp new file mode 100644 index 0000000000..99f3644a97 --- /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 { + 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); + 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; +} diff --git a/tests/PictureTest.cpp b/tests/PictureTest.cpp index de4d1e3e00..45edc715ef 100644 --- a/tests/PictureTest.cpp +++ b/tests/PictureTest.cpp @@ -1151,7 +1151,20 @@ static void test_gen_id(skiatest::Reporter* reporter) { REPORTER_ASSERT(reporter, hasData->uniqueID() != empty->uniqueID()); } +static void test_typeface(skiatest::Reporter* reporter) { + SkPictureRecorder recorder; + SkCanvas* canvas = recorder.beginRecording(10, 10); + SkPaint paint; + paint.setTypeface(SkTypeface::CreateFromName("Arial", SkTypeface::kItalic)); + canvas->drawText("Q", 1, 0, 10, paint); + SkAutoTUnref<SkPicture> picture(recorder.endRecording()); + REPORTER_ASSERT(reporter, picture->hasText()); + SkDynamicMemoryWStream stream; + picture->serialize(&stream); +} + DEF_TEST(Picture, reporter) { + test_typeface(reporter); #ifdef SK_DEBUG test_deleting_empty_picture(); test_serializing_empty_picture(); diff --git a/tools/whitelist_typefaces.cpp b/tools/whitelist_typefaces.cpp new file mode 100644 index 0000000000..205f54ac95 --- /dev/null +++ b/tools/whitelist_typefaces.cpp @@ -0,0 +1,38 @@ +/* + * Copyright 2013 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include "SkForceLinking.h" +#include "SkGraphics.h" + +__SK_FORCE_IMAGE_DECODER_LINKING; + +extern bool CheckChecksums(); +extern bool GenerateChecksums(); + +int tool_main(int argc, char** argv); +int tool_main(int argc, char** argv) { + if (argc == 2) { + SkAutoGraphics ag; // Enable use of SkRTConfig + if (!strcmp(argv[1], "--check")) { + return (int) !CheckChecksums(); + } + if (!strcmp(argv[1], "--generate")) { + if (!GenerateChecksums()) { + return 2; + } + return 0; + } + } + SkDebugf("Usage:\n %s [--check] [--generate]\n\n", argv[0]); + return 3; +} + +#if !defined SK_BUILD_FOR_IOS +int main(int argc, char * const argv[]) { + return tool_main(argc, (char**) argv); +} +#endif |