1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include "SFNTNameTable.h"
11 #include "BigEndianInts.h"
14 #if defined(XP_MACOSX)
15 # include <CoreFoundation/CoreFoundation.h>
21 static const BigEndianUint16 FORMAT_0
= 0;
23 static const BigEndianUint16 NAME_ID_FAMILY
= 1;
24 static const BigEndianUint16 NAME_ID_STYLE
= 2;
25 static const BigEndianUint16 NAME_ID_FULL
= 4;
27 static const BigEndianUint16 PLATFORM_ID_UNICODE
= 0;
28 static const BigEndianUint16 PLATFORM_ID_MAC
= 1;
29 static const BigEndianUint16 PLATFORM_ID_MICROSOFT
= 3;
31 static const BigEndianUint16 ENCODING_ID_MICROSOFT_SYMBOL
= 0;
32 static const BigEndianUint16 ENCODING_ID_MICROSOFT_UNICODEBMP
= 1;
33 static const BigEndianUint16 ENCODING_ID_MICROSOFT_UNICODEFULL
= 10;
35 static const BigEndianUint16 ENCODING_ID_MAC_ROMAN
= 0;
37 static const BigEndianUint16 LANG_ID_MAC_ENGLISH
= 0;
39 static const BigEndianUint16 LANG_ID_MICROSOFT_EN_US
= 0x0409;
43 // Name table has a header, followed by name records, followed by string data.
45 BigEndianUint16 format
; // Format selector (=0).
46 BigEndianUint16 count
; // Number of name records.
48 stringOffset
; // Offset to string storage from start of table.
52 BigEndianUint16 platformID
;
53 BigEndianUint16 encodingID
; // Platform-specific encoding ID
54 BigEndianUint16 languageID
;
55 BigEndianUint16 nameID
;
56 BigEndianUint16 length
; // String length in bytes.
57 BigEndianUint16 offset
; // String offset from start of storage in bytes.
62 enum ENameDecoder
: int {
64 #if defined(XP_MACOSX)
71 UniquePtr
<SFNTNameTable
> SFNTNameTable::Create(const uint8_t* aNameData
,
72 uint32_t aDataLength
) {
73 MOZ_ASSERT(aNameData
);
75 if (aDataLength
< sizeof(NameHeader
)) {
76 gfxWarning() << "Name data too short to contain NameHeader.";
80 const NameHeader
* nameHeader
= reinterpret_cast<const NameHeader
*>(aNameData
);
81 if (nameHeader
->format
!= FORMAT_0
) {
82 gfxWarning() << "Only Name Table Format 0 is supported.";
86 uint16_t stringOffset
= nameHeader
->stringOffset
;
89 sizeof(NameHeader
) + (nameHeader
->count
* sizeof(NameRecord
))) {
90 gfxWarning() << "Name table string offset is incorrect.";
94 if (aDataLength
< stringOffset
) {
95 gfxWarning() << "Name data too short to contain name records.";
99 return UniquePtr
<SFNTNameTable
>(
100 new SFNTNameTable(nameHeader
, aNameData
, aDataLength
));
103 SFNTNameTable::SFNTNameTable(const NameHeader
* aNameHeader
,
104 const uint8_t* aNameData
, uint32_t aDataLength
)
106 reinterpret_cast<const NameRecord
*>(aNameData
+ sizeof(NameHeader
))),
107 mEndOfRecords(mFirstRecord
+ aNameHeader
->count
),
108 mStringData(aNameData
+ aNameHeader
->stringOffset
),
109 mStringDataLength(aDataLength
- aNameHeader
->stringOffset
) {
110 MOZ_ASSERT(reinterpret_cast<const uint8_t*>(aNameHeader
) == aNameData
);
113 static bool IsUTF16Encoding(const NameRecord
* aNameRecord
) {
114 if (aNameRecord
->platformID
== PLATFORM_ID_MICROSOFT
&&
115 (aNameRecord
->encodingID
== ENCODING_ID_MICROSOFT_UNICODEBMP
||
116 aNameRecord
->encodingID
== ENCODING_ID_MICROSOFT_SYMBOL
)) {
120 if (aNameRecord
->platformID
== PLATFORM_ID_UNICODE
) {
127 #if defined(XP_MACOSX)
128 static bool IsMacRomanEncoding(const NameRecord
* aNameRecord
) {
129 if (aNameRecord
->platformID
== PLATFORM_ID_MAC
&&
130 aNameRecord
->encodingID
== ENCODING_ID_MAC_ROMAN
) {
138 static NameRecordMatchers
* CreateCanonicalMatchers(
139 const BigEndianUint16
& aNameID
) {
140 // For Windows, we return only Microsoft platform name record
141 // matchers. On Mac, we return matchers for both Microsoft platform
142 // records and Mac platform records.
143 NameRecordMatchers
* matchers
= new NameRecordMatchers();
145 #if defined(XP_MACOSX)
146 // First, look for the English name.
147 if (!matchers
->append([=](const NameRecord
* aNameRecord
) {
148 if (aNameRecord
->nameID
== aNameID
&&
149 aNameRecord
->languageID
== LANG_ID_MAC_ENGLISH
&&
150 aNameRecord
->platformID
== PLATFORM_ID_MAC
&&
151 IsMacRomanEncoding(aNameRecord
)) {
152 return eNameDecoderMacRoman
;
154 return eNameDecoderNone
;
160 // Second, look for all languages.
161 if (!matchers
->append([=](const NameRecord
* aNameRecord
) {
162 if (aNameRecord
->nameID
== aNameID
&&
163 aNameRecord
->platformID
== PLATFORM_ID_MAC
&&
164 IsMacRomanEncoding(aNameRecord
)) {
165 return eNameDecoderMacRoman
;
167 return eNameDecoderNone
;
172 #endif /* defined(XP_MACOSX) */
174 // First, look for the English name (this will normally succeed).
175 if (!matchers
->append([=](const NameRecord
* aNameRecord
) {
176 if (aNameRecord
->nameID
== aNameID
&&
177 aNameRecord
->languageID
== LANG_ID_MICROSOFT_EN_US
&&
178 aNameRecord
->platformID
== PLATFORM_ID_MICROSOFT
&&
179 IsUTF16Encoding(aNameRecord
)) {
180 return eNameDecoderUTF16
;
182 return eNameDecoderNone
;
188 // Second, look for all languages.
189 if (!matchers
->append([=](const NameRecord
* aNameRecord
) {
190 if (aNameRecord
->nameID
== aNameID
&&
191 aNameRecord
->platformID
== PLATFORM_ID_MICROSOFT
&&
192 IsUTF16Encoding(aNameRecord
)) {
193 return eNameDecoderUTF16
;
195 return eNameDecoderNone
;
204 static const NameRecordMatchers
& FullNameMatchers() {
205 static const NameRecordMatchers
* sFullNameMatchers
=
206 CreateCanonicalMatchers(NAME_ID_FULL
);
207 return *sFullNameMatchers
;
210 static const NameRecordMatchers
& FamilyMatchers() {
211 static const NameRecordMatchers
* sFamilyMatchers
=
212 CreateCanonicalMatchers(NAME_ID_FAMILY
);
213 return *sFamilyMatchers
;
216 static const NameRecordMatchers
& StyleMatchers() {
217 static const NameRecordMatchers
* sStyleMatchers
=
218 CreateCanonicalMatchers(NAME_ID_STYLE
);
219 return *sStyleMatchers
;
222 bool SFNTNameTable::GetU16FullName(mozilla::u16string
& aU16FullName
) {
223 if (ReadU16Name(FullNameMatchers(), aU16FullName
)) {
227 // If the full name record doesn't exist create the name from the family space
228 // concatenated with the style.
229 mozilla::u16string familyName
;
230 if (!ReadU16Name(FamilyMatchers(), familyName
)) {
234 mozilla::u16string styleName
;
235 if (!ReadU16Name(StyleMatchers(), styleName
)) {
239 aU16FullName
.assign(std::move(familyName
));
240 aU16FullName
.append(u
" ");
241 aU16FullName
.append(styleName
);
245 bool SFNTNameTable::ReadU16Name(const NameRecordMatchers
& aMatchers
,
246 mozilla::u16string
& aU16Name
) {
247 MOZ_ASSERT(!aMatchers
.empty());
249 for (size_t i
= 0; i
< aMatchers
.length(); ++i
) {
250 const NameRecord
* record
= mFirstRecord
;
251 while (record
!= mEndOfRecords
) {
252 switch (aMatchers
[i
](record
)) {
253 case eNameDecoderUTF16
:
254 return ReadU16NameFromU16Record(record
, aU16Name
);
255 #if defined(XP_MACOSX)
256 case eNameDecoderMacRoman
:
257 return ReadU16NameFromMacRomanRecord(record
, aU16Name
);
259 case eNameDecoderNone
:
262 MOZ_CRASH("Invalid matcher encoding type");
272 bool SFNTNameTable::ReadU16NameFromU16Record(const NameRecord
* aNameRecord
,
273 mozilla::u16string
& aU16Name
) {
274 uint32_t offset
= aNameRecord
->offset
;
275 uint32_t length
= aNameRecord
->length
;
276 if (mStringDataLength
< offset
+ length
) {
277 gfxWarning() << "Name data too short to contain name string.";
281 const uint8_t* startOfName
= mStringData
+ offset
;
282 size_t actualLength
= length
/ sizeof(char16_t
);
283 UniquePtr
<char16_t
[]> nameData(new char16_t
[actualLength
]);
284 NativeEndian::copyAndSwapFromBigEndian(nameData
.get(), startOfName
,
287 aU16Name
.assign(nameData
.get(), actualLength
);
291 #if defined(XP_MACOSX)
292 bool SFNTNameTable::ReadU16NameFromMacRomanRecord(
293 const NameRecord
* aNameRecord
, mozilla::u16string
& aU16Name
) {
294 uint32_t offset
= aNameRecord
->offset
;
295 uint32_t length
= aNameRecord
->length
;
296 if (mStringDataLength
< offset
+ length
) {
297 gfxWarning() << "Name data too short to contain name string.";
300 if (length
> INT_MAX
) {
301 gfxWarning() << "Name record too long to decode.";
305 // pointer to the Mac Roman encoded string in the name record
306 const uint8_t* encodedStr
= mStringData
+ offset
;
308 CFStringRef cfString
;
309 cfString
= CFStringCreateWithBytesNoCopy(kCFAllocatorDefault
, encodedStr
,
310 length
, kCFStringEncodingMacRoman
,
311 false, kCFAllocatorNull
);
313 // length (in UTF-16 code pairs) of the decoded string
314 CFIndex decodedLength
= CFStringGetLength(cfString
);
317 UniquePtr
<UniChar
[]> u16Buffer
= MakeUnique
<UniChar
[]>(decodedLength
);
319 CFStringGetCharacters(cfString
, CFRangeMake(0, decodedLength
),
324 aU16Name
.assign(reinterpret_cast<char16_t
*>(u16Buffer
.get()), decodedLength
);
331 } // namespace mozilla