Bug 1228799 - Part 2 - If AddFontMemResourceEx rejects a downloaded font that we tried to activate, check if its Microsoft cmap subtable is tagged as symbol-encoded, and if so fix it up and re-try the font activation. r=emk a=ritu
authorJonathan Kew <jkew@mozilla.com>
Thu, 18 Aug 2016 17:22:45 +0100
changeset 350016 257de631a38c0465a05e51416b5c16c7c9ad0f7f
parent 350015 822036786a92c7674231836ceeb73e2140fcdb15
child 350017 33cc5c04357816d99c089dcbafc9f9a842eb8353
push id1230
push userjlund@mozilla.com
push dateMon, 31 Oct 2016 18:13:35 +0000
treeherdermozilla-release@5e06e3766db2 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersemk, ritu
bugs1228799
milestone50.0a2
Bug 1228799 - Part 2 - If AddFontMemResourceEx rejects a downloaded font that we tried to activate, check if its Microsoft cmap subtable is tagged as symbol-encoded, and if so fix it up and re-try the font activation. r=emk a=ritu
gfx/thebes/gfxGDIFontList.cpp
--- a/gfx/thebes/gfxGDIFontList.cpp
+++ b/gfx/thebes/gfxGDIFontList.cpp
@@ -746,16 +746,73 @@ gfxGDIFontList::LookupLocalFont(const ns
 
     // make the new font entry match the userfont entry style characteristics
     fe->mWeight = (aWeight == 0 ? 400 : aWeight);
     fe->mStyle = aStyle;
 
     return fe;
 }
 
+// If aFontData contains only a MS/Symbol cmap subtable, not MS/Unicode,
+// we modify the subtable header to mark it as Unicode instead, because
+// otherwise GDI will refuse to load the font.
+// NOTE that this function does not bounds-check every access to the font data.
+// This is OK because we only use it on data that has already been validated
+// by OTS, and therefore we will not hit out-of-bounds accesses here.
+static bool
+FixupSymbolEncodedFont(uint8_t* aFontData, uint32_t aLength)
+{
+    struct CmapHeader {
+        AutoSwap_PRUint16 version;
+        AutoSwap_PRUint16 numTables;
+    };
+    struct CmapEncodingRecord {
+        AutoSwap_PRUint16 platformID;
+        AutoSwap_PRUint16 encodingID;
+        AutoSwap_PRUint32 offset;
+    };
+    const uint32_t kCMAP = TRUETYPE_TAG('c','m','a','p');
+    const TableDirEntry* dir =
+        gfxFontUtils::FindTableDirEntry(aFontData, kCMAP);
+    if (dir && uint32_t(dir->length) >= sizeof(CmapHeader)) {
+        CmapHeader *cmap =
+            reinterpret_cast<CmapHeader*>(aFontData + uint32_t(dir->offset));
+        CmapEncodingRecord *encRec =
+            reinterpret_cast<CmapEncodingRecord*>(cmap + 1);
+        int32_t symbolSubtable = -1;
+        for (uint32_t i = 0; i < (uint16_t)cmap->numTables; ++i) {
+            if (uint16_t(encRec[i].platformID) !=
+                gfxFontUtils::PLATFORM_ID_MICROSOFT) {
+                continue; // only interested in MS platform
+            }
+            if (uint16_t(encRec[i].encodingID) ==
+                gfxFontUtils::ENCODING_ID_MICROSOFT_UNICODEBMP) {
+                // We've got a Microsoft/Unicode table, so don't interfere.
+                symbolSubtable = -1;
+                break;
+            }
+            if (uint16_t(encRec[i].encodingID) ==
+                gfxFontUtils::ENCODING_ID_MICROSOFT_SYMBOL) {
+                // Found a symbol subtable; remember it for possible fixup,
+                // but if we subsequently find a Microsoft/Unicode subtable,
+                // we'll cancel this.
+                symbolSubtable = i;
+            }
+        }
+        if (symbolSubtable != -1) {
+            // We found a windows/symbol cmap table, and no windows/unicode one;
+            // change the encoding ID so that AddFontMemResourceEx will accept it
+            encRec[symbolSubtable].encodingID =
+                gfxFontUtils::ENCODING_ID_MICROSOFT_UNICODEBMP;
+            return true;
+        }
+    }
+    return false;
+}
+
 gfxFontEntry*
 gfxGDIFontList::MakePlatformFont(const nsAString& aFontName,
                                  uint16_t aWeight,
                                  int16_t aStretch,
                                  uint8_t aStyle,
                                  const uint8_t* aFontData,
                                  uint32_t aLength)
 {
@@ -793,18 +850,24 @@ gfxGDIFontList::MakePlatformFont(const n
     uint32_t fontLength = newFontData.Length();
     NS_ASSERTION(fontData, "null font data after renaming");
 
     // http://msdn.microsoft.com/en-us/library/ms533942(VS.85).aspx
     // "A font that is added by AddFontMemResourceEx is always private 
     //  to the process that made the call and is not enumerable."
     fontRef = AddFontMemResourceEx(fontData, fontLength, 
                                     0 /* reserved */, &numFonts);
-    if (!fontRef)
+    if (!fontRef) {
+        if (FixupSymbolEncodedFont(fontData, fontLength)) {
+            fontRef = AddFontMemResourceEx(fontData, fontLength, 0, &numFonts);
+        }
+    }
+    if (!fontRef) {
         return nullptr;
+    }
 
     // only load fonts with a single face contained in the data
     // AddFontMemResourceEx generates an additional face name for
     // vertical text if the font supports vertical writing but since
     // the font is referenced via the name this can be ignored
     if (fontRef && numFonts > 2) {
         RemoveFontMemResourceEx(fontRef);
         return nullptr;