ad031f5813
Robust reading of name tables. Internally use sk_sp with LocalizedStrings. After landing, update the public api as well. Replace High/Low with Leading/Trailing surrogates in naming. This naming is far easier to get right. Change-Id: I542934ea8189ced1c2cbdd1539f9ebba562f0731 Reviewed-on: https://skia-review.googlesource.com/148123 Reviewed-by: Herb Derby <herb@google.com> Commit-Queue: Ben Wagner <bungeman@google.com>
225 lines
7.7 KiB
C++
225 lines
7.7 KiB
C++
/*
|
|
* 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 "SkCommandLineFlags.h"
|
|
#include "SkFontMgr.h"
|
|
#include "SkOTTable_name.h"
|
|
#include "SkTypeface.h"
|
|
#include "Test.h"
|
|
|
|
#include <stddef.h>
|
|
|
|
namespace {
|
|
|
|
template <size_t R, size_t D> struct Format0NameTable {
|
|
SkOTTableName header;
|
|
SkOTTableName::Record nameRecord[R];
|
|
char data[D];
|
|
};
|
|
|
|
template <size_t R, size_t L, size_t D> struct Format1NameTable {
|
|
SkOTTableName header;
|
|
SkOTTableName::Record nameRecord[R];
|
|
struct {
|
|
SkOTTableName::Format1Ext header;
|
|
SkOTTableName::Format1Ext::LangTagRecord langTagRecord[L];
|
|
} format1ext;
|
|
char data[D];
|
|
};
|
|
|
|
typedef Format0NameTable<1, 9> SimpleFormat0NameTable;
|
|
constexpr SimpleFormat0NameTable simpleFormat0NameTable = {
|
|
/*header*/ {
|
|
/*format*/ SkOTTableName::format_0,
|
|
/*count*/ SkTEndianSwap16<1>::value,
|
|
/*stringOffset*/ SkTEndianSwap16<offsetof(SimpleFormat0NameTable, data)>::value,
|
|
},
|
|
/*nameRecord[]*/ {
|
|
/*Record*/ {
|
|
/*platformID*/ { SkOTTableName::Record::PlatformID::Windows },
|
|
/*encodingID*/ { SkOTTableName::Record::EncodingID::Windows::UnicodeBMPUCS2 },
|
|
/*languageID*/ { SkOTTableName::Record::LanguageID::Windows::English_UnitedStates },
|
|
/*nameID*/ { SkOTTableName::Record::NameID::Predefined::FontFamilyName },
|
|
/*length*/ SkTEndianSwap16<8>::value,
|
|
/*offset*/ SkTEndianSwap16<0>::value,
|
|
}
|
|
},
|
|
/*data*/ "\x0" "T" "\x0" "e" "\x0" "s" "\x0" "t",
|
|
};
|
|
|
|
typedef Format1NameTable<1, 1, 19> SimpleFormat1NameTable;
|
|
constexpr SimpleFormat1NameTable simpleFormat1NameTable = {
|
|
/*header*/ {
|
|
/*format*/ SkOTTableName::format_1,
|
|
/*count*/ SkTEndianSwap16<1>::value,
|
|
/*stringOffset*/ SkTEndianSwap16<offsetof(SimpleFormat1NameTable, data)>::value,
|
|
},
|
|
/*nameRecord[]*/ {
|
|
/*Record*/ {
|
|
/*platformID*/ { SkOTTableName::Record::PlatformID::Windows },
|
|
/*encodingID*/ { SkOTTableName::Record::EncodingID::Windows::UnicodeBMPUCS2 },
|
|
/*languageID*/ { SkTEndianSwap16<0x8000 + 0>::value },
|
|
/*nameID*/ { SkOTTableName::Record::NameID::Predefined::FontFamilyName },
|
|
/*length*/ SkTEndianSwap16<8>::value,
|
|
/*offset*/ SkTEndianSwap16<0>::value,
|
|
}
|
|
},
|
|
/*format1ext*/ {
|
|
/*header*/ {
|
|
/*langTagCount*/ SkTEndianSwap16<1>::value,
|
|
},
|
|
/*langTagRecord[]*/ {
|
|
/*LangTagRecord*/ {
|
|
/*length*/ SkTEndianSwap16<10>::value,
|
|
/*offset*/ SkTEndianSwap16<8>::value,
|
|
},
|
|
},
|
|
},
|
|
/*data*/ "\x0" "T" "\x0" "e" "\x0" "s" "\x0" "t"
|
|
"\x0" "e" "\x0" "n" "\x0" "-" "\x0" "U" "\x0" "S",
|
|
};
|
|
|
|
struct FontNamesTest {
|
|
const uint8_t* data;
|
|
size_t size;
|
|
SkOTTableName::Record::NameID nameID;
|
|
size_t nameCount;
|
|
struct {
|
|
const char* name;
|
|
const char* language;
|
|
} names[10];
|
|
|
|
} tests[] = {
|
|
{
|
|
reinterpret_cast<const uint8_t*>(&simpleFormat0NameTable),
|
|
sizeof(simpleFormat0NameTable),
|
|
{ SkOTTableName::Record::NameID::Predefined::FontFamilyName },
|
|
1,
|
|
{
|
|
{ "Test", "en-US" },
|
|
},
|
|
},
|
|
{
|
|
reinterpret_cast<const uint8_t*>(&simpleFormat1NameTable),
|
|
sizeof(simpleFormat1NameTable),
|
|
{ SkOTTableName::Record::NameID::Predefined::FontFamilyName },
|
|
1,
|
|
{
|
|
{ "Test", "en-US" },
|
|
},
|
|
},
|
|
};
|
|
|
|
static void test_synthetic(skiatest::Reporter* reporter, bool verbose) {
|
|
for (const auto& test : tests) {
|
|
SkOTTableName::Iterator iter(test.data, test.size, test.nameID.predefined.value);
|
|
SkOTTableName::Iterator::Record record;
|
|
size_t nameIndex = 0;
|
|
while (nameIndex < test.nameCount && iter.next(record)) {
|
|
REPORTER_ASSERT(reporter,
|
|
strcmp(test.names[nameIndex].name, record.name.c_str()) == 0,
|
|
"Name did not match.");
|
|
|
|
REPORTER_ASSERT(reporter,
|
|
strcmp(test.names[nameIndex].language, record.language.c_str()) == 0,
|
|
"Language did not match.");
|
|
|
|
if (verbose) {
|
|
SkDebugf("%s <%s>\n", record.name.c_str(), record.language.c_str());
|
|
}
|
|
|
|
++nameIndex;
|
|
}
|
|
|
|
REPORTER_ASSERT(reporter, nameIndex == test.nameCount, "Fewer names than expected.");
|
|
|
|
REPORTER_ASSERT(reporter, !iter.next(record), "More names than expected.");
|
|
}
|
|
}
|
|
|
|
#define MAX_FAMILIES 1000
|
|
static void test_systemfonts(skiatest::Reporter* reporter, bool verbose) {
|
|
static const SkFontTableTag nameTag = SkSetFourByteTag('n','a','m','e');
|
|
|
|
sk_sp<SkFontMgr> fm(SkFontMgr::RefDefault());
|
|
int count = SkMin32(fm->countFamilies(), MAX_FAMILIES);
|
|
for (int i = 0; i < count; ++i) {
|
|
sk_sp<SkFontStyleSet> set(fm->createStyleSet(i));
|
|
for (int j = 0; j < set->count(); ++j) {
|
|
SkString sname;
|
|
SkFontStyle fs;
|
|
set->getStyle(j, &fs, &sname);
|
|
|
|
sk_sp<SkTypeface> typeface(set->createTypeface(j));
|
|
|
|
SkString familyName;
|
|
typeface->getFamilyName(&familyName);
|
|
if (verbose) {
|
|
SkDebugf("[%s]\n", familyName.c_str());
|
|
}
|
|
|
|
sk_sp<SkTypeface::LocalizedStrings> familyNamesIter(
|
|
typeface->createFamilyNameIterator());
|
|
SkTypeface::LocalizedString familyNameLocalized;
|
|
while (familyNamesIter->next(&familyNameLocalized)) {
|
|
if (verbose) {
|
|
SkDebugf("(%s) <%s>\n", familyNameLocalized.fString.c_str(),
|
|
familyNameLocalized.fLanguage.c_str());
|
|
}
|
|
}
|
|
|
|
size_t nameTableSize = typeface->getTableSize(nameTag);
|
|
if (0 == nameTableSize) {
|
|
continue;
|
|
}
|
|
SkAutoTMalloc<uint8_t> nameTableData(nameTableSize);
|
|
size_t copied = typeface->getTableData(nameTag, 0, nameTableSize, nameTableData.get());
|
|
if (copied != nameTableSize) {
|
|
continue;
|
|
}
|
|
|
|
SkOTTableName::Iterator::Record record;
|
|
SkOTTableName::Iterator familyNameIter(nameTableData.get(), nameTableSize,
|
|
SkOTTableName::Record::NameID::Predefined::FontFamilyName);
|
|
while (familyNameIter.next(record)) {
|
|
REPORTER_ASSERT(
|
|
reporter,
|
|
SkOTTableName::Record::NameID::Predefined::FontFamilyName == record.type,
|
|
"Requested family name, got something else.");
|
|
if (verbose) {
|
|
SkDebugf("{%s} <%s>\n", record.name.c_str(), record.language.c_str());
|
|
}
|
|
}
|
|
|
|
SkOTTableName::Iterator styleNameIter(nameTableData.get(), nameTableSize,
|
|
SkOTTableName::Record::NameID::Predefined::FontSubfamilyName);
|
|
while (styleNameIter.next(record)) {
|
|
REPORTER_ASSERT(
|
|
reporter,
|
|
SkOTTableName::Record::NameID::Predefined::FontSubfamilyName == record.type,
|
|
"Requested subfamily name, got something else.");
|
|
if (verbose) {
|
|
SkDebugf("{{%s}} <%s>\n", record.name.c_str(), record.language.c_str());
|
|
}
|
|
}
|
|
|
|
if (verbose) {
|
|
SkDebugf("\n");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
} // namespace
|
|
|
|
DEFINE_bool(verboseFontNames, false, "verbose FontNames test.");
|
|
|
|
DEF_TEST(FontNames, reporter) {
|
|
test_synthetic(reporter, FLAGS_verboseFontNames);
|
|
test_systemfonts(reporter, FLAGS_verboseFontNames);
|
|
}
|