/src/mozilla-central/gfx/thebes/gfxFontUtils.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | /* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ |
2 | | /* This Source Code Form is subject to the terms of the Mozilla Public |
3 | | * License, v. 2.0. If a copy of the MPL was not distributed with this |
4 | | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
5 | | |
6 | | #include "mozilla/ArrayUtils.h" |
7 | | #include "mozilla/BinarySearch.h" |
8 | | |
9 | | #include "gfxFontUtils.h" |
10 | | #include "gfxFontEntry.h" |
11 | | #include "gfxFontVariations.h" |
12 | | |
13 | | #include "nsServiceManagerUtils.h" |
14 | | |
15 | | #include "mozilla/Preferences.h" |
16 | | #include "mozilla/Services.h" |
17 | | #include "mozilla/BinarySearch.h" |
18 | | #include "mozilla/Sprintf.h" |
19 | | #include "mozilla/Unused.h" |
20 | | |
21 | | #include "nsCOMPtr.h" |
22 | | #include "nsIUUIDGenerator.h" |
23 | | #include "mozilla/Encoding.h" |
24 | | |
25 | | #include "harfbuzz/hb.h" |
26 | | |
27 | | #include "plbase64.h" |
28 | | #include "mozilla/Logging.h" |
29 | | |
30 | | #ifdef XP_MACOSX |
31 | | #include <CoreFoundation/CoreFoundation.h> |
32 | | #endif |
33 | | |
34 | 0 | #define LOG(log, args) MOZ_LOG(gfxPlatform::GetLog(log), \ |
35 | 0 | LogLevel::Debug, args) |
36 | | |
37 | 0 | #define UNICODE_BMP_LIMIT 0x10000 |
38 | | |
39 | | using namespace mozilla; |
40 | | |
41 | | #pragma pack(1) |
42 | | |
43 | | typedef struct { |
44 | | AutoSwap_PRUint16 format; |
45 | | AutoSwap_PRUint16 reserved; |
46 | | AutoSwap_PRUint32 length; |
47 | | AutoSwap_PRUint32 language; |
48 | | AutoSwap_PRUint32 startCharCode; |
49 | | AutoSwap_PRUint32 numChars; |
50 | | } Format10CmapHeader; |
51 | | |
52 | | typedef struct { |
53 | | AutoSwap_PRUint16 format; |
54 | | AutoSwap_PRUint16 reserved; |
55 | | AutoSwap_PRUint32 length; |
56 | | AutoSwap_PRUint32 language; |
57 | | AutoSwap_PRUint32 numGroups; |
58 | | } Format12CmapHeader; |
59 | | |
60 | | typedef struct { |
61 | | AutoSwap_PRUint32 startCharCode; |
62 | | AutoSwap_PRUint32 endCharCode; |
63 | | AutoSwap_PRUint32 startGlyphId; |
64 | | } Format12Group; |
65 | | |
66 | | #pragma pack() |
67 | | |
68 | | void |
69 | | gfxSparseBitSet::Dump(const char* aPrefix, eGfxLog aWhichLog) const |
70 | 0 | { |
71 | 0 | uint32_t numBlocks = mBlockIndex.Length(); |
72 | 0 |
|
73 | 0 | for (uint32_t b = 0; b < numBlocks; b++) { |
74 | 0 | if (mBlockIndex[b] == NO_BLOCK) { |
75 | 0 | continue; |
76 | 0 | } |
77 | 0 | const Block* block = &mBlocks[mBlockIndex[b]]; |
78 | 0 | const int BUFSIZE = 256; |
79 | 0 | char outStr[BUFSIZE]; |
80 | 0 | int index = 0; |
81 | 0 | index += snprintf(&outStr[index], BUFSIZE - index, "%s u+%6.6x [", |
82 | 0 | aPrefix, (b * BLOCK_SIZE_BITS)); |
83 | 0 | for (int i = 0; i < 32; i += 4) { |
84 | 0 | for (int j = i; j < i + 4; j++) { |
85 | 0 | uint8_t bits = block->mBits[j]; |
86 | 0 | uint8_t flip1 = ((bits & 0xaa) >> 1) | ((bits & 0x55) << 1); |
87 | 0 | uint8_t flip2 = ((flip1 & 0xcc) >> 2) | ((flip1 & 0x33) << 2); |
88 | 0 | uint8_t flipped = ((flip2 & 0xf0) >> 4) | ((flip2 & 0x0f) << 4); |
89 | 0 |
|
90 | 0 | index += snprintf(&outStr[index], BUFSIZE - index, "%2.2x", flipped); |
91 | 0 | } |
92 | 0 | if (i + 4 != 32) index += snprintf(&outStr[index], BUFSIZE - index, " "); |
93 | 0 | } |
94 | 0 | Unused << snprintf(&outStr[index], BUFSIZE - index, "]"); |
95 | 0 | LOG(aWhichLog, ("%s", outStr)); |
96 | 0 | } |
97 | 0 | } |
98 | | |
99 | | nsresult |
100 | | gfxFontUtils::ReadCMAPTableFormat10(const uint8_t *aBuf, uint32_t aLength, |
101 | | gfxSparseBitSet& aCharacterMap) |
102 | 0 | { |
103 | 0 | // Ensure table is large enough that we can safely read the header |
104 | 0 | NS_ENSURE_TRUE(aLength >= sizeof(Format10CmapHeader), |
105 | 0 | NS_ERROR_GFX_CMAP_MALFORMED); |
106 | 0 |
|
107 | 0 | // Sanity-check header fields |
108 | 0 | const Format10CmapHeader *cmap10 = |
109 | 0 | reinterpret_cast<const Format10CmapHeader*>(aBuf); |
110 | 0 | NS_ENSURE_TRUE(uint16_t(cmap10->format) == 10, |
111 | 0 | NS_ERROR_GFX_CMAP_MALFORMED); |
112 | 0 | NS_ENSURE_TRUE(uint16_t(cmap10->reserved) == 0, |
113 | 0 | NS_ERROR_GFX_CMAP_MALFORMED); |
114 | 0 |
|
115 | 0 | uint32_t tablelen = cmap10->length; |
116 | 0 | NS_ENSURE_TRUE(tablelen >= sizeof(Format10CmapHeader) && |
117 | 0 | tablelen <= aLength, NS_ERROR_GFX_CMAP_MALFORMED); |
118 | 0 |
|
119 | 0 | NS_ENSURE_TRUE(cmap10->language == 0, NS_ERROR_GFX_CMAP_MALFORMED); |
120 | 0 |
|
121 | 0 | uint32_t numChars = cmap10->numChars; |
122 | 0 | NS_ENSURE_TRUE(tablelen == sizeof(Format10CmapHeader) + |
123 | 0 | numChars * sizeof(uint16_t), NS_ERROR_GFX_CMAP_MALFORMED); |
124 | 0 |
|
125 | 0 | uint32_t charCode = cmap10->startCharCode; |
126 | 0 | NS_ENSURE_TRUE(charCode <= CMAP_MAX_CODEPOINT && |
127 | 0 | charCode + numChars <= CMAP_MAX_CODEPOINT, |
128 | 0 | NS_ERROR_GFX_CMAP_MALFORMED); |
129 | 0 |
|
130 | 0 | // glyphs[] array immediately follows the subtable header |
131 | 0 | const AutoSwap_PRUint16 *glyphs = |
132 | 0 | reinterpret_cast<const AutoSwap_PRUint16 *>(cmap10 + 1); |
133 | 0 |
|
134 | 0 | for (uint32_t i = 0; i < numChars; ++i) { |
135 | 0 | if (uint16_t(*glyphs) != 0) { |
136 | 0 | aCharacterMap.set(charCode); |
137 | 0 | } |
138 | 0 | ++charCode; |
139 | 0 | ++glyphs; |
140 | 0 | } |
141 | 0 |
|
142 | 0 | aCharacterMap.Compact(); |
143 | 0 |
|
144 | 0 | return NS_OK; |
145 | 0 | } |
146 | | |
147 | | nsresult |
148 | | gfxFontUtils::ReadCMAPTableFormat12or13(const uint8_t *aBuf, uint32_t aLength, |
149 | | gfxSparseBitSet& aCharacterMap) |
150 | 0 | { |
151 | 0 | // Format 13 has the same structure as format 12, the only difference is |
152 | 0 | // the interpretation of the glyphID field. So we can share the code here |
153 | 0 | // that reads the table and just records character coverage. |
154 | 0 |
|
155 | 0 | // Ensure table is large enough that we can safely read the header |
156 | 0 | NS_ENSURE_TRUE(aLength >= sizeof(Format12CmapHeader), |
157 | 0 | NS_ERROR_GFX_CMAP_MALFORMED); |
158 | 0 |
|
159 | 0 | // Sanity-check header fields |
160 | 0 | const Format12CmapHeader *cmap12 = |
161 | 0 | reinterpret_cast<const Format12CmapHeader*>(aBuf); |
162 | 0 | NS_ENSURE_TRUE(uint16_t(cmap12->format) == 12 || |
163 | 0 | uint16_t(cmap12->format) == 13, |
164 | 0 | NS_ERROR_GFX_CMAP_MALFORMED); |
165 | 0 | NS_ENSURE_TRUE(uint16_t(cmap12->reserved) == 0, |
166 | 0 | NS_ERROR_GFX_CMAP_MALFORMED); |
167 | 0 |
|
168 | 0 | uint32_t tablelen = cmap12->length; |
169 | 0 | NS_ENSURE_TRUE(tablelen >= sizeof(Format12CmapHeader) && |
170 | 0 | tablelen <= aLength, NS_ERROR_GFX_CMAP_MALFORMED); |
171 | 0 |
|
172 | 0 | NS_ENSURE_TRUE(cmap12->language == 0, NS_ERROR_GFX_CMAP_MALFORMED); |
173 | 0 |
|
174 | 0 | // Check that the table is large enough for the group array |
175 | 0 | const uint32_t numGroups = cmap12->numGroups; |
176 | 0 | NS_ENSURE_TRUE((tablelen - sizeof(Format12CmapHeader)) / |
177 | 0 | sizeof(Format12Group) >= numGroups, |
178 | 0 | NS_ERROR_GFX_CMAP_MALFORMED); |
179 | 0 |
|
180 | 0 | // The array of groups immediately follows the subtable header. |
181 | 0 | const Format12Group *group = |
182 | 0 | reinterpret_cast<const Format12Group*>(aBuf + sizeof(Format12CmapHeader)); |
183 | 0 |
|
184 | 0 | // Check that groups are in correct order and do not overlap, |
185 | 0 | // and record character coverage in aCharacterMap. |
186 | 0 | uint32_t prevEndCharCode = 0; |
187 | 0 | for (uint32_t i = 0; i < numGroups; i++, group++) { |
188 | 0 | uint32_t startCharCode = group->startCharCode; |
189 | 0 | const uint32_t endCharCode = group->endCharCode; |
190 | 0 | NS_ENSURE_TRUE((prevEndCharCode < startCharCode || i == 0) && |
191 | 0 | startCharCode <= endCharCode && |
192 | 0 | endCharCode <= CMAP_MAX_CODEPOINT, |
193 | 0 | NS_ERROR_GFX_CMAP_MALFORMED); |
194 | 0 | // don't include a character that maps to glyph ID 0 (.notdef) |
195 | 0 | if (group->startGlyphId == 0) { |
196 | 0 | startCharCode++; |
197 | 0 | } |
198 | 0 | if (startCharCode <= endCharCode) { |
199 | 0 | aCharacterMap.SetRange(startCharCode, endCharCode); |
200 | 0 | } |
201 | 0 | prevEndCharCode = endCharCode; |
202 | 0 | } |
203 | 0 |
|
204 | 0 | aCharacterMap.Compact(); |
205 | 0 |
|
206 | 0 | return NS_OK; |
207 | 0 | } |
208 | | |
209 | | nsresult |
210 | | gfxFontUtils::ReadCMAPTableFormat4(const uint8_t *aBuf, uint32_t aLength, |
211 | | gfxSparseBitSet& aCharacterMap) |
212 | 0 | { |
213 | 0 | enum { |
214 | 0 | OffsetFormat = 0, |
215 | 0 | OffsetLength = 2, |
216 | 0 | OffsetLanguage = 4, |
217 | 0 | OffsetSegCountX2 = 6 |
218 | 0 | }; |
219 | 0 |
|
220 | 0 | NS_ENSURE_TRUE(ReadShortAt(aBuf, OffsetFormat) == 4, |
221 | 0 | NS_ERROR_GFX_CMAP_MALFORMED); |
222 | 0 | uint16_t tablelen = ReadShortAt(aBuf, OffsetLength); |
223 | 0 | NS_ENSURE_TRUE(tablelen <= aLength, NS_ERROR_GFX_CMAP_MALFORMED); |
224 | 0 | NS_ENSURE_TRUE(tablelen > 16, NS_ERROR_GFX_CMAP_MALFORMED); |
225 | 0 |
|
226 | 0 | // This field should normally (except for Mac platform subtables) be zero according to |
227 | 0 | // the OT spec, but some buggy fonts have lang = 1 (which would be English for MacOS). |
228 | 0 | // E.g. Arial Narrow Bold, v. 1.1 (Tiger), Arial Unicode MS (see bug 530614). |
229 | 0 | // So accept either zero or one here; the error should be harmless. |
230 | 0 | NS_ENSURE_TRUE((ReadShortAt(aBuf, OffsetLanguage) & 0xfffe) == 0, |
231 | 0 | NS_ERROR_GFX_CMAP_MALFORMED); |
232 | 0 |
|
233 | 0 | uint16_t segCountX2 = ReadShortAt(aBuf, OffsetSegCountX2); |
234 | 0 | NS_ENSURE_TRUE(tablelen >= 16 + (segCountX2 * 4), |
235 | 0 | NS_ERROR_GFX_CMAP_MALFORMED); |
236 | 0 |
|
237 | 0 | const uint16_t segCount = segCountX2 / 2; |
238 | 0 |
|
239 | 0 | const uint16_t *endCounts = reinterpret_cast<const uint16_t*>(aBuf + 14); |
240 | 0 | const uint16_t *startCounts = endCounts + 1 /* skip one uint16_t for reservedPad */ + segCount; |
241 | 0 | const uint16_t *idDeltas = startCounts + segCount; |
242 | 0 | const uint16_t *idRangeOffsets = idDeltas + segCount; |
243 | 0 | uint16_t prevEndCount = 0; |
244 | 0 | for (uint16_t i = 0; i < segCount; i++) { |
245 | 0 | const uint16_t endCount = ReadShortAt16(endCounts, i); |
246 | 0 | const uint16_t startCount = ReadShortAt16(startCounts, i); |
247 | 0 | const uint16_t idRangeOffset = ReadShortAt16(idRangeOffsets, i); |
248 | 0 |
|
249 | 0 | // sanity-check range |
250 | 0 | // This permits ranges to overlap by 1 character, which is strictly |
251 | 0 | // incorrect but occurs in Baskerville on OS X 10.7 (see bug 689087), |
252 | 0 | // and appears to be harmless in practice |
253 | 0 | NS_ENSURE_TRUE(startCount >= prevEndCount && startCount <= endCount, |
254 | 0 | NS_ERROR_GFX_CMAP_MALFORMED); |
255 | 0 | prevEndCount = endCount; |
256 | 0 |
|
257 | 0 | if (idRangeOffset == 0) { |
258 | 0 | // figure out if there's a code in the range that would map to |
259 | 0 | // glyph ID 0 (.notdef); if so, we need to skip setting that |
260 | 0 | // character code in the map |
261 | 0 | const uint16_t skipCode = 65536 - ReadShortAt16(idDeltas, i); |
262 | 0 | if (startCount < skipCode) { |
263 | 0 | aCharacterMap.SetRange(startCount, |
264 | 0 | std::min<uint16_t>(skipCode - 1, |
265 | 0 | endCount)); |
266 | 0 | } |
267 | 0 | if (skipCode < endCount) { |
268 | 0 | aCharacterMap.SetRange(std::max<uint16_t>(startCount, |
269 | 0 | skipCode + 1), |
270 | 0 | endCount); |
271 | 0 | } |
272 | 0 | } else { |
273 | 0 | // const uint16_t idDelta = ReadShortAt16(idDeltas, i); // Unused: self-documenting. |
274 | 0 | for (uint32_t c = startCount; c <= endCount; ++c) { |
275 | 0 | if (c == 0xFFFF) |
276 | 0 | break; |
277 | 0 | |
278 | 0 | const uint16_t *gdata = (idRangeOffset/2 |
279 | 0 | + (c - startCount) |
280 | 0 | + &idRangeOffsets[i]); |
281 | 0 |
|
282 | 0 | NS_ENSURE_TRUE((uint8_t*)gdata > aBuf && |
283 | 0 | (uint8_t*)gdata < aBuf + aLength, |
284 | 0 | NS_ERROR_GFX_CMAP_MALFORMED); |
285 | 0 |
|
286 | 0 | // make sure we have a glyph |
287 | 0 | if (*gdata != 0) { |
288 | 0 | // The glyph index at this point is: |
289 | 0 | uint16_t glyph = ReadShortAt16(idDeltas, i) + *gdata; |
290 | 0 | if (glyph) { |
291 | 0 | aCharacterMap.set(c); |
292 | 0 | } |
293 | 0 | } |
294 | 0 | } |
295 | 0 | } |
296 | 0 | } |
297 | 0 |
|
298 | 0 | aCharacterMap.Compact(); |
299 | 0 |
|
300 | 0 | return NS_OK; |
301 | 0 | } |
302 | | |
303 | | nsresult |
304 | | gfxFontUtils::ReadCMAPTableFormat14(const uint8_t *aBuf, uint32_t aLength, |
305 | | UniquePtr<uint8_t[]>& aTable) |
306 | 0 | { |
307 | 0 | enum { |
308 | 0 | OffsetFormat = 0, |
309 | 0 | OffsetTableLength = 2, |
310 | 0 | OffsetNumVarSelectorRecords = 6, |
311 | 0 | OffsetVarSelectorRecords = 10, |
312 | 0 |
|
313 | 0 | SizeOfVarSelectorRecord = 11, |
314 | 0 | VSRecOffsetVarSelector = 0, |
315 | 0 | VSRecOffsetDefUVSOffset = 3, |
316 | 0 | VSRecOffsetNonDefUVSOffset = 7, |
317 | 0 |
|
318 | 0 | SizeOfDefUVSTable = 4, |
319 | 0 | DefUVSOffsetStartUnicodeValue = 0, |
320 | 0 | DefUVSOffsetAdditionalCount = 3, |
321 | 0 |
|
322 | 0 | SizeOfNonDefUVSTable = 5, |
323 | 0 | NonDefUVSOffsetUnicodeValue = 0, |
324 | 0 | NonDefUVSOffsetGlyphID = 3 |
325 | 0 | }; |
326 | 0 | NS_ENSURE_TRUE(aLength >= OffsetVarSelectorRecords, |
327 | 0 | NS_ERROR_GFX_CMAP_MALFORMED); |
328 | 0 |
|
329 | 0 | NS_ENSURE_TRUE(ReadShortAt(aBuf, OffsetFormat) == 14, |
330 | 0 | NS_ERROR_GFX_CMAP_MALFORMED); |
331 | 0 |
|
332 | 0 | uint32_t tablelen = ReadLongAt(aBuf, OffsetTableLength); |
333 | 0 | NS_ENSURE_TRUE(tablelen <= aLength, NS_ERROR_GFX_CMAP_MALFORMED); |
334 | 0 | NS_ENSURE_TRUE(tablelen >= OffsetVarSelectorRecords, |
335 | 0 | NS_ERROR_GFX_CMAP_MALFORMED); |
336 | 0 |
|
337 | 0 | const uint32_t numVarSelectorRecords = ReadLongAt(aBuf, OffsetNumVarSelectorRecords); |
338 | 0 | NS_ENSURE_TRUE((tablelen - OffsetVarSelectorRecords) / |
339 | 0 | SizeOfVarSelectorRecord >= numVarSelectorRecords, |
340 | 0 | NS_ERROR_GFX_CMAP_MALFORMED); |
341 | 0 |
|
342 | 0 | const uint8_t *records = aBuf + OffsetVarSelectorRecords; |
343 | 0 | for (uint32_t i = 0; i < numVarSelectorRecords; |
344 | 0 | i++, records += SizeOfVarSelectorRecord) { |
345 | 0 | const uint32_t varSelector = ReadUint24At(records, VSRecOffsetVarSelector); |
346 | 0 | const uint32_t defUVSOffset = ReadLongAt(records, VSRecOffsetDefUVSOffset); |
347 | 0 | const uint32_t nonDefUVSOffset = ReadLongAt(records, VSRecOffsetNonDefUVSOffset); |
348 | 0 | NS_ENSURE_TRUE(varSelector <= CMAP_MAX_CODEPOINT && |
349 | 0 | defUVSOffset <= tablelen - 4 && |
350 | 0 | nonDefUVSOffset <= tablelen - 4, |
351 | 0 | NS_ERROR_GFX_CMAP_MALFORMED); |
352 | 0 |
|
353 | 0 | if (defUVSOffset) { |
354 | 0 | const uint32_t numUnicodeValueRanges = ReadLongAt(aBuf, defUVSOffset); |
355 | 0 | NS_ENSURE_TRUE((tablelen - defUVSOffset) / |
356 | 0 | SizeOfDefUVSTable >= numUnicodeValueRanges, |
357 | 0 | NS_ERROR_GFX_CMAP_MALFORMED); |
358 | 0 | const uint8_t *tables = aBuf + defUVSOffset + 4; |
359 | 0 | uint32_t prevEndUnicode = 0; |
360 | 0 | for (uint32_t j = 0; j < numUnicodeValueRanges; j++, tables += SizeOfDefUVSTable) { |
361 | 0 | const uint32_t startUnicode = ReadUint24At(tables, DefUVSOffsetStartUnicodeValue); |
362 | 0 | const uint32_t endUnicode = startUnicode + tables[DefUVSOffsetAdditionalCount]; |
363 | 0 | NS_ENSURE_TRUE((prevEndUnicode < startUnicode || j == 0) && |
364 | 0 | endUnicode <= CMAP_MAX_CODEPOINT, |
365 | 0 | NS_ERROR_GFX_CMAP_MALFORMED); |
366 | 0 | prevEndUnicode = endUnicode; |
367 | 0 | } |
368 | 0 | } |
369 | 0 |
|
370 | 0 | if (nonDefUVSOffset) { |
371 | 0 | const uint32_t numUVSMappings = ReadLongAt(aBuf, nonDefUVSOffset); |
372 | 0 | NS_ENSURE_TRUE((tablelen - nonDefUVSOffset) / |
373 | 0 | SizeOfNonDefUVSTable >= numUVSMappings, |
374 | 0 | NS_ERROR_GFX_CMAP_MALFORMED); |
375 | 0 | const uint8_t *tables = aBuf + nonDefUVSOffset + 4; |
376 | 0 | uint32_t prevUnicode = 0; |
377 | 0 | for (uint32_t j = 0; j < numUVSMappings; j++, tables += SizeOfNonDefUVSTable) { |
378 | 0 | const uint32_t unicodeValue = ReadUint24At(tables, NonDefUVSOffsetUnicodeValue); |
379 | 0 | NS_ENSURE_TRUE((prevUnicode < unicodeValue || j == 0) && |
380 | 0 | unicodeValue <= CMAP_MAX_CODEPOINT, |
381 | 0 | NS_ERROR_GFX_CMAP_MALFORMED); |
382 | 0 | prevUnicode = unicodeValue; |
383 | 0 | } |
384 | 0 | } |
385 | 0 | } |
386 | 0 |
|
387 | 0 | aTable = MakeUnique<uint8_t[]>(tablelen); |
388 | 0 | memcpy(aTable.get(), aBuf, tablelen); |
389 | 0 |
|
390 | 0 | return NS_OK; |
391 | 0 | } |
392 | | |
393 | | // For fonts with two format-4 tables, the first one (Unicode platform) is preferred on the Mac; |
394 | | // on other platforms we allow the Microsoft-platform subtable to replace it. |
395 | | |
396 | | #if defined(XP_MACOSX) |
397 | | #define acceptableFormat4(p,e,k) (((p) == PLATFORM_ID_MICROSOFT && (e) == EncodingIDMicrosoft && !(k)) || \ |
398 | | ((p) == PLATFORM_ID_UNICODE)) |
399 | | |
400 | | #define acceptableUCS4Encoding(p, e, k) \ |
401 | | (((p) == PLATFORM_ID_MICROSOFT && (e) == EncodingIDUCS4ForMicrosoftPlatform) && (k) != 12 || \ |
402 | | ((p) == PLATFORM_ID_UNICODE && \ |
403 | | ((e) != EncodingIDUVSForUnicodePlatform))) |
404 | | #else |
405 | 0 | #define acceptableFormat4(p,e,k) (((p) == PLATFORM_ID_MICROSOFT && (e) == EncodingIDMicrosoft) || \ |
406 | 0 | ((p) == PLATFORM_ID_UNICODE)) |
407 | | |
408 | | #define acceptableUCS4Encoding(p, e, k) \ |
409 | 0 | ((p) == PLATFORM_ID_MICROSOFT && (e) == EncodingIDUCS4ForMicrosoftPlatform) |
410 | | #endif |
411 | | |
412 | 0 | #define acceptablePlatform(p) ((p) == PLATFORM_ID_UNICODE || (p) == PLATFORM_ID_MICROSOFT) |
413 | 0 | #define isSymbol(p,e) ((p) == PLATFORM_ID_MICROSOFT && (e) == EncodingIDSymbol) |
414 | 0 | #define isUVSEncoding(p, e) ((p) == PLATFORM_ID_UNICODE && (e) == EncodingIDUVSForUnicodePlatform) |
415 | | |
416 | | uint32_t |
417 | | gfxFontUtils::FindPreferredSubtable(const uint8_t *aBuf, uint32_t aBufLength, |
418 | | uint32_t *aTableOffset, |
419 | | uint32_t *aUVSTableOffset) |
420 | 0 | { |
421 | 0 | enum { |
422 | 0 | OffsetVersion = 0, |
423 | 0 | OffsetNumTables = 2, |
424 | 0 | SizeOfHeader = 4, |
425 | 0 |
|
426 | 0 | TableOffsetPlatformID = 0, |
427 | 0 | TableOffsetEncodingID = 2, |
428 | 0 | TableOffsetOffset = 4, |
429 | 0 | SizeOfTable = 8, |
430 | 0 |
|
431 | 0 | SubtableOffsetFormat = 0 |
432 | 0 | }; |
433 | 0 | enum { |
434 | 0 | EncodingIDSymbol = 0, |
435 | 0 | EncodingIDMicrosoft = 1, |
436 | 0 | EncodingIDDefaultForUnicodePlatform = 0, |
437 | 0 | EncodingIDUCS4ForUnicodePlatform = 3, |
438 | 0 | EncodingIDUVSForUnicodePlatform = 5, |
439 | 0 | EncodingIDUCS4ForMicrosoftPlatform = 10 |
440 | 0 | }; |
441 | 0 |
|
442 | 0 | if (aUVSTableOffset) { |
443 | 0 | *aUVSTableOffset = 0; |
444 | 0 | } |
445 | 0 |
|
446 | 0 | if (!aBuf || aBufLength < SizeOfHeader) { |
447 | 0 | // cmap table is missing, or too small to contain header fields! |
448 | 0 | return 0; |
449 | 0 | } |
450 | 0 | |
451 | 0 | // uint16_t version = ReadShortAt(aBuf, OffsetVersion); // Unused: self-documenting. |
452 | 0 | uint16_t numTables = ReadShortAt(aBuf, OffsetNumTables); |
453 | 0 | if (aBufLength < uint32_t(SizeOfHeader + numTables * SizeOfTable)) { |
454 | 0 | return 0; |
455 | 0 | } |
456 | 0 | |
457 | 0 | // save the format we want here |
458 | 0 | uint32_t keepFormat = 0; |
459 | 0 |
|
460 | 0 | const uint8_t *table = aBuf + SizeOfHeader; |
461 | 0 | for (uint16_t i = 0; i < numTables; ++i, table += SizeOfTable) { |
462 | 0 | const uint16_t platformID = ReadShortAt(table, TableOffsetPlatformID); |
463 | 0 | if (!acceptablePlatform(platformID)) |
464 | 0 | continue; |
465 | 0 | |
466 | 0 | const uint16_t encodingID = ReadShortAt(table, TableOffsetEncodingID); |
467 | 0 | const uint32_t offset = ReadLongAt(table, TableOffsetOffset); |
468 | 0 | if (aBufLength - 2 < offset) { |
469 | 0 | // this subtable is not valid - beyond end of buffer |
470 | 0 | return 0; |
471 | 0 | } |
472 | 0 | |
473 | 0 | const uint8_t *subtable = aBuf + offset; |
474 | 0 | const uint16_t format = ReadShortAt(subtable, SubtableOffsetFormat); |
475 | 0 |
|
476 | 0 | if (isSymbol(platformID, encodingID)) { |
477 | 0 | keepFormat = format; |
478 | 0 | *aTableOffset = offset; |
479 | 0 | break; |
480 | 0 | } else if (format == 4 && acceptableFormat4(platformID, encodingID, keepFormat)) { |
481 | 0 | keepFormat = format; |
482 | 0 | *aTableOffset = offset; |
483 | 0 | } else if ((format == 10 || format == 12 || format == 13) && |
484 | 0 | acceptableUCS4Encoding(platformID, encodingID, keepFormat)) { |
485 | 0 | keepFormat = format; |
486 | 0 | *aTableOffset = offset; |
487 | 0 | if (platformID > PLATFORM_ID_UNICODE || !aUVSTableOffset || *aUVSTableOffset) { |
488 | 0 | break; // we don't want to try anything else when this format is available. |
489 | 0 | } |
490 | 0 | } else if (format == 14 && isUVSEncoding(platformID, encodingID) && aUVSTableOffset) { |
491 | 0 | *aUVSTableOffset = offset; |
492 | 0 | if (keepFormat == 10 || keepFormat == 12) { |
493 | 0 | break; |
494 | 0 | } |
495 | 0 | } |
496 | 0 | } |
497 | 0 |
|
498 | 0 | return keepFormat; |
499 | 0 | } |
500 | | |
501 | | nsresult |
502 | | gfxFontUtils::ReadCMAP(const uint8_t *aBuf, uint32_t aBufLength, |
503 | | gfxSparseBitSet& aCharacterMap, |
504 | | uint32_t& aUVSOffset) |
505 | 0 | { |
506 | 0 | uint32_t offset; |
507 | 0 | uint32_t format = FindPreferredSubtable(aBuf, aBufLength, |
508 | 0 | &offset, &aUVSOffset); |
509 | 0 |
|
510 | 0 | switch (format) { |
511 | 0 | case 4: |
512 | 0 | return ReadCMAPTableFormat4(aBuf + offset, aBufLength - offset, |
513 | 0 | aCharacterMap); |
514 | 0 |
|
515 | 0 | case 10: |
516 | 0 | return ReadCMAPTableFormat10(aBuf + offset, aBufLength - offset, |
517 | 0 | aCharacterMap); |
518 | 0 |
|
519 | 0 | case 12: |
520 | 0 | case 13: |
521 | 0 | return ReadCMAPTableFormat12or13(aBuf + offset, aBufLength - offset, |
522 | 0 | aCharacterMap); |
523 | 0 |
|
524 | 0 | default: |
525 | 0 | break; |
526 | 0 | } |
527 | 0 | |
528 | 0 | return NS_ERROR_FAILURE; |
529 | 0 | } |
530 | | |
531 | | #pragma pack(1) |
532 | | |
533 | | typedef struct { |
534 | | AutoSwap_PRUint16 format; |
535 | | AutoSwap_PRUint16 length; |
536 | | AutoSwap_PRUint16 language; |
537 | | AutoSwap_PRUint16 segCountX2; |
538 | | AutoSwap_PRUint16 searchRange; |
539 | | AutoSwap_PRUint16 entrySelector; |
540 | | AutoSwap_PRUint16 rangeShift; |
541 | | |
542 | | AutoSwap_PRUint16 arrays[1]; |
543 | | } Format4Cmap; |
544 | | |
545 | | typedef struct { |
546 | | AutoSwap_PRUint16 format; |
547 | | AutoSwap_PRUint32 length; |
548 | | AutoSwap_PRUint32 numVarSelectorRecords; |
549 | | |
550 | | typedef struct { |
551 | | AutoSwap_PRUint24 varSelector; |
552 | | AutoSwap_PRUint32 defaultUVSOffset; |
553 | | AutoSwap_PRUint32 nonDefaultUVSOffset; |
554 | | } VarSelectorRecord; |
555 | | |
556 | | VarSelectorRecord varSelectorRecords[1]; |
557 | | } Format14Cmap; |
558 | | |
559 | | typedef struct { |
560 | | AutoSwap_PRUint32 numUVSMappings; |
561 | | |
562 | | typedef struct { |
563 | | AutoSwap_PRUint24 unicodeValue; |
564 | | AutoSwap_PRUint16 glyphID; |
565 | | } UVSMapping; |
566 | | |
567 | | UVSMapping uvsMappings[1]; |
568 | | } NonDefUVSTable; |
569 | | |
570 | | #pragma pack() |
571 | | |
572 | | uint32_t |
573 | | gfxFontUtils::MapCharToGlyphFormat4(const uint8_t* aBuf, uint32_t aLength, |
574 | | char16_t aCh) |
575 | 0 | { |
576 | 0 | const Format4Cmap *cmap4 = reinterpret_cast<const Format4Cmap*>(aBuf); |
577 | 0 |
|
578 | 0 | uint16_t segCount = (uint16_t)(cmap4->segCountX2) / 2; |
579 | 0 |
|
580 | 0 | const AutoSwap_PRUint16* endCodes = &cmap4->arrays[0]; |
581 | 0 | const AutoSwap_PRUint16* startCodes = &cmap4->arrays[segCount + 1]; |
582 | 0 | const AutoSwap_PRUint16* idDelta = &startCodes[segCount]; |
583 | 0 | const AutoSwap_PRUint16* idRangeOffset = &idDelta[segCount]; |
584 | 0 |
|
585 | 0 | // Sanity-check that the fixed-size arrays don't exceed the buffer. |
586 | 0 | const uint8_t* const limit = aBuf + aLength; |
587 | 0 | if ((const uint8_t*)(&idRangeOffset[segCount]) > limit) { |
588 | 0 | return 0; // broken font, just bail out safely |
589 | 0 | } |
590 | 0 | |
591 | 0 | // For most efficient binary search, we want to work on a range of segment |
592 | 0 | // indexes that is a power of 2 so that we can always halve it by shifting. |
593 | 0 | // So we find the largest power of 2 that is <= segCount. |
594 | 0 | // We will offset this range by segOffset so as to reach the end |
595 | 0 | // of the table, provided that doesn't put us beyond the target |
596 | 0 | // value from the outset. |
597 | 0 | uint32_t powerOf2 = mozilla::FindHighestBit(segCount); |
598 | 0 | uint32_t segOffset = segCount - powerOf2; |
599 | 0 | uint32_t idx = 0; |
600 | 0 |
|
601 | 0 | if (uint16_t(startCodes[segOffset]) <= aCh) { |
602 | 0 | idx = segOffset; |
603 | 0 | } |
604 | 0 |
|
605 | 0 | // Repeatedly halve the size of the range until we find the target group |
606 | 0 | while (powerOf2 > 1) { |
607 | 0 | powerOf2 >>= 1; |
608 | 0 | if (uint16_t(startCodes[idx + powerOf2]) <= aCh) { |
609 | 0 | idx += powerOf2; |
610 | 0 | } |
611 | 0 | } |
612 | 0 |
|
613 | 0 | if (aCh >= uint16_t(startCodes[idx]) && aCh <= uint16_t(endCodes[idx])) { |
614 | 0 | uint16_t result; |
615 | 0 | if (uint16_t(idRangeOffset[idx]) == 0) { |
616 | 0 | result = aCh; |
617 | 0 | } else { |
618 | 0 | uint16_t offset = aCh - uint16_t(startCodes[idx]); |
619 | 0 | const AutoSwap_PRUint16* glyphIndexTable = |
620 | 0 | (const AutoSwap_PRUint16*)((const char*)&idRangeOffset[idx] + |
621 | 0 | uint16_t(idRangeOffset[idx])); |
622 | 0 | if ((const uint8_t*)(glyphIndexTable + offset + 1) > limit) { |
623 | 0 | return 0; // broken font, just bail out safely |
624 | 0 | } |
625 | 0 | result = glyphIndexTable[offset]; |
626 | 0 | } |
627 | 0 |
|
628 | 0 | // Note that this is unsigned 16-bit arithmetic, and may wrap around |
629 | 0 | // (which is required behavior per spec) |
630 | 0 | result += uint16_t(idDelta[idx]); |
631 | 0 | return result; |
632 | 0 | } |
633 | 0 | |
634 | 0 | return 0; |
635 | 0 | } |
636 | | |
637 | | uint32_t |
638 | | gfxFontUtils::MapCharToGlyphFormat10(const uint8_t *aBuf, uint32_t aCh) |
639 | 0 | { |
640 | 0 | const Format10CmapHeader *cmap10 = |
641 | 0 | reinterpret_cast<const Format10CmapHeader*>(aBuf); |
642 | 0 |
|
643 | 0 | uint32_t startChar = cmap10->startCharCode; |
644 | 0 | uint32_t numChars = cmap10->numChars; |
645 | 0 |
|
646 | 0 | if (aCh < startChar || aCh >= startChar + numChars) { |
647 | 0 | return 0; |
648 | 0 | } |
649 | 0 | |
650 | 0 | const AutoSwap_PRUint16 *glyphs = |
651 | 0 | reinterpret_cast<const AutoSwap_PRUint16 *>(cmap10 + 1); |
652 | 0 |
|
653 | 0 | uint16_t glyph = glyphs[aCh - startChar]; |
654 | 0 | return glyph; |
655 | 0 | } |
656 | | |
657 | | uint32_t |
658 | | gfxFontUtils::MapCharToGlyphFormat12or13(const uint8_t *aBuf, uint32_t aCh) |
659 | 0 | { |
660 | 0 | // The only difference between formats 12 and 13 is the interpretation of |
661 | 0 | // the glyphId field. So the code here uses the same "Format12" structures, |
662 | 0 | // etc., to handle both subtable formats. |
663 | 0 |
|
664 | 0 | const Format12CmapHeader *cmap12 = |
665 | 0 | reinterpret_cast<const Format12CmapHeader*>(aBuf); |
666 | 0 |
|
667 | 0 | // We know that numGroups is within range for the subtable size |
668 | 0 | // because it was checked by ReadCMAPTableFormat12or13. |
669 | 0 | uint32_t numGroups = cmap12->numGroups; |
670 | 0 |
|
671 | 0 | // The array of groups immediately follows the subtable header. |
672 | 0 | const Format12Group *groups = |
673 | 0 | reinterpret_cast<const Format12Group*>(aBuf + sizeof(Format12CmapHeader)); |
674 | 0 |
|
675 | 0 | // For most efficient binary search, we want to work on a range that |
676 | 0 | // is a power of 2 so that we can always halve it by shifting. |
677 | 0 | // So we find the largest power of 2 that is <= numGroups. |
678 | 0 | // We will offset this range by rangeOffset so as to reach the end |
679 | 0 | // of the table, provided that doesn't put us beyond the target |
680 | 0 | // value from the outset. |
681 | 0 | uint32_t powerOf2 = mozilla::FindHighestBit(numGroups); |
682 | 0 | uint32_t rangeOffset = numGroups - powerOf2; |
683 | 0 | uint32_t range = 0; |
684 | 0 | uint32_t startCharCode; |
685 | 0 |
|
686 | 0 | if (groups[rangeOffset].startCharCode <= aCh) { |
687 | 0 | range = rangeOffset; |
688 | 0 | } |
689 | 0 |
|
690 | 0 | // Repeatedly halve the size of the range until we find the target group |
691 | 0 | while (powerOf2 > 1) { |
692 | 0 | powerOf2 >>= 1; |
693 | 0 | if (groups[range + powerOf2].startCharCode <= aCh) { |
694 | 0 | range += powerOf2; |
695 | 0 | } |
696 | 0 | } |
697 | 0 |
|
698 | 0 | // Check if the character is actually present in the range and return |
699 | 0 | // the corresponding glyph ID. Here is where formats 12 and 13 interpret |
700 | 0 | // the startGlyphId (12) or glyphId (13) field differently |
701 | 0 | startCharCode = groups[range].startCharCode; |
702 | 0 | if (startCharCode <= aCh && groups[range].endCharCode >= aCh) { |
703 | 0 | return uint16_t(cmap12->format) == 12 |
704 | 0 | ? uint16_t(groups[range].startGlyphId) + aCh - startCharCode |
705 | 0 | : uint16_t(groups[range].startGlyphId); |
706 | 0 | } |
707 | 0 |
|
708 | 0 | // Else it's not present, so return the .notdef glyph |
709 | 0 | return 0; |
710 | 0 | } |
711 | | |
712 | | namespace { |
713 | | |
714 | | struct Format14CmapWrapper |
715 | | { |
716 | | const Format14Cmap& mCmap14; |
717 | 0 | explicit Format14CmapWrapper(const Format14Cmap& cmap14) : mCmap14(cmap14) {} |
718 | 0 | uint32_t operator[](size_t index) const { |
719 | 0 | return mCmap14.varSelectorRecords[index].varSelector; |
720 | 0 | } |
721 | | }; |
722 | | |
723 | | struct NonDefUVSTableWrapper |
724 | | { |
725 | | const NonDefUVSTable& mTable; |
726 | 0 | explicit NonDefUVSTableWrapper(const NonDefUVSTable& table) : mTable(table) {} |
727 | 0 | uint32_t operator[](size_t index) const { |
728 | 0 | return mTable.uvsMappings[index].unicodeValue; |
729 | 0 | } |
730 | | }; |
731 | | |
732 | | } // namespace |
733 | | |
734 | | uint16_t |
735 | | gfxFontUtils::MapUVSToGlyphFormat14(const uint8_t *aBuf, uint32_t aCh, uint32_t aVS) |
736 | 0 | { |
737 | 0 | using mozilla::BinarySearch; |
738 | 0 | const Format14Cmap *cmap14 = reinterpret_cast<const Format14Cmap*>(aBuf); |
739 | 0 |
|
740 | 0 | size_t index; |
741 | 0 | if (!BinarySearch(Format14CmapWrapper(*cmap14), |
742 | 0 | 0, cmap14->numVarSelectorRecords, aVS, &index)) { |
743 | 0 | return 0; |
744 | 0 | } |
745 | 0 | |
746 | 0 | const uint32_t nonDefUVSOffset = cmap14->varSelectorRecords[index].nonDefaultUVSOffset; |
747 | 0 | if (!nonDefUVSOffset) { |
748 | 0 | return 0; |
749 | 0 | } |
750 | 0 | |
751 | 0 | const NonDefUVSTable *table = reinterpret_cast<const NonDefUVSTable*> |
752 | 0 | (aBuf + nonDefUVSOffset); |
753 | 0 |
|
754 | 0 | if (BinarySearch(NonDefUVSTableWrapper(*table), 0, table->numUVSMappings, |
755 | 0 | aCh, &index)) { |
756 | 0 | return table->uvsMappings[index].glyphID; |
757 | 0 | } |
758 | 0 | |
759 | 0 | return 0; |
760 | 0 | } |
761 | | |
762 | | uint32_t |
763 | | gfxFontUtils::MapCharToGlyph(const uint8_t *aCmapBuf, uint32_t aBufLength, |
764 | | uint32_t aUnicode, uint32_t aVarSelector) |
765 | 0 | { |
766 | 0 | uint32_t offset, uvsOffset; |
767 | 0 | uint32_t format = FindPreferredSubtable(aCmapBuf, aBufLength, &offset, |
768 | 0 | &uvsOffset); |
769 | 0 |
|
770 | 0 | uint32_t gid; |
771 | 0 | switch (format) { |
772 | 0 | case 4: |
773 | 0 | gid = aUnicode < UNICODE_BMP_LIMIT ? |
774 | 0 | MapCharToGlyphFormat4(aCmapBuf + offset, aBufLength - offset, |
775 | 0 | char16_t(aUnicode)) : 0; |
776 | 0 | break; |
777 | 0 | case 10: |
778 | 0 | gid = MapCharToGlyphFormat10(aCmapBuf + offset, aUnicode); |
779 | 0 | break; |
780 | 0 | case 12: |
781 | 0 | case 13: |
782 | 0 | gid = MapCharToGlyphFormat12or13(aCmapBuf + offset, aUnicode); |
783 | 0 | break; |
784 | 0 | default: |
785 | 0 | NS_WARNING("unsupported cmap format, glyphs will be missing"); |
786 | 0 | gid = 0; |
787 | 0 | } |
788 | 0 |
|
789 | 0 | if (aVarSelector && uvsOffset && gid) { |
790 | 0 | uint32_t varGID = |
791 | 0 | gfxFontUtils::MapUVSToGlyphFormat14(aCmapBuf + uvsOffset, |
792 | 0 | aUnicode, aVarSelector); |
793 | 0 | if (!varGID) { |
794 | 0 | aUnicode = gfxFontUtils::GetUVSFallback(aUnicode, aVarSelector); |
795 | 0 | if (aUnicode) { |
796 | 0 | switch (format) { |
797 | 0 | case 4: |
798 | 0 | if (aUnicode < UNICODE_BMP_LIMIT) { |
799 | 0 | varGID = MapCharToGlyphFormat4(aCmapBuf + offset, |
800 | 0 | aBufLength - offset, |
801 | 0 | char16_t(aUnicode)); |
802 | 0 | } |
803 | 0 | break; |
804 | 0 | case 10: |
805 | 0 | varGID = MapCharToGlyphFormat10(aCmapBuf + offset, |
806 | 0 | aUnicode); |
807 | 0 | break; |
808 | 0 | case 12: |
809 | 0 | case 13: |
810 | 0 | varGID = MapCharToGlyphFormat12or13(aCmapBuf + offset, |
811 | 0 | aUnicode); |
812 | 0 | break; |
813 | 0 | } |
814 | 0 | } |
815 | 0 | } |
816 | 0 | if (varGID) { |
817 | 0 | gid = varGID; |
818 | 0 | } |
819 | 0 |
|
820 | 0 | // else the variation sequence was not supported, use default mapping |
821 | 0 | // of the character code alone |
822 | 0 | } |
823 | 0 |
|
824 | 0 | return gid; |
825 | 0 | } |
826 | | |
827 | | void gfxFontUtils::ParseFontList(const nsACString& aFamilyList, |
828 | | nsTArray<nsCString>& aFontList) |
829 | 0 | { |
830 | 0 | const char kComma = ','; |
831 | 0 |
|
832 | 0 | // append each font name to the list |
833 | 0 | nsAutoCString fontname; |
834 | 0 | const char *p, *p_end; |
835 | 0 | aFamilyList.BeginReading(p); |
836 | 0 | aFamilyList.EndReading(p_end); |
837 | 0 |
|
838 | 0 | while (p < p_end) { |
839 | 0 | const char *nameStart = p; |
840 | 0 | while (++p != p_end && *p != kComma) |
841 | 0 | /* nothing */ ; |
842 | 0 |
|
843 | 0 | // pull out a single name and clean out leading/trailing whitespace |
844 | 0 | fontname = Substring(nameStart, p); |
845 | 0 | fontname.CompressWhitespace(true, true); |
846 | 0 |
|
847 | 0 | // append it to the list if it's not empty |
848 | 0 | if (!fontname.IsEmpty()) { |
849 | 0 | aFontList.AppendElement(fontname); |
850 | 0 | } |
851 | 0 | ++p; |
852 | 0 | } |
853 | 0 | } |
854 | | |
855 | | void gfxFontUtils::AppendPrefsFontList(const char *aPrefName, |
856 | | nsTArray<nsCString>& aFontList) |
857 | 0 | { |
858 | 0 | // get the list of single-face font families |
859 | 0 | nsAutoCString fontlistValue; |
860 | 0 | nsresult rv = Preferences::GetCString(aPrefName, fontlistValue); |
861 | 0 | if (NS_FAILED(rv)) { |
862 | 0 | return; |
863 | 0 | } |
864 | 0 | |
865 | 0 | ParseFontList(fontlistValue, aFontList); |
866 | 0 | } |
867 | | |
868 | | void gfxFontUtils::GetPrefsFontList(const char *aPrefName, |
869 | | nsTArray<nsCString>& aFontList) |
870 | 0 | { |
871 | 0 | aFontList.Clear(); |
872 | 0 | AppendPrefsFontList(aPrefName, aFontList); |
873 | 0 | } |
874 | | |
875 | | // produce a unique font name that is (1) a valid Postscript name and (2) less |
876 | | // than 31 characters in length. Using AddFontMemResourceEx on Windows fails |
877 | | // for names longer than 30 characters in length. |
878 | | |
879 | | #define MAX_B64_LEN 32 |
880 | | |
881 | | nsresult gfxFontUtils::MakeUniqueUserFontName(nsAString& aName) |
882 | 0 | { |
883 | 0 | nsCOMPtr<nsIUUIDGenerator> uuidgen = |
884 | 0 | do_GetService("@mozilla.org/uuid-generator;1"); |
885 | 0 | NS_ENSURE_TRUE(uuidgen, NS_ERROR_OUT_OF_MEMORY); |
886 | 0 |
|
887 | 0 | nsID guid; |
888 | 0 |
|
889 | 0 | NS_ASSERTION(sizeof(guid) * 2 <= MAX_B64_LEN, "size of nsID has changed!"); |
890 | 0 |
|
891 | 0 | nsresult rv = uuidgen->GenerateUUIDInPlace(&guid); |
892 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
893 | 0 |
|
894 | 0 | char guidB64[MAX_B64_LEN] = {0}; |
895 | 0 |
|
896 | 0 | if (!PL_Base64Encode(reinterpret_cast<char*>(&guid), sizeof(guid), guidB64)) |
897 | 0 | return NS_ERROR_FAILURE; |
898 | 0 | |
899 | 0 | // all b64 characters except for '/' are allowed in Postscript names, so convert / ==> - |
900 | 0 | char *p; |
901 | 0 | for (p = guidB64; *p; p++) { |
902 | 0 | if (*p == '/') |
903 | 0 | *p = '-'; |
904 | 0 | } |
905 | 0 |
|
906 | 0 | aName.AssignLiteral(u"uf"); |
907 | 0 | aName.AppendASCII(guidB64); |
908 | 0 | return NS_OK; |
909 | 0 | } |
910 | | |
911 | | |
912 | | // TrueType/OpenType table handling code |
913 | | |
914 | | // need byte aligned structs |
915 | | #pragma pack(1) |
916 | | |
917 | | // name table stores set of name record structures, followed by |
918 | | // large block containing all the strings. name record offset and length |
919 | | // indicates the offset and length within that block. |
920 | | // http://www.microsoft.com/typography/otspec/name.htm |
921 | | struct NameRecordData { |
922 | | uint32_t offset; |
923 | | uint32_t length; |
924 | | }; |
925 | | |
926 | | #pragma pack() |
927 | | |
928 | | static bool |
929 | | IsValidSFNTVersion(uint32_t version) |
930 | 0 | { |
931 | 0 | // normally 0x00010000, CFF-style OT fonts == 'OTTO' and Apple TT fonts = 'true' |
932 | 0 | // 'typ1' is also possible for old Type 1 fonts in a SFNT container but not supported |
933 | 0 | return version == 0x10000 || |
934 | 0 | version == TRUETYPE_TAG('O','T','T','O') || |
935 | 0 | version == TRUETYPE_TAG('t','r','u','e'); |
936 | 0 | } |
937 | | |
938 | | gfxUserFontType |
939 | | gfxFontUtils::DetermineFontDataType(const uint8_t *aFontData, uint32_t aFontDataLength) |
940 | 0 | { |
941 | 0 | // test for OpenType font data |
942 | 0 | // problem: EOT-Lite with 0x10000 length will look like TrueType! |
943 | 0 | if (aFontDataLength >= sizeof(SFNTHeader)) { |
944 | 0 | const SFNTHeader *sfntHeader = reinterpret_cast<const SFNTHeader*>(aFontData); |
945 | 0 | uint32_t sfntVersion = sfntHeader->sfntVersion; |
946 | 0 | if (IsValidSFNTVersion(sfntVersion)) { |
947 | 0 | return GFX_USERFONT_OPENTYPE; |
948 | 0 | } |
949 | 0 | } |
950 | 0 | |
951 | 0 | // test for WOFF |
952 | 0 | if (aFontDataLength >= sizeof(AutoSwap_PRUint32)) { |
953 | 0 | const AutoSwap_PRUint32 *version = |
954 | 0 | reinterpret_cast<const AutoSwap_PRUint32*>(aFontData); |
955 | 0 | if (uint32_t(*version) == TRUETYPE_TAG('w','O','F','F')) { |
956 | 0 | return GFX_USERFONT_WOFF; |
957 | 0 | } |
958 | 0 | if (Preferences::GetBool(GFX_PREF_WOFF2_ENABLED) && |
959 | 0 | uint32_t(*version) == TRUETYPE_TAG('w','O','F','2')) { |
960 | 0 | return GFX_USERFONT_WOFF2; |
961 | 0 | } |
962 | 0 | } |
963 | 0 | |
964 | 0 | // tests for other formats here |
965 | 0 | |
966 | 0 | return GFX_USERFONT_UNKNOWN; |
967 | 0 | } |
968 | | |
969 | | static int |
970 | | DirEntryCmp(const void* aKey, const void* aItem) |
971 | 0 | { |
972 | 0 | int32_t tag = *static_cast<const int32_t*>(aKey); |
973 | 0 | const TableDirEntry* entry = static_cast<const TableDirEntry*>(aItem); |
974 | 0 | return tag - int32_t(entry->tag); |
975 | 0 | } |
976 | | |
977 | | /* static */ |
978 | | TableDirEntry* |
979 | | gfxFontUtils::FindTableDirEntry(const void* aFontData, uint32_t aTableTag) |
980 | 0 | { |
981 | 0 | const SFNTHeader* header = |
982 | 0 | reinterpret_cast<const SFNTHeader*>(aFontData); |
983 | 0 | const TableDirEntry* dir = |
984 | 0 | reinterpret_cast<const TableDirEntry*>(header + 1); |
985 | 0 | return static_cast<TableDirEntry*> |
986 | 0 | (bsearch(&aTableTag, dir, uint16_t(header->numTables), |
987 | 0 | sizeof(TableDirEntry), DirEntryCmp)); |
988 | 0 | } |
989 | | |
990 | | /* static */ |
991 | | hb_blob_t* |
992 | | gfxFontUtils::GetTableFromFontData(const void* aFontData, uint32_t aTableTag) |
993 | 0 | { |
994 | 0 | const TableDirEntry* dir = FindTableDirEntry(aFontData, aTableTag); |
995 | 0 | if (dir) { |
996 | 0 | return hb_blob_create(reinterpret_cast<const char*>(aFontData) + |
997 | 0 | dir->offset, dir->length, |
998 | 0 | HB_MEMORY_MODE_READONLY, nullptr, nullptr); |
999 | 0 |
|
1000 | 0 | } |
1001 | 0 | return nullptr; |
1002 | 0 | } |
1003 | | |
1004 | | nsresult |
1005 | | gfxFontUtils::RenameFont(const nsAString& aName, const uint8_t *aFontData, |
1006 | | uint32_t aFontDataLength, FallibleTArray<uint8_t> *aNewFont) |
1007 | 0 | { |
1008 | 0 | NS_ASSERTION(aNewFont, "null font data array"); |
1009 | 0 |
|
1010 | 0 | uint64_t dataLength(aFontDataLength); |
1011 | 0 |
|
1012 | 0 | // new name table |
1013 | 0 | static const uint32_t neededNameIDs[] = {NAME_ID_FAMILY, |
1014 | 0 | NAME_ID_STYLE, |
1015 | 0 | NAME_ID_UNIQUE, |
1016 | 0 | NAME_ID_FULL, |
1017 | 0 | NAME_ID_POSTSCRIPT}; |
1018 | 0 |
|
1019 | 0 | // calculate new name table size |
1020 | 0 | uint16_t nameCount = ArrayLength(neededNameIDs); |
1021 | 0 |
|
1022 | 0 | // leave room for null-terminator |
1023 | 0 | uint32_t nameStrLength = (aName.Length() + 1) * sizeof(char16_t); |
1024 | 0 | if (nameStrLength > 65535) { |
1025 | 0 | // The name length _in bytes_ must fit in an unsigned short field; |
1026 | 0 | // therefore, a name longer than this cannot be used. |
1027 | 0 | return NS_ERROR_FAILURE; |
1028 | 0 | } |
1029 | 0 | |
1030 | 0 | // round name table size up to 4-byte multiple |
1031 | 0 | uint32_t nameTableSize = (sizeof(NameHeader) + |
1032 | 0 | sizeof(NameRecord) * nameCount + |
1033 | 0 | nameStrLength + |
1034 | 0 | 3) & ~3; |
1035 | 0 |
|
1036 | 0 | if (dataLength + nameTableSize > UINT32_MAX) |
1037 | 0 | return NS_ERROR_FAILURE; |
1038 | 0 | |
1039 | 0 | // bug 505386 - need to handle unpadded font length |
1040 | 0 | uint32_t paddedFontDataSize = (aFontDataLength + 3) & ~3; |
1041 | 0 | uint32_t adjFontDataSize = paddedFontDataSize + nameTableSize; |
1042 | 0 |
|
1043 | 0 | // create new buffer: old font data plus new name table |
1044 | 0 | if (!aNewFont->AppendElements(adjFontDataSize, fallible)) |
1045 | 0 | return NS_ERROR_OUT_OF_MEMORY; |
1046 | 0 | |
1047 | 0 | // copy the old font data |
1048 | 0 | uint8_t *newFontData = reinterpret_cast<uint8_t*>(aNewFont->Elements()); |
1049 | 0 |
|
1050 | 0 | // null the last four bytes in case the font length is not a multiple of 4 |
1051 | 0 | memset(newFontData + aFontDataLength, 0, paddedFontDataSize - aFontDataLength); |
1052 | 0 |
|
1053 | 0 | // copy font data |
1054 | 0 | memcpy(newFontData, aFontData, aFontDataLength); |
1055 | 0 |
|
1056 | 0 | // null out the last 4 bytes for checksum calculations |
1057 | 0 | memset(newFontData + adjFontDataSize - 4, 0, 4); |
1058 | 0 |
|
1059 | 0 | NameHeader *nameHeader = reinterpret_cast<NameHeader*>(newFontData + |
1060 | 0 | paddedFontDataSize); |
1061 | 0 |
|
1062 | 0 | // -- name header |
1063 | 0 | nameHeader->format = 0; |
1064 | 0 | nameHeader->count = nameCount; |
1065 | 0 | nameHeader->stringOffset = sizeof(NameHeader) + nameCount * sizeof(NameRecord); |
1066 | 0 |
|
1067 | 0 | // -- name records |
1068 | 0 | uint32_t i; |
1069 | 0 | NameRecord *nameRecord = reinterpret_cast<NameRecord*>(nameHeader + 1); |
1070 | 0 |
|
1071 | 0 | for (i = 0; i < nameCount; i++, nameRecord++) { |
1072 | 0 | nameRecord->platformID = PLATFORM_ID_MICROSOFT; |
1073 | 0 | nameRecord->encodingID = ENCODING_ID_MICROSOFT_UNICODEBMP; |
1074 | 0 | nameRecord->languageID = LANG_ID_MICROSOFT_EN_US; |
1075 | 0 | nameRecord->nameID = neededNameIDs[i]; |
1076 | 0 | nameRecord->offset = 0; |
1077 | 0 | nameRecord->length = nameStrLength; |
1078 | 0 | } |
1079 | 0 |
|
1080 | 0 | // -- string data, located after the name records, stored in big-endian form |
1081 | 0 | char16_t *strData = reinterpret_cast<char16_t*>(nameRecord); |
1082 | 0 |
|
1083 | 0 | mozilla::NativeEndian::copyAndSwapToBigEndian(strData, |
1084 | 0 | aName.BeginReading(), |
1085 | 0 | aName.Length()); |
1086 | 0 | strData[aName.Length()] = 0; // add null termination |
1087 | 0 |
|
1088 | 0 | // adjust name table header to point to the new name table |
1089 | 0 | SFNTHeader *sfntHeader = reinterpret_cast<SFNTHeader*>(newFontData); |
1090 | 0 |
|
1091 | 0 | // table directory entries begin immediately following SFNT header |
1092 | 0 | TableDirEntry *dirEntry = |
1093 | 0 | FindTableDirEntry(newFontData, TRUETYPE_TAG('n','a','m','e')); |
1094 | 0 | // function only called if font validates, so this should always be true |
1095 | 0 | MOZ_ASSERT(dirEntry, "attempt to rename font with no name table"); |
1096 | 0 |
|
1097 | 0 | uint32_t numTables = sfntHeader->numTables; |
1098 | 0 |
|
1099 | 0 | // note: dirEntry now points to 'name' table record |
1100 | 0 |
|
1101 | 0 | // recalculate name table checksum |
1102 | 0 | uint32_t checkSum = 0; |
1103 | 0 | AutoSwap_PRUint32 *nameData = reinterpret_cast<AutoSwap_PRUint32*> (nameHeader); |
1104 | 0 | AutoSwap_PRUint32 *nameDataEnd = nameData + (nameTableSize >> 2); |
1105 | 0 |
|
1106 | 0 | while (nameData < nameDataEnd) |
1107 | 0 | checkSum = checkSum + *nameData++; |
1108 | 0 |
|
1109 | 0 | // adjust name table entry to point to new name table |
1110 | 0 | dirEntry->offset = paddedFontDataSize; |
1111 | 0 | dirEntry->length = nameTableSize; |
1112 | 0 | dirEntry->checkSum = checkSum; |
1113 | 0 |
|
1114 | 0 | // fix up checksums |
1115 | 0 | uint32_t checksum = 0; |
1116 | 0 |
|
1117 | 0 | // checksum for font = (checksum of header) + (checksum of tables) |
1118 | 0 | uint32_t headerLen = sizeof(SFNTHeader) + sizeof(TableDirEntry) * numTables; |
1119 | 0 | const AutoSwap_PRUint32 *headerData = |
1120 | 0 | reinterpret_cast<const AutoSwap_PRUint32*>(newFontData); |
1121 | 0 |
|
1122 | 0 | // header length is in bytes, checksum calculated in longwords |
1123 | 0 | for (i = 0; i < (headerLen >> 2); i++, headerData++) { |
1124 | 0 | checksum += *headerData; |
1125 | 0 | } |
1126 | 0 |
|
1127 | 0 | uint32_t headOffset = 0; |
1128 | 0 | dirEntry = reinterpret_cast<TableDirEntry*>(newFontData + sizeof(SFNTHeader)); |
1129 | 0 |
|
1130 | 0 | for (i = 0; i < numTables; i++, dirEntry++) { |
1131 | 0 | if (dirEntry->tag == TRUETYPE_TAG('h','e','a','d')) { |
1132 | 0 | headOffset = dirEntry->offset; |
1133 | 0 | } |
1134 | 0 | checksum += dirEntry->checkSum; |
1135 | 0 | } |
1136 | 0 |
|
1137 | 0 | NS_ASSERTION(headOffset != 0, "no head table for font"); |
1138 | 0 |
|
1139 | 0 | HeadTable *headData = reinterpret_cast<HeadTable*>(newFontData + headOffset); |
1140 | 0 |
|
1141 | 0 | headData->checkSumAdjustment = HeadTable::HEAD_CHECKSUM_CALC_CONST - checksum; |
1142 | 0 |
|
1143 | 0 | return NS_OK; |
1144 | 0 | } |
1145 | | |
1146 | | // This is only called after the basic validity of the downloaded sfnt |
1147 | | // data has been checked, so it should never fail to find the name table |
1148 | | // (though it might fail to read it, if memory isn't available); |
1149 | | // other checks here are just for extra paranoia. |
1150 | | nsresult |
1151 | | gfxFontUtils::GetFullNameFromSFNT(const uint8_t* aFontData, uint32_t aLength, |
1152 | | nsACString& aFullName) |
1153 | 0 | { |
1154 | 0 | aFullName = "(MISSING NAME)"; // should always get replaced |
1155 | 0 |
|
1156 | 0 | const TableDirEntry *dirEntry = |
1157 | 0 | FindTableDirEntry(aFontData, TRUETYPE_TAG('n','a','m','e')); |
1158 | 0 |
|
1159 | 0 | // should never fail, as we're only called after font validation succeeded |
1160 | 0 | NS_ENSURE_TRUE(dirEntry, NS_ERROR_NOT_AVAILABLE); |
1161 | 0 |
|
1162 | 0 | uint32_t len = dirEntry->length; |
1163 | 0 | NS_ENSURE_TRUE(aLength > len && aLength - len >= dirEntry->offset, |
1164 | 0 | NS_ERROR_UNEXPECTED); |
1165 | 0 |
|
1166 | 0 | hb_blob_t *nameBlob = |
1167 | 0 | hb_blob_create((const char*)aFontData + dirEntry->offset, len, |
1168 | 0 | HB_MEMORY_MODE_READONLY, nullptr, nullptr); |
1169 | 0 | nsresult rv = GetFullNameFromTable(nameBlob, aFullName); |
1170 | 0 | hb_blob_destroy(nameBlob); |
1171 | 0 |
|
1172 | 0 | return rv; |
1173 | 0 | } |
1174 | | |
1175 | | nsresult |
1176 | | gfxFontUtils::GetFullNameFromTable(hb_blob_t *aNameTable, |
1177 | | nsACString& aFullName) |
1178 | 0 | { |
1179 | 0 | nsAutoCString name; |
1180 | 0 | nsresult rv = |
1181 | 0 | gfxFontUtils::ReadCanonicalName(aNameTable, |
1182 | 0 | gfxFontUtils::NAME_ID_FULL, |
1183 | 0 | name); |
1184 | 0 | if (NS_SUCCEEDED(rv) && !name.IsEmpty()) { |
1185 | 0 | aFullName = name; |
1186 | 0 | return NS_OK; |
1187 | 0 | } |
1188 | 0 | rv = gfxFontUtils::ReadCanonicalName(aNameTable, |
1189 | 0 | gfxFontUtils::NAME_ID_FAMILY, |
1190 | 0 | name); |
1191 | 0 | if (NS_SUCCEEDED(rv) && !name.IsEmpty()) { |
1192 | 0 | nsAutoCString styleName; |
1193 | 0 | rv = gfxFontUtils::ReadCanonicalName(aNameTable, |
1194 | 0 | gfxFontUtils::NAME_ID_STYLE, |
1195 | 0 | styleName); |
1196 | 0 | if (NS_SUCCEEDED(rv) && !styleName.IsEmpty()) { |
1197 | 0 | name.Append(' '); |
1198 | 0 | name.Append(styleName); |
1199 | 0 | aFullName = name; |
1200 | 0 | } |
1201 | 0 | return NS_OK; |
1202 | 0 | } |
1203 | 0 |
|
1204 | 0 | return NS_ERROR_NOT_AVAILABLE; |
1205 | 0 | } |
1206 | | |
1207 | | nsresult |
1208 | | gfxFontUtils::GetFamilyNameFromTable(hb_blob_t *aNameTable, |
1209 | | nsACString& aFamilyName) |
1210 | 0 | { |
1211 | 0 | nsAutoCString name; |
1212 | 0 | nsresult rv = |
1213 | 0 | gfxFontUtils::ReadCanonicalName(aNameTable, |
1214 | 0 | gfxFontUtils::NAME_ID_FAMILY, |
1215 | 0 | name); |
1216 | 0 | if (NS_SUCCEEDED(rv) && !name.IsEmpty()) { |
1217 | 0 | aFamilyName = name; |
1218 | 0 | return NS_OK; |
1219 | 0 | } |
1220 | 0 | return NS_ERROR_NOT_AVAILABLE; |
1221 | 0 | } |
1222 | | |
1223 | | enum { |
1224 | | #if defined(XP_MACOSX) |
1225 | | CANONICAL_LANG_ID = gfxFontUtils::LANG_ID_MAC_ENGLISH, |
1226 | | PLATFORM_ID = gfxFontUtils::PLATFORM_ID_MAC |
1227 | | #else |
1228 | | CANONICAL_LANG_ID = gfxFontUtils::LANG_ID_MICROSOFT_EN_US, |
1229 | | PLATFORM_ID = gfxFontUtils::PLATFORM_ID_MICROSOFT |
1230 | | #endif |
1231 | | }; |
1232 | | |
1233 | | nsresult |
1234 | | gfxFontUtils::ReadNames(const char *aNameData, uint32_t aDataLen, |
1235 | | uint32_t aNameID, int32_t aPlatformID, |
1236 | | nsTArray<nsCString>& aNames) |
1237 | 0 | { |
1238 | 0 | return ReadNames(aNameData, aDataLen, aNameID, LANG_ALL, |
1239 | 0 | aPlatformID, aNames); |
1240 | 0 | } |
1241 | | |
1242 | | nsresult |
1243 | | gfxFontUtils::ReadCanonicalName(hb_blob_t *aNameTable, uint32_t aNameID, |
1244 | | nsCString& aName) |
1245 | 0 | { |
1246 | 0 | uint32_t nameTableLen; |
1247 | 0 | const char *nameTable = hb_blob_get_data(aNameTable, &nameTableLen); |
1248 | 0 | return ReadCanonicalName(nameTable, nameTableLen, aNameID, aName); |
1249 | 0 | } |
1250 | | |
1251 | | nsresult |
1252 | | gfxFontUtils::ReadCanonicalName(const char *aNameData, uint32_t aDataLen, |
1253 | | uint32_t aNameID, nsCString& aName) |
1254 | 0 | { |
1255 | 0 | nsresult rv; |
1256 | 0 |
|
1257 | 0 | nsTArray<nsCString> names; |
1258 | 0 |
|
1259 | 0 | // first, look for the English name (this will succeed 99% of the time) |
1260 | 0 | rv = ReadNames(aNameData, aDataLen, aNameID, CANONICAL_LANG_ID, |
1261 | 0 | PLATFORM_ID, names); |
1262 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
1263 | 0 |
|
1264 | 0 | // otherwise, grab names for all languages |
1265 | 0 | if (names.Length() == 0) { |
1266 | 0 | rv = ReadNames(aNameData, aDataLen, aNameID, LANG_ALL, |
1267 | 0 | PLATFORM_ID, names); |
1268 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
1269 | 0 | } |
1270 | 0 |
|
1271 | | #if defined(XP_MACOSX) |
1272 | | // may be dealing with font that only has Microsoft name entries |
1273 | | if (names.Length() == 0) { |
1274 | | rv = ReadNames(aNameData, aDataLen, aNameID, LANG_ID_MICROSOFT_EN_US, |
1275 | | PLATFORM_ID_MICROSOFT, names); |
1276 | | NS_ENSURE_SUCCESS(rv, rv); |
1277 | | |
1278 | | // getting really desperate now, take anything! |
1279 | | if (names.Length() == 0) { |
1280 | | rv = ReadNames(aNameData, aDataLen, aNameID, LANG_ALL, |
1281 | | PLATFORM_ID_MICROSOFT, names); |
1282 | | NS_ENSURE_SUCCESS(rv, rv); |
1283 | | } |
1284 | | } |
1285 | | #endif |
1286 | |
|
1287 | 0 | // return the first name (99.9% of the time names will |
1288 | 0 | // contain a single English name) |
1289 | 0 | if (names.Length()) { |
1290 | 0 | aName.Assign(names[0]); |
1291 | 0 | return NS_OK; |
1292 | 0 | } |
1293 | 0 | |
1294 | 0 | return NS_ERROR_FAILURE; |
1295 | 0 | } |
1296 | | |
1297 | | // Charsets to use for decoding Mac platform font names. |
1298 | | // This table is sorted by {encoding, language}, with the wildcard "ANY" being |
1299 | | // greater than any defined values for each field; we use a binary search on both |
1300 | | // fields, and fall back to matching only encoding if necessary |
1301 | | |
1302 | | // Some "redundant" entries for specific combinations are included such as |
1303 | | // encoding=roman, lang=english, in order that common entries will be found |
1304 | | // on the first search. |
1305 | | |
1306 | | const uint16_t ANY = 0xffff; |
1307 | | const gfxFontUtils::MacFontNameCharsetMapping gfxFontUtils::gMacFontNameCharsets[] = |
1308 | | { |
1309 | | { ENCODING_ID_MAC_ROMAN, LANG_ID_MAC_ENGLISH, MACINTOSH_ENCODING }, |
1310 | | { ENCODING_ID_MAC_ROMAN, LANG_ID_MAC_ICELANDIC, X_USER_DEFINED_ENCODING }, |
1311 | | { ENCODING_ID_MAC_ROMAN, LANG_ID_MAC_TURKISH, X_USER_DEFINED_ENCODING }, |
1312 | | { ENCODING_ID_MAC_ROMAN, LANG_ID_MAC_POLISH, X_USER_DEFINED_ENCODING }, |
1313 | | { ENCODING_ID_MAC_ROMAN, LANG_ID_MAC_ROMANIAN, X_USER_DEFINED_ENCODING }, |
1314 | | { ENCODING_ID_MAC_ROMAN, LANG_ID_MAC_CZECH, X_USER_DEFINED_ENCODING }, |
1315 | | { ENCODING_ID_MAC_ROMAN, LANG_ID_MAC_SLOVAK, X_USER_DEFINED_ENCODING }, |
1316 | | { ENCODING_ID_MAC_ROMAN, ANY, MACINTOSH_ENCODING }, |
1317 | | { ENCODING_ID_MAC_JAPANESE, LANG_ID_MAC_JAPANESE, SHIFT_JIS_ENCODING }, |
1318 | | { ENCODING_ID_MAC_JAPANESE, ANY, SHIFT_JIS_ENCODING }, |
1319 | | { ENCODING_ID_MAC_TRAD_CHINESE, LANG_ID_MAC_TRAD_CHINESE, BIG5_ENCODING }, |
1320 | | { ENCODING_ID_MAC_TRAD_CHINESE, ANY, BIG5_ENCODING }, |
1321 | | { ENCODING_ID_MAC_KOREAN, LANG_ID_MAC_KOREAN, EUC_KR_ENCODING }, |
1322 | | { ENCODING_ID_MAC_KOREAN, ANY, EUC_KR_ENCODING }, |
1323 | | { ENCODING_ID_MAC_ARABIC, LANG_ID_MAC_ARABIC, X_USER_DEFINED_ENCODING }, |
1324 | | { ENCODING_ID_MAC_ARABIC, LANG_ID_MAC_URDU, X_USER_DEFINED_ENCODING }, |
1325 | | { ENCODING_ID_MAC_ARABIC, LANG_ID_MAC_FARSI, X_USER_DEFINED_ENCODING }, |
1326 | | { ENCODING_ID_MAC_ARABIC, ANY, X_USER_DEFINED_ENCODING }, |
1327 | | { ENCODING_ID_MAC_HEBREW, LANG_ID_MAC_HEBREW, X_USER_DEFINED_ENCODING }, |
1328 | | { ENCODING_ID_MAC_HEBREW, ANY, X_USER_DEFINED_ENCODING }, |
1329 | | { ENCODING_ID_MAC_GREEK, ANY, X_USER_DEFINED_ENCODING }, |
1330 | | { ENCODING_ID_MAC_CYRILLIC, ANY, X_MAC_CYRILLIC_ENCODING }, |
1331 | | { ENCODING_ID_MAC_DEVANAGARI, ANY, X_USER_DEFINED_ENCODING }, |
1332 | | { ENCODING_ID_MAC_GURMUKHI, ANY, X_USER_DEFINED_ENCODING }, |
1333 | | { ENCODING_ID_MAC_GUJARATI, ANY, X_USER_DEFINED_ENCODING }, |
1334 | | { ENCODING_ID_MAC_SIMP_CHINESE, LANG_ID_MAC_SIMP_CHINESE, GB18030_ENCODING }, |
1335 | | { ENCODING_ID_MAC_SIMP_CHINESE, ANY, GB18030_ENCODING } |
1336 | | }; |
1337 | | |
1338 | | const Encoding* gfxFontUtils::gISOFontNameCharsets[] = |
1339 | | { |
1340 | | /* 0 */ WINDOWS_1252_ENCODING, /* US-ASCII */ |
1341 | | /* 1 */ nullptr , /* spec says "ISO 10646" but does not specify encoding form! */ |
1342 | | /* 2 */ WINDOWS_1252_ENCODING /* ISO-8859-1 */ |
1343 | | }; |
1344 | | |
1345 | | const Encoding* gfxFontUtils::gMSFontNameCharsets[] = |
1346 | | { |
1347 | | /* [0] ENCODING_ID_MICROSOFT_SYMBOL */ UTF_16BE_ENCODING, |
1348 | | /* [1] ENCODING_ID_MICROSOFT_UNICODEBMP */ UTF_16BE_ENCODING, |
1349 | | /* [2] ENCODING_ID_MICROSOFT_SHIFTJIS */ SHIFT_JIS_ENCODING, |
1350 | | /* [3] ENCODING_ID_MICROSOFT_PRC */ nullptr , |
1351 | | /* [4] ENCODING_ID_MICROSOFT_BIG5 */ BIG5_ENCODING, |
1352 | | /* [5] ENCODING_ID_MICROSOFT_WANSUNG */ nullptr , |
1353 | | /* [6] ENCODING_ID_MICROSOFT_JOHAB */ nullptr , |
1354 | | /* [7] reserved */ nullptr , |
1355 | | /* [8] reserved */ nullptr , |
1356 | | /* [9] reserved */ nullptr , |
1357 | | /*[10] ENCODING_ID_MICROSOFT_UNICODEFULL */ UTF_16BE_ENCODING |
1358 | | }; |
1359 | | |
1360 | | struct MacCharsetMappingComparator |
1361 | | { |
1362 | | typedef gfxFontUtils::MacFontNameCharsetMapping MacFontNameCharsetMapping; |
1363 | | const MacFontNameCharsetMapping& mSearchValue; |
1364 | | explicit MacCharsetMappingComparator(const MacFontNameCharsetMapping& aSearchValue) |
1365 | 0 | : mSearchValue(aSearchValue) {} |
1366 | 0 | int operator()(const MacFontNameCharsetMapping& aEntry) const { |
1367 | 0 | if (mSearchValue < aEntry) { |
1368 | 0 | return -1; |
1369 | 0 | } |
1370 | 0 | if (aEntry < mSearchValue) { |
1371 | 0 | return 1; |
1372 | 0 | } |
1373 | 0 | return 0; |
1374 | 0 | } |
1375 | | }; |
1376 | | |
1377 | | // Return the Encoding object we should use to decode a font name |
1378 | | // given the name table attributes. |
1379 | | // Special return values: |
1380 | | // X_USER_DEFINED_ENCODING One of Mac legacy encodings that is not a part |
1381 | | // of Encoding Standard |
1382 | | // nullptr unknown charset, do not attempt conversion |
1383 | | const Encoding* |
1384 | | gfxFontUtils::GetCharsetForFontName(uint16_t aPlatform, uint16_t aScript, uint16_t aLanguage) |
1385 | 0 | { |
1386 | 0 | switch (aPlatform) |
1387 | 0 | { |
1388 | 0 | case PLATFORM_ID_UNICODE: |
1389 | 0 | return UTF_16BE_ENCODING; |
1390 | 0 |
|
1391 | 0 | case PLATFORM_ID_MAC: |
1392 | 0 | { |
1393 | 0 | MacFontNameCharsetMapping searchValue = { aScript, aLanguage, nullptr }; |
1394 | 0 | for (uint32_t i = 0; i < 2; ++i) { |
1395 | 0 | size_t idx; |
1396 | 0 | if (BinarySearchIf(gMacFontNameCharsets, 0, ArrayLength(gMacFontNameCharsets), |
1397 | 0 | MacCharsetMappingComparator(searchValue), &idx)) { |
1398 | 0 | return gMacFontNameCharsets[idx].mEncoding; |
1399 | 0 | } |
1400 | 0 | |
1401 | 0 | // no match, so try again finding one in any language |
1402 | 0 | searchValue.mLanguage = ANY; |
1403 | 0 | } |
1404 | 0 | } |
1405 | 0 | break; |
1406 | 0 |
|
1407 | 0 | case PLATFORM_ID_ISO: |
1408 | 0 | if (aScript < ArrayLength(gISOFontNameCharsets)) { |
1409 | 0 | return gISOFontNameCharsets[aScript]; |
1410 | 0 | } |
1411 | 0 | break; |
1412 | 0 |
|
1413 | 0 | case PLATFORM_ID_MICROSOFT: |
1414 | 0 | if (aScript < ArrayLength(gMSFontNameCharsets)) { |
1415 | 0 | return gMSFontNameCharsets[aScript]; |
1416 | 0 | } |
1417 | 0 | break; |
1418 | 0 | } |
1419 | 0 | |
1420 | 0 | return nullptr; |
1421 | 0 | } |
1422 | | |
1423 | | template<int N> |
1424 | | static bool |
1425 | | StartsWith(const nsACString& string, const char (&prefix)[N]) |
1426 | | { |
1427 | | if (N - 1 > string.Length()) { |
1428 | | return false; |
1429 | | } |
1430 | | return memcmp(string.Data(), prefix, N - 1) == 0; |
1431 | | } |
1432 | | |
1433 | | // convert a raw name from the name table to an nsString, if possible; |
1434 | | // return value indicates whether conversion succeeded |
1435 | | bool |
1436 | | gfxFontUtils::DecodeFontName(const char *aNameData, int32_t aByteLen, |
1437 | | uint32_t aPlatformCode, uint32_t aScriptCode, |
1438 | | uint32_t aLangCode, nsACString& aName) |
1439 | 0 | { |
1440 | 0 | if (aByteLen <= 0) { |
1441 | 0 | NS_WARNING("empty font name"); |
1442 | 0 | aName.SetLength(0); |
1443 | 0 | return true; |
1444 | 0 | } |
1445 | 0 |
|
1446 | 0 | auto encoding = GetCharsetForFontName(aPlatformCode, aScriptCode, aLangCode); |
1447 | 0 |
|
1448 | 0 | if (!encoding) { |
1449 | 0 | // nullptr -> unknown charset |
1450 | | #ifdef DEBUG |
1451 | | char warnBuf[128]; |
1452 | | if (aByteLen > 64) |
1453 | | aByteLen = 64; |
1454 | | SprintfLiteral(warnBuf, "skipping font name, unknown charset %d:%d:%d for <%.*s>", |
1455 | | aPlatformCode, aScriptCode, aLangCode, aByteLen, aNameData); |
1456 | | NS_WARNING(warnBuf); |
1457 | | #endif |
1458 | | return false; |
1459 | 0 | } |
1460 | 0 |
|
1461 | 0 | if (encoding == X_USER_DEFINED_ENCODING) { |
1462 | | #ifdef XP_MACOSX |
1463 | | // Special case for macOS only: support legacy Mac encodings |
1464 | | // that aren't part of the Encoding Standard. |
1465 | | if (aPlatformCode == PLATFORM_ID_MAC) { |
1466 | | CFStringRef str = |
1467 | | CFStringCreateWithBytes(kCFAllocatorDefault, |
1468 | | (const UInt8*)aNameData, aByteLen, |
1469 | | aScriptCode, false); |
1470 | | if (str) { |
1471 | | CFIndex length = CFStringGetLength(str); |
1472 | | nsAutoString name16; |
1473 | | name16.SetLength(length); |
1474 | | CFStringGetCharacters(str, CFRangeMake(0, length), |
1475 | | (UniChar*)name16.BeginWriting()); |
1476 | | CFRelease(str); |
1477 | | CopyUTF16toUTF8(name16, aName); |
1478 | | return true; |
1479 | | } |
1480 | | } |
1481 | | #endif |
1482 | 0 | NS_WARNING("failed to get the decoder for a font name string"); |
1483 | 0 | return false; |
1484 | 0 | } |
1485 | 0 |
|
1486 | 0 | auto rv = encoding->DecodeWithoutBOMHandling( |
1487 | 0 | nsDependentCSubstring(aNameData, aByteLen), aName); |
1488 | 0 | return NS_SUCCEEDED(rv); |
1489 | 0 | } |
1490 | | |
1491 | | nsresult |
1492 | | gfxFontUtils::ReadNames(const char *aNameData, uint32_t aDataLen, |
1493 | | uint32_t aNameID, |
1494 | | int32_t aLangID, int32_t aPlatformID, |
1495 | | nsTArray<nsCString>& aNames) |
1496 | 0 | { |
1497 | 0 | NS_ASSERTION(aDataLen != 0, "null name table"); |
1498 | 0 |
|
1499 | 0 | if (!aDataLen) { |
1500 | 0 | return NS_ERROR_FAILURE; |
1501 | 0 | } |
1502 | 0 | |
1503 | 0 | // -- name table data |
1504 | 0 | const NameHeader *nameHeader = reinterpret_cast<const NameHeader*>(aNameData); |
1505 | 0 |
|
1506 | 0 | uint32_t nameCount = nameHeader->count; |
1507 | 0 |
|
1508 | 0 | // -- sanity check the number of name records |
1509 | 0 | if (uint64_t(nameCount) * sizeof(NameRecord) > aDataLen) { |
1510 | 0 | NS_WARNING("invalid font (name table data)"); |
1511 | 0 | return NS_ERROR_FAILURE; |
1512 | 0 | } |
1513 | 0 |
|
1514 | 0 | // -- iterate through name records |
1515 | 0 | const NameRecord *nameRecord |
1516 | 0 | = reinterpret_cast<const NameRecord*>(aNameData + sizeof(NameHeader)); |
1517 | 0 | uint64_t nameStringsBase = uint64_t(nameHeader->stringOffset); |
1518 | 0 |
|
1519 | 0 | uint32_t i; |
1520 | 0 | for (i = 0; i < nameCount; i++, nameRecord++) { |
1521 | 0 | uint32_t platformID; |
1522 | 0 |
|
1523 | 0 | // skip over unwanted nameID's |
1524 | 0 | if (uint32_t(nameRecord->nameID) != aNameID) { |
1525 | 0 | continue; |
1526 | 0 | } |
1527 | 0 | |
1528 | 0 | // skip over unwanted platform data |
1529 | 0 | platformID = nameRecord->platformID; |
1530 | 0 | if (aPlatformID != PLATFORM_ALL && |
1531 | 0 | platformID != uint32_t(aPlatformID)) { |
1532 | 0 | continue; |
1533 | 0 | } |
1534 | 0 | |
1535 | 0 | // skip over unwanted languages |
1536 | 0 | if (aLangID != LANG_ALL && |
1537 | 0 | uint32_t(nameRecord->languageID) != uint32_t(aLangID)) { |
1538 | 0 | continue; |
1539 | 0 | } |
1540 | 0 | |
1541 | 0 | // add name to names array |
1542 | 0 | |
1543 | 0 | // -- calculate string location |
1544 | 0 | uint32_t namelen = nameRecord->length; |
1545 | 0 | uint32_t nameoff = nameRecord->offset; // offset from base of string storage |
1546 | 0 |
|
1547 | 0 | if (nameStringsBase + uint64_t(nameoff) + uint64_t(namelen) |
1548 | 0 | > aDataLen) { |
1549 | 0 | NS_WARNING("invalid font (name table strings)"); |
1550 | 0 | return NS_ERROR_FAILURE; |
1551 | 0 | } |
1552 | 0 |
|
1553 | 0 | // -- decode if necessary and make nsString |
1554 | 0 | nsAutoCString name; |
1555 | 0 |
|
1556 | 0 | DecodeFontName(aNameData + nameStringsBase + nameoff, namelen, |
1557 | 0 | platformID, uint32_t(nameRecord->encodingID), |
1558 | 0 | uint32_t(nameRecord->languageID), name); |
1559 | 0 |
|
1560 | 0 | uint32_t k, numNames; |
1561 | 0 | bool foundName = false; |
1562 | 0 |
|
1563 | 0 | numNames = aNames.Length(); |
1564 | 0 | for (k = 0; k < numNames; k++) { |
1565 | 0 | if (name.Equals(aNames[k])) { |
1566 | 0 | foundName = true; |
1567 | 0 | break; |
1568 | 0 | } |
1569 | 0 | } |
1570 | 0 |
|
1571 | 0 | if (!foundName) |
1572 | 0 | aNames.AppendElement(name); |
1573 | 0 |
|
1574 | 0 | } |
1575 | 0 |
|
1576 | 0 | return NS_OK; |
1577 | 0 | } |
1578 | | |
1579 | | #pragma pack(1) |
1580 | | |
1581 | | struct COLRBaseGlyphRecord { |
1582 | | AutoSwap_PRUint16 glyphId; |
1583 | | AutoSwap_PRUint16 firstLayerIndex; |
1584 | | AutoSwap_PRUint16 numLayers; |
1585 | | }; |
1586 | | |
1587 | | struct COLRLayerRecord { |
1588 | | AutoSwap_PRUint16 glyphId; |
1589 | | AutoSwap_PRUint16 paletteEntryIndex; |
1590 | | }; |
1591 | | |
1592 | | struct CPALColorRecord { |
1593 | | uint8_t blue; |
1594 | | uint8_t green; |
1595 | | uint8_t red; |
1596 | | uint8_t alpha; |
1597 | | }; |
1598 | | |
1599 | | #pragma pack() |
1600 | | |
1601 | | bool |
1602 | | gfxFontUtils::ValidateColorGlyphs(hb_blob_t* aCOLR, hb_blob_t* aCPAL) |
1603 | 0 | { |
1604 | 0 | unsigned int colrLength; |
1605 | 0 | const COLRHeader* colr = |
1606 | 0 | reinterpret_cast<const COLRHeader*>(hb_blob_get_data(aCOLR, &colrLength)); |
1607 | 0 | unsigned int cpalLength; |
1608 | 0 | const CPALHeaderVersion0* cpal = |
1609 | 0 | reinterpret_cast<const CPALHeaderVersion0*>(hb_blob_get_data(aCPAL, &cpalLength)); |
1610 | 0 |
|
1611 | 0 | if (!colr || !cpal || !colrLength || !cpalLength) { |
1612 | 0 | return false; |
1613 | 0 | } |
1614 | 0 | |
1615 | 0 | if (uint16_t(colr->version) != 0 || uint16_t(cpal->version) != 0) { |
1616 | 0 | // We only support version 0 headers. |
1617 | 0 | return false; |
1618 | 0 | } |
1619 | 0 | |
1620 | 0 | const uint32_t offsetBaseGlyphRecord = colr->offsetBaseGlyphRecord; |
1621 | 0 | const uint16_t numBaseGlyphRecord = colr->numBaseGlyphRecord; |
1622 | 0 | const uint32_t offsetLayerRecord = colr->offsetLayerRecord; |
1623 | 0 | const uint16_t numLayerRecords = colr->numLayerRecords; |
1624 | 0 |
|
1625 | 0 | const uint32_t offsetFirstColorRecord = cpal->offsetFirstColorRecord; |
1626 | 0 | const uint16_t numColorRecords = cpal->numColorRecords; |
1627 | 0 | const uint32_t numPaletteEntries = cpal->numPaletteEntries; |
1628 | 0 |
|
1629 | 0 | if (offsetBaseGlyphRecord >= colrLength) { |
1630 | 0 | return false; |
1631 | 0 | } |
1632 | 0 | |
1633 | 0 | if (offsetLayerRecord >= colrLength) { |
1634 | 0 | return false; |
1635 | 0 | } |
1636 | 0 | |
1637 | 0 | if (offsetFirstColorRecord >= cpalLength) { |
1638 | 0 | return false; |
1639 | 0 | } |
1640 | 0 | |
1641 | 0 | if (!numPaletteEntries) { |
1642 | 0 | return false; |
1643 | 0 | } |
1644 | 0 | |
1645 | 0 | if (sizeof(COLRBaseGlyphRecord) * numBaseGlyphRecord > |
1646 | 0 | colrLength - offsetBaseGlyphRecord) { |
1647 | 0 | // COLR base glyph record will be overflow |
1648 | 0 | return false; |
1649 | 0 | } |
1650 | 0 | |
1651 | 0 | if (sizeof(COLRLayerRecord) * numLayerRecords > |
1652 | 0 | colrLength - offsetLayerRecord) { |
1653 | 0 | // COLR layer record will be overflow |
1654 | 0 | return false; |
1655 | 0 | } |
1656 | 0 | |
1657 | 0 | if (sizeof(CPALColorRecord) * numColorRecords > |
1658 | 0 | cpalLength - offsetFirstColorRecord) { |
1659 | 0 | // CPAL color record will be overflow |
1660 | 0 | return false; |
1661 | 0 | } |
1662 | 0 | |
1663 | 0 | if (numPaletteEntries * uint16_t(cpal->numPalettes) != numColorRecords ) { |
1664 | 0 | // palette of CPAL color record will be overflow. |
1665 | 0 | return false; |
1666 | 0 | } |
1667 | 0 | |
1668 | 0 | uint16_t lastGlyphId = 0; |
1669 | 0 | const COLRBaseGlyphRecord* baseGlyph = |
1670 | 0 | reinterpret_cast<const COLRBaseGlyphRecord*>( |
1671 | 0 | reinterpret_cast<const uint8_t*>(colr) + offsetBaseGlyphRecord); |
1672 | 0 |
|
1673 | 0 | for (uint16_t i = 0; i < numBaseGlyphRecord; i++, baseGlyph++) { |
1674 | 0 | const uint32_t firstLayerIndex = baseGlyph->firstLayerIndex; |
1675 | 0 | const uint16_t numLayers = baseGlyph->numLayers; |
1676 | 0 | const uint16_t glyphId = baseGlyph->glyphId; |
1677 | 0 |
|
1678 | 0 | if (lastGlyphId && lastGlyphId >= glyphId) { |
1679 | 0 | // glyphId must be sorted |
1680 | 0 | return false; |
1681 | 0 | } |
1682 | 0 | lastGlyphId = glyphId; |
1683 | 0 |
|
1684 | 0 | if (!numLayers) { |
1685 | 0 | // no layer |
1686 | 0 | return false; |
1687 | 0 | } |
1688 | 0 | if (firstLayerIndex + numLayers > numLayerRecords) { |
1689 | 0 | // layer length of target glyph is overflow |
1690 | 0 | return false; |
1691 | 0 | } |
1692 | 0 | } |
1693 | 0 |
|
1694 | 0 | const COLRLayerRecord* layer = |
1695 | 0 | reinterpret_cast<const COLRLayerRecord*>( |
1696 | 0 | reinterpret_cast<const uint8_t*>(colr) + offsetLayerRecord); |
1697 | 0 |
|
1698 | 0 | for (uint16_t i = 0; i < numLayerRecords; i++, layer++) { |
1699 | 0 | if (uint16_t(layer->paletteEntryIndex) >= numPaletteEntries && |
1700 | 0 | uint16_t(layer->paletteEntryIndex) != 0xFFFF) { |
1701 | 0 | // CPAL palette entry record is overflow |
1702 | 0 | return false; |
1703 | 0 | } |
1704 | 0 | } |
1705 | 0 |
|
1706 | 0 | return true; |
1707 | 0 | } |
1708 | | |
1709 | | static int |
1710 | | CompareBaseGlyph(const void* key, const void* data) |
1711 | 0 | { |
1712 | 0 | uint32_t glyphId = (uint32_t)(uintptr_t)key; |
1713 | 0 | const COLRBaseGlyphRecord* baseGlyph = |
1714 | 0 | reinterpret_cast<const COLRBaseGlyphRecord*>(data); |
1715 | 0 | uint32_t baseGlyphId = uint16_t(baseGlyph->glyphId); |
1716 | 0 |
|
1717 | 0 | if (baseGlyphId == glyphId) { |
1718 | 0 | return 0; |
1719 | 0 | } |
1720 | 0 | |
1721 | 0 | return baseGlyphId > glyphId ? -1 : 1; |
1722 | 0 | } |
1723 | | |
1724 | | static |
1725 | | COLRBaseGlyphRecord* |
1726 | | LookForBaseGlyphRecord(const COLRHeader* aCOLR, uint32_t aGlyphId) |
1727 | 0 | { |
1728 | 0 | const uint8_t* baseGlyphRecords = |
1729 | 0 | reinterpret_cast<const uint8_t*>(aCOLR) + |
1730 | 0 | uint32_t(aCOLR->offsetBaseGlyphRecord); |
1731 | 0 | // BaseGlyphRecord is sorted by glyphId |
1732 | 0 | return reinterpret_cast<COLRBaseGlyphRecord*>( |
1733 | 0 | bsearch((void*)(uintptr_t)aGlyphId, |
1734 | 0 | baseGlyphRecords, |
1735 | 0 | uint16_t(aCOLR->numBaseGlyphRecord), |
1736 | 0 | sizeof(COLRBaseGlyphRecord), |
1737 | 0 | CompareBaseGlyph)); |
1738 | 0 | } |
1739 | | |
1740 | | bool |
1741 | | gfxFontUtils::GetColorGlyphLayers(hb_blob_t* aCOLR, |
1742 | | hb_blob_t* aCPAL, |
1743 | | uint32_t aGlyphId, |
1744 | | const mozilla::gfx::Color& aDefaultColor, |
1745 | | nsTArray<uint16_t>& aGlyphs, |
1746 | | nsTArray<mozilla::gfx::Color>& aColors) |
1747 | 0 | { |
1748 | 0 | unsigned int blobLength; |
1749 | 0 | const COLRHeader* colr = |
1750 | 0 | reinterpret_cast<const COLRHeader*>(hb_blob_get_data(aCOLR, |
1751 | 0 | &blobLength)); |
1752 | 0 | MOZ_ASSERT(colr, "Cannot get COLR raw data"); |
1753 | 0 | MOZ_ASSERT(blobLength, "Found COLR data, but length is 0"); |
1754 | 0 |
|
1755 | 0 | COLRBaseGlyphRecord* baseGlyph = LookForBaseGlyphRecord(colr, aGlyphId); |
1756 | 0 | if (!baseGlyph) { |
1757 | 0 | return false; |
1758 | 0 | } |
1759 | 0 | |
1760 | 0 | const CPALHeaderVersion0* cpal = |
1761 | 0 | reinterpret_cast<const CPALHeaderVersion0*>( |
1762 | 0 | hb_blob_get_data(aCPAL, &blobLength)); |
1763 | 0 | MOZ_ASSERT(cpal, "Cannot get CPAL raw data"); |
1764 | 0 | MOZ_ASSERT(blobLength, "Found CPAL data, but length is 0"); |
1765 | 0 |
|
1766 | 0 | const COLRLayerRecord* layer = |
1767 | 0 | reinterpret_cast<const COLRLayerRecord*>( |
1768 | 0 | reinterpret_cast<const uint8_t*>(colr) + |
1769 | 0 | uint32_t(colr->offsetLayerRecord) + |
1770 | 0 | sizeof(COLRLayerRecord) * uint16_t(baseGlyph->firstLayerIndex)); |
1771 | 0 | const uint16_t numLayers = baseGlyph->numLayers; |
1772 | 0 | const uint32_t offsetFirstColorRecord = cpal->offsetFirstColorRecord; |
1773 | 0 |
|
1774 | 0 | for (uint16_t layerIndex = 0; layerIndex < numLayers; layerIndex++) { |
1775 | 0 | aGlyphs.AppendElement(uint16_t(layer->glyphId)); |
1776 | 0 | if (uint16_t(layer->paletteEntryIndex) == 0xFFFF) { |
1777 | 0 | aColors.AppendElement(aDefaultColor); |
1778 | 0 | } else { |
1779 | 0 | const CPALColorRecord* color = |
1780 | 0 | reinterpret_cast<const CPALColorRecord*>( |
1781 | 0 | reinterpret_cast<const uint8_t*>(cpal) + |
1782 | 0 | offsetFirstColorRecord + |
1783 | 0 | sizeof(CPALColorRecord) * uint16_t(layer->paletteEntryIndex)); |
1784 | 0 | aColors.AppendElement(mozilla::gfx::Color(color->red / 255.0, |
1785 | 0 | color->green / 255.0, |
1786 | 0 | color->blue / 255.0, |
1787 | 0 | color->alpha / 255.0)); |
1788 | 0 | } |
1789 | 0 | layer++; |
1790 | 0 | } |
1791 | 0 | return true; |
1792 | 0 | } |
1793 | | |
1794 | | void |
1795 | | gfxFontUtils::GetVariationInstances(gfxFontEntry* aFontEntry, |
1796 | | nsTArray<gfxFontVariationInstance>& aInstances) |
1797 | 0 | { |
1798 | 0 | MOZ_ASSERT(aInstances.IsEmpty()); |
1799 | 0 |
|
1800 | 0 | if (!aFontEntry->HasVariations()) { |
1801 | 0 | return; |
1802 | 0 | } |
1803 | 0 | |
1804 | 0 | // Some platforms don't offer a simple API to return the list of instances, |
1805 | 0 | // so we have to interpret the 'fvar' table ourselves. |
1806 | 0 | |
1807 | 0 | // https://www.microsoft.com/typography/otspec/fvar.htm#fvarHeader |
1808 | 0 | struct FvarHeader { |
1809 | 0 | AutoSwap_PRUint16 majorVersion; |
1810 | 0 | AutoSwap_PRUint16 minorVersion; |
1811 | 0 | AutoSwap_PRUint16 axesArrayOffset; |
1812 | 0 | AutoSwap_PRUint16 reserved; |
1813 | 0 | AutoSwap_PRUint16 axisCount; |
1814 | 0 | AutoSwap_PRUint16 axisSize; |
1815 | 0 | AutoSwap_PRUint16 instanceCount; |
1816 | 0 | AutoSwap_PRUint16 instanceSize; |
1817 | 0 | }; |
1818 | 0 |
|
1819 | 0 | // https://www.microsoft.com/typography/otspec/fvar.htm#variationAxisRecord |
1820 | 0 | struct AxisRecord { |
1821 | 0 | AutoSwap_PRUint32 axisTag; |
1822 | 0 | AutoSwap_PRInt32 minValue; |
1823 | 0 | AutoSwap_PRInt32 defaultValue; |
1824 | 0 | AutoSwap_PRInt32 maxValue; |
1825 | 0 | AutoSwap_PRUint16 flags; |
1826 | 0 | AutoSwap_PRUint16 axisNameID; |
1827 | 0 | }; |
1828 | 0 |
|
1829 | 0 | // https://www.microsoft.com/typography/otspec/fvar.htm#instanceRecord |
1830 | 0 | struct InstanceRecord { |
1831 | 0 | AutoSwap_PRUint16 subfamilyNameID; |
1832 | 0 | AutoSwap_PRUint16 flags; |
1833 | 0 | AutoSwap_PRInt32 coordinates[1]; // variable-size array [axisCount] |
1834 | 0 | // The variable-length 'coordinates' array may be followed by an |
1835 | 0 | // optional extra field 'postScriptNameID'. We can't directly |
1836 | 0 | // represent this in the struct, because its offset varies depending |
1837 | 0 | // on the number of axes present. |
1838 | 0 | // (Not currently used by our code here anyhow.) |
1839 | 0 | // AutoSwap_PRUint16 postScriptNameID; |
1840 | 0 | }; |
1841 | 0 |
|
1842 | 0 | // Helper to ensure we free a font table when we return. |
1843 | 0 | class AutoHBBlob { |
1844 | 0 | public: |
1845 | 0 | explicit AutoHBBlob(hb_blob_t* aBlob) : mBlob(aBlob) |
1846 | 0 | { } |
1847 | 0 |
|
1848 | 0 | ~AutoHBBlob() { |
1849 | 0 | if (mBlob) { |
1850 | 0 | hb_blob_destroy(mBlob); |
1851 | 0 | } |
1852 | 0 | } |
1853 | 0 |
|
1854 | 0 | operator hb_blob_t* () { return mBlob; } |
1855 | 0 |
|
1856 | 0 | private: |
1857 | 0 | hb_blob_t* const mBlob; |
1858 | 0 | }; |
1859 | 0 |
|
1860 | 0 | // Load the two font tables we need as harfbuzz blobs; if either is absent, |
1861 | 0 | // just bail out. |
1862 | 0 | AutoHBBlob fvarTable(aFontEntry->GetFontTable(TRUETYPE_TAG('f','v','a','r'))); |
1863 | 0 | AutoHBBlob nameTable(aFontEntry->GetFontTable(TRUETYPE_TAG('n','a','m','e'))); |
1864 | 0 | if (!fvarTable || !nameTable) { |
1865 | 0 | return; |
1866 | 0 | } |
1867 | 0 | unsigned int len; |
1868 | 0 | const char* data = hb_blob_get_data(fvarTable, &len); |
1869 | 0 | if (len < sizeof(FvarHeader)) { |
1870 | 0 | return; |
1871 | 0 | } |
1872 | 0 | // Read the fields of the table header; bail out if it looks broken. |
1873 | 0 | auto fvar = reinterpret_cast<const FvarHeader*>(data); |
1874 | 0 | if (uint16_t(fvar->majorVersion) != 1 || |
1875 | 0 | uint16_t(fvar->minorVersion) != 0 || |
1876 | 0 | uint16_t(fvar->reserved) != 2) { |
1877 | 0 | return; |
1878 | 0 | } |
1879 | 0 | uint16_t axisCount = fvar->axisCount; |
1880 | 0 | uint16_t axisSize = fvar->axisSize; |
1881 | 0 | uint16_t instanceCount = fvar->instanceCount; |
1882 | 0 | uint16_t instanceSize = fvar->instanceSize; |
1883 | 0 | if (axisCount == 0 || // no axes? |
1884 | 0 | // https://www.microsoft.com/typography/otspec/fvar.htm#axisSize |
1885 | 0 | axisSize != 20 || // required value for current table version |
1886 | 0 | instanceCount == 0 || // no instances? |
1887 | 0 | // https://www.microsoft.com/typography/otspec/fvar.htm#instanceSize |
1888 | 0 | (instanceSize != axisCount * sizeof(int32_t) + 4 && |
1889 | 0 | instanceSize != axisCount * sizeof(int32_t) + 6)) { |
1890 | 0 | return; |
1891 | 0 | } |
1892 | 0 | // Check that axis array will not exceed table size |
1893 | 0 | uint16_t axesOffset = fvar->axesArrayOffset; |
1894 | 0 | if (axesOffset + uint32_t(axisCount) * axisSize > len) { |
1895 | 0 | return; |
1896 | 0 | } |
1897 | 0 | // Get pointer to the array of axis records |
1898 | 0 | auto axes = reinterpret_cast<const AxisRecord*>(data + axesOffset); |
1899 | 0 | // Get address of instance array, and check it doesn't overflow table size. |
1900 | 0 | // https://www.microsoft.com/typography/otspec/fvar.htm#axisAndInstanceArrays |
1901 | 0 | auto instData = data + axesOffset + axisCount * axisSize; |
1902 | 0 | if (instData + uint32_t(instanceCount) * instanceSize > data + len) { |
1903 | 0 | return; |
1904 | 0 | } |
1905 | 0 | aInstances.SetCapacity(instanceCount); |
1906 | 0 | for (unsigned i = 0; i < instanceCount; ++i, instData += instanceSize) { |
1907 | 0 | // Typed pointer to the current instance record, to read its fields. |
1908 | 0 | auto inst = reinterpret_cast<const InstanceRecord*>(instData); |
1909 | 0 | // Pointer to the coordinates array within the instance record. |
1910 | 0 | // This array has axisCount elements, and is included in instanceSize |
1911 | 0 | // (which depends on axisCount, and was validated above) so we know |
1912 | 0 | // access to coords[j] below will not be outside the table bounds. |
1913 | 0 | auto coords = &inst->coordinates[0]; |
1914 | 0 | gfxFontVariationInstance instance; |
1915 | 0 | uint16_t nameID = inst->subfamilyNameID; |
1916 | 0 | nsresult rv = |
1917 | 0 | ReadCanonicalName(nameTable, nameID, instance.mName); |
1918 | 0 | if (NS_FAILED(rv)) { |
1919 | 0 | // If no name was available for the instance, ignore it. |
1920 | 0 | continue; |
1921 | 0 | } |
1922 | 0 | instance.mValues.SetCapacity(axisCount); |
1923 | 0 | for (unsigned j = 0; j < axisCount; ++j) { |
1924 | 0 | gfxFontVariationValue value; |
1925 | 0 | value.mAxis = axes[j].axisTag; |
1926 | 0 | value.mValue = int32_t(coords[j]) / 65536.0; |
1927 | 0 | instance.mValues.AppendElement(value); |
1928 | 0 | } |
1929 | 0 | aInstances.AppendElement(instance); |
1930 | 0 | } |
1931 | 0 | } |
1932 | | |
1933 | | #ifdef XP_WIN |
1934 | | |
1935 | | /* static */ |
1936 | | bool |
1937 | | gfxFontUtils::IsCffFont(const uint8_t* aFontData) |
1938 | | { |
1939 | | // this is only called after aFontData has passed basic validation, |
1940 | | // so we know there is enough data present to allow us to read the version! |
1941 | | const SFNTHeader *sfntHeader = reinterpret_cast<const SFNTHeader*>(aFontData); |
1942 | | return (sfntHeader->sfntVersion == TRUETYPE_TAG('O','T','T','O')); |
1943 | | } |
1944 | | |
1945 | | #endif |
1946 | | |
1947 | | #undef acceptablePlatform |
1948 | | #undef isSymbol |
1949 | | #undef isUVSEncoding |
1950 | | #undef LOG |
1951 | | #undef LOG_ENABLED |