Bug 1918098 - ruff: upgrade ruff to v0.6.4 r=linter-reviewers,ahal
[gecko.git] / gfx / thebes / gfxFontUtils.cpp
blob0ca17989200e36408947d727ebe8c41078347445
1 /* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
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/. */
6 #include "mozilla/ArrayUtils.h"
7 #include "mozilla/BinarySearch.h"
9 #include "gfxFontUtils.h"
10 #include "gfxFontEntry.h"
11 #include "gfxFontVariations.h"
12 #include "gfxUtils.h"
14 #include "nsServiceManagerUtils.h"
16 #include "mozilla/Preferences.h"
17 #include "mozilla/BinarySearch.h"
18 #include "mozilla/Sprintf.h"
19 #include "mozilla/Unused.h"
21 #include "nsCOMPtr.h"
22 #include "nsIUUIDGenerator.h"
23 #include "mozilla/Encoding.h"
25 #include "mozilla/ServoStyleSet.h"
26 #include "mozilla/dom/WorkerCommon.h"
28 #include "plbase64.h"
29 #include "mozilla/Logging.h"
31 #ifdef XP_DARWIN
32 # include <CoreFoundation/CoreFoundation.h>
33 #endif
35 #define LOG(log, args) MOZ_LOG(gfxPlatform::GetLog(log), LogLevel::Debug, args)
37 #define UNICODE_BMP_LIMIT 0x10000
39 using namespace mozilla;
41 #pragma pack(1)
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;
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;
60 typedef struct {
61 AutoSwap_PRUint32 startCharCode;
62 AutoSwap_PRUint32 endCharCode;
63 AutoSwap_PRUint32 startGlyphId;
64 } Format12Group;
66 #pragma pack()
68 void gfxSparseBitSet::Dump(const char* aPrefix, eGfxLog aWhichLog) const {
69 uint32_t numBlocks = mBlockIndex.Length();
71 for (uint32_t b = 0; b < numBlocks; b++) {
72 if (mBlockIndex[b] == NO_BLOCK) {
73 continue;
75 const Block* block = &mBlocks[mBlockIndex[b]];
76 const int BUFSIZE = 256;
77 char outStr[BUFSIZE];
78 int index = 0;
79 index += snprintf(&outStr[index], BUFSIZE - index, "%s u+%6.6x [", aPrefix,
80 (b * BLOCK_SIZE_BITS));
81 for (int i = 0; i < 32; i += 4) {
82 for (int j = i; j < i + 4; j++) {
83 uint8_t bits = block->mBits[j];
84 uint8_t flip1 = ((bits & 0xaa) >> 1) | ((bits & 0x55) << 1);
85 uint8_t flip2 = ((flip1 & 0xcc) >> 2) | ((flip1 & 0x33) << 2);
86 uint8_t flipped = ((flip2 & 0xf0) >> 4) | ((flip2 & 0x0f) << 4);
88 index += snprintf(&outStr[index], BUFSIZE - index, "%2.2x", flipped);
90 if (i + 4 != 32) index += snprintf(&outStr[index], BUFSIZE - index, " ");
92 Unused << snprintf(&outStr[index], BUFSIZE - index, "]");
93 LOG(aWhichLog, ("%s", outStr));
97 nsresult gfxFontUtils::ReadCMAPTableFormat10(const uint8_t* aBuf,
98 uint32_t aLength,
99 gfxSparseBitSet& aCharacterMap) {
100 // Ensure table is large enough that we can safely read the header
101 NS_ENSURE_TRUE(aLength >= sizeof(Format10CmapHeader),
102 NS_ERROR_GFX_CMAP_MALFORMED);
104 // Sanity-check header fields
105 const Format10CmapHeader* cmap10 =
106 reinterpret_cast<const Format10CmapHeader*>(aBuf);
107 NS_ENSURE_TRUE(uint16_t(cmap10->format) == 10, NS_ERROR_GFX_CMAP_MALFORMED);
108 NS_ENSURE_TRUE(uint16_t(cmap10->reserved) == 0, NS_ERROR_GFX_CMAP_MALFORMED);
110 uint32_t tablelen = cmap10->length;
111 NS_ENSURE_TRUE(tablelen >= sizeof(Format10CmapHeader) && tablelen <= aLength,
112 NS_ERROR_GFX_CMAP_MALFORMED);
114 NS_ENSURE_TRUE(cmap10->language == 0, NS_ERROR_GFX_CMAP_MALFORMED);
116 uint32_t numChars = cmap10->numChars;
117 NS_ENSURE_TRUE(
118 tablelen == sizeof(Format10CmapHeader) + numChars * sizeof(uint16_t),
119 NS_ERROR_GFX_CMAP_MALFORMED);
121 uint32_t charCode = cmap10->startCharCode;
122 NS_ENSURE_TRUE(charCode <= CMAP_MAX_CODEPOINT &&
123 charCode + numChars <= CMAP_MAX_CODEPOINT,
124 NS_ERROR_GFX_CMAP_MALFORMED);
126 // glyphs[] array immediately follows the subtable header
127 const AutoSwap_PRUint16* glyphs =
128 reinterpret_cast<const AutoSwap_PRUint16*>(cmap10 + 1);
130 for (uint32_t i = 0; i < numChars; ++i) {
131 if (uint16_t(*glyphs) != 0) {
132 aCharacterMap.set(charCode);
134 ++charCode;
135 ++glyphs;
138 aCharacterMap.Compact();
140 return NS_OK;
143 nsresult gfxFontUtils::ReadCMAPTableFormat12or13(
144 const uint8_t* aBuf, uint32_t aLength, gfxSparseBitSet& aCharacterMap) {
145 // Format 13 has the same structure as format 12, the only difference is
146 // the interpretation of the glyphID field. So we can share the code here
147 // that reads the table and just records character coverage.
149 // Ensure table is large enough that we can safely read the header
150 NS_ENSURE_TRUE(aLength >= sizeof(Format12CmapHeader),
151 NS_ERROR_GFX_CMAP_MALFORMED);
153 // Sanity-check header fields
154 const Format12CmapHeader* cmap12 =
155 reinterpret_cast<const Format12CmapHeader*>(aBuf);
156 NS_ENSURE_TRUE(
157 uint16_t(cmap12->format) == 12 || uint16_t(cmap12->format) == 13,
158 NS_ERROR_GFX_CMAP_MALFORMED);
159 NS_ENSURE_TRUE(uint16_t(cmap12->reserved) == 0, NS_ERROR_GFX_CMAP_MALFORMED);
161 uint32_t tablelen = cmap12->length;
162 NS_ENSURE_TRUE(tablelen >= sizeof(Format12CmapHeader) && tablelen <= aLength,
163 NS_ERROR_GFX_CMAP_MALFORMED);
165 NS_ENSURE_TRUE(cmap12->language == 0, NS_ERROR_GFX_CMAP_MALFORMED);
167 // Check that the table is large enough for the group array
168 const uint32_t numGroups = cmap12->numGroups;
169 NS_ENSURE_TRUE(
170 (tablelen - sizeof(Format12CmapHeader)) / sizeof(Format12Group) >=
171 numGroups,
172 NS_ERROR_GFX_CMAP_MALFORMED);
174 // The array of groups immediately follows the subtable header.
175 const Format12Group* group =
176 reinterpret_cast<const Format12Group*>(aBuf + sizeof(Format12CmapHeader));
178 // Check that groups are in correct order and do not overlap,
179 // and record character coverage in aCharacterMap.
180 uint32_t prevEndCharCode = 0;
181 for (uint32_t i = 0; i < numGroups; i++, group++) {
182 uint32_t startCharCode = group->startCharCode;
183 const uint32_t endCharCode = group->endCharCode;
184 NS_ENSURE_TRUE((prevEndCharCode < startCharCode || i == 0) &&
185 startCharCode <= endCharCode &&
186 endCharCode <= CMAP_MAX_CODEPOINT,
187 NS_ERROR_GFX_CMAP_MALFORMED);
188 // don't include a character that maps to glyph ID 0 (.notdef)
189 if (group->startGlyphId == 0) {
190 startCharCode++;
192 if (startCharCode <= endCharCode) {
193 aCharacterMap.SetRange(startCharCode, endCharCode);
195 prevEndCharCode = endCharCode;
198 aCharacterMap.Compact();
200 return NS_OK;
203 nsresult gfxFontUtils::ReadCMAPTableFormat4(const uint8_t* aBuf,
204 uint32_t aLength,
205 gfxSparseBitSet& aCharacterMap,
206 bool aIsSymbolFont) {
207 enum {
208 OffsetFormat = 0,
209 OffsetLength = 2,
210 OffsetLanguage = 4,
211 OffsetSegCountX2 = 6
214 NS_ENSURE_TRUE(ReadShortAt(aBuf, OffsetFormat) == 4,
215 NS_ERROR_GFX_CMAP_MALFORMED);
216 uint16_t tablelen = ReadShortAt(aBuf, OffsetLength);
217 NS_ENSURE_TRUE(tablelen <= aLength, NS_ERROR_GFX_CMAP_MALFORMED);
218 NS_ENSURE_TRUE(tablelen > 16, NS_ERROR_GFX_CMAP_MALFORMED);
220 // This field should normally (except for Mac platform subtables) be zero
221 // according to the OT spec, but some buggy fonts have lang = 1 (which would
222 // be English for MacOS). E.g. Arial Narrow Bold, v. 1.1 (Tiger), Arial
223 // Unicode MS (see bug 530614). So accept either zero or one here; the error
224 // should be harmless.
225 NS_ENSURE_TRUE((ReadShortAt(aBuf, OffsetLanguage) & 0xfffe) == 0,
226 NS_ERROR_GFX_CMAP_MALFORMED);
228 uint16_t segCountX2 = ReadShortAt(aBuf, OffsetSegCountX2);
229 NS_ENSURE_TRUE(tablelen >= 16 + (segCountX2 * 4),
230 NS_ERROR_GFX_CMAP_MALFORMED);
232 const uint16_t segCount = segCountX2 / 2;
234 const uint16_t* endCounts = reinterpret_cast<const uint16_t*>(aBuf + 14);
235 const uint16_t* startCounts =
236 endCounts + 1 /* skip one uint16_t for reservedPad */ + segCount;
237 const uint16_t* idDeltas = startCounts + segCount;
238 const uint16_t* idRangeOffsets = idDeltas + segCount;
239 uint16_t prevEndCount = 0;
240 for (uint16_t i = 0; i < segCount; i++) {
241 const uint16_t endCount = ReadShortAt16(endCounts, i);
242 const uint16_t startCount = ReadShortAt16(startCounts, i);
243 const uint16_t idRangeOffset = ReadShortAt16(idRangeOffsets, i);
245 // sanity-check range
246 // This permits ranges to overlap by 1 character, which is strictly
247 // incorrect but occurs in Baskerville on OS X 10.7 (see bug 689087),
248 // and appears to be harmless in practice
249 NS_ENSURE_TRUE(startCount >= prevEndCount && startCount <= endCount,
250 NS_ERROR_GFX_CMAP_MALFORMED);
251 prevEndCount = endCount;
253 if (idRangeOffset == 0) {
254 // figure out if there's a code in the range that would map to
255 // glyph ID 0 (.notdef); if so, we need to skip setting that
256 // character code in the map
257 const uint16_t skipCode = 65536 - ReadShortAt16(idDeltas, i);
258 if (startCount < skipCode) {
259 aCharacterMap.SetRange(startCount,
260 std::min<uint16_t>(skipCode - 1, endCount));
262 if (skipCode < endCount) {
263 aCharacterMap.SetRange(std::max<uint16_t>(startCount, skipCode + 1),
264 endCount);
266 } else {
267 // Unused: self-documenting.
268 // const uint16_t idDelta = ReadShortAt16(idDeltas, i);
269 for (uint32_t c = startCount; c <= endCount; ++c) {
270 if (c == 0xFFFF) break;
272 const uint16_t* gdata =
273 (idRangeOffset / 2 + (c - startCount) + &idRangeOffsets[i]);
275 NS_ENSURE_TRUE(
276 (uint8_t*)gdata > aBuf && (uint8_t*)gdata < aBuf + aLength,
277 NS_ERROR_GFX_CMAP_MALFORMED);
279 // make sure we have a glyph
280 if (*gdata != 0) {
281 // The glyph index at this point is:
282 uint16_t glyph = ReadShortAt16(idDeltas, i) + *gdata;
283 if (glyph) {
284 aCharacterMap.set(c);
291 if (aIsSymbolFont) {
292 // For fonts with "MS Symbol" encoding, we duplicate character mappings in
293 // the U+F0xx range down to U+00xx codepoints, so as to support fonts such
294 // as Wingdings.
295 // Note that if the font actually has cmap coverage for the U+00xx range
296 // (either duplicating the PUA codepoints or mapping to separate glyphs),
297 // this will not affect it.
298 for (uint32_t c = 0x0020; c <= 0x00ff; ++c) {
299 if (aCharacterMap.test(0xf000 + c)) {
300 aCharacterMap.set(c);
305 aCharacterMap.Compact();
307 return NS_OK;
310 nsresult gfxFontUtils::ReadCMAPTableFormat14(const uint8_t* aBuf,
311 uint32_t aLength,
312 const uint8_t*& aTable) {
313 enum {
314 OffsetFormat = 0,
315 OffsetTableLength = 2,
316 OffsetNumVarSelectorRecords = 6,
317 OffsetVarSelectorRecords = 10,
319 SizeOfVarSelectorRecord = 11,
320 VSRecOffsetVarSelector = 0,
321 VSRecOffsetDefUVSOffset = 3,
322 VSRecOffsetNonDefUVSOffset = 7,
324 SizeOfDefUVSTable = 4,
325 DefUVSOffsetStartUnicodeValue = 0,
326 DefUVSOffsetAdditionalCount = 3,
328 SizeOfNonDefUVSTable = 5,
329 NonDefUVSOffsetUnicodeValue = 0,
330 NonDefUVSOffsetGlyphID = 3
332 NS_ENSURE_TRUE(aLength >= OffsetVarSelectorRecords,
333 NS_ERROR_GFX_CMAP_MALFORMED);
335 NS_ENSURE_TRUE(ReadShortAt(aBuf, OffsetFormat) == 14,
336 NS_ERROR_GFX_CMAP_MALFORMED);
338 uint32_t tablelen = ReadLongAt(aBuf, OffsetTableLength);
339 NS_ENSURE_TRUE(tablelen <= aLength, NS_ERROR_GFX_CMAP_MALFORMED);
340 NS_ENSURE_TRUE(tablelen >= OffsetVarSelectorRecords,
341 NS_ERROR_GFX_CMAP_MALFORMED);
343 const uint32_t numVarSelectorRecords =
344 ReadLongAt(aBuf, OffsetNumVarSelectorRecords);
345 NS_ENSURE_TRUE(
346 (tablelen - OffsetVarSelectorRecords) / SizeOfVarSelectorRecord >=
347 numVarSelectorRecords,
348 NS_ERROR_GFX_CMAP_MALFORMED);
350 const uint8_t* records = aBuf + OffsetVarSelectorRecords;
351 for (uint32_t i = 0; i < numVarSelectorRecords;
352 i++, records += SizeOfVarSelectorRecord) {
353 const uint32_t varSelector = ReadUint24At(records, VSRecOffsetVarSelector);
354 const uint32_t defUVSOffset = ReadLongAt(records, VSRecOffsetDefUVSOffset);
355 const uint32_t nonDefUVSOffset =
356 ReadLongAt(records, VSRecOffsetNonDefUVSOffset);
357 NS_ENSURE_TRUE(varSelector <= CMAP_MAX_CODEPOINT &&
358 defUVSOffset <= tablelen - 4 &&
359 nonDefUVSOffset <= tablelen - 4,
360 NS_ERROR_GFX_CMAP_MALFORMED);
362 if (defUVSOffset) {
363 const uint32_t numUnicodeValueRanges = ReadLongAt(aBuf, defUVSOffset);
364 NS_ENSURE_TRUE((tablelen - defUVSOffset) / SizeOfDefUVSTable >=
365 numUnicodeValueRanges,
366 NS_ERROR_GFX_CMAP_MALFORMED);
367 const uint8_t* tables = aBuf + defUVSOffset + 4;
368 uint32_t prevEndUnicode = 0;
369 for (uint32_t j = 0; j < numUnicodeValueRanges;
370 j++, tables += SizeOfDefUVSTable) {
371 const uint32_t startUnicode =
372 ReadUint24At(tables, DefUVSOffsetStartUnicodeValue);
373 const uint32_t endUnicode =
374 startUnicode + tables[DefUVSOffsetAdditionalCount];
375 NS_ENSURE_TRUE((prevEndUnicode < startUnicode || j == 0) &&
376 endUnicode <= CMAP_MAX_CODEPOINT,
377 NS_ERROR_GFX_CMAP_MALFORMED);
378 prevEndUnicode = endUnicode;
382 if (nonDefUVSOffset) {
383 const uint32_t numUVSMappings = ReadLongAt(aBuf, nonDefUVSOffset);
384 NS_ENSURE_TRUE(
385 (tablelen - nonDefUVSOffset) / SizeOfNonDefUVSTable >= numUVSMappings,
386 NS_ERROR_GFX_CMAP_MALFORMED);
387 const uint8_t* tables = aBuf + nonDefUVSOffset + 4;
388 uint32_t prevUnicode = 0;
389 for (uint32_t j = 0; j < numUVSMappings;
390 j++, tables += SizeOfNonDefUVSTable) {
391 const uint32_t unicodeValue =
392 ReadUint24At(tables, NonDefUVSOffsetUnicodeValue);
393 NS_ENSURE_TRUE((prevUnicode < unicodeValue || j == 0) &&
394 unicodeValue <= CMAP_MAX_CODEPOINT,
395 NS_ERROR_GFX_CMAP_MALFORMED);
396 prevUnicode = unicodeValue;
401 uint8_t* table = new uint8_t[tablelen];
402 memcpy(table, aBuf, tablelen);
404 aTable = static_cast<const uint8_t*>(table);
406 return NS_OK;
409 // For fonts with two format-4 tables, the first one (Unicode platform) is
410 // preferred on the Mac; on other platforms we allow the Microsoft-platform
411 // subtable to replace it.
413 #if defined(XP_MACOSX)
414 # define acceptableFormat4(p, e, k) \
415 (((p) == PLATFORM_ID_MICROSOFT && (e) == EncodingIDMicrosoft && !(k)) || \
416 ((p) == PLATFORM_ID_UNICODE))
418 # define acceptableUCS4Encoding(p, e, k) \
419 (((p) == PLATFORM_ID_MICROSOFT && \
420 (e) == EncodingIDUCS4ForMicrosoftPlatform) && \
421 (k) != 12 || \
422 ((p) == PLATFORM_ID_UNICODE && ((e) != EncodingIDUVSForUnicodePlatform)))
423 #else
424 # define acceptableFormat4(p, e, k) \
425 (((p) == PLATFORM_ID_MICROSOFT && (e) == EncodingIDMicrosoft) || \
426 ((p) == PLATFORM_ID_UNICODE))
428 # define acceptableUCS4Encoding(p, e, k) \
429 ((p) == PLATFORM_ID_MICROSOFT && (e) == EncodingIDUCS4ForMicrosoftPlatform)
430 #endif
432 #define acceptablePlatform(p) \
433 ((p) == PLATFORM_ID_UNICODE || (p) == PLATFORM_ID_MICROSOFT)
434 #define isSymbol(p, e) ((p) == PLATFORM_ID_MICROSOFT && (e) == EncodingIDSymbol)
435 #define isUVSEncoding(p, e) \
436 ((p) == PLATFORM_ID_UNICODE && (e) == EncodingIDUVSForUnicodePlatform)
438 uint32_t gfxFontUtils::FindPreferredSubtable(const uint8_t* aBuf,
439 uint32_t aBufLength,
440 uint32_t* aTableOffset,
441 uint32_t* aUVSTableOffset,
442 bool* aIsSymbolFont) {
443 enum {
444 OffsetVersion = 0,
445 OffsetNumTables = 2,
446 SizeOfHeader = 4,
448 TableOffsetPlatformID = 0,
449 TableOffsetEncodingID = 2,
450 TableOffsetOffset = 4,
451 SizeOfTable = 8,
453 SubtableOffsetFormat = 0
455 enum {
456 EncodingIDSymbol = 0,
457 EncodingIDMicrosoft = 1,
458 EncodingIDDefaultForUnicodePlatform = 0,
459 EncodingIDUCS4ForUnicodePlatform = 3,
460 EncodingIDUVSForUnicodePlatform = 5,
461 EncodingIDUCS4ForMicrosoftPlatform = 10
464 if (aUVSTableOffset) {
465 *aUVSTableOffset = 0;
467 if (aIsSymbolFont) {
468 *aIsSymbolFont = false;
471 if (!aBuf || aBufLength < SizeOfHeader) {
472 // cmap table is missing, or too small to contain header fields!
473 return 0;
476 // uint16_t version = ReadShortAt(aBuf, OffsetVersion); // Unused:
477 // self-documenting.
478 uint16_t numTables = ReadShortAt(aBuf, OffsetNumTables);
479 if (aBufLength < uint32_t(SizeOfHeader + numTables * SizeOfTable)) {
480 return 0;
483 // save the format we want here
484 uint32_t keepFormat = 0;
486 const uint8_t* table = aBuf + SizeOfHeader;
487 for (uint16_t i = 0; i < numTables; ++i, table += SizeOfTable) {
488 const uint16_t platformID = ReadShortAt(table, TableOffsetPlatformID);
489 if (!acceptablePlatform(platformID)) continue;
491 const uint16_t encodingID = ReadShortAt(table, TableOffsetEncodingID);
492 const uint32_t offset = ReadLongAt(table, TableOffsetOffset);
493 if (aBufLength - 2 < offset) {
494 // this subtable is not valid - beyond end of buffer
495 return 0;
498 const uint8_t* subtable = aBuf + offset;
499 const uint16_t format = ReadShortAt(subtable, SubtableOffsetFormat);
501 if (isSymbol(platformID, encodingID)) {
502 keepFormat = format;
503 *aTableOffset = offset;
504 if (aIsSymbolFont) {
505 *aIsSymbolFont = true;
507 break;
508 } else if (format == 4 &&
509 acceptableFormat4(platformID, encodingID, keepFormat)) {
510 keepFormat = format;
511 *aTableOffset = offset;
512 } else if ((format == 10 || format == 12 || format == 13) &&
513 acceptableUCS4Encoding(platformID, encodingID, keepFormat)) {
514 keepFormat = format;
515 *aTableOffset = offset;
516 if (platformID > PLATFORM_ID_UNICODE || !aUVSTableOffset ||
517 *aUVSTableOffset) {
518 break; // we don't want to try anything else when this format is
519 // available.
521 } else if (format == 14 && isUVSEncoding(platformID, encodingID) &&
522 aUVSTableOffset) {
523 *aUVSTableOffset = offset;
524 if (keepFormat == 10 || keepFormat == 12) {
525 break;
530 return keepFormat;
533 nsresult gfxFontUtils::ReadCMAP(const uint8_t* aBuf, uint32_t aBufLength,
534 gfxSparseBitSet& aCharacterMap,
535 uint32_t& aUVSOffset) {
536 uint32_t offset;
537 bool isSymbolFont;
538 uint32_t format = FindPreferredSubtable(aBuf, aBufLength, &offset,
539 &aUVSOffset, &isSymbolFont);
541 switch (format) {
542 case 4:
543 return ReadCMAPTableFormat4(aBuf + offset, aBufLength - offset,
544 aCharacterMap, isSymbolFont);
546 case 10:
547 return ReadCMAPTableFormat10(aBuf + offset, aBufLength - offset,
548 aCharacterMap);
550 case 12:
551 case 13:
552 return ReadCMAPTableFormat12or13(aBuf + offset, aBufLength - offset,
553 aCharacterMap);
555 default:
556 break;
559 return NS_ERROR_FAILURE;
562 #pragma pack(1)
564 typedef struct {
565 AutoSwap_PRUint16 format;
566 AutoSwap_PRUint16 length;
567 AutoSwap_PRUint16 language;
568 AutoSwap_PRUint16 segCountX2;
569 AutoSwap_PRUint16 searchRange;
570 AutoSwap_PRUint16 entrySelector;
571 AutoSwap_PRUint16 rangeShift;
573 AutoSwap_PRUint16 arrays[1];
574 } Format4Cmap;
576 typedef struct Format14Cmap {
577 AutoSwap_PRUint16 format;
578 AutoSwap_PRUint32 length;
579 AutoSwap_PRUint32 numVarSelectorRecords;
581 typedef struct {
582 AutoSwap_PRUint24 varSelector;
583 AutoSwap_PRUint32 defaultUVSOffset;
584 AutoSwap_PRUint32 nonDefaultUVSOffset;
585 } VarSelectorRecord;
587 VarSelectorRecord varSelectorRecords[1];
588 } Format14Cmap;
590 typedef struct NonDefUVSTable {
591 AutoSwap_PRUint32 numUVSMappings;
593 typedef struct {
594 AutoSwap_PRUint24 unicodeValue;
595 AutoSwap_PRUint16 glyphID;
596 } UVSMapping;
598 UVSMapping uvsMappings[1];
599 } NonDefUVSTable;
601 #pragma pack()
603 uint32_t gfxFontUtils::MapCharToGlyphFormat4(const uint8_t* aBuf,
604 uint32_t aLength, char16_t aCh) {
605 const Format4Cmap* cmap4 = reinterpret_cast<const Format4Cmap*>(aBuf);
607 uint16_t segCount = (uint16_t)(cmap4->segCountX2) / 2;
609 const AutoSwap_PRUint16* endCodes = &cmap4->arrays[0];
610 const AutoSwap_PRUint16* startCodes = &cmap4->arrays[segCount + 1];
611 const AutoSwap_PRUint16* idDelta = &startCodes[segCount];
612 const AutoSwap_PRUint16* idRangeOffset = &idDelta[segCount];
614 // Sanity-check that the fixed-size arrays don't exceed the buffer.
615 const uint8_t* const limit = aBuf + aLength;
616 if ((const uint8_t*)(&idRangeOffset[segCount]) > limit) {
617 return 0; // broken font, just bail out safely
620 // For most efficient binary search, we want to work on a range of segment
621 // indexes that is a power of 2 so that we can always halve it by shifting.
622 // So we find the largest power of 2 that is <= segCount.
623 // We will offset this range by segOffset so as to reach the end
624 // of the table, provided that doesn't put us beyond the target
625 // value from the outset.
626 uint32_t powerOf2 = mozilla::FindHighestBit(segCount);
627 uint32_t segOffset = segCount - powerOf2;
628 uint32_t idx = 0;
630 if (uint16_t(startCodes[segOffset]) <= aCh) {
631 idx = segOffset;
634 // Repeatedly halve the size of the range until we find the target group
635 while (powerOf2 > 1) {
636 powerOf2 >>= 1;
637 if (uint16_t(startCodes[idx + powerOf2]) <= aCh) {
638 idx += powerOf2;
642 if (aCh >= uint16_t(startCodes[idx]) && aCh <= uint16_t(endCodes[idx])) {
643 uint16_t result;
644 if (uint16_t(idRangeOffset[idx]) == 0) {
645 result = aCh;
646 } else {
647 uint16_t offset = aCh - uint16_t(startCodes[idx]);
648 const AutoSwap_PRUint16* glyphIndexTable =
649 (const AutoSwap_PRUint16*)((const char*)&idRangeOffset[idx] +
650 uint16_t(idRangeOffset[idx]));
651 if ((const uint8_t*)(glyphIndexTable + offset + 1) > limit) {
652 return 0; // broken font, just bail out safely
654 result = glyphIndexTable[offset];
657 // Note that this is unsigned 16-bit arithmetic, and may wrap around
658 // (which is required behavior per spec)
659 result += uint16_t(idDelta[idx]);
660 return result;
663 return 0;
666 uint32_t gfxFontUtils::MapCharToGlyphFormat10(const uint8_t* aBuf,
667 uint32_t aCh) {
668 const Format10CmapHeader* cmap10 =
669 reinterpret_cast<const Format10CmapHeader*>(aBuf);
671 uint32_t startChar = cmap10->startCharCode;
672 uint32_t numChars = cmap10->numChars;
674 if (aCh < startChar || aCh >= startChar + numChars) {
675 return 0;
678 const AutoSwap_PRUint16* glyphs =
679 reinterpret_cast<const AutoSwap_PRUint16*>(cmap10 + 1);
681 uint16_t glyph = glyphs[aCh - startChar];
682 return glyph;
685 uint32_t gfxFontUtils::MapCharToGlyphFormat12or13(const uint8_t* aBuf,
686 uint32_t aCh) {
687 // The only difference between formats 12 and 13 is the interpretation of
688 // the glyphId field. So the code here uses the same "Format12" structures,
689 // etc., to handle both subtable formats.
691 const Format12CmapHeader* cmap12 =
692 reinterpret_cast<const Format12CmapHeader*>(aBuf);
694 // We know that numGroups is within range for the subtable size
695 // because it was checked by ReadCMAPTableFormat12or13.
696 uint32_t numGroups = cmap12->numGroups;
698 // The array of groups immediately follows the subtable header.
699 const Format12Group* groups =
700 reinterpret_cast<const Format12Group*>(aBuf + sizeof(Format12CmapHeader));
702 // For most efficient binary search, we want to work on a range that
703 // is a power of 2 so that we can always halve it by shifting.
704 // So we find the largest power of 2 that is <= numGroups.
705 // We will offset this range by rangeOffset so as to reach the end
706 // of the table, provided that doesn't put us beyond the target
707 // value from the outset.
708 uint32_t powerOf2 = mozilla::FindHighestBit(numGroups);
709 uint32_t rangeOffset = numGroups - powerOf2;
710 uint32_t range = 0;
711 uint32_t startCharCode;
713 if (groups[rangeOffset].startCharCode <= aCh) {
714 range = rangeOffset;
717 // Repeatedly halve the size of the range until we find the target group
718 while (powerOf2 > 1) {
719 powerOf2 >>= 1;
720 if (groups[range + powerOf2].startCharCode <= aCh) {
721 range += powerOf2;
725 // Check if the character is actually present in the range and return
726 // the corresponding glyph ID. Here is where formats 12 and 13 interpret
727 // the startGlyphId (12) or glyphId (13) field differently
728 startCharCode = groups[range].startCharCode;
729 if (startCharCode <= aCh && groups[range].endCharCode >= aCh) {
730 return uint16_t(cmap12->format) == 12
731 ? uint16_t(groups[range].startGlyphId) + aCh - startCharCode
732 : uint16_t(groups[range].startGlyphId);
735 // Else it's not present, so return the .notdef glyph
736 return 0;
739 namespace {
741 struct Format14CmapWrapper {
742 const Format14Cmap& mCmap14;
743 explicit Format14CmapWrapper(const Format14Cmap& cmap14) : mCmap14(cmap14) {}
744 uint32_t operator[](size_t index) const {
745 return mCmap14.varSelectorRecords[index].varSelector;
749 struct NonDefUVSTableWrapper {
750 const NonDefUVSTable& mTable;
751 explicit NonDefUVSTableWrapper(const NonDefUVSTable& table) : mTable(table) {}
752 uint32_t operator[](size_t index) const {
753 return mTable.uvsMappings[index].unicodeValue;
757 } // namespace
759 uint16_t gfxFontUtils::MapUVSToGlyphFormat14(const uint8_t* aBuf, uint32_t aCh,
760 uint32_t aVS) {
761 using mozilla::BinarySearch;
762 const Format14Cmap* cmap14 = reinterpret_cast<const Format14Cmap*>(aBuf);
764 size_t index;
765 if (!BinarySearch(Format14CmapWrapper(*cmap14), 0,
766 cmap14->numVarSelectorRecords, aVS, &index)) {
767 return 0;
770 const uint32_t nonDefUVSOffset =
771 cmap14->varSelectorRecords[index].nonDefaultUVSOffset;
772 if (!nonDefUVSOffset) {
773 return 0;
776 const NonDefUVSTable* table =
777 reinterpret_cast<const NonDefUVSTable*>(aBuf + nonDefUVSOffset);
779 if (BinarySearch(NonDefUVSTableWrapper(*table), 0, table->numUVSMappings, aCh,
780 &index)) {
781 return table->uvsMappings[index].glyphID;
784 return 0;
787 uint32_t gfxFontUtils::MapCharToGlyph(const uint8_t* aCmapBuf,
788 uint32_t aBufLength, uint32_t aUnicode,
789 uint32_t aVarSelector) {
790 uint32_t offset, uvsOffset;
791 bool isSymbolFont;
792 uint32_t format = FindPreferredSubtable(aCmapBuf, aBufLength, &offset,
793 &uvsOffset, &isSymbolFont);
795 uint32_t gid;
796 switch (format) {
797 case 4:
798 gid = aUnicode < UNICODE_BMP_LIMIT
799 ? MapCharToGlyphFormat4(aCmapBuf + offset, aBufLength - offset,
800 char16_t(aUnicode))
801 : 0;
802 if (!gid && isSymbolFont) {
803 if (auto pua = MapLegacySymbolFontCharToPUA(aUnicode)) {
804 gid = MapCharToGlyphFormat4(aCmapBuf + offset, aBufLength - offset,
805 pua);
808 break;
809 case 10:
810 gid = MapCharToGlyphFormat10(aCmapBuf + offset, aUnicode);
811 break;
812 case 12:
813 case 13:
814 gid = MapCharToGlyphFormat12or13(aCmapBuf + offset, aUnicode);
815 break;
816 default:
817 NS_WARNING("unsupported cmap format, glyphs will be missing");
818 gid = 0;
821 if (aVarSelector && uvsOffset && gid) {
822 uint32_t varGID = gfxFontUtils::MapUVSToGlyphFormat14(
823 aCmapBuf + uvsOffset, aUnicode, aVarSelector);
824 if (!varGID) {
825 aUnicode = gfxFontUtils::GetUVSFallback(aUnicode, aVarSelector);
826 if (aUnicode) {
827 switch (format) {
828 case 4:
829 if (aUnicode < UNICODE_BMP_LIMIT) {
830 varGID = MapCharToGlyphFormat4(
831 aCmapBuf + offset, aBufLength - offset, char16_t(aUnicode));
833 break;
834 case 10:
835 varGID = MapCharToGlyphFormat10(aCmapBuf + offset, aUnicode);
836 break;
837 case 12:
838 case 13:
839 varGID = MapCharToGlyphFormat12or13(aCmapBuf + offset, aUnicode);
840 break;
844 if (varGID) {
845 gid = varGID;
848 // else the variation sequence was not supported, use default mapping
849 // of the character code alone
852 return gid;
855 void gfxFontUtils::ParseFontList(const nsACString& aFamilyList,
856 nsTArray<nsCString>& aFontList) {
857 const char kComma = ',';
859 // append each font name to the list
860 nsAutoCString fontname;
861 const char *p, *p_end;
862 aFamilyList.BeginReading(p);
863 aFamilyList.EndReading(p_end);
865 while (p < p_end) {
866 const char* nameStart = p;
867 while (++p != p_end && *p != kComma) /* nothing */
870 // pull out a single name and clean out leading/trailing whitespace
871 fontname = Substring(nameStart, p);
872 fontname.CompressWhitespace(true, true);
874 // append it to the list if it's not empty
875 if (!fontname.IsEmpty()) {
876 aFontList.AppendElement(fontname);
878 ++p;
882 void gfxFontUtils::GetPrefsFontList(const char* aPrefName,
883 nsTArray<nsCString>& aFontList,
884 bool aLocalized) {
885 aFontList.Clear();
887 nsAutoCString fontlistValue;
888 nsresult rv = aLocalized
889 ? Preferences::GetLocalizedCString(aPrefName, fontlistValue)
890 : Preferences::GetCString(aPrefName, fontlistValue);
891 if (NS_FAILED(rv)) {
892 return;
895 ParseFontList(fontlistValue, aFontList);
898 // produce a unique font name that is (1) a valid Postscript name and (2) less
899 // than 31 characters in length. Using AddFontMemResourceEx on Windows fails
900 // for names longer than 30 characters in length.
902 #define MAX_B64_LEN 32
904 nsresult gfxFontUtils::MakeUniqueUserFontName(nsAString& aName) {
905 nsCOMPtr<nsIUUIDGenerator> uuidgen =
906 do_GetService("@mozilla.org/uuid-generator;1");
907 NS_ENSURE_TRUE(uuidgen, NS_ERROR_OUT_OF_MEMORY);
909 nsID guid;
911 NS_ASSERTION(sizeof(guid) * 2 <= MAX_B64_LEN, "size of nsID has changed!");
913 nsresult rv = uuidgen->GenerateUUIDInPlace(&guid);
914 NS_ENSURE_SUCCESS(rv, rv);
916 char guidB64[MAX_B64_LEN] = {0};
918 if (!PL_Base64Encode(reinterpret_cast<char*>(&guid), sizeof(guid), guidB64))
919 return NS_ERROR_FAILURE;
921 // all b64 characters except for '/' are allowed in Postscript names, so
922 // convert / ==> -
923 char* p;
924 for (p = guidB64; *p; p++) {
925 if (*p == '/') *p = '-';
928 aName.AssignLiteral(u"uf");
929 aName.AppendASCII(guidB64);
930 return NS_OK;
933 // TrueType/OpenType table handling code
935 // need byte aligned structs
936 #pragma pack(1)
938 // name table stores set of name record structures, followed by
939 // large block containing all the strings. name record offset and length
940 // indicates the offset and length within that block.
941 // http://www.microsoft.com/typography/otspec/name.htm
942 struct NameRecordData {
943 uint32_t offset;
944 uint32_t length;
947 #pragma pack()
949 static bool IsValidSFNTVersion(uint32_t version) {
950 // normally 0x00010000, CFF-style OT fonts == 'OTTO' and Apple TT fonts =
951 // 'true' 'typ1' is also possible for old Type 1 fonts in a SFNT container but
952 // not supported
953 return version == 0x10000 || version == TRUETYPE_TAG('O', 'T', 'T', 'O') ||
954 version == TRUETYPE_TAG('t', 'r', 'u', 'e');
957 gfxUserFontType gfxFontUtils::DetermineFontDataType(const uint8_t* aFontData,
958 uint32_t aFontDataLength) {
959 // test for OpenType font data
960 // problem: EOT-Lite with 0x10000 length will look like TrueType!
961 if (aFontDataLength >= sizeof(SFNTHeader)) {
962 const SFNTHeader* sfntHeader =
963 reinterpret_cast<const SFNTHeader*>(aFontData);
964 uint32_t sfntVersion = sfntHeader->sfntVersion;
965 if (IsValidSFNTVersion(sfntVersion)) {
966 return GFX_USERFONT_OPENTYPE;
970 // test for WOFF or WOFF2
971 if (aFontDataLength >= sizeof(AutoSwap_PRUint32)) {
972 const AutoSwap_PRUint32* version =
973 reinterpret_cast<const AutoSwap_PRUint32*>(aFontData);
974 if (uint32_t(*version) == TRUETYPE_TAG('w', 'O', 'F', 'F')) {
975 return GFX_USERFONT_WOFF;
977 if (uint32_t(*version) == TRUETYPE_TAG('w', 'O', 'F', '2')) {
978 return GFX_USERFONT_WOFF2;
982 // tests for other formats here
984 return GFX_USERFONT_UNKNOWN;
987 static int DirEntryCmp(const void* aKey, const void* aItem) {
988 int32_t tag = *static_cast<const int32_t*>(aKey);
989 const TableDirEntry* entry = static_cast<const TableDirEntry*>(aItem);
990 return tag - int32_t(entry->tag);
993 /* static */
994 TableDirEntry* gfxFontUtils::FindTableDirEntry(const void* aFontData,
995 uint32_t aTableTag) {
996 const SFNTHeader* header = reinterpret_cast<const SFNTHeader*>(aFontData);
997 const TableDirEntry* dir = reinterpret_cast<const TableDirEntry*>(header + 1);
998 return static_cast<TableDirEntry*>(
999 bsearch(&aTableTag, dir, uint16_t(header->numTables),
1000 sizeof(TableDirEntry), DirEntryCmp));
1003 /* static */
1004 hb_blob_t* gfxFontUtils::GetTableFromFontData(const void* aFontData,
1005 uint32_t aTableTag) {
1006 const TableDirEntry* dir = FindTableDirEntry(aFontData, aTableTag);
1007 if (dir) {
1008 return hb_blob_create(
1009 reinterpret_cast<const char*>(aFontData) + dir->offset, dir->length,
1010 HB_MEMORY_MODE_READONLY, nullptr, nullptr);
1012 return nullptr;
1015 nsresult gfxFontUtils::RenameFont(const nsAString& aName,
1016 const uint8_t* aFontData,
1017 uint32_t aFontDataLength,
1018 FallibleTArray<uint8_t>* aNewFont) {
1019 NS_ASSERTION(aNewFont, "null font data array");
1021 uint64_t dataLength(aFontDataLength);
1023 // new name table
1024 static const uint32_t neededNameIDs[] = {NAME_ID_FAMILY, NAME_ID_STYLE,
1025 NAME_ID_UNIQUE, NAME_ID_FULL,
1026 NAME_ID_POSTSCRIPT};
1028 // calculate new name table size
1029 uint16_t nameCount = std::size(neededNameIDs);
1031 // leave room for null-terminator
1032 uint32_t nameStrLength = (aName.Length() + 1) * sizeof(char16_t);
1033 if (nameStrLength > 65535) {
1034 // The name length _in bytes_ must fit in an unsigned short field;
1035 // therefore, a name longer than this cannot be used.
1036 return NS_ERROR_FAILURE;
1039 // round name table size up to 4-byte multiple
1040 uint32_t nameTableSize =
1041 (sizeof(NameHeader) + sizeof(NameRecord) * nameCount + nameStrLength +
1042 3) &
1045 if (dataLength + nameTableSize > UINT32_MAX) return NS_ERROR_FAILURE;
1047 // bug 505386 - need to handle unpadded font length
1048 uint32_t paddedFontDataSize = (aFontDataLength + 3) & ~3;
1049 uint32_t adjFontDataSize = paddedFontDataSize + nameTableSize;
1051 // create new buffer: old font data plus new name table
1052 if (!aNewFont->AppendElements(adjFontDataSize, fallible))
1053 return NS_ERROR_OUT_OF_MEMORY;
1055 // copy the old font data
1056 uint8_t* newFontData = reinterpret_cast<uint8_t*>(aNewFont->Elements());
1058 // null the last four bytes in case the font length is not a multiple of 4
1059 memset(newFontData + aFontDataLength, 0,
1060 paddedFontDataSize - aFontDataLength);
1062 // copy font data
1063 memcpy(newFontData, aFontData, aFontDataLength);
1065 // null out the last 4 bytes for checksum calculations
1066 memset(newFontData + adjFontDataSize - 4, 0, 4);
1068 NameHeader* nameHeader =
1069 reinterpret_cast<NameHeader*>(newFontData + paddedFontDataSize);
1071 // -- name header
1072 nameHeader->format = 0;
1073 nameHeader->count = nameCount;
1074 nameHeader->stringOffset =
1075 sizeof(NameHeader) + nameCount * sizeof(NameRecord);
1077 // -- name records
1078 uint32_t i;
1079 NameRecord* nameRecord = reinterpret_cast<NameRecord*>(nameHeader + 1);
1081 for (i = 0; i < nameCount; i++, nameRecord++) {
1082 nameRecord->platformID = PLATFORM_ID_MICROSOFT;
1083 nameRecord->encodingID = ENCODING_ID_MICROSOFT_UNICODEBMP;
1084 nameRecord->languageID = LANG_ID_MICROSOFT_EN_US;
1085 nameRecord->nameID = neededNameIDs[i];
1086 nameRecord->offset = 0;
1087 nameRecord->length = nameStrLength;
1090 // -- string data, located after the name records, stored in big-endian form
1091 char16_t* strData = reinterpret_cast<char16_t*>(nameRecord);
1093 mozilla::NativeEndian::copyAndSwapToBigEndian(strData, aName.BeginReading(),
1094 aName.Length());
1095 strData[aName.Length()] = 0; // add null termination
1097 // adjust name table header to point to the new name table
1098 SFNTHeader* sfntHeader = reinterpret_cast<SFNTHeader*>(newFontData);
1100 // table directory entries begin immediately following SFNT header
1101 TableDirEntry* dirEntry =
1102 FindTableDirEntry(newFontData, TRUETYPE_TAG('n', 'a', 'm', 'e'));
1103 // function only called if font validates, so this should always be true
1104 MOZ_ASSERT(dirEntry, "attempt to rename font with no name table");
1106 uint32_t numTables = sfntHeader->numTables;
1108 // note: dirEntry now points to 'name' table record
1110 // recalculate name table checksum
1111 uint32_t checkSum = 0;
1112 AutoSwap_PRUint32* nameData =
1113 reinterpret_cast<AutoSwap_PRUint32*>(nameHeader);
1114 AutoSwap_PRUint32* nameDataEnd = nameData + (nameTableSize >> 2);
1116 while (nameData < nameDataEnd) checkSum = checkSum + *nameData++;
1118 // adjust name table entry to point to new name table
1119 dirEntry->offset = paddedFontDataSize;
1120 dirEntry->length = nameTableSize;
1121 dirEntry->checkSum = checkSum;
1123 // fix up checksums
1124 uint32_t checksum = 0;
1126 // checksum for font = (checksum of header) + (checksum of tables)
1127 uint32_t headerLen = sizeof(SFNTHeader) + sizeof(TableDirEntry) * numTables;
1128 const AutoSwap_PRUint32* headerData =
1129 reinterpret_cast<const AutoSwap_PRUint32*>(newFontData);
1131 // header length is in bytes, checksum calculated in longwords
1132 for (i = 0; i < (headerLen >> 2); i++, headerData++) {
1133 checksum += *headerData;
1136 uint32_t headOffset = 0;
1137 dirEntry = reinterpret_cast<TableDirEntry*>(newFontData + sizeof(SFNTHeader));
1139 for (i = 0; i < numTables; i++, dirEntry++) {
1140 if (dirEntry->tag == TRUETYPE_TAG('h', 'e', 'a', 'd')) {
1141 headOffset = dirEntry->offset;
1143 checksum += dirEntry->checkSum;
1146 NS_ASSERTION(headOffset != 0, "no head table for font");
1148 HeadTable* headData = reinterpret_cast<HeadTable*>(newFontData + headOffset);
1150 headData->checkSumAdjustment = HeadTable::HEAD_CHECKSUM_CALC_CONST - checksum;
1152 return NS_OK;
1155 // This is only called after the basic validity of the downloaded sfnt
1156 // data has been checked, so it should never fail to find the name table
1157 // (though it might fail to read it, if memory isn't available);
1158 // other checks here are just for extra paranoia.
1159 nsresult gfxFontUtils::GetFullNameFromSFNT(const uint8_t* aFontData,
1160 uint32_t aLength,
1161 nsACString& aFullName) {
1162 aFullName = "(MISSING NAME)"; // should always get replaced
1164 const TableDirEntry* dirEntry =
1165 FindTableDirEntry(aFontData, TRUETYPE_TAG('n', 'a', 'm', 'e'));
1167 // should never fail, as we're only called after font validation succeeded
1168 NS_ENSURE_TRUE(dirEntry, NS_ERROR_NOT_AVAILABLE);
1170 uint32_t len = dirEntry->length;
1171 NS_ENSURE_TRUE(aLength > len && aLength - len >= dirEntry->offset,
1172 NS_ERROR_UNEXPECTED);
1174 AutoHBBlob nameBlob(hb_blob_create((const char*)aFontData + dirEntry->offset,
1175 len, HB_MEMORY_MODE_READONLY, nullptr,
1176 nullptr));
1177 nsresult rv = GetFullNameFromTable(nameBlob, aFullName);
1179 return rv;
1182 nsresult gfxFontUtils::GetFullNameFromTable(hb_blob_t* aNameTable,
1183 nsACString& aFullName) {
1184 nsAutoCString name;
1185 nsresult rv = gfxFontUtils::ReadCanonicalName(
1186 aNameTable, gfxFontUtils::NAME_ID_FULL, name);
1187 if (NS_SUCCEEDED(rv) && !name.IsEmpty()) {
1188 aFullName = name;
1189 return NS_OK;
1191 rv = gfxFontUtils::ReadCanonicalName(aNameTable, gfxFontUtils::NAME_ID_FAMILY,
1192 name);
1193 if (NS_SUCCEEDED(rv) && !name.IsEmpty()) {
1194 nsAutoCString styleName;
1195 rv = gfxFontUtils::ReadCanonicalName(
1196 aNameTable, gfxFontUtils::NAME_ID_STYLE, styleName);
1197 if (NS_SUCCEEDED(rv) && !styleName.IsEmpty()) {
1198 name.Append(' ');
1199 name.Append(styleName);
1200 aFullName = name;
1202 return NS_OK;
1205 return NS_ERROR_NOT_AVAILABLE;
1208 nsresult gfxFontUtils::GetFamilyNameFromTable(hb_blob_t* aNameTable,
1209 nsACString& aFamilyName) {
1210 nsAutoCString name;
1211 nsresult rv = gfxFontUtils::ReadCanonicalName(
1212 aNameTable, gfxFontUtils::NAME_ID_FAMILY, name);
1213 if (NS_SUCCEEDED(rv) && !name.IsEmpty()) {
1214 aFamilyName = name;
1215 return NS_OK;
1217 return NS_ERROR_NOT_AVAILABLE;
1220 enum {
1221 #if defined(XP_MACOSX)
1222 CANONICAL_LANG_ID = gfxFontUtils::LANG_ID_MAC_ENGLISH,
1223 PLATFORM_ID = gfxFontUtils::PLATFORM_ID_MAC
1224 #else
1225 CANONICAL_LANG_ID = gfxFontUtils::LANG_ID_MICROSOFT_EN_US,
1226 PLATFORM_ID = gfxFontUtils::PLATFORM_ID_MICROSOFT
1227 #endif
1230 nsresult gfxFontUtils::ReadNames(const char* aNameData, uint32_t aDataLen,
1231 uint32_t aNameID, int32_t aPlatformID,
1232 nsTArray<nsCString>& aNames) {
1233 return ReadNames(aNameData, aDataLen, aNameID, LANG_ALL, aPlatformID, aNames);
1236 nsresult gfxFontUtils::ReadCanonicalName(hb_blob_t* aNameTable,
1237 uint32_t aNameID, nsCString& aName) {
1238 uint32_t nameTableLen;
1239 const char* nameTable = hb_blob_get_data(aNameTable, &nameTableLen);
1240 return ReadCanonicalName(nameTable, nameTableLen, aNameID, aName);
1243 nsresult gfxFontUtils::ReadCanonicalName(const char* aNameData,
1244 uint32_t aDataLen, uint32_t aNameID,
1245 nsCString& aName) {
1246 nsresult rv;
1248 nsTArray<nsCString> names;
1250 // first, look for the English name (this will succeed 99% of the time)
1251 rv = ReadNames(aNameData, aDataLen, aNameID, CANONICAL_LANG_ID, PLATFORM_ID,
1252 names);
1253 NS_ENSURE_SUCCESS(rv, rv);
1255 // otherwise, grab names for all languages
1256 if (names.Length() == 0) {
1257 rv = ReadNames(aNameData, aDataLen, aNameID, LANG_ALL, PLATFORM_ID, names);
1258 NS_ENSURE_SUCCESS(rv, rv);
1261 #if defined(XP_MACOSX)
1262 // may be dealing with font that only has Microsoft name entries
1263 if (names.Length() == 0) {
1264 rv = ReadNames(aNameData, aDataLen, aNameID, LANG_ID_MICROSOFT_EN_US,
1265 PLATFORM_ID_MICROSOFT, names);
1266 NS_ENSURE_SUCCESS(rv, rv);
1268 // getting really desperate now, take anything!
1269 if (names.Length() == 0) {
1270 rv = ReadNames(aNameData, aDataLen, aNameID, LANG_ALL,
1271 PLATFORM_ID_MICROSOFT, names);
1272 NS_ENSURE_SUCCESS(rv, rv);
1275 #endif
1277 // return the first name (99.9% of the time names will
1278 // contain a single English name)
1279 if (names.Length()) {
1280 aName.Assign(names[0]);
1281 return NS_OK;
1284 return NS_ERROR_FAILURE;
1287 // Charsets to use for decoding Mac platform font names.
1288 // This table is sorted by {encoding, language}, with the wildcard "ANY" being
1289 // greater than any defined values for each field; we use a binary search on
1290 // both fields, and fall back to matching only encoding if necessary
1292 // Some "redundant" entries for specific combinations are included such as
1293 // encoding=roman, lang=english, in order that common entries will be found
1294 // on the first search.
1296 const uint16_t ANY = 0xffff;
1297 MOZ_RUNINIT const gfxFontUtils::MacFontNameCharsetMapping
1298 gfxFontUtils::gMacFontNameCharsets[] = {
1299 {ENCODING_ID_MAC_ROMAN, LANG_ID_MAC_ENGLISH, MACINTOSH_ENCODING},
1300 {ENCODING_ID_MAC_ROMAN, LANG_ID_MAC_ICELANDIC, X_USER_DEFINED_ENCODING},
1301 {ENCODING_ID_MAC_ROMAN, LANG_ID_MAC_TURKISH, X_USER_DEFINED_ENCODING},
1302 {ENCODING_ID_MAC_ROMAN, LANG_ID_MAC_POLISH, X_USER_DEFINED_ENCODING},
1303 {ENCODING_ID_MAC_ROMAN, LANG_ID_MAC_ROMANIAN, X_USER_DEFINED_ENCODING},
1304 {ENCODING_ID_MAC_ROMAN, LANG_ID_MAC_CZECH, X_USER_DEFINED_ENCODING},
1305 {ENCODING_ID_MAC_ROMAN, LANG_ID_MAC_SLOVAK, X_USER_DEFINED_ENCODING},
1306 {ENCODING_ID_MAC_ROMAN, ANY, MACINTOSH_ENCODING},
1307 {ENCODING_ID_MAC_JAPANESE, LANG_ID_MAC_JAPANESE, SHIFT_JIS_ENCODING},
1308 {ENCODING_ID_MAC_JAPANESE, ANY, SHIFT_JIS_ENCODING},
1309 {ENCODING_ID_MAC_TRAD_CHINESE, LANG_ID_MAC_TRAD_CHINESE, BIG5_ENCODING},
1310 {ENCODING_ID_MAC_TRAD_CHINESE, ANY, BIG5_ENCODING},
1311 {ENCODING_ID_MAC_KOREAN, LANG_ID_MAC_KOREAN, EUC_KR_ENCODING},
1312 {ENCODING_ID_MAC_KOREAN, ANY, EUC_KR_ENCODING},
1313 {ENCODING_ID_MAC_ARABIC, LANG_ID_MAC_ARABIC, X_USER_DEFINED_ENCODING},
1314 {ENCODING_ID_MAC_ARABIC, LANG_ID_MAC_URDU, X_USER_DEFINED_ENCODING},
1315 {ENCODING_ID_MAC_ARABIC, LANG_ID_MAC_FARSI, X_USER_DEFINED_ENCODING},
1316 {ENCODING_ID_MAC_ARABIC, ANY, X_USER_DEFINED_ENCODING},
1317 {ENCODING_ID_MAC_HEBREW, LANG_ID_MAC_HEBREW, X_USER_DEFINED_ENCODING},
1318 {ENCODING_ID_MAC_HEBREW, ANY, X_USER_DEFINED_ENCODING},
1319 {ENCODING_ID_MAC_GREEK, ANY, X_USER_DEFINED_ENCODING},
1320 {ENCODING_ID_MAC_CYRILLIC, ANY, X_MAC_CYRILLIC_ENCODING},
1321 {ENCODING_ID_MAC_DEVANAGARI, ANY, X_USER_DEFINED_ENCODING},
1322 {ENCODING_ID_MAC_GURMUKHI, ANY, X_USER_DEFINED_ENCODING},
1323 {ENCODING_ID_MAC_GUJARATI, ANY, X_USER_DEFINED_ENCODING},
1324 {ENCODING_ID_MAC_SIMP_CHINESE, LANG_ID_MAC_SIMP_CHINESE,
1325 GB18030_ENCODING},
1326 {ENCODING_ID_MAC_SIMP_CHINESE, ANY, GB18030_ENCODING}};
1328 MOZ_RUNINIT const Encoding* gfxFontUtils::gISOFontNameCharsets[] = {
1329 /* 0 */ WINDOWS_1252_ENCODING, /* US-ASCII */
1330 /* 1 */ nullptr, /* spec says "ISO 10646" but does not specify encoding
1331 form! */
1332 /* 2 */ WINDOWS_1252_ENCODING /* ISO-8859-1 */
1335 MOZ_RUNINIT const Encoding* gfxFontUtils::gMSFontNameCharsets[] = {
1336 /* [0] ENCODING_ID_MICROSOFT_SYMBOL */ UTF_16BE_ENCODING,
1337 /* [1] ENCODING_ID_MICROSOFT_UNICODEBMP */ UTF_16BE_ENCODING,
1338 /* [2] ENCODING_ID_MICROSOFT_SHIFTJIS */ SHIFT_JIS_ENCODING,
1339 /* [3] ENCODING_ID_MICROSOFT_PRC */ nullptr,
1340 /* [4] ENCODING_ID_MICROSOFT_BIG5 */ BIG5_ENCODING,
1341 /* [5] ENCODING_ID_MICROSOFT_WANSUNG */ nullptr,
1342 /* [6] ENCODING_ID_MICROSOFT_JOHAB */ nullptr,
1343 /* [7] reserved */ nullptr,
1344 /* [8] reserved */ nullptr,
1345 /* [9] reserved */ nullptr,
1346 /*[10] ENCODING_ID_MICROSOFT_UNICODEFULL */ UTF_16BE_ENCODING};
1348 struct MacCharsetMappingComparator {
1349 typedef gfxFontUtils::MacFontNameCharsetMapping MacFontNameCharsetMapping;
1350 const MacFontNameCharsetMapping& mSearchValue;
1351 explicit MacCharsetMappingComparator(
1352 const MacFontNameCharsetMapping& aSearchValue)
1353 : mSearchValue(aSearchValue) {}
1354 int operator()(const MacFontNameCharsetMapping& aEntry) const {
1355 if (mSearchValue < aEntry) {
1356 return -1;
1358 if (aEntry < mSearchValue) {
1359 return 1;
1361 return 0;
1365 // Return the Encoding object we should use to decode a font name
1366 // given the name table attributes.
1367 // Special return values:
1368 // X_USER_DEFINED_ENCODING One of Mac legacy encodings that is not a part
1369 // of Encoding Standard
1370 // nullptr unknown charset, do not attempt conversion
1371 const Encoding* gfxFontUtils::GetCharsetForFontName(uint16_t aPlatform,
1372 uint16_t aScript,
1373 uint16_t aLanguage) {
1374 switch (aPlatform) {
1375 case PLATFORM_ID_UNICODE:
1376 return UTF_16BE_ENCODING;
1378 case PLATFORM_ID_MAC: {
1379 MacFontNameCharsetMapping searchValue = {aScript, aLanguage, nullptr};
1380 for (uint32_t i = 0; i < 2; ++i) {
1381 size_t idx;
1382 if (BinarySearchIf(gMacFontNameCharsets, 0,
1383 std::size(gMacFontNameCharsets),
1384 MacCharsetMappingComparator(searchValue), &idx)) {
1385 return gMacFontNameCharsets[idx].mEncoding;
1388 // no match, so try again finding one in any language
1389 searchValue.mLanguage = ANY;
1391 } break;
1393 case PLATFORM_ID_ISO:
1394 if (aScript < std::size(gISOFontNameCharsets)) {
1395 return gISOFontNameCharsets[aScript];
1397 break;
1399 case PLATFORM_ID_MICROSOFT:
1400 if (aScript < std::size(gMSFontNameCharsets)) {
1401 return gMSFontNameCharsets[aScript];
1403 break;
1406 return nullptr;
1409 template <int N>
1410 static bool StartsWith(const nsACString& string, const char (&prefix)[N]) {
1411 if (N - 1 > string.Length()) {
1412 return false;
1414 return memcmp(string.Data(), prefix, N - 1) == 0;
1417 // convert a raw name from the name table to an nsString, if possible;
1418 // return value indicates whether conversion succeeded
1419 bool gfxFontUtils::DecodeFontName(const char* aNameData, int32_t aByteLen,
1420 uint32_t aPlatformCode, uint32_t aScriptCode,
1421 uint32_t aLangCode, nsACString& aName) {
1422 if (aByteLen <= 0) {
1423 NS_WARNING("empty font name");
1424 aName.SetLength(0);
1425 return true;
1428 auto encoding = GetCharsetForFontName(aPlatformCode, aScriptCode, aLangCode);
1430 if (!encoding) {
1431 // nullptr -> unknown charset
1432 #ifdef DEBUG
1433 char warnBuf[128];
1434 if (aByteLen > 64) aByteLen = 64;
1435 SprintfLiteral(warnBuf,
1436 "skipping font name, unknown charset %d:%d:%d for <%.*s>",
1437 aPlatformCode, aScriptCode, aLangCode, aByteLen, aNameData);
1438 NS_WARNING(warnBuf);
1439 #endif
1440 return false;
1443 if (encoding == X_USER_DEFINED_ENCODING) {
1444 #ifdef XP_DARWIN
1445 // Special case for macOS only: support legacy Mac encodings
1446 // that aren't part of the Encoding Standard.
1447 if (aPlatformCode == PLATFORM_ID_MAC) {
1448 CFStringRef str =
1449 CFStringCreateWithBytes(kCFAllocatorDefault, (const UInt8*)aNameData,
1450 aByteLen, aScriptCode, false);
1451 if (str) {
1452 CFIndex length = CFStringGetLength(str);
1453 nsAutoString name16;
1454 name16.SetLength(length);
1455 CFStringGetCharacters(str, CFRangeMake(0, length),
1456 (UniChar*)name16.BeginWriting());
1457 CFRelease(str);
1458 CopyUTF16toUTF8(name16, aName);
1459 return true;
1462 #endif
1463 NS_WARNING("failed to get the decoder for a font name string");
1464 return false;
1467 auto rv = encoding->DecodeWithoutBOMHandling(
1468 nsDependentCSubstring(aNameData, aByteLen), aName);
1469 return NS_SUCCEEDED(rv);
1472 nsresult gfxFontUtils::ReadNames(const char* aNameData, uint32_t aDataLen,
1473 uint32_t aNameID, int32_t aLangID,
1474 int32_t aPlatformID,
1475 nsTArray<nsCString>& aNames) {
1476 NS_ASSERTION(aDataLen != 0, "null name table");
1478 if (!aDataLen) {
1479 return NS_ERROR_FAILURE;
1482 // -- name table data
1483 const NameHeader* nameHeader = reinterpret_cast<const NameHeader*>(aNameData);
1485 uint32_t nameCount = nameHeader->count;
1487 // -- sanity check the number of name records
1488 if (uint64_t(nameCount) * sizeof(NameRecord) > aDataLen) {
1489 NS_WARNING("invalid font (name table data)");
1490 return NS_ERROR_FAILURE;
1493 // -- iterate through name records
1494 const NameRecord* nameRecord =
1495 reinterpret_cast<const NameRecord*>(aNameData + sizeof(NameHeader));
1496 uint64_t nameStringsBase = uint64_t(nameHeader->stringOffset);
1498 uint32_t i;
1499 for (i = 0; i < nameCount; i++, nameRecord++) {
1500 uint32_t platformID;
1502 // skip over unwanted nameID's
1503 if (uint32_t(nameRecord->nameID) != aNameID) {
1504 continue;
1507 // skip over unwanted platform data
1508 platformID = nameRecord->platformID;
1509 if (aPlatformID != PLATFORM_ALL && platformID != uint32_t(aPlatformID)) {
1510 continue;
1513 // skip over unwanted languages
1514 if (aLangID != LANG_ALL &&
1515 uint32_t(nameRecord->languageID) != uint32_t(aLangID)) {
1516 continue;
1519 // add name to names array
1521 // -- calculate string location
1522 uint32_t namelen = nameRecord->length;
1523 uint32_t nameoff =
1524 nameRecord->offset; // offset from base of string storage
1526 if (nameStringsBase + uint64_t(nameoff) + uint64_t(namelen) > aDataLen) {
1527 NS_WARNING("invalid font (name table strings)");
1528 return NS_ERROR_FAILURE;
1531 // -- decode if necessary and make nsString
1532 nsAutoCString name;
1534 DecodeFontName(aNameData + nameStringsBase + nameoff, namelen, platformID,
1535 uint32_t(nameRecord->encodingID),
1536 uint32_t(nameRecord->languageID), name);
1538 uint32_t k, numNames;
1539 bool foundName = false;
1541 numNames = aNames.Length();
1542 for (k = 0; k < numNames; k++) {
1543 if (name.Equals(aNames[k])) {
1544 foundName = true;
1545 break;
1549 if (!foundName) aNames.AppendElement(name);
1552 return NS_OK;
1555 void gfxFontUtils::GetVariationData(
1556 gfxFontEntry* aFontEntry, nsTArray<gfxFontVariationAxis>* aAxes,
1557 nsTArray<gfxFontVariationInstance>* aInstances) {
1558 MOZ_ASSERT(!aAxes || aAxes->IsEmpty());
1559 MOZ_ASSERT(!aInstances || aInstances->IsEmpty());
1561 if (!aFontEntry->HasVariations()) {
1562 return;
1565 // Some platforms don't offer a simple API to return the list of instances,
1566 // so we have to interpret the 'fvar' table ourselves.
1568 // https://www.microsoft.com/typography/otspec/fvar.htm#fvarHeader
1569 struct FvarHeader {
1570 AutoSwap_PRUint16 majorVersion;
1571 AutoSwap_PRUint16 minorVersion;
1572 AutoSwap_PRUint16 axesArrayOffset;
1573 AutoSwap_PRUint16 reserved;
1574 AutoSwap_PRUint16 axisCount;
1575 AutoSwap_PRUint16 axisSize;
1576 AutoSwap_PRUint16 instanceCount;
1577 AutoSwap_PRUint16 instanceSize;
1580 // https://www.microsoft.com/typography/otspec/fvar.htm#variationAxisRecord
1581 struct AxisRecord {
1582 AutoSwap_PRUint32 axisTag;
1583 AutoSwap_PRInt32 minValue;
1584 AutoSwap_PRInt32 defaultValue;
1585 AutoSwap_PRInt32 maxValue;
1586 AutoSwap_PRUint16 flags;
1587 AutoSwap_PRUint16 axisNameID;
1589 const uint16_t HIDDEN_AXIS = 0x0001; // AxisRecord flags value
1591 // https://www.microsoft.com/typography/otspec/fvar.htm#instanceRecord
1592 struct InstanceRecord {
1593 AutoSwap_PRUint16 subfamilyNameID;
1594 AutoSwap_PRUint16 flags;
1595 AutoSwap_PRInt32 coordinates[1]; // variable-size array [axisCount]
1596 // The variable-length 'coordinates' array may be followed by an
1597 // optional extra field 'postScriptNameID'. We can't directly
1598 // represent this in the struct, because its offset varies depending
1599 // on the number of axes present.
1600 // (Not currently used by our code here anyhow.)
1601 // AutoSwap_PRUint16 postScriptNameID;
1604 // Load the two font tables we need as harfbuzz blobs; if either is absent,
1605 // just bail out.
1606 AutoHBBlob fvarTable(
1607 aFontEntry->GetFontTable(TRUETYPE_TAG('f', 'v', 'a', 'r')));
1608 AutoHBBlob nameTable(
1609 aFontEntry->GetFontTable(TRUETYPE_TAG('n', 'a', 'm', 'e')));
1610 if (!fvarTable || !nameTable) {
1611 return;
1613 unsigned int len;
1614 const char* data = hb_blob_get_data(fvarTable, &len);
1615 if (len < sizeof(FvarHeader)) {
1616 return;
1618 // Read the fields of the table header; bail out if it looks broken.
1619 auto fvar = reinterpret_cast<const FvarHeader*>(data);
1620 if (uint16_t(fvar->majorVersion) != 1 || uint16_t(fvar->minorVersion) != 0 ||
1621 uint16_t(fvar->reserved) != 2) {
1622 return;
1624 uint16_t axisCount = fvar->axisCount;
1625 uint16_t axisSize = fvar->axisSize;
1626 uint16_t instanceCount = fvar->instanceCount;
1627 uint16_t instanceSize = fvar->instanceSize;
1628 if (axisCount ==
1629 0 || // no axes?
1630 // https://www.microsoft.com/typography/otspec/fvar.htm#axisSize
1631 axisSize != 20 || // required value for current table version
1632 // https://www.microsoft.com/typography/otspec/fvar.htm#instanceSize
1633 (instanceSize != axisCount * sizeof(int32_t) + 4 &&
1634 instanceSize != axisCount * sizeof(int32_t) + 6)) {
1635 return;
1637 // Check that axis array will not exceed table size
1638 uint16_t axesOffset = fvar->axesArrayOffset;
1639 if (axesOffset + uint32_t(axisCount) * axisSize > len) {
1640 return;
1642 // Get pointer to the array of axis records
1643 auto axes = reinterpret_cast<const AxisRecord*>(data + axesOffset);
1644 // Get address of instance array, and check it doesn't overflow table size.
1645 // https://www.microsoft.com/typography/otspec/fvar.htm#axisAndInstanceArrays
1646 auto instData = data + axesOffset + axisCount * axisSize;
1647 if (instData + uint32_t(instanceCount) * instanceSize > data + len) {
1648 return;
1650 if (aInstances) {
1651 aInstances->SetCapacity(instanceCount);
1652 for (unsigned i = 0; i < instanceCount; ++i, instData += instanceSize) {
1653 // Typed pointer to the current instance record, to read its fields.
1654 auto inst = reinterpret_cast<const InstanceRecord*>(instData);
1655 // Pointer to the coordinates array within the instance record.
1656 // This array has axisCount elements, and is included in instanceSize
1657 // (which depends on axisCount, and was validated above) so we know
1658 // access to coords[j] below will not be outside the table bounds.
1659 auto coords = &inst->coordinates[0];
1660 gfxFontVariationInstance instance;
1661 uint16_t nameID = inst->subfamilyNameID;
1662 nsresult rv = ReadCanonicalName(nameTable, nameID, instance.mName);
1663 if (NS_FAILED(rv)) {
1664 // If no name was available for the instance, ignore it.
1665 continue;
1667 instance.mValues.SetCapacity(axisCount);
1668 for (unsigned j = 0; j < axisCount; ++j) {
1669 gfxFontVariationValue value = {axes[j].axisTag,
1670 int32_t(coords[j]) / 65536.0f};
1671 instance.mValues.AppendElement(value);
1673 aInstances->AppendElement(std::move(instance));
1676 if (aAxes) {
1677 aAxes->SetCapacity(axisCount);
1678 for (unsigned i = 0; i < axisCount; ++i) {
1679 if (uint16_t(axes[i].flags) & HIDDEN_AXIS) {
1680 continue;
1682 gfxFontVariationAxis axis;
1683 axis.mTag = axes[i].axisTag;
1684 uint16_t nameID = axes[i].axisNameID;
1685 nsresult rv = ReadCanonicalName(nameTable, nameID, axis.mName);
1686 if (NS_FAILED(rv)) {
1687 axis.mName.Truncate(0);
1689 // Convert values from 16.16 fixed-point to float
1690 axis.mMinValue = int32_t(axes[i].minValue) / 65536.0f;
1691 axis.mDefaultValue = int32_t(axes[i].defaultValue) / 65536.0f;
1692 axis.mMaxValue = int32_t(axes[i].maxValue) / 65536.0f;
1693 aAxes->AppendElement(axis);
1698 void gfxFontUtils::ReadOtherFamilyNamesForFace(
1699 const nsACString& aFamilyName, const char* aNameData, uint32_t aDataLength,
1700 nsTArray<nsCString>& aOtherFamilyNames, bool useFullName) {
1701 const NameHeader* nameHeader = reinterpret_cast<const NameHeader*>(aNameData);
1703 uint32_t nameCount = nameHeader->count;
1704 if (nameCount * sizeof(NameRecord) > aDataLength) {
1705 NS_WARNING("invalid font (name records)");
1706 return;
1709 const NameRecord* nameRecord =
1710 reinterpret_cast<const NameRecord*>(aNameData + sizeof(NameHeader));
1711 uint32_t stringsBase = uint32_t(nameHeader->stringOffset);
1713 for (uint32_t i = 0; i < nameCount; i++, nameRecord++) {
1714 uint32_t nameLen = nameRecord->length;
1715 uint32_t nameOff =
1716 nameRecord->offset; // offset from base of string storage
1718 if (stringsBase + nameOff + nameLen > aDataLength) {
1719 NS_WARNING("invalid font (name table strings)");
1720 return;
1723 uint16_t nameID = nameRecord->nameID;
1724 if ((useFullName && nameID == NAME_ID_FULL) ||
1725 (!useFullName &&
1726 (nameID == NAME_ID_FAMILY || nameID == NAME_ID_PREFERRED_FAMILY))) {
1727 nsAutoCString otherFamilyName;
1728 bool ok = DecodeFontName(
1729 aNameData + stringsBase + nameOff, nameLen,
1730 uint32_t(nameRecord->platformID), uint32_t(nameRecord->encodingID),
1731 uint32_t(nameRecord->languageID), otherFamilyName);
1732 // add if not same as canonical family name
1733 if (ok && otherFamilyName != aFamilyName &&
1734 !aOtherFamilyNames.Contains(otherFamilyName)) {
1735 aOtherFamilyNames.AppendElement(otherFamilyName);
1741 #ifdef XP_WIN
1743 /* static */
1744 bool gfxFontUtils::IsCffFont(const uint8_t* aFontData) {
1745 // this is only called after aFontData has passed basic validation,
1746 // so we know there is enough data present to allow us to read the version!
1747 const SFNTHeader* sfntHeader = reinterpret_cast<const SFNTHeader*>(aFontData);
1748 return (sfntHeader->sfntVersion == TRUETYPE_TAG('O', 'T', 'T', 'O'));
1751 #endif
1753 /* static */ bool gfxFontUtils::IsInServoTraversal() {
1754 if (NS_IsMainThread()) {
1755 return ServoStyleSet::IsInServoTraversal();
1758 if (dom::GetCurrentThreadWorkerPrivate()) {
1759 return false;
1762 // The only permissible threads are the main thread, the worker thread, the
1763 // servo threads. If the latter, we must be traversing.
1764 bool traversing = ServoStyleSet::IsInServoTraversal();
1765 MOZ_ASSERT(traversing);
1766 return traversing;
1769 /* static */ ServoStyleSet* gfxFontUtils::CurrentServoStyleSet() {
1770 // If we are on a worker thread, we must not check for the current set since
1771 // the main/servo threads may be busy in parallel.
1772 if (dom::GetCurrentThreadWorkerPrivate()) {
1773 return nullptr;
1776 return ServoStyleSet::Current();
1779 #ifdef DEBUG
1780 /* static */ void gfxFontUtils::AssertSafeThreadOrServoFontMetricsLocked() {
1781 if (!dom::GetCurrentThreadWorkerPrivate()) {
1782 AssertIsMainThreadOrServoFontMetricsLocked();
1785 #endif
1787 #undef acceptablePlatform
1788 #undef isSymbol
1789 #undef isUVSEncoding
1790 #undef LOG
1791 #undef LOG_ENABLED