/*
|
* 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;
|
}
|