Bug 1228022 - part 2 - Add support for reading Mac OS Roman encoded names from SFNTNameTables; r=jfkthame

When reading a U16 font name from the SFNTNameTable, a name entry
with platformID == 1 (Macintosh) and platformSpecificID
(aka encodingID) == 0 (Roman) is read as Mac Roman and converted
to U16.

This patch refactors the matchers created in CreateCanonicalU16Matchers
to return name encoding type instead of a boolean. The encoding
type can then be used to call the appropriate decoding function.

CreateCanonicalU16Matchers is also changed so that it doesn't
enqueue unnecessary matchers on OS X. On OS X, if the nametable
record's platformID field is PLATFORM_ID, IsUTF16Encoding() will
always return false so matchers requiring both of those conditions
will never match.

There are several other platformSpecificID's in Mac SFNTameTables
such as Japanese, Traditional Chinese, and Korean. Fonts with names
in those encodings won't have their names properly encoded, but
that should be OK as SFNTData::GetUniqueKey falls back to another
scheme for hashing fonts if the GetU16FullName call fails.

Tests on El Capitan and Sierra revealed Mac's use Microsoft/Unicode
SFNTNameTable names as well as Mac/Roman.

MozReview-Commit-ID: F8fyDVDwHs7

--HG--
extra : transplant_source : %F6%3F%5B%E9y%FD%93%8C%26s%D1n%FC%AEYp%5C%3D%A6j
This commit is contained in:
Haik Aftandilian 2016-09-09 13:55:21 -07:00
parent e4b0bb24b7
commit d46766b464
2 changed files with 142 additions and 44 deletions

View File

@ -10,6 +10,10 @@
#include "Logging.h"
#include "mozilla/Move.h"
#if defined(XP_MACOSX)
#include <CoreFoundation/CoreFoundation.h>
#endif
namespace mozilla {
namespace gfx {
@ -27,6 +31,8 @@ static const BigEndianUint16 ENCODING_ID_MICROSOFT_SYMBOL = 0;
static const BigEndianUint16 ENCODING_ID_MICROSOFT_UNICODEBMP = 1;
static const BigEndianUint16 ENCODING_ID_MICROSOFT_UNICODEFULL = 10;
static const BigEndianUint16 ENCODING_ID_MAC_ROMAN = 0;
static const BigEndianUint16 LANG_ID_MAC_ENGLISH = 0;
static const BigEndianUint16 LANG_ID_MICROSOFT_EN_US = 0x0409;
@ -53,6 +59,15 @@ struct NameRecord
#pragma pack(pop)
enum ENameDecoder : int
{
eNameDecoderUTF16,
#if defined(XP_MACOSX)
eNameDecoderMacRoman,
#endif
eNameDecoderNone
};
/* static */
UniquePtr<SFNTNameTable>
SFNTNameTable::Create(const uint8_t *aNameData, uint32_t aDataLength)
@ -98,14 +113,6 @@ SFNTNameTable::SFNTNameTable(const NameHeader *aNameHeader,
MOZ_ASSERT(reinterpret_cast<const uint8_t*>(aNameHeader) == aNameData);
}
#if defined(XP_MACOSX)
static const BigEndianUint16 CANONICAL_LANG_ID = LANG_ID_MAC_ENGLISH;
static const BigEndianUint16 PLATFORM_ID = PLATFORM_ID_MAC;
#else
static const BigEndianUint16 CANONICAL_LANG_ID = LANG_ID_MICROSOFT_EN_US;
static const BigEndianUint16 PLATFORM_ID = PLATFORM_ID_MICROSOFT;
#endif
static bool
IsUTF16Encoding(const NameRecord *aNameRecord)
{
@ -122,18 +129,39 @@ IsUTF16Encoding(const NameRecord *aNameRecord)
return false;
}
static NameRecordMatchers*
CreateCanonicalU16Matchers(const BigEndianUint16& aNameID)
#if defined(XP_MACOSX)
static bool
IsMacRomanEncoding(const NameRecord *aNameRecord)
{
if (aNameRecord->platformID == PLATFORM_ID_MAC &&
aNameRecord->encodingID == ENCODING_ID_MAC_ROMAN) {
return true;
}
return false;
}
#endif
static NameRecordMatchers*
CreateCanonicalMatchers(const BigEndianUint16& aNameID)
{
// For Windows, we return only Microsoft platform name record
// matchers. On Mac, we return matchers for both Microsoft platform
// records and Mac platform records.
NameRecordMatchers *matchers = new NameRecordMatchers();
// First, look for the English name (this will normally succeed).
#if defined(XP_MACOSX)
// First, look for the English name.
if (!matchers->append(
[=](const NameRecord *aNameRecord) {
return aNameRecord->nameID == aNameID &&
aNameRecord->languageID == CANONICAL_LANG_ID &&
aNameRecord->platformID == PLATFORM_ID &&
IsUTF16Encoding(aNameRecord);
if (aNameRecord->nameID == aNameID &&
aNameRecord->languageID == LANG_ID_MAC_ENGLISH &&
aNameRecord->platformID == PLATFORM_ID_MAC &&
IsMacRomanEncoding(aNameRecord)) {
return eNameDecoderMacRoman;
} else {
return eNameDecoderNone;
}
})) {
MOZ_CRASH();
}
@ -141,33 +169,46 @@ CreateCanonicalU16Matchers(const BigEndianUint16& aNameID)
// Second, look for all languages.
if (!matchers->append(
[=](const NameRecord *aNameRecord) {
return aNameRecord->nameID == aNameID &&
aNameRecord->platformID == PLATFORM_ID &&
IsUTF16Encoding(aNameRecord);
if (aNameRecord->nameID == aNameID &&
aNameRecord->platformID == PLATFORM_ID_MAC &&
IsMacRomanEncoding(aNameRecord)) {
return eNameDecoderMacRoman;
} else {
return eNameDecoderNone;
}
})) {
MOZ_CRASH();
}
#endif /* defined(XP_MACOSX) */
// First, look for the English name (this will normally succeed).
if (!matchers->append(
[=](const NameRecord *aNameRecord) {
if (aNameRecord->nameID == aNameID &&
aNameRecord->languageID == LANG_ID_MICROSOFT_EN_US &&
aNameRecord->platformID == PLATFORM_ID_MICROSOFT &&
IsUTF16Encoding(aNameRecord)) {
return eNameDecoderUTF16;
} else {
return eNameDecoderNone;
}
})) {
MOZ_CRASH();
}
#if defined(XP_MACOSX)
// On Mac may be dealing with font that only has Microsoft name entries.
// Second, look for all languages.
if (!matchers->append(
[=](const NameRecord *aNameRecord) {
return aNameRecord->nameID == aNameID &&
aNameRecord->languageID == LANG_ID_MICROSOFT_EN_US &&
aNameRecord->platformID == PLATFORM_ID_MICROSOFT &&
IsUTF16Encoding(aNameRecord);
if (aNameRecord->nameID == aNameID &&
aNameRecord->platformID == PLATFORM_ID_MICROSOFT &&
IsUTF16Encoding(aNameRecord)) {
return eNameDecoderUTF16;
} else {
return eNameDecoderNone;
}
})) {
MOZ_CRASH();
}
if (!matchers->append(
[=](const NameRecord *aNameRecord) {
return aNameRecord->nameID == aNameID &&
aNameRecord->platformID == PLATFORM_ID_MICROSOFT &&
IsUTF16Encoding(aNameRecord);
})) {
MOZ_CRASH();
}
#endif
return matchers;
}
@ -176,7 +217,7 @@ static const NameRecordMatchers&
FullNameMatchers()
{
static const NameRecordMatchers *sFullNameMatchers =
CreateCanonicalU16Matchers(NAME_ID_FULL);
CreateCanonicalMatchers(NAME_ID_FULL);
return *sFullNameMatchers;
}
@ -184,7 +225,7 @@ static const NameRecordMatchers&
FamilyMatchers()
{
static const NameRecordMatchers *sFamilyMatchers =
CreateCanonicalU16Matchers(NAME_ID_FAMILY);
CreateCanonicalMatchers(NAME_ID_FAMILY);
return *sFamilyMatchers;
}
@ -192,7 +233,7 @@ static const NameRecordMatchers&
StyleMatchers()
{
static const NameRecordMatchers *sStyleMatchers =
CreateCanonicalU16Matchers(NAME_ID_STYLE);
CreateCanonicalMatchers(NAME_ID_STYLE);
return *sStyleMatchers;
}
@ -230,8 +271,18 @@ SFNTNameTable::ReadU16Name(const NameRecordMatchers& aMatchers,
for (size_t i = 0; i < aMatchers.length(); ++i) {
const NameRecord* record = mFirstRecord;
while (record != mEndOfRecords) {
if (aMatchers[i](record)) {
return ReadU16NameFromRecord(record, aU16Name);
switch (aMatchers[i](record)) {
case eNameDecoderUTF16:
return ReadU16NameFromU16Record(record, aU16Name);
#if defined(XP_MACOSX)
case eNameDecoderMacRoman:
return ReadU16NameFromMacRomanRecord(record, aU16Name);
#endif
case eNameDecoderNone:
break;
default:
MOZ_CRASH("Invalid matcher encoding type");
break;
}
++record;
}
@ -241,8 +292,8 @@ SFNTNameTable::ReadU16Name(const NameRecordMatchers& aMatchers,
}
bool
SFNTNameTable::ReadU16NameFromRecord(const NameRecord *aNameRecord,
mozilla::u16string& aU16Name)
SFNTNameTable::ReadU16NameFromU16Record(const NameRecord *aNameRecord,
mozilla::u16string& aU16Name)
{
uint32_t offset = aNameRecord->offset;
uint32_t length = aNameRecord->length;
@ -261,5 +312,46 @@ SFNTNameTable::ReadU16NameFromRecord(const NameRecord *aNameRecord,
return true;
}
#if defined(XP_MACOSX)
bool
SFNTNameTable::ReadU16NameFromMacRomanRecord(const NameRecord *aNameRecord,
mozilla::u16string& aU16Name)
{
uint32_t offset = aNameRecord->offset;
uint32_t length = aNameRecord->length;
if (mStringDataLength < offset + length) {
gfxWarning() << "Name data too short to contain name string.";
return false;
}
if (length > INT_MAX) {
gfxWarning() << "Name record too long to decode.";
return false;
}
// pointer to the Mac Roman encoded string in the name record
const uint8_t *encodedStr = mStringData + offset;
CFStringRef cfString;
cfString = CFStringCreateWithBytesNoCopy(kCFAllocatorDefault, encodedStr,
length, kCFStringEncodingMacRoman,
false, kCFAllocatorNull);
// length (in UTF-16 code pairs) of the decoded string
CFIndex decodedLength = CFStringGetLength(cfString);
// temporary buffer
UniquePtr<UniChar[]> u16Buffer = MakeUnique<UniChar[]>(decodedLength);
CFStringGetCharacters(cfString, CFRangeMake(0, decodedLength),
u16Buffer.get());
CFRelease(cfString);
aU16Name.assign(reinterpret_cast<char16_t*>(u16Buffer.get()), decodedLength);
return true;
}
#endif
} // gfx
} // mozilla

View File

@ -17,8 +17,9 @@ namespace gfx {
struct NameHeader;
struct NameRecord;
enum ENameDecoder : int;
typedef Vector<function<bool(const NameRecord*)>> NameRecordMatchers;
typedef Vector<function<ENameDecoder(const NameRecord*)>> NameRecordMatchers;
class SFNTNameTable final
{
@ -38,7 +39,7 @@ public:
/**
* Gets the full name from the name table. If the full name string is not
* present it will use the family space concatenated with the style.
* This will only read names that are already UTF16.
* This will only read names that are already UTF16 or Mac OS Roman.
*
* @param aU16FullName string to be populated with the full name.
* @return true if the full name is successfully read.
@ -52,8 +53,13 @@ private:
bool ReadU16Name(const NameRecordMatchers& aMatchers, mozilla::u16string& aU16Name);
bool ReadU16NameFromRecord(const NameRecord *aNameRecord,
mozilla::u16string& aU16Name);
bool ReadU16NameFromU16Record(const NameRecord *aNameRecord,
mozilla::u16string& aU16Name);
#if defined(XP_MACOSX)
bool ReadU16NameFromMacRomanRecord(const NameRecord *aNameRecord,
mozilla::u16string& aU16Name);
#endif
const NameRecord *mFirstRecord;
const NameRecord *mEndOfRecords;