blob: eb6adc4f4f3cae94b220a241a9b73151e44b3a74 [file] [log] [blame]
/*
* 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 "SkUTF.h"
#include "../sfnt/SkOTUtils.h"
#include "SkWhitelistChecksums.inc"
#define WHITELIST_DEBUG 0
extern void WhitelistSerializeTypeface(const SkTypeface*, SkWStream* );
sk_sp<SkTypeface> 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<SkTypeface> defaultFace(SkTypeface::MakeFromName(nullptr, style));
sk_sp<SkTypeface> foundFace(SkTypeface::MakeFromName(fontName, style));
return defaultFace != foundFace;
}
static int whitelist_name_index(const SkTypeface* tf) {
SkString fontNameStr;
sk_sp<SkTypeface::LocalizedStrings> nameIter =
SkOTUtils::LocalizedStrings_NameTable::MakeForFamilyNames(*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<SkTypeface::LocalizedStrings> debugIter =
SkOTUtils::LocalizedStrings_NameTable::MakeForFamilyNames(*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<SkFontData> fontData = tf->makeFontData();
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 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<SkTypeface> WhitelistDeserializeTypeface(SkStream* stream) {
SkFontDescriptor desc;
if (!SkFontDescriptor::Deserialize(stream, &desc)) {
return nullptr;
}
std::unique_ptr<SkFontData> data = desc.detachFontData();
if (data) {
sk_sp<SkTypeface> 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<SkTypeface> 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<SkTypeface> 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;
}