/src/mozilla-central/gfx/2d/SFNTNameTable.cpp
Line | Count | Source (jump to first uncovered line) |
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/. */ |
6 | | |
7 | | #include "SFNTNameTable.h" |
8 | | |
9 | | #include "BigEndianInts.h" |
10 | | #include "Logging.h" |
11 | | #include "mozilla/Move.h" |
12 | | |
13 | | #if defined(XP_MACOSX) |
14 | | #include <CoreFoundation/CoreFoundation.h> |
15 | | #endif |
16 | | |
17 | | namespace mozilla { |
18 | | namespace gfx { |
19 | | |
20 | | static const BigEndianUint16 FORMAT_0 = 0; |
21 | | |
22 | | static const BigEndianUint16 NAME_ID_FAMILY = 1; |
23 | | static const BigEndianUint16 NAME_ID_STYLE = 2; |
24 | | static const BigEndianUint16 NAME_ID_FULL = 4; |
25 | | |
26 | | static const BigEndianUint16 PLATFORM_ID_UNICODE = 0; |
27 | | static const BigEndianUint16 PLATFORM_ID_MAC = 1; |
28 | | static const BigEndianUint16 PLATFORM_ID_MICROSOFT = 3; |
29 | | |
30 | | static const BigEndianUint16 ENCODING_ID_MICROSOFT_SYMBOL = 0; |
31 | | static const BigEndianUint16 ENCODING_ID_MICROSOFT_UNICODEBMP = 1; |
32 | | static const BigEndianUint16 ENCODING_ID_MICROSOFT_UNICODEFULL = 10; |
33 | | |
34 | | static const BigEndianUint16 ENCODING_ID_MAC_ROMAN = 0; |
35 | | |
36 | | static const BigEndianUint16 LANG_ID_MAC_ENGLISH = 0; |
37 | | |
38 | | static const BigEndianUint16 LANG_ID_MICROSOFT_EN_US = 0x0409; |
39 | | |
40 | | #pragma pack(push, 1) |
41 | | |
42 | | // Name table has a header, followed by name records, followed by string data. |
43 | | struct NameHeader |
44 | | { |
45 | | BigEndianUint16 format; // Format selector (=0). |
46 | | BigEndianUint16 count; // Number of name records. |
47 | | BigEndianUint16 stringOffset; // Offset to string storage from start of table. |
48 | | }; |
49 | | |
50 | | struct NameRecord |
51 | | { |
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. |
58 | | }; |
59 | | |
60 | | #pragma pack(pop) |
61 | | |
62 | | enum ENameDecoder : int |
63 | | { |
64 | | eNameDecoderUTF16, |
65 | | #if defined(XP_MACOSX) |
66 | | eNameDecoderMacRoman, |
67 | | #endif |
68 | | eNameDecoderNone |
69 | | }; |
70 | | |
71 | | /* static */ |
72 | | UniquePtr<SFNTNameTable> |
73 | | SFNTNameTable::Create(const uint8_t *aNameData, uint32_t aDataLength) |
74 | 0 | { |
75 | 0 | MOZ_ASSERT(aNameData); |
76 | 0 |
|
77 | 0 | if (aDataLength < sizeof(NameHeader)) { |
78 | 0 | gfxWarning() << "Name data too short to contain NameHeader."; |
79 | 0 | return nullptr; |
80 | 0 | } |
81 | 0 |
|
82 | 0 | const NameHeader *nameHeader = reinterpret_cast<const NameHeader*>(aNameData); |
83 | 0 | if (nameHeader->format != FORMAT_0) { |
84 | 0 | gfxWarning() << "Only Name Table Format 0 is supported."; |
85 | 0 | return nullptr; |
86 | 0 | } |
87 | 0 |
|
88 | 0 | uint16_t stringOffset = nameHeader->stringOffset; |
89 | 0 |
|
90 | 0 | if (stringOffset != |
91 | 0 | sizeof(NameHeader) + (nameHeader->count * sizeof(NameRecord))) { |
92 | 0 | gfxWarning() << "Name table string offset is incorrect."; |
93 | 0 | return nullptr; |
94 | 0 | } |
95 | 0 |
|
96 | 0 | if (aDataLength < stringOffset) { |
97 | 0 | gfxWarning() << "Name data too short to contain name records."; |
98 | 0 | return nullptr; |
99 | 0 | } |
100 | 0 |
|
101 | 0 | return UniquePtr<SFNTNameTable>( |
102 | 0 | new SFNTNameTable(nameHeader, aNameData, aDataLength)); |
103 | 0 | } |
104 | | |
105 | | SFNTNameTable::SFNTNameTable(const NameHeader *aNameHeader, |
106 | | const uint8_t *aNameData, uint32_t aDataLength) |
107 | | : mFirstRecord(reinterpret_cast<const NameRecord*>(aNameData |
108 | | + sizeof(NameHeader))) |
109 | | , mEndOfRecords(mFirstRecord + aNameHeader->count) |
110 | | , mStringData(aNameData + aNameHeader->stringOffset) |
111 | | , mStringDataLength(aDataLength - aNameHeader->stringOffset) |
112 | 0 | { |
113 | 0 | MOZ_ASSERT(reinterpret_cast<const uint8_t*>(aNameHeader) == aNameData); |
114 | 0 | } |
115 | | |
116 | | static bool |
117 | | IsUTF16Encoding(const NameRecord *aNameRecord) |
118 | 0 | { |
119 | 0 | if (aNameRecord->platformID == PLATFORM_ID_MICROSOFT && |
120 | 0 | (aNameRecord->encodingID == ENCODING_ID_MICROSOFT_UNICODEBMP || |
121 | 0 | aNameRecord->encodingID == ENCODING_ID_MICROSOFT_SYMBOL)) { |
122 | 0 | return true; |
123 | 0 | } |
124 | 0 | |
125 | 0 | if (aNameRecord->platformID == PLATFORM_ID_UNICODE) { |
126 | 0 | return true; |
127 | 0 | } |
128 | 0 | |
129 | 0 | return false; |
130 | 0 | } |
131 | | |
132 | | #if defined(XP_MACOSX) |
133 | | static bool |
134 | | IsMacRomanEncoding(const NameRecord *aNameRecord) |
135 | | { |
136 | | if (aNameRecord->platformID == PLATFORM_ID_MAC && |
137 | | aNameRecord->encodingID == ENCODING_ID_MAC_ROMAN) { |
138 | | return true; |
139 | | } |
140 | | |
141 | | return false; |
142 | | } |
143 | | #endif |
144 | | |
145 | | static NameRecordMatchers* |
146 | | CreateCanonicalMatchers(const BigEndianUint16& aNameID) |
147 | 0 | { |
148 | 0 | // For Windows, we return only Microsoft platform name record |
149 | 0 | // matchers. On Mac, we return matchers for both Microsoft platform |
150 | 0 | // records and Mac platform records. |
151 | 0 | NameRecordMatchers *matchers = new NameRecordMatchers(); |
152 | 0 |
|
153 | | #if defined(XP_MACOSX) |
154 | | // First, look for the English name. |
155 | | if (!matchers->append( |
156 | | [=](const NameRecord *aNameRecord) { |
157 | | if (aNameRecord->nameID == aNameID && |
158 | | aNameRecord->languageID == LANG_ID_MAC_ENGLISH && |
159 | | aNameRecord->platformID == PLATFORM_ID_MAC && |
160 | | IsMacRomanEncoding(aNameRecord)) { |
161 | | return eNameDecoderMacRoman; |
162 | | } else { |
163 | | return eNameDecoderNone; |
164 | | } |
165 | | })) { |
166 | | MOZ_CRASH(); |
167 | | } |
168 | | |
169 | | // Second, look for all languages. |
170 | | if (!matchers->append( |
171 | | [=](const NameRecord *aNameRecord) { |
172 | | if (aNameRecord->nameID == aNameID && |
173 | | aNameRecord->platformID == PLATFORM_ID_MAC && |
174 | | IsMacRomanEncoding(aNameRecord)) { |
175 | | return eNameDecoderMacRoman; |
176 | | } else { |
177 | | return eNameDecoderNone; |
178 | | } |
179 | | })) { |
180 | | MOZ_CRASH(); |
181 | | } |
182 | | #endif /* defined(XP_MACOSX) */ |
183 | |
|
184 | 0 | // First, look for the English name (this will normally succeed). |
185 | 0 | if (!matchers->append( |
186 | 0 | [=](const NameRecord *aNameRecord) { |
187 | 0 | if (aNameRecord->nameID == aNameID && |
188 | 0 | aNameRecord->languageID == LANG_ID_MICROSOFT_EN_US && |
189 | 0 | aNameRecord->platformID == PLATFORM_ID_MICROSOFT && |
190 | 0 | IsUTF16Encoding(aNameRecord)) { |
191 | 0 | return eNameDecoderUTF16; |
192 | 0 | } else { |
193 | 0 | return eNameDecoderNone; |
194 | 0 | } |
195 | 0 | })) { |
196 | 0 | MOZ_CRASH(); |
197 | 0 | } |
198 | 0 |
|
199 | 0 | // Second, look for all languages. |
200 | 0 | if (!matchers->append( |
201 | 0 | [=](const NameRecord *aNameRecord) { |
202 | 0 | if (aNameRecord->nameID == aNameID && |
203 | 0 | aNameRecord->platformID == PLATFORM_ID_MICROSOFT && |
204 | 0 | IsUTF16Encoding(aNameRecord)) { |
205 | 0 | return eNameDecoderUTF16; |
206 | 0 | } else { |
207 | 0 | return eNameDecoderNone; |
208 | 0 | } |
209 | 0 | })) { |
210 | 0 | MOZ_CRASH(); |
211 | 0 | } |
212 | 0 |
|
213 | 0 | return matchers; |
214 | 0 | } |
215 | | |
216 | | static const NameRecordMatchers& |
217 | | FullNameMatchers() |
218 | 0 | { |
219 | 0 | static const NameRecordMatchers *sFullNameMatchers = |
220 | 0 | CreateCanonicalMatchers(NAME_ID_FULL); |
221 | 0 | return *sFullNameMatchers; |
222 | 0 | } |
223 | | |
224 | | static const NameRecordMatchers& |
225 | | FamilyMatchers() |
226 | 0 | { |
227 | 0 | static const NameRecordMatchers *sFamilyMatchers = |
228 | 0 | CreateCanonicalMatchers(NAME_ID_FAMILY); |
229 | 0 | return *sFamilyMatchers; |
230 | 0 | } |
231 | | |
232 | | static const NameRecordMatchers& |
233 | | StyleMatchers() |
234 | 0 | { |
235 | 0 | static const NameRecordMatchers *sStyleMatchers = |
236 | 0 | CreateCanonicalMatchers(NAME_ID_STYLE); |
237 | 0 | return *sStyleMatchers; |
238 | 0 | } |
239 | | |
240 | | bool |
241 | | SFNTNameTable::GetU16FullName(mozilla::u16string& aU16FullName) |
242 | 0 | { |
243 | 0 | if (ReadU16Name(FullNameMatchers(), aU16FullName)) { |
244 | 0 | return true; |
245 | 0 | } |
246 | 0 | |
247 | 0 | // If the full name record doesn't exist create the name from the family space |
248 | 0 | // concatenated with the style. |
249 | 0 | mozilla::u16string familyName; |
250 | 0 | if (!ReadU16Name(FamilyMatchers(), familyName)) { |
251 | 0 | return false; |
252 | 0 | } |
253 | 0 | |
254 | 0 | mozilla::u16string styleName; |
255 | 0 | if (!ReadU16Name(StyleMatchers(), styleName)) { |
256 | 0 | return false; |
257 | 0 | } |
258 | 0 | |
259 | 0 | aU16FullName.assign(std::move(familyName)); |
260 | 0 | aU16FullName.append(u" "); |
261 | 0 | aU16FullName.append(styleName); |
262 | 0 | return true; |
263 | 0 | } |
264 | | |
265 | | bool |
266 | | SFNTNameTable::ReadU16Name(const NameRecordMatchers& aMatchers, |
267 | | mozilla::u16string& aU16Name) |
268 | 0 | { |
269 | 0 | MOZ_ASSERT(!aMatchers.empty()); |
270 | 0 |
|
271 | 0 | for (size_t i = 0; i < aMatchers.length(); ++i) { |
272 | 0 | const NameRecord* record = mFirstRecord; |
273 | 0 | while (record != mEndOfRecords) { |
274 | 0 | switch (aMatchers[i](record)) { |
275 | 0 | case eNameDecoderUTF16: |
276 | 0 | return ReadU16NameFromU16Record(record, aU16Name); |
277 | | #if defined(XP_MACOSX) |
278 | | case eNameDecoderMacRoman: |
279 | | return ReadU16NameFromMacRomanRecord(record, aU16Name); |
280 | | #endif |
281 | 0 | case eNameDecoderNone: |
282 | 0 | break; |
283 | 0 | default: |
284 | 0 | MOZ_CRASH("Invalid matcher encoding type"); |
285 | 0 | break; |
286 | 0 | } |
287 | 0 | ++record; |
288 | 0 | } |
289 | 0 | } |
290 | 0 |
|
291 | 0 | return false; |
292 | 0 | } |
293 | | |
294 | | bool |
295 | | SFNTNameTable::ReadU16NameFromU16Record(const NameRecord *aNameRecord, |
296 | | mozilla::u16string& aU16Name) |
297 | 0 | { |
298 | 0 | uint32_t offset = aNameRecord->offset; |
299 | 0 | uint32_t length = aNameRecord->length; |
300 | 0 | if (mStringDataLength < offset + length) { |
301 | 0 | gfxWarning() << "Name data too short to contain name string."; |
302 | 0 | return false; |
303 | 0 | } |
304 | 0 |
|
305 | 0 | const uint8_t *startOfName = mStringData + offset; |
306 | 0 | size_t actualLength = length / sizeof(char16_t); |
307 | 0 | UniquePtr<char16_t[]> nameData(new char16_t[actualLength]); |
308 | 0 | NativeEndian::copyAndSwapFromBigEndian(nameData.get(), startOfName, |
309 | 0 | actualLength); |
310 | 0 |
|
311 | 0 | aU16Name.assign(nameData.get(), actualLength); |
312 | 0 | return true; |
313 | 0 | } |
314 | | |
315 | | #if defined(XP_MACOSX) |
316 | | bool |
317 | | SFNTNameTable::ReadU16NameFromMacRomanRecord(const NameRecord *aNameRecord, |
318 | | mozilla::u16string& aU16Name) |
319 | | { |
320 | | uint32_t offset = aNameRecord->offset; |
321 | | uint32_t length = aNameRecord->length; |
322 | | if (mStringDataLength < offset + length) { |
323 | | gfxWarning() << "Name data too short to contain name string."; |
324 | | return false; |
325 | | } |
326 | | if (length > INT_MAX) { |
327 | | gfxWarning() << "Name record too long to decode."; |
328 | | return false; |
329 | | } |
330 | | |
331 | | // pointer to the Mac Roman encoded string in the name record |
332 | | const uint8_t *encodedStr = mStringData + offset; |
333 | | |
334 | | CFStringRef cfString; |
335 | | cfString = CFStringCreateWithBytesNoCopy(kCFAllocatorDefault, encodedStr, |
336 | | length, kCFStringEncodingMacRoman, |
337 | | false, kCFAllocatorNull); |
338 | | |
339 | | // length (in UTF-16 code pairs) of the decoded string |
340 | | CFIndex decodedLength = CFStringGetLength(cfString); |
341 | | |
342 | | // temporary buffer |
343 | | UniquePtr<UniChar[]> u16Buffer = MakeUnique<UniChar[]>(decodedLength); |
344 | | |
345 | | CFStringGetCharacters(cfString, CFRangeMake(0, decodedLength), |
346 | | u16Buffer.get()); |
347 | | |
348 | | CFRelease(cfString); |
349 | | |
350 | | aU16Name.assign(reinterpret_cast<char16_t*>(u16Buffer.get()), decodedLength); |
351 | | |
352 | | return true; |
353 | | } |
354 | | #endif |
355 | | |
356 | | } // gfx |
357 | | } // mozilla |