bug 875629 - handle UTF-16 surrogate pairs in the SVG-in-OpenType glyphchar attribute. r=roc
authorJonathan Kew <jkew@mozilla.com>
Wed, 05 Jun 2013 12:34:48 +0100
changeset 134095 5df3bc40a38d0f26d0f9d75d5f6afd19dd52e44a
parent 134094 627af04199a50a8eac5c6afb630fe42b5de892e6
child 134096 566576fe7088cf32bc87ddb7f83f515f7c026486
push id24783
push userryanvm@gmail.com
push dateWed, 05 Jun 2013 20:30:03 +0000
treeherdermozilla-central@204de5b7e0a6 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersroc
bugs875629
milestone24.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
bug 875629 - handle UTF-16 surrogate pairs in the SVG-in-OpenType glyphchar attribute. r=roc
gfx/thebes/gfxSVGGlyphs.cpp
--- a/gfx/thebes/gfxSVGGlyphs.cpp
+++ b/gfx/thebes/gfxSVGGlyphs.cpp
@@ -398,50 +398,75 @@ gfxSVGGlyphsDocument::InsertGlyphId(Elem
 
     if (NS_FAILED(rv)) {
         return;
     }
 
     mGlyphIdMap.Put(glyphId, aGlyphElement);
 }
 
+// Get the Unicode character at index aPos in the string, and update aPos to
+// point to the next char (i.e. advance by one or two, depending whether we
+// found a surrogate pair).
+// This will assert (and return junk) if the string is not well-formed UTF16.
+// However, this is only used to process an attribute that comes from the
+// SVG-glyph XML document, and is not exposed to modification via the DOM,
+// so it must be well-formed UTF16 data (no unpaired surrogate codepoints)
+// unless our Unicode handling is seriously broken.
+static uint32_t
+NextUSV(const nsAString& aString, uint32_t& aPos)
+{
+    mozilla::DebugOnly<uint32_t> len = aString.Length();
+    NS_ASSERTION(aPos < len, "already at end of string");
+
+    uint32_t c1 = aString[aPos++];
+    if (NS_IS_HIGH_SURROGATE(c1)) {
+        NS_ASSERTION(aPos < len, "trailing high surrogate");
+        uint32_t c2 = aString[aPos++];
+        NS_ASSERTION(NS_IS_LOW_SURROGATE(c2), "isolated high surrogate");
+        return SURROGATE_TO_UCS4(c1, c2);
+    }
+
+    NS_ASSERTION(!NS_IS_LOW_SURROGATE(c1), "isolated low surrogate");
+    return c1;
+}
+
 void
-gfxSVGGlyphsDocument::InsertGlyphChar(Element *aGlyphElement, hb_blob_t *aCmapTable)
+gfxSVGGlyphsDocument::InsertGlyphChar(Element *aGlyphElement,
+                                      hb_blob_t *aCmapTable)
 {
     nsAutoString glyphChar;
-    if (!aGlyphElement->GetAttr(kNameSpaceID_None, nsGkAtoms::glyphchar, glyphChar)) {
+    if (!aGlyphElement->GetAttr(kNameSpaceID_None, nsGkAtoms::glyphchar,
+                                glyphChar)) {
         return;
     }
 
-    uint32_t varSelector;
+    uint32_t charCode, varSelector = 0, len = glyphChar.Length(), index = 0;
+    if (!len) {
+        NS_WARNING("glyphchar is empty");
+        return;
+    }
 
-    // XXX jfkthame
-    // This will not handle surrogate pairs properly!
-    switch (glyphChar.Length()) {
-        case 0:
-            NS_WARNING("glyphchar is empty");
-            return;
-        case 1:
-            varSelector = 0;
-            break;
-        case 2:
-            if (gfxFontUtils::IsVarSelector(glyphChar.CharAt(1))) {
-                varSelector = glyphChar.CharAt(1);
-                break;
-            }
-        default:
+    charCode = NextUSV(glyphChar, index);
+    if (index < len) {
+        varSelector = NextUSV(glyphChar, index);
+        if (!gfxFontUtils::IsVarSelector(varSelector)) {
             NS_WARNING("glyphchar contains more than one character");
             return;
+        }
     }
 
-    uint32_t len;
+    if (index < len) {
+        NS_WARNING("glyphchar contains more than one character");
+        return;
+    }
+
     const uint8_t *data = (const uint8_t*)hb_blob_get_data(aCmapTable, &len);
-    uint32_t glyphId = gfxFontUtils::MapCharToGlyph(data, len,
-                                                    glyphChar.CharAt(0),
-                                                    varSelector);
+    uint32_t glyphId =
+        gfxFontUtils::MapCharToGlyph(data, len, charCode, varSelector);
 
     if (glyphId) {
         mGlyphIdMap.Put(glyphId, aGlyphElement);
     }
 }
 
 void
 gfxTextObjectPaint::InitStrokeGeometry(gfxContext *aContext,