aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--gyp/tools.gyp11
-rw-r--r--gyp/utils.gypi1
-rw-r--r--include/core/SkTypeface.h4
-rw-r--r--src/core/SkPictureData.cpp5
-rw-r--r--src/core/SkTypeface.cpp27
-rw-r--r--src/utils/SkWhitelistChecksums.cpp50
-rw-r--r--src/utils/SkWhitelistTypefaces.cpp335
-rw-r--r--tests/PictureTest.cpp13
-rw-r--r--tools/whitelist_typefaces.cpp38
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