gfx/thebes/gfxFontUtils.cpp

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

     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/. */
     6 #ifdef MOZ_LOGGING
     7 #define FORCE_PR_LOG /* Allow logging in the release build */
     8 #include "prlog.h"
     9 #endif
    11 #include "mozilla/ArrayUtils.h"
    13 #include "gfxFontUtils.h"
    15 #include "nsServiceManagerUtils.h"
    17 #include "mozilla/dom/EncodingUtils.h"
    18 #include "mozilla/Preferences.h"
    19 #include "mozilla/Services.h"
    21 #include "nsCOMPtr.h"
    22 #include "nsIUUIDGenerator.h"
    23 #include "nsIUnicodeDecoder.h"
    25 #include "harfbuzz/hb.h"
    27 #include "plbase64.h"
    28 #include "prlog.h"
    30 #ifdef PR_LOGGING
    32 #define LOG(log, args) PR_LOG(gfxPlatform::GetLog(log), \
    33                                PR_LOG_DEBUG, args)
    35 #endif // PR_LOGGING
    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 numGroups;
    49 } Format12CmapHeader;
    51 typedef struct {
    52     AutoSwap_PRUint32 startCharCode;
    53     AutoSwap_PRUint32 endCharCode;
    54     AutoSwap_PRUint32 startGlyphId;
    55 } Format12Group;
    57 #pragma pack()
    59 #if PR_LOGGING
    60 void
    61 gfxSparseBitSet::Dump(const char* aPrefix, eGfxLog aWhichLog) const
    62 {
    63     NS_ASSERTION(mBlocks.DebugGetHeader(), "mHdr is null, this is bad");
    64     uint32_t b, numBlocks = mBlocks.Length();
    66     for (b = 0; b < numBlocks; b++) {
    67         Block *block = mBlocks[b];
    68         if (!block) continue;
    69         char outStr[256];
    70         int index = 0;
    71         index += sprintf(&outStr[index], "%s u+%6.6x [", aPrefix, (b << BLOCK_INDEX_SHIFT));
    72         for (int i = 0; i < 32; i += 4) {
    73             for (int j = i; j < i + 4; j++) {
    74                 uint8_t bits = block->mBits[j];
    75                 uint8_t flip1 = ((bits & 0xaa) >> 1) | ((bits & 0x55) << 1);
    76                 uint8_t flip2 = ((flip1 & 0xcc) >> 2) | ((flip1 & 0x33) << 2);
    77                 uint8_t flipped = ((flip2 & 0xf0) >> 4) | ((flip2 & 0x0f) << 4);
    79                 index += sprintf(&outStr[index], "%2.2x", flipped);
    80             }
    81             if (i + 4 != 32) index += sprintf(&outStr[index], " ");
    82         }
    83         index += sprintf(&outStr[index], "]");
    84         LOG(aWhichLog, ("%s", outStr));
    85     }
    86 }
    87 #endif
    90 nsresult
    91 gfxFontUtils::ReadCMAPTableFormat12(const uint8_t *aBuf, uint32_t aLength,
    92                                     gfxSparseBitSet& aCharacterMap) 
    93 {
    94     // Ensure table is large enough that we can safely read the header
    95     NS_ENSURE_TRUE(aLength >= sizeof(Format12CmapHeader),
    96                     NS_ERROR_GFX_CMAP_MALFORMED);
    98     // Sanity-check header fields
    99     const Format12CmapHeader *cmap12 =
   100         reinterpret_cast<const Format12CmapHeader*>(aBuf);
   101     NS_ENSURE_TRUE(uint16_t(cmap12->format) == 12, 
   102                    NS_ERROR_GFX_CMAP_MALFORMED);
   103     NS_ENSURE_TRUE(uint16_t(cmap12->reserved) == 0, 
   104                    NS_ERROR_GFX_CMAP_MALFORMED);
   106     uint32_t tablelen = cmap12->length;
   107     NS_ENSURE_TRUE(tablelen >= sizeof(Format12CmapHeader) &&
   108                    tablelen <= aLength, NS_ERROR_GFX_CMAP_MALFORMED);
   110     NS_ENSURE_TRUE(cmap12->language == 0, NS_ERROR_GFX_CMAP_MALFORMED);
   112     // Check that the table is large enough for the group array
   113     const uint32_t numGroups = cmap12->numGroups;
   114     NS_ENSURE_TRUE((tablelen - sizeof(Format12CmapHeader)) /
   115                        sizeof(Format12Group) >= numGroups,
   116                    NS_ERROR_GFX_CMAP_MALFORMED);
   118     // The array of groups immediately follows the subtable header.
   119     const Format12Group *group =
   120         reinterpret_cast<const Format12Group*>(aBuf + sizeof(Format12CmapHeader));
   122     // Check that groups are in correct order and do not overlap,
   123     // and record character coverage in aCharacterMap.
   124     uint32_t prevEndCharCode = 0;
   125     for (uint32_t i = 0; i < numGroups; i++, group++) {
   126         uint32_t startCharCode = group->startCharCode;
   127         const uint32_t endCharCode = group->endCharCode;
   128         NS_ENSURE_TRUE((prevEndCharCode < startCharCode || i == 0) &&
   129                        startCharCode <= endCharCode &&
   130                        endCharCode <= CMAP_MAX_CODEPOINT, 
   131                        NS_ERROR_GFX_CMAP_MALFORMED);
   132         // don't include a character that maps to glyph ID 0 (.notdef)
   133         if (group->startGlyphId == 0) {
   134             startCharCode++;
   135         }
   136         if (startCharCode <= endCharCode) {
   137             aCharacterMap.SetRange(startCharCode, endCharCode);
   138         }
   139         prevEndCharCode = endCharCode;
   140     }
   142     aCharacterMap.Compact();
   144     return NS_OK;
   145 }
   147 nsresult 
   148 gfxFontUtils::ReadCMAPTableFormat4(const uint8_t *aBuf, uint32_t aLength,
   149                                    gfxSparseBitSet& aCharacterMap)
   150 {
   151     enum {
   152         OffsetFormat = 0,
   153         OffsetLength = 2,
   154         OffsetLanguage = 4,
   155         OffsetSegCountX2 = 6
   156     };
   158     NS_ENSURE_TRUE(ReadShortAt(aBuf, OffsetFormat) == 4, 
   159                    NS_ERROR_GFX_CMAP_MALFORMED);
   160     uint16_t tablelen = ReadShortAt(aBuf, OffsetLength);
   161     NS_ENSURE_TRUE(tablelen <= aLength, NS_ERROR_GFX_CMAP_MALFORMED);
   162     NS_ENSURE_TRUE(tablelen > 16, NS_ERROR_GFX_CMAP_MALFORMED);
   164     // This field should normally (except for Mac platform subtables) be zero according to
   165     // the OT spec, but some buggy fonts have lang = 1 (which would be English for MacOS).
   166     // E.g. Arial Narrow Bold, v. 1.1 (Tiger), Arial Unicode MS (see bug 530614).
   167     // So accept either zero or one here; the error should be harmless.
   168     NS_ENSURE_TRUE((ReadShortAt(aBuf, OffsetLanguage) & 0xfffe) == 0, 
   169                    NS_ERROR_GFX_CMAP_MALFORMED);
   171     uint16_t segCountX2 = ReadShortAt(aBuf, OffsetSegCountX2);
   172     NS_ENSURE_TRUE(tablelen >= 16 + (segCountX2 * 4), 
   173                    NS_ERROR_GFX_CMAP_MALFORMED);
   175     const uint16_t segCount = segCountX2 / 2;
   177     const uint16_t *endCounts = reinterpret_cast<const uint16_t*>(aBuf + 14);
   178     const uint16_t *startCounts = endCounts + 1 /* skip one uint16_t for reservedPad */ + segCount;
   179     const uint16_t *idDeltas = startCounts + segCount;
   180     const uint16_t *idRangeOffsets = idDeltas + segCount;
   181     uint16_t prevEndCount = 0;
   182     for (uint16_t i = 0; i < segCount; i++) {
   183         const uint16_t endCount = ReadShortAt16(endCounts, i);
   184         const uint16_t startCount = ReadShortAt16(startCounts, i);
   185         const uint16_t idRangeOffset = ReadShortAt16(idRangeOffsets, i);
   187         // sanity-check range
   188         // This permits ranges to overlap by 1 character, which is strictly
   189         // incorrect but occurs in Baskerville on OS X 10.7 (see bug 689087),
   190         // and appears to be harmless in practice
   191         NS_ENSURE_TRUE(startCount >= prevEndCount && startCount <= endCount,
   192                        NS_ERROR_GFX_CMAP_MALFORMED);
   193         prevEndCount = endCount;
   195         if (idRangeOffset == 0) {
   196             // figure out if there's a code in the range that would map to
   197             // glyph ID 0 (.notdef); if so, we need to skip setting that
   198             // character code in the map
   199             const uint16_t skipCode = 65536 - ReadShortAt16(idDeltas, i);
   200             if (startCount < skipCode) {
   201                 aCharacterMap.SetRange(startCount,
   202                                        std::min<uint16_t>(skipCode - 1,
   203                                                           endCount));
   204             }
   205             if (skipCode < endCount) {
   206                 aCharacterMap.SetRange(std::max<uint16_t>(startCount,
   207                                                           skipCode + 1),
   208                                        endCount);
   209             }
   210         } else {
   211             // const uint16_t idDelta = ReadShortAt16(idDeltas, i); // Unused: self-documenting.
   212             for (uint32_t c = startCount; c <= endCount; ++c) {
   213                 if (c == 0xFFFF)
   214                     break;
   216                 const uint16_t *gdata = (idRangeOffset/2 
   217                                          + (c - startCount)
   218                                          + &idRangeOffsets[i]);
   220                 NS_ENSURE_TRUE((uint8_t*)gdata > aBuf && 
   221                                (uint8_t*)gdata < aBuf + aLength, 
   222                                NS_ERROR_GFX_CMAP_MALFORMED);
   224                 // make sure we have a glyph
   225                 if (*gdata != 0) {
   226                     // The glyph index at this point is:
   227                     uint16_t glyph = ReadShortAt16(idDeltas, i) + *gdata;
   228                     if (glyph) {
   229                         aCharacterMap.set(c);
   230                     }
   231                 }
   232             }
   233         }
   234     }
   236     aCharacterMap.Compact();
   238     return NS_OK;
   239 }
   241 nsresult
   242 gfxFontUtils::ReadCMAPTableFormat14(const uint8_t *aBuf, uint32_t aLength,
   243                                     uint8_t*& aTable)
   244 {
   245     enum {
   246         OffsetFormat = 0,
   247         OffsetTableLength = 2,
   248         OffsetNumVarSelectorRecords = 6,
   249         OffsetVarSelectorRecords = 10,
   251         SizeOfVarSelectorRecord = 11,
   252         VSRecOffsetVarSelector = 0,
   253         VSRecOffsetDefUVSOffset = 3,
   254         VSRecOffsetNonDefUVSOffset = 7,
   256         SizeOfDefUVSTable = 4,
   257         DefUVSOffsetStartUnicodeValue = 0,
   258         DefUVSOffsetAdditionalCount = 3,
   260         SizeOfNonDefUVSTable = 5,
   261         NonDefUVSOffsetUnicodeValue = 0,
   262         NonDefUVSOffsetGlyphID = 3
   263     };
   264     NS_ENSURE_TRUE(aLength >= OffsetVarSelectorRecords,
   265                    NS_ERROR_GFX_CMAP_MALFORMED);
   267     NS_ENSURE_TRUE(ReadShortAt(aBuf, OffsetFormat) == 14, 
   268                    NS_ERROR_GFX_CMAP_MALFORMED);
   270     uint32_t tablelen = ReadLongAt(aBuf, OffsetTableLength);
   271     NS_ENSURE_TRUE(tablelen <= aLength, NS_ERROR_GFX_CMAP_MALFORMED);
   272     NS_ENSURE_TRUE(tablelen >= OffsetVarSelectorRecords,
   273                    NS_ERROR_GFX_CMAP_MALFORMED);
   275     const uint32_t numVarSelectorRecords = ReadLongAt(aBuf, OffsetNumVarSelectorRecords);
   276     NS_ENSURE_TRUE((tablelen - OffsetVarSelectorRecords) /
   277                    SizeOfVarSelectorRecord >= numVarSelectorRecords,
   278                    NS_ERROR_GFX_CMAP_MALFORMED);
   280     const uint8_t *records = aBuf + OffsetVarSelectorRecords;
   281     for (uint32_t i = 0; i < numVarSelectorRecords; 
   282          i++, records += SizeOfVarSelectorRecord) {
   283         const uint32_t varSelector = ReadUint24At(records, VSRecOffsetVarSelector);
   284         const uint32_t defUVSOffset = ReadLongAt(records, VSRecOffsetDefUVSOffset);
   285         const uint32_t nonDefUVSOffset = ReadLongAt(records, VSRecOffsetNonDefUVSOffset);
   286         NS_ENSURE_TRUE(varSelector <= CMAP_MAX_CODEPOINT &&
   287                        defUVSOffset <= tablelen - 4 &&
   288                        nonDefUVSOffset <= tablelen - 4, 
   289                        NS_ERROR_GFX_CMAP_MALFORMED);
   291         if (defUVSOffset) {
   292             const uint32_t numUnicodeValueRanges = ReadLongAt(aBuf, defUVSOffset);
   293             NS_ENSURE_TRUE((tablelen - defUVSOffset) /
   294                            SizeOfDefUVSTable >= numUnicodeValueRanges,
   295                            NS_ERROR_GFX_CMAP_MALFORMED);
   296             const uint8_t *tables = aBuf + defUVSOffset + 4;
   297             uint32_t prevEndUnicode = 0;
   298             for (uint32_t j = 0; j < numUnicodeValueRanges; j++, tables += SizeOfDefUVSTable) {
   299                 const uint32_t startUnicode = ReadUint24At(tables, DefUVSOffsetStartUnicodeValue);
   300                 const uint32_t endUnicode = startUnicode + tables[DefUVSOffsetAdditionalCount];
   301                 NS_ENSURE_TRUE((prevEndUnicode < startUnicode || j == 0) &&
   302                                endUnicode <= CMAP_MAX_CODEPOINT, 
   303                                NS_ERROR_GFX_CMAP_MALFORMED);
   304                 prevEndUnicode = endUnicode;
   305             }
   306         }
   308         if (nonDefUVSOffset) {
   309             const uint32_t numUVSMappings = ReadLongAt(aBuf, nonDefUVSOffset);
   310             NS_ENSURE_TRUE((tablelen - nonDefUVSOffset) /
   311                            SizeOfNonDefUVSTable >= numUVSMappings,
   312                            NS_ERROR_GFX_CMAP_MALFORMED);
   313             const uint8_t *tables = aBuf + nonDefUVSOffset + 4;
   314             uint32_t prevUnicode = 0;
   315             for (uint32_t j = 0; j < numUVSMappings; j++, tables += SizeOfNonDefUVSTable) {
   316                 const uint32_t unicodeValue = ReadUint24At(tables, NonDefUVSOffsetUnicodeValue);
   317                 NS_ENSURE_TRUE((prevUnicode < unicodeValue || j == 0) &&
   318                                unicodeValue <= CMAP_MAX_CODEPOINT, 
   319                                NS_ERROR_GFX_CMAP_MALFORMED);
   320                 prevUnicode = unicodeValue;
   321             }
   322         }
   323     }
   325     aTable = new uint8_t[tablelen];
   326     memcpy(aTable, aBuf, tablelen);
   328     return NS_OK;
   329 }
   331 // Windows requires fonts to have a format-4 cmap with a Microsoft ID (3).  On the Mac, fonts either have
   332 // a format-4 cmap with Microsoft platform/encoding id or they have one with a platformID == Unicode (0)
   333 // For fonts with two format-4 tables, the first one (Unicode platform) is preferred on the Mac.
   335 #if defined(XP_MACOSX)
   336     #define acceptableFormat4(p,e,k) (((p) == PLATFORM_ID_MICROSOFT && (e) == EncodingIDMicrosoft && !(k)) || \
   337                                       ((p) == PLATFORM_ID_UNICODE))
   339     #define acceptableUCS4Encoding(p, e, k) \
   340         (((p) == PLATFORM_ID_MICROSOFT && (e) == EncodingIDUCS4ForMicrosoftPlatform) && (k) != 12 || \
   341          ((p) == PLATFORM_ID_UNICODE   && \
   342           ((e) == EncodingIDDefaultForUnicodePlatform || (e) >= EncodingIDUCS4ForUnicodePlatform)))
   343 #else
   344     #define acceptableFormat4(p,e,k) ((p) == PLATFORM_ID_MICROSOFT && (e) == EncodingIDMicrosoft)
   346     #define acceptableUCS4Encoding(p, e, k) \
   347         ((p) == PLATFORM_ID_MICROSOFT && (e) == EncodingIDUCS4ForMicrosoftPlatform)
   348 #endif
   350 #define acceptablePlatform(p) ((p) == PLATFORM_ID_UNICODE || (p) == PLATFORM_ID_MICROSOFT)
   351 #define isSymbol(p,e)         ((p) == PLATFORM_ID_MICROSOFT && (e) == EncodingIDSymbol)
   352 #define isUVSEncoding(p, e)   ((p) == PLATFORM_ID_UNICODE && (e) == EncodingIDUVSForUnicodePlatform)
   354 uint32_t
   355 gfxFontUtils::FindPreferredSubtable(const uint8_t *aBuf, uint32_t aBufLength,
   356                                     uint32_t *aTableOffset,
   357                                     uint32_t *aUVSTableOffset,
   358                                     bool *aSymbolEncoding)
   359 {
   360     enum {
   361         OffsetVersion = 0,
   362         OffsetNumTables = 2,
   363         SizeOfHeader = 4,
   365         TableOffsetPlatformID = 0,
   366         TableOffsetEncodingID = 2,
   367         TableOffsetOffset = 4,
   368         SizeOfTable = 8,
   370         SubtableOffsetFormat = 0
   371     };
   372     enum {
   373         EncodingIDSymbol = 0,
   374         EncodingIDMicrosoft = 1,
   375         EncodingIDDefaultForUnicodePlatform = 0,
   376         EncodingIDUCS4ForUnicodePlatform = 3,
   377         EncodingIDUVSForUnicodePlatform = 5,
   378         EncodingIDUCS4ForMicrosoftPlatform = 10
   379     };
   381     if (aUVSTableOffset) {
   382         *aUVSTableOffset = 0;
   383     }
   385     if (!aBuf || aBufLength < SizeOfHeader) {
   386         // cmap table is missing, or too small to contain header fields!
   387         return 0;
   388     }
   390     // uint16_t version = ReadShortAt(aBuf, OffsetVersion); // Unused: self-documenting.
   391     uint16_t numTables = ReadShortAt(aBuf, OffsetNumTables);
   392     if (aBufLength < uint32_t(SizeOfHeader + numTables * SizeOfTable)) {
   393         return 0;
   394     }
   396     // save the format we want here
   397     uint32_t keepFormat = 0;
   399     const uint8_t *table = aBuf + SizeOfHeader;
   400     for (uint16_t i = 0; i < numTables; ++i, table += SizeOfTable) {
   401         const uint16_t platformID = ReadShortAt(table, TableOffsetPlatformID);
   402         if (!acceptablePlatform(platformID))
   403             continue;
   405         const uint16_t encodingID = ReadShortAt(table, TableOffsetEncodingID);
   406         const uint32_t offset = ReadLongAt(table, TableOffsetOffset);
   407         if (aBufLength - 2 < offset) {
   408             // this subtable is not valid - beyond end of buffer
   409             return 0;
   410         }
   412         const uint8_t *subtable = aBuf + offset;
   413         const uint16_t format = ReadShortAt(subtable, SubtableOffsetFormat);
   415         if (isSymbol(platformID, encodingID)) {
   416             keepFormat = format;
   417             *aTableOffset = offset;
   418             *aSymbolEncoding = true;
   419             break;
   420         } else if (format == 4 && acceptableFormat4(platformID, encodingID, keepFormat)) {
   421             keepFormat = format;
   422             *aTableOffset = offset;
   423             *aSymbolEncoding = false;
   424         } else if (format == 12 && acceptableUCS4Encoding(platformID, encodingID, keepFormat)) {
   425             keepFormat = format;
   426             *aTableOffset = offset;
   427             *aSymbolEncoding = false;
   428             if (platformID > PLATFORM_ID_UNICODE || !aUVSTableOffset || *aUVSTableOffset) {
   429                 break; // we don't want to try anything else when this format is available.
   430             }
   431         } else if (format == 14 && isUVSEncoding(platformID, encodingID) && aUVSTableOffset) {
   432             *aUVSTableOffset = offset;
   433             if (keepFormat == 12) {
   434                 break;
   435             }
   436         }
   437     }
   439     return keepFormat;
   440 }
   442 nsresult
   443 gfxFontUtils::ReadCMAP(const uint8_t *aBuf, uint32_t aBufLength,
   444                        gfxSparseBitSet& aCharacterMap,
   445                        uint32_t& aUVSOffset,
   446                        bool& aUnicodeFont, bool& aSymbolFont)
   447 {
   448     uint32_t offset;
   449     bool     symbol;
   450     uint32_t format = FindPreferredSubtable(aBuf, aBufLength,
   451                                             &offset, &aUVSOffset, &symbol);
   453     if (format == 4) {
   454         if (symbol) {
   455             aUnicodeFont = false;
   456             aSymbolFont = true;
   457         } else {
   458             aUnicodeFont = true;
   459             aSymbolFont = false;
   460         }
   461         return ReadCMAPTableFormat4(aBuf + offset, aBufLength - offset,
   462                                     aCharacterMap);
   463     }
   465     if (format == 12) {
   466         aUnicodeFont = true;
   467         aSymbolFont = false;
   468         return ReadCMAPTableFormat12(aBuf + offset, aBufLength - offset,
   469                                      aCharacterMap);
   470     }
   472     return NS_ERROR_FAILURE;
   473 }
   475 #pragma pack(1)
   477 typedef struct {
   478     AutoSwap_PRUint16 format;
   479     AutoSwap_PRUint16 length;
   480     AutoSwap_PRUint16 language;
   481     AutoSwap_PRUint16 segCountX2;
   482     AutoSwap_PRUint16 searchRange;
   483     AutoSwap_PRUint16 entrySelector;
   484     AutoSwap_PRUint16 rangeShift;
   486     AutoSwap_PRUint16 arrays[1];
   487 } Format4Cmap;
   489 typedef struct {
   490     AutoSwap_PRUint16 format;
   491     AutoSwap_PRUint32 length;
   492     AutoSwap_PRUint32 numVarSelectorRecords;
   494     typedef struct {
   495         AutoSwap_PRUint24 varSelector;
   496         AutoSwap_PRUint32 defaultUVSOffset;
   497         AutoSwap_PRUint32 nonDefaultUVSOffset;
   498     } VarSelectorRecord;
   500     VarSelectorRecord varSelectorRecords[1];
   501 } Format14Cmap;
   503 typedef struct {
   504     AutoSwap_PRUint32 numUVSMappings;
   506     typedef struct {
   507         AutoSwap_PRUint24 unicodeValue;
   508         AutoSwap_PRUint16 glyphID;
   509     } UVSMapping;
   511     UVSMapping uvsMappings[1];
   512 } NonDefUVSTable;
   514 #pragma pack()
   516 uint32_t
   517 gfxFontUtils::MapCharToGlyphFormat4(const uint8_t *aBuf, char16_t aCh)
   518 {
   519     const Format4Cmap *cmap4 = reinterpret_cast<const Format4Cmap*>(aBuf);
   520     uint16_t segCount;
   521     const AutoSwap_PRUint16 *endCodes;
   522     const AutoSwap_PRUint16 *startCodes;
   523     const AutoSwap_PRUint16 *idDelta;
   524     const AutoSwap_PRUint16 *idRangeOffset;
   525     uint16_t probe;
   526     uint16_t rangeShiftOver2;
   527     uint16_t index;
   529     segCount = (uint16_t)(cmap4->segCountX2) / 2;
   531     endCodes = &cmap4->arrays[0];
   532     startCodes = &cmap4->arrays[segCount + 1]; // +1 for reserved word between arrays
   533     idDelta = &startCodes[segCount];
   534     idRangeOffset = &idDelta[segCount];
   536     probe = 1 << (uint16_t)(cmap4->entrySelector);
   537     rangeShiftOver2 = (uint16_t)(cmap4->rangeShift) / 2;
   539     if ((uint16_t)(startCodes[rangeShiftOver2]) <= aCh) {
   540         index = rangeShiftOver2;
   541     } else {
   542         index = 0;
   543     }
   545     while (probe > 1) {
   546         probe >>= 1;
   547         if ((uint16_t)(startCodes[index + probe]) <= aCh) {
   548             index += probe;
   549         }
   550     }
   552     if (aCh >= (uint16_t)(startCodes[index]) && aCh <= (uint16_t)(endCodes[index])) {
   553         uint16_t result;
   554         if ((uint16_t)(idRangeOffset[index]) == 0) {
   555             result = aCh;
   556         } else {
   557             uint16_t offset = aCh - (uint16_t)(startCodes[index]);
   558             const AutoSwap_PRUint16 *glyphIndexTable =
   559                 (const AutoSwap_PRUint16*)((const char*)&idRangeOffset[index] +
   560                                            (uint16_t)(idRangeOffset[index]));
   561             result = glyphIndexTable[offset];
   562         }
   564         // note that this is unsigned 16-bit arithmetic, and may wrap around
   565         result += (uint16_t)(idDelta[index]);
   566         return result;
   567     }
   569     return 0;
   570 }
   572 uint32_t
   573 gfxFontUtils::MapCharToGlyphFormat12(const uint8_t *aBuf, uint32_t aCh)
   574 {
   575     const Format12CmapHeader *cmap12 =
   576         reinterpret_cast<const Format12CmapHeader*>(aBuf);
   578     // We know that numGroups is within range for the subtable size
   579     // because it was checked by ReadCMAPTableFormat12.
   580     uint32_t numGroups = cmap12->numGroups;
   582     // The array of groups immediately follows the subtable header.
   583     const Format12Group *groups =
   584         reinterpret_cast<const Format12Group*>(aBuf + sizeof(Format12CmapHeader));
   586     // For most efficient binary search, we want to work on a range that
   587     // is a power of 2 so that we can always halve it by shifting.
   588     // So we find the largest power of 2 that is <= numGroups.
   589     // We will offset this range by rangeOffset so as to reach the end
   590     // of the table, provided that doesn't put us beyond the target
   591     // value from the outset.
   592     uint32_t powerOf2 = mozilla::FindHighestBit(numGroups);
   593     uint32_t rangeOffset = numGroups - powerOf2;
   594     uint32_t range = 0;
   595     uint32_t startCharCode;
   597     if (groups[rangeOffset].startCharCode <= aCh) {
   598         range = rangeOffset;
   599     }
   601     // Repeatedly halve the size of the range until we find the target group
   602     while (powerOf2 > 1) {
   603         powerOf2 >>= 1;
   604         if (groups[range + powerOf2].startCharCode <= aCh) {
   605             range += powerOf2;
   606         }
   607     }
   609     // Check if the character is actually present in the range and return
   610     // the corresponding glyph ID
   611     startCharCode = groups[range].startCharCode;
   612     if (startCharCode <= aCh && groups[range].endCharCode >= aCh) {
   613         return groups[range].startGlyphId + aCh - startCharCode;
   614     }
   616     // Else it's not present, so return the .notdef glyph
   617     return 0;
   618 }
   620 uint16_t
   621 gfxFontUtils::MapUVSToGlyphFormat14(const uint8_t *aBuf, uint32_t aCh, uint32_t aVS)
   622 {
   623     const Format14Cmap *cmap14 = reinterpret_cast<const Format14Cmap*>(aBuf);
   625     // binary search in varSelectorRecords
   626     uint32_t min = 0;
   627     uint32_t max = cmap14->numVarSelectorRecords;
   628     uint32_t nonDefUVSOffset = 0;
   629     while (min < max) {
   630         uint32_t index = (min + max) >> 1;
   631         uint32_t varSelector = cmap14->varSelectorRecords[index].varSelector;
   632         if (aVS == varSelector) {
   633             nonDefUVSOffset = cmap14->varSelectorRecords[index].nonDefaultUVSOffset;
   634             break;
   635         }
   636         if (aVS < varSelector) {
   637             max = index;
   638         } else {
   639             min = index + 1;
   640         }
   641     }
   642     if (!nonDefUVSOffset) {
   643         return 0;
   644     }
   646     const NonDefUVSTable *table = reinterpret_cast<const NonDefUVSTable*>
   647                                       (aBuf + nonDefUVSOffset);
   649     // binary search in uvsMappings
   650     min = 0;
   651     max = table->numUVSMappings;
   652     while (min < max) {
   653         uint32_t index = (min + max) >> 1;
   654         uint32_t unicodeValue = table->uvsMappings[index].unicodeValue;
   655         if (aCh == unicodeValue) {
   656             return table->uvsMappings[index].glyphID;
   657         }
   658         if (aCh < unicodeValue) {
   659             max = index;
   660         } else {
   661             min = index + 1;
   662         }
   663     }
   665     return 0;
   666 }
   668 uint32_t
   669 gfxFontUtils::MapCharToGlyph(const uint8_t *aCmapBuf, uint32_t aBufLength,
   670                              uint32_t aUnicode, uint32_t aVarSelector)
   671 {
   672     uint32_t offset, uvsOffset;
   673     bool     symbol;
   674     uint32_t format = FindPreferredSubtable(aCmapBuf, aBufLength, &offset,
   675                                             &uvsOffset, &symbol);
   677     uint32_t gid;
   678     switch (format) {
   679     case 4:
   680         gid = aUnicode < UNICODE_BMP_LIMIT ?
   681             MapCharToGlyphFormat4(aCmapBuf + offset, char16_t(aUnicode)) : 0;
   682         break;
   683     case 12:
   684         gid = MapCharToGlyphFormat12(aCmapBuf + offset, aUnicode);
   685         break;
   686     default:
   687         NS_WARNING("unsupported cmap format, glyphs will be missing");
   688         gid = 0;
   689     }
   691     if (aVarSelector && uvsOffset && gid) {
   692         uint32_t varGID =
   693             gfxFontUtils::MapUVSToGlyphFormat14(aCmapBuf + uvsOffset,
   694                                                 aUnicode, aVarSelector);
   695         if (!varGID) {
   696             aUnicode = gfxFontUtils::GetUVSFallback(aUnicode, aVarSelector);
   697             if (aUnicode) {
   698                 switch (format) {
   699                 case 4:
   700                     if (aUnicode < UNICODE_BMP_LIMIT) {
   701                         varGID = MapCharToGlyphFormat4(aCmapBuf + offset,
   702                                                        char16_t(aUnicode));
   703                     }
   704                     break;
   705                 case 12:
   706                     varGID = MapCharToGlyphFormat12(aCmapBuf + offset,
   707                                                     aUnicode);
   708                     break;
   709                 }
   710             }
   711         }
   712         if (varGID) {
   713             gid = varGID;
   714         }
   716         // else the variation sequence was not supported, use default mapping
   717         // of the character code alone
   718     }
   720     return gid;
   721 }
   723 void gfxFontUtils::GetPrefsFontList(const char *aPrefName, nsTArray<nsString>& aFontList)
   724 {
   725     const char16_t kComma = char16_t(',');
   727     aFontList.Clear();
   729     // get the list of single-face font families
   730     nsAdoptingString fontlistValue = Preferences::GetString(aPrefName);
   731     if (!fontlistValue) {
   732         return;
   733     }
   735     // append each font name to the list
   736     nsAutoString fontname;
   737     const char16_t *p, *p_end;
   738     fontlistValue.BeginReading(p);
   739     fontlistValue.EndReading(p_end);
   741      while (p < p_end) {
   742         const char16_t *nameStart = p;
   743         while (++p != p_end && *p != kComma)
   744         /* nothing */ ;
   746         // pull out a single name and clean out leading/trailing whitespace        
   747         fontname = Substring(nameStart, p);
   748         fontname.CompressWhitespace(true, true);
   750         // append it to the list
   751         aFontList.AppendElement(fontname);
   752         ++p;
   753     }
   755 }
   757 // produce a unique font name that is (1) a valid Postscript name and (2) less
   758 // than 31 characters in length.  Using AddFontMemResourceEx on Windows fails 
   759 // for names longer than 30 characters in length.
   761 #define MAX_B64_LEN 32
   763 nsresult gfxFontUtils::MakeUniqueUserFontName(nsAString& aName)
   764 {
   765     nsCOMPtr<nsIUUIDGenerator> uuidgen =
   766       do_GetService("@mozilla.org/uuid-generator;1");
   767     NS_ENSURE_TRUE(uuidgen, NS_ERROR_OUT_OF_MEMORY);
   769     nsID guid;
   771     NS_ASSERTION(sizeof(guid) * 2 <= MAX_B64_LEN, "size of nsID has changed!");
   773     nsresult rv = uuidgen->GenerateUUIDInPlace(&guid);
   774     NS_ENSURE_SUCCESS(rv, rv);
   776     char guidB64[MAX_B64_LEN] = {0};
   778     if (!PL_Base64Encode(reinterpret_cast<char*>(&guid), sizeof(guid), guidB64))
   779         return NS_ERROR_FAILURE;
   781     // all b64 characters except for '/' are allowed in Postscript names, so convert / ==> -
   782     char *p;
   783     for (p = guidB64; *p; p++) {
   784         if (*p == '/')
   785             *p = '-';
   786     }
   788     aName.Assign(NS_LITERAL_STRING("uf"));
   789     aName.AppendASCII(guidB64);
   790     return NS_OK;
   791 }
   794 // TrueType/OpenType table handling code
   796 // need byte aligned structs
   797 #pragma pack(1)
   799 // name table stores set of name record structures, followed by
   800 // large block containing all the strings.  name record offset and length
   801 // indicates the offset and length within that block.
   802 // http://www.microsoft.com/typography/otspec/name.htm
   803 struct NameRecordData {
   804     uint32_t  offset;
   805     uint32_t  length;
   806 };
   808 #pragma pack()
   810 static bool
   811 IsValidSFNTVersion(uint32_t version)
   812 {
   813     // normally 0x00010000, CFF-style OT fonts == 'OTTO' and Apple TT fonts = 'true'
   814     // 'typ1' is also possible for old Type 1 fonts in a SFNT container but not supported
   815     return version == 0x10000 ||
   816            version == TRUETYPE_TAG('O','T','T','O') ||
   817            version == TRUETYPE_TAG('t','r','u','e');
   818 }
   820 // copy and swap UTF-16 values, assume no surrogate pairs, can be in place
   821 static void
   822 CopySwapUTF16(const uint16_t *aInBuf, uint16_t *aOutBuf, uint32_t aLen)
   823 {
   824     const uint16_t *end = aInBuf + aLen;
   825     while (aInBuf < end) {
   826         uint16_t value = *aInBuf;
   827         *aOutBuf = (value >> 8) | (value & 0xff) << 8;
   828         aOutBuf++;
   829         aInBuf++;
   830     }
   831 }
   833 gfxUserFontType
   834 gfxFontUtils::DetermineFontDataType(const uint8_t *aFontData, uint32_t aFontDataLength)
   835 {
   836     // test for OpenType font data
   837     // problem: EOT-Lite with 0x10000 length will look like TrueType!
   838     if (aFontDataLength >= sizeof(SFNTHeader)) {
   839         const SFNTHeader *sfntHeader = reinterpret_cast<const SFNTHeader*>(aFontData);
   840         uint32_t sfntVersion = sfntHeader->sfntVersion;
   841         if (IsValidSFNTVersion(sfntVersion)) {
   842             return GFX_USERFONT_OPENTYPE;
   843         }
   844     }
   846     // test for WOFF
   847     if (aFontDataLength >= sizeof(AutoSwap_PRUint32)) {
   848         const AutoSwap_PRUint32 *version = 
   849             reinterpret_cast<const AutoSwap_PRUint32*>(aFontData);
   850         if (uint32_t(*version) == TRUETYPE_TAG('w','O','F','F')) {
   851             return GFX_USERFONT_WOFF;
   852         }
   853     }
   855     // tests for other formats here
   857     return GFX_USERFONT_UNKNOWN;
   858 }
   860 nsresult
   861 gfxFontUtils::RenameFont(const nsAString& aName, const uint8_t *aFontData, 
   862                          uint32_t aFontDataLength, FallibleTArray<uint8_t> *aNewFont)
   863 {
   864     NS_ASSERTION(aNewFont, "null font data array");
   866     uint64_t dataLength(aFontDataLength);
   868     // new name table
   869     static const uint32_t neededNameIDs[] = {NAME_ID_FAMILY, 
   870                                              NAME_ID_STYLE,
   871                                              NAME_ID_UNIQUE,
   872                                              NAME_ID_FULL,
   873                                              NAME_ID_POSTSCRIPT};
   875     // calculate new name table size
   876     uint16_t nameCount = ArrayLength(neededNameIDs);
   878     // leave room for null-terminator
   879     uint16_t nameStrLength = (aName.Length() + 1) * sizeof(char16_t); 
   881     // round name table size up to 4-byte multiple
   882     uint32_t nameTableSize = (sizeof(NameHeader) +
   883                               sizeof(NameRecord) * nameCount +
   884                               nameStrLength +
   885                               3) & ~3;
   887     if (dataLength + nameTableSize > UINT32_MAX)
   888         return NS_ERROR_FAILURE;
   890     // bug 505386 - need to handle unpadded font length
   891     uint32_t paddedFontDataSize = (aFontDataLength + 3) & ~3;
   892     uint32_t adjFontDataSize = paddedFontDataSize + nameTableSize;
   894     // create new buffer: old font data plus new name table
   895     if (!aNewFont->AppendElements(adjFontDataSize))
   896         return NS_ERROR_OUT_OF_MEMORY;
   898     // copy the old font data
   899     uint8_t *newFontData = reinterpret_cast<uint8_t*>(aNewFont->Elements());
   901     // null the last four bytes in case the font length is not a multiple of 4
   902     memset(newFontData + aFontDataLength, 0, paddedFontDataSize - aFontDataLength);
   904     // copy font data
   905     memcpy(newFontData, aFontData, aFontDataLength);
   907     // null out the last 4 bytes for checksum calculations
   908     memset(newFontData + adjFontDataSize - 4, 0, 4);
   910     NameHeader *nameHeader = reinterpret_cast<NameHeader*>(newFontData +
   911                                                             paddedFontDataSize);
   913     // -- name header
   914     nameHeader->format = 0;
   915     nameHeader->count = nameCount;
   916     nameHeader->stringOffset = sizeof(NameHeader) + nameCount * sizeof(NameRecord);
   918     // -- name records
   919     uint32_t i;
   920     NameRecord *nameRecord = reinterpret_cast<NameRecord*>(nameHeader + 1);
   922     for (i = 0; i < nameCount; i++, nameRecord++) {
   923         nameRecord->platformID = PLATFORM_ID_MICROSOFT;
   924         nameRecord->encodingID = ENCODING_ID_MICROSOFT_UNICODEBMP;
   925         nameRecord->languageID = LANG_ID_MICROSOFT_EN_US;
   926         nameRecord->nameID = neededNameIDs[i];
   927         nameRecord->offset = 0;
   928         nameRecord->length = nameStrLength;
   929     }
   931     // -- string data, located after the name records, stored in big-endian form
   932     char16_t *strData = reinterpret_cast<char16_t*>(nameRecord);
   934     mozilla::NativeEndian::copyAndSwapToBigEndian(strData,
   935                                                   aName.BeginReading(),
   936                                                   aName.Length());
   937     strData[aName.Length()] = 0; // add null termination
   939     // adjust name table header to point to the new name table
   940     SFNTHeader *sfntHeader = reinterpret_cast<SFNTHeader*>(newFontData);
   942     // table directory entries begin immediately following SFNT header
   943     TableDirEntry *dirEntry = 
   944         reinterpret_cast<TableDirEntry*>(newFontData + sizeof(SFNTHeader));
   946     uint32_t numTables = sfntHeader->numTables;
   948     for (i = 0; i < numTables; i++, dirEntry++) {
   949         if (dirEntry->tag == TRUETYPE_TAG('n','a','m','e')) {
   950             break;
   951         }
   952     }
   954     // function only called if font validates, so this should always be true
   955     NS_ASSERTION(i < numTables, "attempt to rename font with no name table");
   957     // note: dirEntry now points to name record
   959     // recalculate name table checksum
   960     uint32_t checkSum = 0;
   961     AutoSwap_PRUint32 *nameData = reinterpret_cast<AutoSwap_PRUint32*> (nameHeader);
   962     AutoSwap_PRUint32 *nameDataEnd = nameData + (nameTableSize >> 2);
   964     while (nameData < nameDataEnd)
   965         checkSum = checkSum + *nameData++;
   967     // adjust name table entry to point to new name table
   968     dirEntry->offset = paddedFontDataSize;
   969     dirEntry->length = nameTableSize;
   970     dirEntry->checkSum = checkSum;
   972     // fix up checksums
   973     uint32_t checksum = 0;
   975     // checksum for font = (checksum of header) + (checksum of tables)
   976     uint32_t headerLen = sizeof(SFNTHeader) + sizeof(TableDirEntry) * numTables;
   977     const AutoSwap_PRUint32 *headerData = 
   978         reinterpret_cast<const AutoSwap_PRUint32*>(newFontData);
   980     // header length is in bytes, checksum calculated in longwords
   981     for (i = 0; i < (headerLen >> 2); i++, headerData++) {
   982         checksum += *headerData;
   983     }
   985     uint32_t headOffset = 0;
   986     dirEntry = reinterpret_cast<TableDirEntry*>(newFontData + sizeof(SFNTHeader));
   988     for (i = 0; i < numTables; i++, dirEntry++) {
   989         if (dirEntry->tag == TRUETYPE_TAG('h','e','a','d')) {
   990             headOffset = dirEntry->offset;
   991         }
   992         checksum += dirEntry->checkSum;
   993     }
   995     NS_ASSERTION(headOffset != 0, "no head table for font");
   997     HeadTable *headData = reinterpret_cast<HeadTable*>(newFontData + headOffset);
   999     headData->checkSumAdjustment = HeadTable::HEAD_CHECKSUM_CALC_CONST - checksum;
  1001     return NS_OK;
  1004 // This is only called after the basic validity of the downloaded sfnt
  1005 // data has been checked, so it should never fail to find the name table
  1006 // (though it might fail to read it, if memory isn't available);
  1007 // other checks here are just for extra paranoia.
  1008 nsresult
  1009 gfxFontUtils::GetFullNameFromSFNT(const uint8_t* aFontData, uint32_t aLength,
  1010                                   nsAString& aFullName)
  1012     aFullName.AssignLiteral("(MISSING NAME)"); // should always get replaced
  1014     NS_ENSURE_TRUE(aLength >= sizeof(SFNTHeader), NS_ERROR_UNEXPECTED);
  1015     const SFNTHeader *sfntHeader =
  1016         reinterpret_cast<const SFNTHeader*>(aFontData);
  1017     const TableDirEntry *dirEntry =
  1018         reinterpret_cast<const TableDirEntry*>(aFontData + sizeof(SFNTHeader));
  1019     uint32_t numTables = sfntHeader->numTables;
  1020     NS_ENSURE_TRUE(aLength >=
  1021                    sizeof(SFNTHeader) + numTables * sizeof(TableDirEntry),
  1022                    NS_ERROR_UNEXPECTED);
  1023     bool foundName = false;
  1024     for (uint32_t i = 0; i < numTables; i++, dirEntry++) {
  1025         if (dirEntry->tag == TRUETYPE_TAG('n','a','m','e')) {
  1026             foundName = true;
  1027             break;
  1031     // should never fail, as we're only called after font validation succeeded
  1032     NS_ENSURE_TRUE(foundName, NS_ERROR_NOT_AVAILABLE);
  1034     uint32_t len = dirEntry->length;
  1035     NS_ENSURE_TRUE(aLength > len && aLength - len >= dirEntry->offset,
  1036                    NS_ERROR_UNEXPECTED);
  1038     hb_blob_t *nameBlob =
  1039         hb_blob_create((const char*)aFontData + dirEntry->offset, len,
  1040                        HB_MEMORY_MODE_READONLY, nullptr, nullptr);
  1041     nsresult rv = GetFullNameFromTable(nameBlob, aFullName);
  1042     hb_blob_destroy(nameBlob);
  1044     return rv;
  1047 nsresult
  1048 gfxFontUtils::GetFullNameFromTable(hb_blob_t *aNameTable,
  1049                                    nsAString& aFullName)
  1051     nsAutoString name;
  1052     nsresult rv =
  1053         gfxFontUtils::ReadCanonicalName(aNameTable,
  1054                                         gfxFontUtils::NAME_ID_FULL,
  1055                                         name);
  1056     if (NS_SUCCEEDED(rv) && !name.IsEmpty()) {
  1057         aFullName = name;
  1058         return NS_OK;
  1060     rv = gfxFontUtils::ReadCanonicalName(aNameTable,
  1061                                          gfxFontUtils::NAME_ID_FAMILY,
  1062                                          name);
  1063     if (NS_SUCCEEDED(rv) && !name.IsEmpty()) {
  1064         nsAutoString styleName;
  1065         rv = gfxFontUtils::ReadCanonicalName(aNameTable,
  1066                                              gfxFontUtils::NAME_ID_STYLE,
  1067                                              styleName);
  1068         if (NS_SUCCEEDED(rv) && !styleName.IsEmpty()) {
  1069             name.AppendLiteral(" ");
  1070             name.Append(styleName);
  1071             aFullName = name;
  1073         return NS_OK;
  1076     return NS_ERROR_NOT_AVAILABLE;
  1079 nsresult
  1080 gfxFontUtils::GetFamilyNameFromTable(hb_blob_t *aNameTable,
  1081                                      nsAString& aFullName)
  1083     nsAutoString name;
  1084     nsresult rv =
  1085         gfxFontUtils::ReadCanonicalName(aNameTable,
  1086                                         gfxFontUtils::NAME_ID_FAMILY,
  1087                                         name);
  1088     if (NS_SUCCEEDED(rv) && !name.IsEmpty()) {
  1089         aFullName = name;
  1090         return NS_OK;
  1092     return NS_ERROR_NOT_AVAILABLE;
  1095 enum {
  1096 #if defined(XP_MACOSX)
  1097     CANONICAL_LANG_ID = gfxFontUtils::LANG_ID_MAC_ENGLISH,
  1098     PLATFORM_ID       = gfxFontUtils::PLATFORM_ID_MAC
  1099 #else
  1100     CANONICAL_LANG_ID = gfxFontUtils::LANG_ID_MICROSOFT_EN_US,
  1101     PLATFORM_ID       = gfxFontUtils::PLATFORM_ID_MICROSOFT
  1102 #endif
  1103 };    
  1105 nsresult
  1106 gfxFontUtils::ReadNames(const char *aNameData, uint32_t aDataLen,
  1107                         uint32_t aNameID, int32_t aPlatformID,
  1108                         nsTArray<nsString>& aNames)
  1110     return ReadNames(aNameData, aDataLen, aNameID, LANG_ALL,
  1111                      aPlatformID, aNames);
  1114 nsresult
  1115 gfxFontUtils::ReadCanonicalName(hb_blob_t *aNameTable, uint32_t aNameID,
  1116                                 nsString& aName)
  1118     uint32_t nameTableLen;
  1119     const char *nameTable = hb_blob_get_data(aNameTable, &nameTableLen);
  1120     return ReadCanonicalName(nameTable, nameTableLen, aNameID, aName);
  1123 nsresult
  1124 gfxFontUtils::ReadCanonicalName(const char *aNameData, uint32_t aDataLen,
  1125                                 uint32_t aNameID, nsString& aName)
  1127     nsresult rv;
  1129     nsTArray<nsString> names;
  1131     // first, look for the English name (this will succeed 99% of the time)
  1132     rv = ReadNames(aNameData, aDataLen, aNameID, CANONICAL_LANG_ID, 
  1133                    PLATFORM_ID, names);
  1134     NS_ENSURE_SUCCESS(rv, rv);
  1136     // otherwise, grab names for all languages
  1137     if (names.Length() == 0) {
  1138         rv = ReadNames(aNameData, aDataLen, aNameID, LANG_ALL,
  1139                        PLATFORM_ID, names);
  1140         NS_ENSURE_SUCCESS(rv, rv);
  1143 #if defined(XP_MACOSX)
  1144     // may be dealing with font that only has Microsoft name entries
  1145     if (names.Length() == 0) {
  1146         rv = ReadNames(aNameData, aDataLen, aNameID, LANG_ID_MICROSOFT_EN_US,
  1147                        PLATFORM_ID_MICROSOFT, names);
  1148         NS_ENSURE_SUCCESS(rv, rv);
  1150         // getting really desperate now, take anything!
  1151         if (names.Length() == 0) {
  1152             rv = ReadNames(aNameData, aDataLen, aNameID, LANG_ALL,
  1153                            PLATFORM_ID_MICROSOFT, names);
  1154             NS_ENSURE_SUCCESS(rv, rv);
  1157 #endif
  1159     // return the first name (99.9% of the time names will
  1160     // contain a single English name)
  1161     if (names.Length()) {
  1162         aName.Assign(names[0]);
  1163         return NS_OK;
  1166     return NS_ERROR_FAILURE;
  1169 // Charsets to use for decoding Mac platform font names.
  1170 // This table is sorted by {encoding, language}, with the wildcard "ANY" being
  1171 // greater than any defined values for each field; we use a binary search on both
  1172 // fields, and fall back to matching only encoding if necessary
  1174 // Some "redundant" entries for specific combinations are included such as
  1175 // encoding=roman, lang=english, in order that common entries will be found
  1176 // on the first search.
  1178 #define ANY 0xffff
  1179 const gfxFontUtils::MacFontNameCharsetMapping gfxFontUtils::gMacFontNameCharsets[] =
  1181     { ENCODING_ID_MAC_ROMAN,        LANG_ID_MAC_ENGLISH,      "macintosh"       },
  1182     { ENCODING_ID_MAC_ROMAN,        LANG_ID_MAC_ICELANDIC,    "x-mac-icelandic" },
  1183     { ENCODING_ID_MAC_ROMAN,        LANG_ID_MAC_TURKISH,      "x-mac-turkish"   },
  1184     { ENCODING_ID_MAC_ROMAN,        LANG_ID_MAC_POLISH,       "x-mac-ce"        },
  1185     { ENCODING_ID_MAC_ROMAN,        LANG_ID_MAC_ROMANIAN,     "x-mac-romanian"  },
  1186     { ENCODING_ID_MAC_ROMAN,        LANG_ID_MAC_CZECH,        "x-mac-ce"        },
  1187     { ENCODING_ID_MAC_ROMAN,        LANG_ID_MAC_SLOVAK,       "x-mac-ce"        },
  1188     { ENCODING_ID_MAC_ROMAN,        ANY,                      "macintosh"       },
  1189     { ENCODING_ID_MAC_JAPANESE,     LANG_ID_MAC_JAPANESE,     "Shift_JIS"       },
  1190     { ENCODING_ID_MAC_JAPANESE,     ANY,                      "Shift_JIS"       },
  1191     { ENCODING_ID_MAC_TRAD_CHINESE, LANG_ID_MAC_TRAD_CHINESE, "Big5"            },
  1192     { ENCODING_ID_MAC_TRAD_CHINESE, ANY,                      "Big5"            },
  1193     { ENCODING_ID_MAC_KOREAN,       LANG_ID_MAC_KOREAN,       "EUC-KR"          },
  1194     { ENCODING_ID_MAC_KOREAN,       ANY,                      "EUC-KR"          },
  1195     { ENCODING_ID_MAC_ARABIC,       LANG_ID_MAC_ARABIC,       "x-mac-arabic"    },
  1196     { ENCODING_ID_MAC_ARABIC,       LANG_ID_MAC_URDU,         "x-mac-farsi"     },
  1197     { ENCODING_ID_MAC_ARABIC,       LANG_ID_MAC_FARSI,        "x-mac-farsi"     },
  1198     { ENCODING_ID_MAC_ARABIC,       ANY,                      "x-mac-arabic"    },
  1199     { ENCODING_ID_MAC_HEBREW,       LANG_ID_MAC_HEBREW,       "x-mac-hebrew"    },
  1200     { ENCODING_ID_MAC_HEBREW,       ANY,                      "x-mac-hebrew"    },
  1201     { ENCODING_ID_MAC_GREEK,        ANY,                      "x-mac-greek"     },
  1202     { ENCODING_ID_MAC_CYRILLIC,     ANY,                      "x-mac-cyrillic"  },
  1203     { ENCODING_ID_MAC_DEVANAGARI,   ANY,                      "x-mac-devanagari"},
  1204     { ENCODING_ID_MAC_GURMUKHI,     ANY,                      "x-mac-gurmukhi"  },
  1205     { ENCODING_ID_MAC_GUJARATI,     ANY,                      "x-mac-gujarati"  },
  1206     { ENCODING_ID_MAC_SIMP_CHINESE, LANG_ID_MAC_SIMP_CHINESE, "GB2312"          },
  1207     { ENCODING_ID_MAC_SIMP_CHINESE, ANY,                      "GB2312"          }
  1208 };
  1210 const char* gfxFontUtils::gISOFontNameCharsets[] = 
  1212     /* 0 */ "us-ascii"   ,
  1213     /* 1 */ nullptr       , /* spec says "ISO 10646" but does not specify encoding form! */
  1214     /* 2 */ "ISO-8859-1"
  1215 };
  1217 const char* gfxFontUtils::gMSFontNameCharsets[] =
  1219     /* [0] ENCODING_ID_MICROSOFT_SYMBOL */      ""          ,
  1220     /* [1] ENCODING_ID_MICROSOFT_UNICODEBMP */  ""          ,
  1221     /* [2] ENCODING_ID_MICROSOFT_SHIFTJIS */    "Shift_JIS" ,
  1222     /* [3] ENCODING_ID_MICROSOFT_PRC */         nullptr      ,
  1223     /* [4] ENCODING_ID_MICROSOFT_BIG5 */        "Big5"      ,
  1224     /* [5] ENCODING_ID_MICROSOFT_WANSUNG */     nullptr      ,
  1225     /* [6] ENCODING_ID_MICROSOFT_JOHAB */       "x-johab"   ,
  1226     /* [7] reserved */                          nullptr      ,
  1227     /* [8] reserved */                          nullptr      ,
  1228     /* [9] reserved */                          nullptr      ,
  1229     /*[10] ENCODING_ID_MICROSOFT_UNICODEFULL */ ""
  1230 };
  1232 // Return the name of the charset we should use to decode a font name
  1233 // given the name table attributes.
  1234 // Special return values:
  1235 //    ""       charset is UTF16BE, no need for a converter
  1236 //    nullptr   unknown charset, do not attempt conversion
  1237 const char*
  1238 gfxFontUtils::GetCharsetForFontName(uint16_t aPlatform, uint16_t aScript, uint16_t aLanguage)
  1240     switch (aPlatform)
  1242     case PLATFORM_ID_UNICODE:
  1243         return "";
  1245     case PLATFORM_ID_MAC:
  1247             uint32_t lo = 0, hi = ArrayLength(gMacFontNameCharsets);
  1248             MacFontNameCharsetMapping searchValue = { aScript, aLanguage, nullptr };
  1249             for (uint32_t i = 0; i < 2; ++i) {
  1250                 // binary search; if not found, set language to ANY and try again
  1251                 while (lo < hi) {
  1252                     uint32_t mid = (lo + hi) / 2;
  1253                     const MacFontNameCharsetMapping& entry = gMacFontNameCharsets[mid];
  1254                     if (entry < searchValue) {
  1255                         lo = mid + 1;
  1256                         continue;
  1258                     if (searchValue < entry) {
  1259                         hi = mid;
  1260                         continue;
  1262                     // found a match
  1263                     return entry.mCharsetName;
  1266                 // no match, so reset high bound for search and re-try
  1267                 hi = ArrayLength(gMacFontNameCharsets);
  1268                 searchValue.mLanguage = ANY;
  1271         break;
  1273     case PLATFORM_ID_ISO:
  1274         if (aScript < ArrayLength(gISOFontNameCharsets)) {
  1275             return gISOFontNameCharsets[aScript];
  1277         break;
  1279     case PLATFORM_ID_MICROSOFT:
  1280         if (aScript < ArrayLength(gMSFontNameCharsets)) {
  1281             return gMSFontNameCharsets[aScript];
  1283         break;
  1286     return nullptr;
  1289 // convert a raw name from the name table to an nsString, if possible;
  1290 // return value indicates whether conversion succeeded
  1291 bool
  1292 gfxFontUtils::DecodeFontName(const char *aNameData, int32_t aByteLen, 
  1293                              uint32_t aPlatformCode, uint32_t aScriptCode,
  1294                              uint32_t aLangCode, nsAString& aName)
  1296     if (aByteLen <= 0) {
  1297         NS_WARNING("empty font name");
  1298         aName.SetLength(0);
  1299         return true;
  1302     const char *csName = GetCharsetForFontName(aPlatformCode, aScriptCode, aLangCode);
  1304     if (!csName) {
  1305         // nullptr -> unknown charset
  1306 #ifdef DEBUG
  1307         char warnBuf[128];
  1308         if (aByteLen > 64)
  1309             aByteLen = 64;
  1310         sprintf(warnBuf, "skipping font name, unknown charset %d:%d:%d for <%.*s>",
  1311                 aPlatformCode, aScriptCode, aLangCode, aByteLen, aNameData);
  1312         NS_WARNING(warnBuf);
  1313 #endif
  1314         return false;
  1317     if (csName[0] == 0) {
  1318         // empty charset name: data is utf16be, no need to instantiate a converter
  1319         uint32_t strLen = aByteLen / 2;
  1320 #ifdef IS_LITTLE_ENDIAN
  1321         aName.SetLength(strLen);
  1322         CopySwapUTF16(reinterpret_cast<const uint16_t*>(aNameData),
  1323                       reinterpret_cast<uint16_t*>(aName.BeginWriting()), strLen);
  1324 #else
  1325         aName.Assign(reinterpret_cast<const char16_t*>(aNameData), strLen);
  1326 #endif    
  1327         return true;
  1330     nsCOMPtr<nsIUnicodeDecoder> decoder =
  1331         mozilla::dom::EncodingUtils::DecoderForEncoding(csName);
  1332     if (!decoder) {
  1333         NS_WARNING("failed to get the decoder for a font name string");
  1334         return false;
  1337     int32_t destLength;
  1338     nsresult rv = decoder->GetMaxLength(aNameData, aByteLen, &destLength);
  1339     if (NS_FAILED(rv)) {
  1340         NS_WARNING("decoder->GetMaxLength failed, invalid font name?");
  1341         return false;
  1344     // make space for the converted string
  1345     aName.SetLength(destLength);
  1346     rv = decoder->Convert(aNameData, &aByteLen,
  1347                           aName.BeginWriting(), &destLength);
  1348     if (NS_FAILED(rv)) {
  1349         NS_WARNING("decoder->Convert failed, invalid font name?");
  1350         return false;
  1352     aName.Truncate(destLength); // set the actual length
  1354     return true;
  1357 nsresult
  1358 gfxFontUtils::ReadNames(const char *aNameData, uint32_t aDataLen,
  1359                         uint32_t aNameID,
  1360                         int32_t aLangID, int32_t aPlatformID,
  1361                         nsTArray<nsString>& aNames)
  1363     NS_ASSERTION(aDataLen != 0, "null name table");
  1365     if (!aDataLen) {
  1366         return NS_ERROR_FAILURE;
  1369     // -- name table data
  1370     const NameHeader *nameHeader = reinterpret_cast<const NameHeader*>(aNameData);
  1372     uint32_t nameCount = nameHeader->count;
  1374     // -- sanity check the number of name records
  1375     if (uint64_t(nameCount) * sizeof(NameRecord) > aDataLen) {
  1376         NS_WARNING("invalid font (name table data)");
  1377         return NS_ERROR_FAILURE;
  1380     // -- iterate through name records
  1381     const NameRecord *nameRecord
  1382         = reinterpret_cast<const NameRecord*>(aNameData + sizeof(NameHeader));
  1383     uint64_t nameStringsBase = uint64_t(nameHeader->stringOffset);
  1385     uint32_t i;
  1386     for (i = 0; i < nameCount; i++, nameRecord++) {
  1387         uint32_t platformID;
  1389         // skip over unwanted nameID's
  1390         if (uint32_t(nameRecord->nameID) != aNameID)
  1391             continue;
  1393         // skip over unwanted platform data
  1394         platformID = nameRecord->platformID;
  1395         if (aPlatformID != PLATFORM_ALL
  1396             && uint32_t(nameRecord->platformID) != PLATFORM_ID)
  1397             continue;
  1399         // skip over unwanted languages
  1400         if (aLangID != LANG_ALL
  1401               && uint32_t(nameRecord->languageID) != uint32_t(aLangID))
  1402             continue;
  1404         // add name to names array
  1406         // -- calculate string location
  1407         uint32_t namelen = nameRecord->length;
  1408         uint32_t nameoff = nameRecord->offset;  // offset from base of string storage
  1410         if (nameStringsBase + uint64_t(nameoff) + uint64_t(namelen)
  1411                 > aDataLen) {
  1412             NS_WARNING("invalid font (name table strings)");
  1413             return NS_ERROR_FAILURE;
  1416         // -- decode if necessary and make nsString
  1417         nsAutoString name;
  1419         DecodeFontName(aNameData + nameStringsBase + nameoff, namelen,
  1420                        platformID, uint32_t(nameRecord->encodingID),
  1421                        uint32_t(nameRecord->languageID), name);
  1423         uint32_t k, numNames;
  1424         bool foundName = false;
  1426         numNames = aNames.Length();
  1427         for (k = 0; k < numNames; k++) {
  1428             if (name.Equals(aNames[k])) {
  1429                 foundName = true;
  1430                 break;
  1434         if (!foundName)
  1435             aNames.AppendElement(name);
  1439     return NS_OK;
  1442 #ifdef XP_WIN
  1444 /* static */
  1445 bool
  1446 gfxFontUtils::IsCffFont(const uint8_t* aFontData)
  1448     // this is only called after aFontData has passed basic validation,
  1449     // so we know there is enough data present to allow us to read the version!
  1450     const SFNTHeader *sfntHeader = reinterpret_cast<const SFNTHeader*>(aFontData);
  1451     return (sfntHeader->sfntVersion == TRUETYPE_TAG('O','T','T','O'));
  1454 #endif

mercurial