Title: [174279] trunk/Source/WebCore
Revision
174279
Author
[email protected]
Date
2014-10-03 13:10:56 -0700 (Fri, 03 Oct 2014)

Log Message

[SVG -> OTF Converter] Support non-BMP codepoints
https://bugs.webkit.org/show_bug.cgi?id=137097

Patch by Myles C. Maxfield <[email protected]> on 2014-09-28
Reviewed by Darin Adler.

This patch has two pieces:

1) Moves write16(), overwrite32() and overwrite16() inside SVGToOTFFontConverter. This
is so that we don't have to keep passing around references to m_result everywhere. Instead,
put the output vector inside the class as an instance member variable. In addition, rename
write32() and write16() to append32() and append16(), and provide a copy of append32 inside
SVGToOTFFontConverter.

2) Change the "Codepoint" typedef to be a String, and rename it to Codepoints. This is so that
we can implement ligatures later on. Update all the places that use this variable to work with
a String of multiple codepoints.

This patch updates the CMAP table to use format 12 instead of format 4. It currently only maps
single codepoints to glyph IDs and skips all multi-codepoint glyphs. We will implement
ligatures in another patch.

Test: svg/custom/glyph-selection-non-bmp.svg

* svg/SVGToOTFFontConversion.cpp:
(WebCore::append32): Append a result to a given vector. This is for the path transcoder.
(WebCore::SVGToOTFFontConverter::releaseResult): WTF::move()'s the result.
(WebCore::SVGToOTFFontConverter::GlyphData::GlyphData): Update to take Codepoints instead of Codepoint.
(WebCore::SVGToOTFFontConverter::append32): Piece 1 above.
(WebCore::SVGToOTFFontConverter::append16): Ditto.
(WebCore::SVGToOTFFontConverter::append4ByteCode): Convenience function.
(WebCore::SVGToOTFFontConverter::overwrite32): Piece 1 above.
(WebCore::SVGToOTFFontConverter::overwrite16): Ditto.
(WebCore::SVGToOTFFontConverter::appendCMAPTable): Update to format 12.
(WebCore::SVGToOTFFontConverter::appendHEADTable): Passing in a result vector is no longer necessary.
(WebCore::SVGToOTFFontConverter::appendHHEATable): Ditto.
(WebCore::SVGToOTFFontConverter::appendHMTXTable): Ditto.
(WebCore::SVGToOTFFontConverter::appendMAXPTable): Ditto.
(WebCore::SVGToOTFFontConverter::appendNAMETable): Ditto.
(WebCore::SVGToOTFFontConverter::appendOS2Table): Ditto.
(WebCore::SVGToOTFFontConverter::appendPOSTTable): Ditto.
(WebCore::isValidStringForCFF): Ditto.
(WebCore::SVGToOTFFontConverter::appendCFFValidString): Ditto.
(WebCore::SVGToOTFFontConverter::appendCFFTable): Ditto.
(WebCore::SVGToOTFFontConverter::appendVORGTable): Ditto.
(WebCore::SVGToOTFFontConverter::appendVHEATable): Ditto.
(WebCore::SVGToOTFFontConverter::appendVMTXTable): Ditto.
(WebCore::SVGToOTFFontConverter::addCodepointRanges): Update for Codepoints.
(WebCore::SVGToOTFFontConverter::addCodepoints): Ditto.
(WebCore::SVGToOTFFontConverter::addGlyphNames): Ditto.
(WebCore::SVGToOTFFontConverter::appendKERNSubtable): Ditto.
(WebCore::SVGToOTFFontConverter::appendKERNTable): Ditto.
(WebCore::writeCFFEncodedNumber): Passing in a result vector is no longer necessary.
(WebCore::SVGToOTFFontConverter::appendGlyphData): Update for Codepoints.
(WebCore::SVGToOTFFontConverter::processGlyphElement): Update for Codepoints.
(WebCore::SVGToOTFFontConverter::compareCodepointsLexicographically): Comparing Codepoints
(WebCore::SVGToOTFFontConverter::SVGToOTFFontConverter): Update for Codepoints.
(WebCore::SVGToOTFFontConverter::calculateChecksum): Passing in a result vector is no longer necessary.
(WebCore::SVGToOTFFontConverter::appendTable): Ditto.
(WebCore::SVGToOTFFontConverter::convertSVGToOTFFont): Ditto.
(WebCore::convertSVGToOTFFont): Move out of the result vector.
(WebCore::write32): Deleted.
(WebCore::write16): Deleted.
(WebCore::overwrite32): Deleted.
(WebCore::appendCFFValidString): Deleted.
(WebCore::transcodeGlyphPaths): Deleted.
(WebCore::calculateChecksum): Deleted.

Modified Paths

Diff

Modified: trunk/Source/WebCore/ChangeLog (174278 => 174279)


--- trunk/Source/WebCore/ChangeLog	2014-10-03 20:08:32 UTC (rev 174278)
+++ trunk/Source/WebCore/ChangeLog	2014-10-03 20:10:56 UTC (rev 174279)
@@ -1,3 +1,72 @@
+2014-09-28  Myles C. Maxfield  <[email protected]>
+
+        [SVG -> OTF Converter] Support non-BMP codepoints
+        https://bugs.webkit.org/show_bug.cgi?id=137097
+
+        Reviewed by Darin Adler.
+
+        This patch has two pieces:
+
+        1) Moves write16(), overwrite32() and overwrite16() inside SVGToOTFFontConverter. This
+        is so that we don't have to keep passing around references to m_result everywhere. Instead,
+        put the output vector inside the class as an instance member variable. In addition, rename
+        write32() and write16() to append32() and append16(), and provide a copy of append32 inside
+        SVGToOTFFontConverter.
+
+        2) Change the "Codepoint" typedef to be a String, and rename it to Codepoints. This is so that
+        we can implement ligatures later on. Update all the places that use this variable to work with
+        a String of multiple codepoints.
+
+        This patch updates the CMAP table to use format 12 instead of format 4. It currently only maps
+        single codepoints to glyph IDs and skips all multi-codepoint glyphs. We will implement
+        ligatures in another patch.
+
+        Test: svg/custom/glyph-selection-non-bmp.svg
+
+        * svg/SVGToOTFFontConversion.cpp:
+        (WebCore::append32): Append a result to a given vector. This is for the path transcoder.
+        (WebCore::SVGToOTFFontConverter::releaseResult): WTF::move()'s the result.
+        (WebCore::SVGToOTFFontConverter::GlyphData::GlyphData): Update to take Codepoints instead of Codepoint.
+        (WebCore::SVGToOTFFontConverter::append32): Piece 1 above.
+        (WebCore::SVGToOTFFontConverter::append16): Ditto.
+        (WebCore::SVGToOTFFontConverter::append4ByteCode): Convenience function.
+        (WebCore::SVGToOTFFontConverter::overwrite32): Piece 1 above.
+        (WebCore::SVGToOTFFontConverter::overwrite16): Ditto.
+        (WebCore::SVGToOTFFontConverter::appendCMAPTable): Update to format 12.
+        (WebCore::SVGToOTFFontConverter::appendHEADTable): Passing in a result vector is no longer necessary.
+        (WebCore::SVGToOTFFontConverter::appendHHEATable): Ditto.
+        (WebCore::SVGToOTFFontConverter::appendHMTXTable): Ditto.
+        (WebCore::SVGToOTFFontConverter::appendMAXPTable): Ditto.
+        (WebCore::SVGToOTFFontConverter::appendNAMETable): Ditto.
+        (WebCore::SVGToOTFFontConverter::appendOS2Table): Ditto.
+        (WebCore::SVGToOTFFontConverter::appendPOSTTable): Ditto.
+        (WebCore::isValidStringForCFF): Ditto.
+        (WebCore::SVGToOTFFontConverter::appendCFFValidString): Ditto.
+        (WebCore::SVGToOTFFontConverter::appendCFFTable): Ditto.
+        (WebCore::SVGToOTFFontConverter::appendVORGTable): Ditto.
+        (WebCore::SVGToOTFFontConverter::appendVHEATable): Ditto.
+        (WebCore::SVGToOTFFontConverter::appendVMTXTable): Ditto.
+        (WebCore::SVGToOTFFontConverter::addCodepointRanges): Update for Codepoints.
+        (WebCore::SVGToOTFFontConverter::addCodepoints): Ditto.
+        (WebCore::SVGToOTFFontConverter::addGlyphNames): Ditto.
+        (WebCore::SVGToOTFFontConverter::appendKERNSubtable): Ditto.
+        (WebCore::SVGToOTFFontConverter::appendKERNTable): Ditto.
+        (WebCore::writeCFFEncodedNumber): Passing in a result vector is no longer necessary.
+        (WebCore::SVGToOTFFontConverter::appendGlyphData): Update for Codepoints.
+        (WebCore::SVGToOTFFontConverter::processGlyphElement): Update for Codepoints.
+        (WebCore::SVGToOTFFontConverter::compareCodepointsLexicographically): Comparing Codepoints
+        (WebCore::SVGToOTFFontConverter::SVGToOTFFontConverter): Update for Codepoints.
+        (WebCore::SVGToOTFFontConverter::calculateChecksum): Passing in a result vector is no longer necessary.
+        (WebCore::SVGToOTFFontConverter::appendTable): Ditto.
+        (WebCore::SVGToOTFFontConverter::convertSVGToOTFFont): Ditto.
+        (WebCore::convertSVGToOTFFont): Move out of the result vector.
+        (WebCore::write32): Deleted.
+        (WebCore::write16): Deleted.
+        (WebCore::overwrite32): Deleted.
+        (WebCore::appendCFFValidString): Deleted.
+        (WebCore::transcodeGlyphPaths): Deleted.
+        (WebCore::calculateChecksum): Deleted.
+
 2014-10-03  Commit Queue  <[email protected]>
 
         Unreviewed, rolling out r174270.

Modified: trunk/Source/WebCore/svg/SVGToOTFFontConversion.cpp (174278 => 174279)


--- trunk/Source/WebCore/svg/SVGToOTFFontConversion.cpp	2014-10-03 20:08:32 UTC (rev 174278)
+++ trunk/Source/WebCore/svg/SVGToOTFFontConversion.cpp	2014-10-03 20:10:56 UTC (rev 174279)
@@ -40,60 +40,41 @@
 
 namespace WebCore {
 
-static inline void write32(Vector<char>& vector, uint32_t value)
+static inline void append32(Vector<char>& result, uint32_t value)
 {
-    vector.append(value >> 24);
-    vector.append(value >> 16);
-    vector.append(value >> 8);
-    vector.append(value);
+    result.append(value >> 24);
+    result.append(value >> 16);
+    result.append(value >> 8);
+    result.append(value);
 }
 
-static inline void write16(Vector<char>& vector, uint16_t value)
-{
-    vector.append(value >> 8);
-    vector.append(value);
-}
-
-static inline void overwrite32(Vector<char>& vector, unsigned location, uint32_t value)
-{
-    ASSERT(vector.size() >= location + 4);
-    vector[location] = value >> 24;
-    vector[location + 1] = value >> 16;
-    vector[location + 2] = value >> 8;
-    vector[location + 3] = value;
-}
-
-static inline void overwrite16(Vector<char>& vector, unsigned location, uint16_t value)
-{
-    ASSERT(vector.size() >= location + 2);
-    vector[location] = value >> 8;
-    vector[location + 1] = value;
-}
-
 class SVGToOTFFontConverter {
 public:
     SVGToOTFFontConverter(const SVGFontElement&);
-    Vector<char> convertSVGToOTFFont();
+    void convertSVGToOTFFont();
 
+    Vector<char> releaseResult()
+    {
+        return WTF::move(m_result);
+    }
+
 private:
-    typedef UChar Codepoint; // FIXME: Only support BMP for now
-
     struct GlyphData {
-        GlyphData(Vector<char>&& charString, const SVGGlyphElement* glyphElement, float horizontalAdvance, float verticalAdvance, FloatRect boundingBox, Codepoint codepoint)
+        GlyphData(Vector<char>&& charString, const SVGGlyphElement* glyphElement, float horizontalAdvance, float verticalAdvance, FloatRect boundingBox, const String& codepoints)
             : boundingBox(boundingBox)
             , charString(charString)
+            , codepoints(codepoints)
             , glyphElement(glyphElement)
             , horizontalAdvance(horizontalAdvance)
             , verticalAdvance(verticalAdvance)
-            , codepoint(codepoint)
         {
         }
         FloatRect boundingBox;
         Vector<char> charString;
+        String codepoints;
         const SVGGlyphElement* glyphElement;
         float horizontalAdvance;
         float verticalAdvance;
-        Codepoint codepoint;
     };
 
     struct KerningData {
@@ -108,39 +89,81 @@
         int16_t adjustment;
     };
 
+    void append32(uint32_t value)
+    {
+        WebCore::append32(m_result, value);
+    }
+
+    void append32BitCode(const char code[4])
+    {
+        m_result.append(code[0]);
+        m_result.append(code[1]);
+        m_result.append(code[2]);
+        m_result.append(code[3]);
+    }
+
+    void append16(uint16_t value)
+    {
+        m_result.append(value >> 8);
+        m_result.append(value);
+    }
+
+    void overwrite32(unsigned location, uint32_t value)
+    {
+        ASSERT(m_result.size() >= location + 4);
+        m_result[location] = value >> 24;
+        m_result[location + 1] = value >> 16;
+        m_result[location + 2] = value >> 8;
+        m_result[location + 3] = value;
+    }
+
+    void overwrite16(unsigned location, uint16_t value)
+    {
+        ASSERT(m_result.size() >= location + 2);
+        m_result[location] = value >> 8;
+        m_result[location + 1] = value;
+    }
+
     static const size_t headerSize = 12;
     static const size_t directoryEntrySize = 16;
 
-    void processGlyphElement(const SVGElement& glyphOrMissingGlyphElement, const SVGGlyphElement*, float defaultHorizontalAdvance, float defaultVerticalAdvance, Codepoint, bool& initialGlyph);
+    uint32_t calculateChecksum(size_t startingOffset, size_t endingOffset) const;
 
-    typedef void (SVGToOTFFontConverter::*FontAppendingFunction)(Vector<char> &) const;
-    void appendTable(const char identifier[4], Vector<char>&, FontAppendingFunction);
-    void appendCMAPTable(Vector<char>&) const;
-    void appendHEADTable(Vector<char>&) const;
-    void appendHHEATable(Vector<char>&) const;
-    void appendHMTXTable(Vector<char>&) const;
-    void appendVHEATable(Vector<char>&) const;
-    void appendVMTXTable(Vector<char>&) const;
-    void appendKERNTable(Vector<char>&) const;
-    void appendMAXPTable(Vector<char>&) const;
-    void appendNAMETable(Vector<char>&) const;
-    void appendOS2Table(Vector<char>&) const;
-    void appendPOSTTable(Vector<char>&) const;
-    void appendCFFTable(Vector<char>&) const;
-    void appendVORGTable(Vector<char>&) const;
+    void processGlyphElement(const SVGElement& glyphOrMissingGlyphElement, const SVGGlyphElement*, float defaultHorizontalAdvance, float defaultVerticalAdvance, const String& codepoints, bool& initialGlyph);
 
+    typedef void (SVGToOTFFontConverter::*FontAppendingFunction)();
+    void appendTable(const char identifier[4], FontAppendingFunction);
+    void appendCMAPTable();
+    void appendHEADTable();
+    void appendHHEATable();
+    void appendHMTXTable();
+    void appendVHEATable();
+    void appendVMTXTable();
+    void appendKERNTable();
+    void appendMAXPTable();
+    void appendNAMETable();
+    void appendOS2Table();
+    void appendPOSTTable();
+    void appendCFFTable();
+    void appendVORGTable();
+
+    static bool compareCodepointsLexicographically(const GlyphData&, const GlyphData&);
+
+    void appendValidCFFString(const String&);
+
     Vector<char> transcodeGlyphPaths(float width, const SVGElement& glyphOrMissingGlyphElement, FloatRect& boundingBox) const;
 
-    void addCodepointRanges(const UnicodeRanges&, HashSet<uint16_t>& glyphSet) const;
-    void addCodepoints(const HashSet<String>& codepoints, HashSet<uint16_t>& glyphSet) const;
-    void addGlyphNames(const HashSet<String>& glyphNames, HashSet<uint16_t>& glyphSet) const;
+    void addCodepointRanges(const UnicodeRanges&, HashSet<Glyph>& glyphSet) const;
+    void addCodepoints(const HashSet<String>& codepoints, HashSet<Glyph>& glyphSet) const;
+    void addGlyphNames(const HashSet<String>& glyphNames, HashSet<Glyph>& glyphSet) const;
     void addKerningPair(Vector<KerningData>&, const SVGKerningPair&) const;
-    template<typename T> size_t appendKERNSubtable(Vector<char>& result, bool (T::*buildKerningPair)(SVGKerningPair&) const, uint16_t coverage) const;
-    size_t finishAppendingKERNSubtable(Vector<char>& result, Vector<KerningData>, uint16_t coverage) const;
+    template<typename T> size_t appendKERNSubtable(bool (T::*buildKerningPair)(SVGKerningPair&) const, uint16_t coverage);
+    size_t finishAppendingKERNSubtable(Vector<KerningData>, uint16_t coverage);
 
     Vector<GlyphData> m_glyphs;
     HashMap<String, Glyph> m_glyphNameToIndexMap; // SVG 1.1: "It is recommended that glyph names be unique within a font."
-    HashMap<UChar32, Glyph> m_codepointToIndexMap; // FIXME: There might be many glyphs that map to a single codepoint.
+    HashMap<String, Vector<Glyph, 1>> m_codepointsToIndicesMap;
+    Vector<char> m_result;
     FloatRect m_boundingBox;
     const SVGFontElement& m_fontElement;
     const SVGFontFaceElement* m_fontFaceElement;
@@ -172,68 +195,73 @@
     return result;
 }
 
-void SVGToOTFFontConverter::appendCMAPTable(Vector<char>& result) const
+void SVGToOTFFontConverter::appendCMAPTable()
 {
-    auto startingOffset = result.size();
-    write16(result, 0);
-    write16(result, 1); // Number subtables
+    auto startingOffset = m_result.size();
+    append16(0);
+    append16(1); // Number subtables
 
-    write16(result, 0); // Unicode
-    write16(result, 3); // Unicode version 2.2+
-    write32(result, result.size() - startingOffset + sizeof(uint32_t)); // Byte offset of subtable
+    append16(0); // Unicode
+    append16(3); // Unicode version 2.2+
+    append32(m_result.size() - startingOffset + sizeof(uint32_t)); // Byte offset of subtable
 
     // Braindead scheme: One segment for each character
     ASSERT(m_glyphs.size() < 0xFFFF);
-    uint16_t segCount = m_glyphs.size() + 1;
-    write16(result, 4); // Format: Only support the Basic Multilingual Plane for now
-    write16(result, 22 + m_glyphs.size() * 2); // length
-    write16(result, 0); // Language independent
-    write16(result, 2 * segCount); // segCountX2: "2 x segCount"
-    uint16_t originalSearchRange = roundDownToPowerOfTwo(segCount);
-    uint16_t searchRange = 2 * originalSearchRange; // searchRange: "2 x (2**floor(log2(segCount)))"
-    write16(result, searchRange);
-    write16(result, integralLog2(originalSearchRange)); // entrySelector: "log2(searchRange/2)"
-    write16(result, (2 * segCount) - searchRange); // rangeShift: "2 x segCount - searchRange"
+    auto subtableLocation = m_result.size();
+    append32(12 << 16); // Format 12
+    append32(0); // Placeholder for byte length
+    append32(0); // Language independent
+    append32(0); // Placeholder for nGroups
 
-    for (auto& glyph : m_glyphs)
-        write16(result, glyph.codepoint); // EndCode
-    write16(result, 0xFFFF); // "To ensure that the search will terminate, the final endCode value must be 0xFFFF."
-    write16(result, 0); // Reserved
-    for (auto& glyph : m_glyphs)
-        write16(result, glyph.codepoint); // startCode
-    write16(result, 0xFFFF);
-    for (Glyph i = 0; i < m_glyphs.size(); ++i) {
-        // Note that this value can be "negative," but that is okay because wrapping is defined and expected here
-        write16(result, i - m_glyphs[i].codepoint); // idDelta
+    uint16_t nGroups = 0;
+    UChar32 previousCodepoint = std::numeric_limits<UChar32>::max();
+    for (size_t i = 0; i < m_glyphs.size(); ++i) {
+        auto& glyph = m_glyphs[i];
+        unsigned codepointSize = 0;
+        UChar32 codepoint;
+        if (glyph.codepoints.isNull())
+            codepoint = 0;
+        else if (glyph.codepoints.is8Bit())
+            codepoint = glyph.codepoints[codepointSize++];
+        else
+            U16_NEXT(glyph.codepoints.characters16(), codepointSize, glyph.codepoints.length(), codepoint);
+
+        // Don't map ligatures here.
+        if (glyph.codepoints.length() > codepointSize || codepoint == previousCodepoint)
+            continue;
+
+        append32(codepoint); // startCharCode
+        append32(codepoint); // endCharCode
+        append32(i); // startGlyphCode
+        ++nGroups;
+        previousCodepoint = codepoint;
     }
-    write16(result, 1);
-    for (unsigned i = 0; i < m_glyphs.size(); ++i)
-        write16(result, 0); // idRangeOffset
-    write16(result, 0);
+    overwrite32(subtableLocation + 4, m_result.size() - subtableLocation);
+    overwrite32(subtableLocation + 12, nGroups);
 }
 
-void SVGToOTFFontConverter::appendHEADTable(Vector<char>& result) const
+void SVGToOTFFontConverter::appendHEADTable()
 {
-    write32(result, 0x00010000); // Version
-    write32(result, 0x00010000); // Revision
-    write32(result, 0); // Checksum placeholder; to be overwritten by the caller.
-    write32(result, 0x5F0F3CF5); // Magic number.
-    write16(result, (1 << 9) | 1);
+    append32(0x00010000); // Version
+    append32(0x00010000); // Revision
+    append32(0); // Checksum placeholder; to be overwritten by the caller.
+    append32(0x5F0F3CF5); // Magic number.
+    append16((1 << 9) | 1);
 
-    write16(result, m_unitsPerEm);
-    write32(result, 0); // First half of creation date
-    write32(result, 0); // Last half of creation date
-    write32(result, 0); // First half of modification date
-    write32(result, 0); // Last half of modification date
-    write16(result, m_boundingBox.x()); // Minimum X
-    write16(result, m_boundingBox.y()); // Minimum Y
-    write16(result, m_boundingBox.maxX()); // Maximum X
-    write16(result, m_boundingBox.maxY()); // Maximum Y
-    write16(result, (m_italic ? 1 << 1 : 0) | (m_weight >= 7 ? 1 : 0));
-    write16(result, 3); // Smallest readable size in pixels
-    write16(result, 0); // Might contain LTR or RTL glyphs
-    write16(result, 0); // Short offsets in the 'loca' table. However, OTF fonts don't have a 'loca' table so this is irrelevant
-    write16(result, 0); // Glyph data format
+    append16(m_unitsPerEm);
+    append32(0); // First half of creation date
+    append32(0); // Last half of creation date
+    append32(0); // First half of modification date
+    append32(0); // Last half of modification date
+    append16(m_boundingBox.x()); // Minimum X
+    append16(m_boundingBox.y()); // Minimum Y
+    append16(m_boundingBox.maxX()); // Maximum X
+    append16(m_boundingBox.maxY()); // Maximum Y
+    append16((m_italic ? 1 << 1 : 0) | (m_weight >= 7 ? 1 : 0));
+    append16(3); // Smallest readable size in pixels
+    append16(0); // Might contain LTR or RTL glyphs
+    append16(0); // Short offsets in the 'loca' table. However, OTF fonts don't have a 'loca' table so this is irrelevant
+    append16(0); // Glyph data format
 }
 
 // Assumption: T2 can hold every value that a T1 can hold.
@@ -244,13 +272,13 @@
     return static_cast<T1>(x);
 }
 
-void SVGToOTFFontConverter::appendHHEATable(Vector<char>& result) const
+void SVGToOTFFontConverter::appendHHEATable()
 {
     int16_t ascent;
     int16_t descent;
     if (!m_fontFaceElement) {
-        ascent = std::numeric_limits<int16_t>::max();
-        descent = std::numeric_limits<int16_t>::max();
+        ascent = m_unitsPerEm;
+        descent = 1;
     } else {
         ascent = m_fontFaceElement->ascent();
         descent = m_fontFaceElement->descent();
@@ -264,70 +292,70 @@
             descent = 1;
     }
 
-    write32(result, 0x00010000); // Version
-    write16(result, ascent);
-    write16(result, descent);
+    append32(0x00010000); // Version
+    append16(ascent);
+    append16(descent);
     // WebKit SVG font rendering has hard coded the line gap to be 1/10th of the font size since 2008 (see r29719).
-    write16(result, m_unitsPerEm / 10); // Line gap
-    write16(result, clampTo<uint16_t>(m_advanceWidthMax));
-    write16(result, clampTo<int16_t>(m_boundingBox.x())); // Minimum left side bearing
-    write16(result, clampTo<int16_t>(m_minRightSideBearing)); // Minimum right side bearing
-    write16(result, clampTo<int16_t>(m_boundingBox.maxX())); // X maximum extent
+    append16(m_unitsPerEm / 10); // Line gap
+    append16(clampTo<uint16_t>(m_advanceWidthMax));
+    append16(clampTo<int16_t>(m_boundingBox.x())); // Minimum left side bearing
+    append16(clampTo<int16_t>(m_minRightSideBearing)); // Minimum right side bearing
+    append16(clampTo<int16_t>(m_boundingBox.maxX())); // X maximum extent
     // Since WebKit draws the caret and ignores the following values, it doesn't matter what we set them to.
-    write16(result, 1); // Vertical caret
-    write16(result, 0); // Vertical caret
-    write16(result, 0); // "Set value to 0 for non-slanted fonts"
-    write32(result, 0); // Reserved
-    write32(result, 0); // Reserved
-    write16(result, 0); // Current format
-    write16(result, m_glyphs.size()); // Number of advance widths in HMTX table
+    append16(1); // Vertical caret
+    append16(0); // Vertical caret
+    append16(0); // "Set value to 0 for non-slanted fonts"
+    append32(0); // Reserved
+    append32(0); // Reserved
+    append16(0); // Current format
+    append16(m_glyphs.size()); // Number of advance widths in HMTX table
 }
 
-void SVGToOTFFontConverter::appendHMTXTable(Vector<char>& result) const
+void SVGToOTFFontConverter::appendHMTXTable()
 {
     for (auto& glyph : m_glyphs) {
-        write16(result, clampTo<uint16_t>(glyph.horizontalAdvance));
-        write16(result, clampTo<int16_t>(glyph.boundingBox.x()));
+        append16(clampTo<uint16_t>(glyph.horizontalAdvance));
+        append16(clampTo<int16_t>(glyph.boundingBox.x()));
     }
 }
 
-void SVGToOTFFontConverter::appendMAXPTable(Vector<char>& result) const
+void SVGToOTFFontConverter::appendMAXPTable()
 {
-    write32(result, 0x00010000); // Version
-    write16(result, m_glyphs.size());
-    write16(result, 0xFFFF); // Maximum number of points in non-compound glyph
-    write16(result, 0xFFFF); // Maximum number of contours in non-compound glyph
-    write16(result, 0xFFFF); // Maximum number of points in compound glyph
-    write16(result, 0xFFFF); // Maximum number of contours in compound glyph
-    write16(result, 2); // Maximum number of zones
-    write16(result, 0); // Maximum number of points used in zone 0
-    write16(result, 0); // Maximum number of storage area locations
-    write16(result, 0); // Maximum number of function definitions
-    write16(result, 0); // Maximum number of instruction definitions
-    write16(result, 0); // Maximum stack depth
-    write16(result, 0); // Maximum size of instructions
-    write16(result, m_glyphs.size()); // Maximum number of glyphs referenced at top level
-    write16(result, 0); // No compound glyphs
+    append32(0x00010000); // Version
+    append16(m_glyphs.size());
+    append16(0xFFFF); // Maximum number of points in non-compound glyph
+    append16(0xFFFF); // Maximum number of contours in non-compound glyph
+    append16(0xFFFF); // Maximum number of points in compound glyph
+    append16(0xFFFF); // Maximum number of contours in compound glyph
+    append16(2); // Maximum number of zones
+    append16(0); // Maximum number of points used in zone 0
+    append16(0); // Maximum number of storage area locations
+    append16(0); // Maximum number of function definitions
+    append16(0); // Maximum number of instruction definitions
+    append16(0); // Maximum stack depth
+    append16(0); // Maximum size of instructions
+    append16(m_glyphs.size()); // Maximum number of glyphs referenced at top level
+    append16(0); // No compound glyphs
 }
 
-void SVGToOTFFontConverter::appendNAMETable(Vector<char>& result) const
+void SVGToOTFFontConverter::appendNAMETable()
 {
-    write16(result, 0); // Format selector
-    write16(result, 1); // Number of name records in table
-    write16(result, 18); // Offset in bytes to the beginning of name character strings
+    append16(0); // Format selector
+    append16(1); // Number of name records in table
+    append16(18); // Offset in bytes to the beginning of name character strings
 
-    write16(result, 0); // Unicode
-    write16(result, 3); // Unicode version 2.0 or later
-    write16(result, 0); // Language
-    write16(result, 1); // Name identifier. 1 = Font family
-    write16(result, m_fontFamily.length());
-    write16(result, 0); // Offset into name data
+    append16(0); // Unicode
+    append16(3); // Unicode version 2.0 or later
+    append16(0); // Language
+    append16(1); // Name identifier. 1 = Font family
+    append16(m_fontFamily.length());
+    append16(0); // Offset into name data
 
-    for (unsigned i = 0; i < m_fontFamily.length(); ++i)
-        write16(result, m_fontFamily[i]);
+    for (auto codeUnit : StringView(m_fontFamily).codeUnits())
+        append16(codeUnit);
 }
 
-void SVGToOTFFontConverter::appendOS2Table(Vector<char>& result) const
+void SVGToOTFFontConverter::appendOS2Table()
 {
     int16_t averageAdvance = m_unitsPerEm;
     bool ok;
@@ -337,23 +365,23 @@
     if (ok)
         averageAdvance = clampTo<int16_t>(value);
 
-    write16(result, 0); // Version
-    write16(result, averageAdvance);
-    write16(result, m_weight); // Weight class
-    write16(result, 5); // Width class
-    write16(result, 0); // Protected font
+    append16(0); // Version
+    append16(averageAdvance);
+    append16(m_weight); // Weight class
+    append16(5); // Width class
+    append16(0); // Protected font
     // WebKit handles these superscripts and subscripts
-    write16(result, 0); // Subscript X Size
-    write16(result, 0); // Subscript Y Size
-    write16(result, 0); // Subscript X Offset
-    write16(result, 0); // Subscript Y Offset
-    write16(result, 0); // Superscript X Size
-    write16(result, 0); // Superscript Y Size
-    write16(result, 0); // Superscript X Offset
-    write16(result, 0); // Superscript Y Offset
-    write16(result, 0); // Strikeout width
-    write16(result, 0); // Strikeout Position
-    write16(result, 0); // No classification
+    append16(0); // Subscript X Size
+    append16(0); // Subscript Y Size
+    append16(0); // Subscript X Offset
+    append16(0); // Subscript Y Offset
+    append16(0); // Superscript X Size
+    append16(0); // Superscript Y Size
+    append16(0); // Superscript X Offset
+    append16(0); // Superscript Y Offset
+    append16(0); // Strikeout width
+    append16(0); // Strikeout Position
+    append16(0); // No classification
 
     unsigned numPanoseBytes = 0;
     const unsigned panoseSize = 10;
@@ -372,54 +400,54 @@
     }
     if (numPanoseBytes != panoseSize)
         memset(panoseBytes, 0, panoseSize);
-    result.append(panoseBytes, panoseSize);
+    m_result.append(panoseBytes, panoseSize);
 
     for (int i = 0; i < 4; ++i)
-        write32(result, 0); // "Bit assignments are pending. Set to 0"
-    write32(result, 0x544B4257); // Font Vendor. "WBKT"
-    write16(result, (m_weight >= 7 ? 1 << 5 : 0) | (m_italic ? 1 : 0)); // Font Patterns.
-    write16(result, m_glyphs.first().codepoint); // First unicode index
-    write16(result, m_glyphs.last().codepoint); // Last unicode index
+        append32(0); // "Bit assignments are pending. Set to 0"
+    append32(0x544B4257); // Font Vendor. "WBKT"
+    append16((m_weight >= 7 ? 1 << 5 : 0) | (m_italic ? 1 : 0)); // Font Patterns.
+    append16(0); // First unicode index
+    append16(0xFFFF); // Last unicode index
 }
 
-void SVGToOTFFontConverter::appendPOSTTable(Vector<char>& result) const
+void SVGToOTFFontConverter::appendPOSTTable()
 {
-    write32(result, 0x00030000); // Format. Printing is undefined
-    write32(result, 0); // Italic angle in degrees
-    write16(result, 0); // Underline position
-    write16(result, 0); // Underline thickness
-    write32(result, 0); // Monospaced
-    write32(result, 0); // "Minimum memory usage when a TrueType font is downloaded as a Type 42 font"
-    write32(result, 0); // "Maximum memory usage when a TrueType font is downloaded as a Type 42 font"
-    write32(result, 0); // "Minimum memory usage when a TrueType font is downloaded as a Type 1 font"
-    write32(result, 0); // "Maximum memory usage when a TrueType font is downloaded as a Type 1 font"
+    append32(0x00030000); // Format. Printing is undefined
+    append32(0); // Italic angle in degrees
+    append16(0); // Underline position
+    append16(0); // Underline thickness
+    append32(0); // Monospaced
+    append32(0); // "Minimum memory usage when a TrueType font is downloaded as a Type 42 font"
+    append32(0); // "Maximum memory usage when a TrueType font is downloaded as a Type 42 font"
+    append32(0); // "Minimum memory usage when a TrueType font is downloaded as a Type 1 font"
+    append32(0); // "Maximum memory usage when a TrueType font is downloaded as a Type 1 font"
 }
 
 static bool isValidStringForCFF(const String& string)
 {
-    for (unsigned i = 0; i < string.length(); ++i) {
-        if (string[i] < 33 || string[i] > 126)
+    for (auto c : StringView(string).codeUnits()) {
+        if (c < 33 || c > 126)
             return false;
     }
     return true;
 }
 
-static void appendValidCFFString(Vector<char>& output, const String& string)
+void SVGToOTFFontConverter::appendValidCFFString(const String& string)
 {
     ASSERT(isValidStringForCFF(string));
-    for (unsigned i = 0; i < string.length(); ++i)
-        output.append(string[i]);
+    for (auto c : StringView(string).codeUnits())
+        m_result.append(c);
 }
 
-void SVGToOTFFontConverter::appendCFFTable(Vector<char>& result) const
+void SVGToOTFFontConverter::appendCFFTable()
 {
-    auto startingOffset = result.size();
+    auto startingOffset = m_result.size();
 
     // Header
-    result.append(1); // Major version
-    result.append(0); // Minor version
-    result.append(4); // Header size
-    result.append(4); // Offsets within CFF table are 4 bytes long
+    m_result.append(1); // Major version
+    m_result.append(0); // Minor version
+    m_result.append(4); // Header size
+    m_result.append(4); // Offsets within CFF table are 4 bytes long
 
     // Name INDEX
     String fontName;
@@ -429,11 +457,11 @@
         if (isValidStringForCFF(potentialFontName))
             fontName = potentialFontName;
     }
-    write16(result, 1); // INDEX contains 1 element
-    result.append(4); // Offsets in this INDEX are 4 bytes long
-    write32(result, 1); // 1-index offset of name data
-    write32(result, fontName.length() + 1); // 1-index offset just past end of name data
-    appendValidCFFString(result, fontName);
+    append16(1); // INDEX contains 1 element
+    m_result.append(4); // Offsets in this INDEX are 4 bytes long
+    append32(1); // 1-index offset of name data
+    append32(fontName.length() + 1); // 1-index offset just past end of name data
+    appendValidCFFString(fontName);
 
     String weight;
     if (m_fontFaceElement) {
@@ -455,131 +483,131 @@
     const unsigned sizeOfTopIndex = 45 + (hasWeight ? 6 : 0);
 
     // Top DICT INDEX.
-    write16(result, 1); // INDEX contains 1 element
-    result.append(4); // Offsets in this INDEX are 4 bytes long
-    write32(result, 1); // 1-index offset of DICT data
-    write32(result, 1 + sizeOfTopIndex); // 1-index offset just past end of DICT data
+    append16(1); // INDEX contains 1 element
+    m_result.append(4); // Offsets in this INDEX are 4 bytes long
+    append32(1); // 1-index offset of DICT data
+    append32(1 + sizeOfTopIndex); // 1-index offset just past end of DICT data
 
     // DICT information
 #if !ASSERT_DISABLED
-    unsigned topDictStart = result.size();
+    unsigned topDictStart = m_result.size();
 #endif
-    result.append(operand32Bit);
-    write32(result, userDefinedStringStartIndex);
-    result.append(fullNameKey);
-    result.append(operand32Bit);
-    write32(result, userDefinedStringStartIndex);
-    result.append(familyNameKey);
+    m_result.append(operand32Bit);
+    append32(userDefinedStringStartIndex);
+    m_result.append(fullNameKey);
+    m_result.append(operand32Bit);
+    append32(userDefinedStringStartIndex);
+    m_result.append(familyNameKey);
     if (hasWeight) {
-        result.append(operand32Bit);
-        write32(result, userDefinedStringStartIndex + 1);
-        result.append(weightKey);
+        m_result.append(operand32Bit);
+        append32(userDefinedStringStartIndex + 1);
+        m_result.append(weightKey);
     }
-    result.append(operand32Bit);
-    write32(result, clampTo<int32_t>(m_boundingBox.x()));
-    result.append(operand32Bit);
-    write32(result, clampTo<int32_t>(m_boundingBox.maxX()));
-    result.append(operand32Bit);
-    write32(result, clampTo<int32_t>(m_boundingBox.y()));
-    result.append(operand32Bit);
-    write32(result, clampTo<int32_t>(m_boundingBox.maxY()));
-    result.append(fontBBoxKey);
-    result.append(operand32Bit);
-    unsigned charsetOffsetLocation = result.size();
-    write32(result, 0); // Offset of Charset info. Will be overwritten later.
-    result.append(charsetIndexKey);
-    result.append(operand32Bit);
-    unsigned charstringsOffsetLocation = result.size();
-    write32(result, 0); // Offset of CharStrings INDEX. Will be overwritten later.
-    result.append(charstringsIndexKey);
-    ASSERT(result.size() == topDictStart + sizeOfTopIndex);
+    m_result.append(operand32Bit);
+    append32(clampTo<int32_t>(m_boundingBox.x()));
+    m_result.append(operand32Bit);
+    append32(clampTo<int32_t>(m_boundingBox.maxX()));
+    m_result.append(operand32Bit);
+    append32(clampTo<int32_t>(m_boundingBox.y()));
+    m_result.append(operand32Bit);
+    append32(clampTo<int32_t>(m_boundingBox.maxY()));
+    m_result.append(fontBBoxKey);
+    m_result.append(operand32Bit);
+    unsigned charsetOffsetLocation = m_result.size();
+    append32(0); // Offset of Charset info. Will be overwritten later.
+    m_result.append(charsetIndexKey);
+    m_result.append(operand32Bit);
+    unsigned charstringsOffsetLocation = m_result.size();
+    append32(0); // Offset of CharStrings INDEX. Will be overwritten later.
+    m_result.append(charstringsIndexKey);
+    ASSERT(m_result.size() == topDictStart + sizeOfTopIndex);
 
     // String INDEX
-    write16(result, 1 + (hasWeight ? 1 : 0)); // Number of elements in INDEX
-    result.append(4); // Offsets in this INDEX are 4 bytes long
+    append16(1 + (hasWeight ? 1 : 0)); // Number of elements in INDEX
+    m_result.append(4); // Offsets in this INDEX are 4 bytes long
     uint32_t offset = 1;
-    write32(result, offset);
+    append32(offset);
     offset += fontName.length();
-    write32(result, offset);
+    append32(offset);
     if (hasWeight) {
         offset += weight.length();
-        write32(result, offset);
+        append32(offset);
     }
-    appendValidCFFString(result, fontName);
-    appendValidCFFString(result, weight);
+    appendValidCFFString(fontName);
+    appendValidCFFString(weight);
 
-    write16(result, 0); // Empty subroutine INDEX
+    append16(0); // Empty subroutine INDEX
 
     // Charset info
-    overwrite32(result, charsetOffsetLocation, result.size() - startingOffset);
-    result.append(0);
+    overwrite32(charsetOffsetLocation, m_result.size() - startingOffset);
+    m_result.append(0);
     for (Glyph i = 1; i < m_glyphs.size(); ++i)
-        write16(result, i);
+        append16(i);
 
     // CharStrings INDEX
-    overwrite32(result, charstringsOffsetLocation, result.size() - startingOffset);
-    write16(result, m_glyphs.size());
-    result.append(4); // Offsets in this INDEX are 4 bytes long
+    overwrite32(charstringsOffsetLocation, m_result.size() - startingOffset);
+    append16(m_glyphs.size());
+    m_result.append(4); // Offsets in this INDEX are 4 bytes long
     offset = 1;
-    write32(result, offset);
+    append32(offset);
     for (auto& glyph : m_glyphs) {
         offset += glyph.charString.size();
-        write32(result, offset);
+        append32(offset);
     }
     for (auto& glyph : m_glyphs)
-        result.appendVector(glyph.charString);
+        m_result.appendVector(glyph.charString);
 }
 
-void SVGToOTFFontConverter::appendVORGTable(Vector<char>& result) const
+void SVGToOTFFontConverter::appendVORGTable()
 {
-    write16(result, 1); // Major version
-    write16(result, 0); // Minor version
+    append16(1); // Major version
+    append16(0); // Minor version
 
     bool ok;
     int16_t defaultVerticalOriginY = clampTo<int16_t>(m_fontElement.fastGetAttribute(SVGNames::vert_origin_yAttr).toInt(&ok));
     if (!ok && m_missingGlyphElement)
         defaultVerticalOriginY = clampTo<int16_t>(m_missingGlyphElement->fastGetAttribute(SVGNames::vert_origin_yAttr).toInt());
-    write16(result, defaultVerticalOriginY);
+    append16(defaultVerticalOriginY);
 
-    auto tableSizeOffset = result.size();
-    write16(result, 0); // Place to write table size.
+    auto tableSizeOffset = m_result.size();
+    append16(0); // Place to write table size.
     for (Glyph i = 0; i < m_glyphs.size(); ++i) {
         if (auto* glyph = m_glyphs[i].glyphElement) {
             if (int16_t verticalOriginY = clampTo<int16_t>(glyph->fastGetAttribute(SVGNames::vert_origin_yAttr).toInt())) {
-                write16(result, i);
-                write16(result, verticalOriginY);
+                append16(i);
+                append16(verticalOriginY);
             }
         }
     }
-    ASSERT(!((result.size() - tableSizeOffset - 2) % 4));
-    overwrite16(result, tableSizeOffset, (result.size() - tableSizeOffset - 2) / 4);
+    ASSERT(!((m_result.size() - tableSizeOffset - 2) % 4));
+    overwrite16(tableSizeOffset, (m_result.size() - tableSizeOffset - 2) / 4);
 }
 
-void SVGToOTFFontConverter::appendVHEATable(Vector<char>& result) const
+void SVGToOTFFontConverter::appendVHEATable()
 {
-    write32(result, 0x00011000); // Version
-    write16(result, m_unitsPerEm / 2); // Vertical typographic ascender (vertical baseline to the right)
-    write16(result, clampTo<int16_t>(-static_cast<int>(m_unitsPerEm / 2))); // Vertical typographic descender
-    write16(result, m_unitsPerEm / 10); // Vertical typographic line gap
-    write16(result, clampTo<int16_t>(m_advanceHeightMax));
-    write16(result, clampTo<int16_t>(m_unitsPerEm - m_boundingBox.maxY())); // Minimum top side bearing
-    write16(result, clampTo<int16_t>(m_boundingBox.y())); // Minimum bottom side bearing
-    write16(result, clampTo<int16_t>(m_unitsPerEm - m_boundingBox.y())); // Y maximum extent
+    append32(0x00011000); // Version
+    append16(m_unitsPerEm / 2); // Vertical typographic ascender (vertical baseline to the right)
+    append16(clampTo<int16_t>(-static_cast<int>(m_unitsPerEm / 2))); // Vertical typographic descender
+    append16(m_unitsPerEm / 10); // Vertical typographic line gap
+    append16(clampTo<int16_t>(m_advanceHeightMax));
+    append16(clampTo<int16_t>(m_unitsPerEm - m_boundingBox.maxY())); // Minimum top side bearing
+    append16(clampTo<int16_t>(m_boundingBox.y())); // Minimum bottom side bearing
+    append16(clampTo<int16_t>(m_unitsPerEm - m_boundingBox.y())); // Y maximum extent
     // Since WebKit draws the caret and ignores the following values, it doesn't matter what we set them to.
-    write16(result, 1); // Vertical caret
-    write16(result, 0); // Vertical caret
-    write16(result, 0); // "Set value to 0 for non-slanted fonts"
-    write32(result, 0); // Reserved
-    write32(result, 0); // Reserved
-    write16(result, 0); // "Set to 0"
-    write16(result, m_glyphs.size()); // Number of advance heights in VMTX table
+    append16(1); // Vertical caret
+    append16(0); // Vertical caret
+    append16(0); // "Set value to 0 for non-slanted fonts"
+    append32(0); // Reserved
+    append32(0); // Reserved
+    append16(0); // "Set to 0"
+    append16(m_glyphs.size()); // Number of advance heights in VMTX table
 }
 
-void SVGToOTFFontConverter::appendVMTXTable(Vector<char>& result) const
+void SVGToOTFFontConverter::appendVMTXTable()
 {
     for (auto& glyph : m_glyphs) {
-        write16(result, clampTo<uint16_t>(glyph.verticalAdvance));
-        write16(result, clampTo<int16_t>(m_unitsPerEm - glyph.boundingBox.maxY())); // top side bearing
+        append16(clampTo<uint16_t>(glyph.verticalAdvance));
+        append16(clampTo<int16_t>(m_unitsPerEm - glyph.boundingBox.maxY())); // top side bearing
     }
 }
 
@@ -587,10 +615,14 @@
 {
     for (auto& unicodeRange : unicodeRanges) {
         for (auto codepoint = unicodeRange.first; codepoint <= unicodeRange.second; ++codepoint) {
-            if (!m_codepointToIndexMap.isValidKey(codepoint))
-                continue;
-            if (Glyph glyph = m_codepointToIndexMap.get(codepoint))
-                glyphSet.add(glyph);
+            UChar buffer[2];
+            uint8_t length = 0;
+            UBool error = false;
+            U16_APPEND(buffer, length, 2, codepoint, error);
+            if (!error) {
+                for (auto index : m_codepointsToIndicesMap.get(String(buffer, length)))
+                    glyphSet.add(index);
+            }
         }
     }
 }
@@ -598,24 +630,12 @@
 void SVGToOTFFontConverter::addCodepoints(const HashSet<String>& codepoints, HashSet<Glyph>& glyphSet) const
 {
     for (auto& codepointString : codepoints) {
-        auto codepointStringLength = codepointString.length();
-        unsigned i = 0;
-        while (i < codepointStringLength) {
-            // FIXME: Canonicalization might be necessary
-            UChar32 codepoint;
-            if (codepointString.is8Bit())
-                codepoint = codepointString.characters8()[i++];
-            else
-                U16_NEXT(codepointString.characters16(), i, codepointStringLength, codepoint);
-            if (!m_codepointToIndexMap.isValidKey(codepoint))
-                continue;
-            if (Glyph glyph = m_codepointToIndexMap.get(codepoint))
-                glyphSet.add(glyph);
-        }
+        for (auto index : m_codepointsToIndicesMap.get(codepointString))
+            glyphSet.add(index);
     }
 }
 
-void SVGToOTFFontConverter::addGlyphNames(const HashSet<String>& glyphNames, HashSet<uint16_t>& glyphSet) const
+void SVGToOTFFontConverter::addGlyphNames(const HashSet<String>& glyphNames, HashSet<Glyph>& glyphSet) const
 {
     for (auto& glyphName : glyphNames) {
         if (Glyph glyph = m_glyphNameToIndexMap.get(glyphName))
@@ -642,7 +662,7 @@
     }
 }
 
-template<typename T> inline size_t SVGToOTFFontConverter::appendKERNSubtable(Vector<char>& result, bool (T::*buildKerningPair)(SVGKerningPair&) const, uint16_t coverage) const
+template<typename T> inline size_t SVGToOTFFontConverter::appendKERNSubtable(bool (T::*buildKerningPair)(SVGKerningPair&) const, uint16_t coverage)
 {
     Vector<KerningData> kerningData;
     for (auto& element : childrenOfType<T>(m_fontElement)) {
@@ -650,10 +670,10 @@
         if ((element.*buildKerningPair)(kerningPair))
             addKerningPair(kerningData, kerningPair);
     }
-    return finishAppendingKERNSubtable(result, WTF::move(kerningData), coverage);
+    return finishAppendingKERNSubtable(WTF::move(kerningData), coverage);
 }
 
-size_t SVGToOTFFontConverter::finishAppendingKERNSubtable(Vector<char>& result, Vector<KerningData> kerningData, uint16_t coverage) const
+size_t SVGToOTFFontConverter::finishAppendingKERNSubtable(Vector<KerningData> kerningData, uint16_t coverage)
 {
     std::sort(kerningData.begin(), kerningData.end(), [](const KerningData& a, const KerningData& b) {
         return a.glyph1 < b.glyph1 || (a.glyph1 == b.glyph1 && a.glyph2 < b.glyph2);
@@ -665,50 +685,50 @@
         sizeOfKerningDataTable = 14;
     }
 
-    write16(result, 0); // Version of subtable
-    write16(result, sizeOfKerningDataTable); // Length of this subtable
-    write16(result, coverage); // Table coverage bitfield
+    append16(0); // Version of subtable
+    append16(sizeOfKerningDataTable); // Length of this subtable
+    append16(coverage); // Table coverage bitfield
 
     uint16_t roundedNumKerningPairs = roundDownToPowerOfTwo(kerningData.size());
 
-    write16(result, kerningData.size());
-    write16(result, roundedNumKerningPairs * 6); // searchRange: "The largest power of two less than or equal to the value of nPairs, multiplied by the size in bytes of an entry in the table."
-    write16(result, integralLog2(roundedNumKerningPairs)); // entrySelector: "log2 of the largest power of two less than or equal to the value of nPairs."
-    write16(result, (kerningData.size() - roundedNumKerningPairs) * 6); // rangeShift: "The value of nPairs minus the largest power of two less than or equal to nPairs,
+    append16(kerningData.size());
+    append16(roundedNumKerningPairs * 6); // searchRange: "The largest power of two less than or equal to the value of nPairs, multiplied by the size in bytes of an entry in the table."
+    append16(integralLog2(roundedNumKerningPairs)); // entrySelector: "log2 of the largest power of two less than or equal to the value of nPairs."
+    append16((kerningData.size() - roundedNumKerningPairs) * 6); // rangeShift: "The value of nPairs minus the largest power of two less than or equal to nPairs,
                                                                         // and then multiplied by the size in bytes of an entry in the table."
 
     for (auto& kerningData : kerningData) {
-        write16(result, kerningData.glyph1);
-        write16(result, kerningData.glyph2);
-        write16(result, kerningData.adjustment);
+        append16(kerningData.glyph1);
+        append16(kerningData.glyph2);
+        append16(kerningData.adjustment);
     }
 
     return sizeOfKerningDataTable;
 }
 
-void SVGToOTFFontConverter::appendKERNTable(Vector<char>& result) const
+void SVGToOTFFontConverter::appendKERNTable()
 {
-    write16(result, 0); // Version
-    write16(result, 2); // Number of subtables
+    append16(0); // Version
+    append16(2); // Number of subtables
 
 #if !ASSERT_DISABLED
-    auto subtablesOffset = result.size();
+    auto subtablesOffset = m_result.size();
 #endif
 
-    size_t sizeOfHorizontalSubtable = appendKERNSubtable<SVGHKernElement>(result, &SVGHKernElement::buildHorizontalKerningPair, 1);
-    ASSERT_UNUSED(sizeOfHorizontalSubtable, subtablesOffset + sizeOfHorizontalSubtable == result.size());
-    size_t sizeOfVerticalSubtable = appendKERNSubtable<SVGVKernElement>(result, &SVGVKernElement::buildVerticalKerningPair, 0);
-    ASSERT_UNUSED(sizeOfVerticalSubtable, subtablesOffset + sizeOfHorizontalSubtable + sizeOfVerticalSubtable == result.size());
+    size_t sizeOfHorizontalSubtable = appendKERNSubtable<SVGHKernElement>(&SVGHKernElement::buildHorizontalKerningPair, 1);
+    ASSERT_UNUSED(sizeOfHorizontalSubtable, subtablesOffset + sizeOfHorizontalSubtable == m_result.size());
+    size_t sizeOfVerticalSubtable = appendKERNSubtable<SVGVKernElement>(&SVGVKernElement::buildVerticalKerningPair, 0);
+    ASSERT_UNUSED(sizeOfVerticalSubtable, subtablesOffset + sizeOfHorizontalSubtable + sizeOfVerticalSubtable == m_result.size());
 
     // Work around a bug in Apple's font parser by adding some padding bytes. <rdar://problem/18401901>
     for (int i = 0; i < 6; ++i)
-        result.append(0);
+        m_result.append(0);
 }
 
 static void writeCFFEncodedNumber(Vector<char>& vector, float number)
 {
     vector.append(0xFF);
-    write32(vector, number * 0x10000);
+    append32(vector, number * 0x10000);
 }
 
 static const char rLineTo = 0x05;
@@ -847,7 +867,7 @@
     return result;
 }
 
-void SVGToOTFFontConverter::processGlyphElement(const SVGElement& glyphOrMissingGlyphElement, const SVGGlyphElement* glyphElement, float defaultHorizontalAdvance, float defaultVerticalAdvance, Codepoint codepoint, bool& initialGlyph)
+void SVGToOTFFontConverter::processGlyphElement(const SVGElement& glyphOrMissingGlyphElement, const SVGGlyphElement* glyphElement, float defaultHorizontalAdvance, float defaultVerticalAdvance, const String& codepoints, bool& initialGlyph)
 {
     bool ok;
     float horizontalAdvance = glyphOrMissingGlyphElement.fastGetAttribute(SVGNames::horiz_adv_xAttr).toFloat(&ok);
@@ -868,9 +888,39 @@
     m_minRightSideBearing = std::min(m_minRightSideBearing, horizontalAdvance - glyphBoundingBox.maxX());
     initialGlyph = false;
 
-    m_glyphs.append(GlyphData(WTF::move(path), glyphElement, horizontalAdvance, verticalAdvance, m_boundingBox, codepoint));
+    m_glyphs.append(GlyphData(WTF::move(path), glyphElement, horizontalAdvance, verticalAdvance, m_boundingBox, codepoints));
 }
 
+bool SVGToOTFFontConverter::compareCodepointsLexicographically(const GlyphData& data1, const GlyphData& data2)
+{
+    unsigned i1 = 0;
+    unsigned i2 = 0;
+    unsigned length1 = data1.codepoints.length();
+    unsigned length2 = data2.codepoints.length();
+    while (i1 < length1 && i2 < length2) {
+        UChar32 codepoint1, codepoint2;
+        if (data1.codepoints.is8Bit())
+            codepoint1 = data1.codepoints[i1++];
+        else
+            U16_NEXT(data1.codepoints.characters16(), i1, length1, codepoint1);
+
+        if (data2.codepoints.is8Bit())
+            codepoint2 = data2.codepoints[i2++];
+        else
+            U16_NEXT(data2.codepoints.characters16(), i2, length2, codepoint2);
+
+        if (codepoint1 < codepoint2)
+            return true;
+        if (codepoint1 > codepoint2)
+            return false;
+    }
+
+    if (length1 == length2 && data1.glyphElement
+        && equalIgnoringCase(data1.glyphElement->fastGetAttribute(SVGNames::arabic_formAttr), "isolated"))
+        return true;
+    return length1 < length2;
+}
+
 SVGToOTFFontConverter::SVGToOTFFontConverter(const SVGFontElement& fontElement)
     : m_fontElement(fontElement)
     , m_fontFaceElement(childrenOfType<SVGFontFaceElement>(m_fontElement).first())
@@ -891,7 +941,7 @@
         m_unitsPerEm = m_fontFaceElement->unitsPerEm();
 
     if (m_missingGlyphElement)
-        processGlyphElement(*m_missingGlyphElement, nullptr, defaultHorizontalAdvance, defaultVerticalAdvance, 0, initialGlyph);
+        processGlyphElement(*m_missingGlyphElement, nullptr, defaultHorizontalAdvance, defaultVerticalAdvance, String(), initialGlyph);
     else {
         Vector<char> notdefCharString;
         writeCFFEncodedNumber(notdefCharString, m_unitsPerEm);
@@ -899,14 +949,13 @@
         writeCFFEncodedNumber(notdefCharString, 0);
         notdefCharString.append(rMoveTo);
         notdefCharString.append(endChar);
-        m_glyphs.append(GlyphData(WTF::move(notdefCharString), nullptr, m_unitsPerEm, m_unitsPerEm, FloatRect(), 0));
+        m_glyphs.append(GlyphData(WTF::move(notdefCharString), nullptr, m_unitsPerEm, m_unitsPerEm, FloatRect(), String()));
     }
 
     for (auto& glyphElement : childrenOfType<SVGGlyphElement>(m_fontElement)) {
         auto& unicodeAttribute = glyphElement.fastGetAttribute(SVGNames::unicodeAttr);
-        // Only support Basic Multilingual Plane w/o ligatures for now
-        if (unicodeAttribute.length() == 1)
-            processGlyphElement(glyphElement, &glyphElement, defaultHorizontalAdvance, defaultVerticalAdvance, unicodeAttribute[0], initialGlyph);
+        if (!unicodeAttribute.isEmpty()) // If we can never actually trigger this glyph, ignore it completely
+            processGlyphElement(glyphElement, &glyphElement, defaultHorizontalAdvance, defaultVerticalAdvance, unicodeAttribute, initialGlyph);
     }
 
     if (m_glyphs.size() > std::numeric_limits<Glyph>::max()) {
@@ -914,9 +963,7 @@
         return;
     }
 
-    std::sort(m_glyphs.begin(), m_glyphs.end(), [](const GlyphData& data1, const GlyphData& data2) {
-        return data1.codepoint < data2.codepoint;
-    });
+    std::sort(m_glyphs.begin(), m_glyphs.end(), &compareCodepointsLexicographically);
 
     for (Glyph i = 0; i < m_glyphs.size(); ++i) {
         GlyphData& glyph = m_glyphs[i];
@@ -925,8 +972,8 @@
             if (!glyphName.isNull())
                 m_glyphNameToIndexMap.add(glyphName, i);
         }
-        if (m_codepointToIndexMap.isValidKey(glyph.codepoint))
-            m_codepointToIndexMap.add(glyph.codepoint, i);
+        if (m_codepointsToIndicesMap.isValidKey(glyph.codepoints))
+            m_codepointsToIndicesMap.add(glyph.codepoints, Vector<Glyph>()).iterator->value.append(i);
     }
 
     // FIXME: Handle commas.
@@ -963,92 +1010,92 @@
     return !(x & 3);
 }
 
-static uint32_t calculateChecksum(const Vector<char>& table, size_t startingOffset, size_t endingOffset)
+uint32_t SVGToOTFFontConverter::calculateChecksum(size_t startingOffset, size_t endingOffset) const
 {
     ASSERT(isFourByteAligned(endingOffset - startingOffset));
     uint32_t sum = 0;
     for (size_t offset = startingOffset; offset < endingOffset; offset += 4) {
-        sum += (static_cast<unsigned char>(table[offset + 3]) << 24)
-            | (static_cast<unsigned char>(table[offset + 2]) << 16)
-            | (static_cast<unsigned char>(table[offset + 1]) << 8)
-            | static_cast<unsigned char>(table[offset]);
+        sum += (static_cast<unsigned char>(m_result[offset + 3]) << 24)
+            | (static_cast<unsigned char>(m_result[offset + 2]) << 16)
+            | (static_cast<unsigned char>(m_result[offset + 1]) << 8)
+            | static_cast<unsigned char>(m_result[offset]);
     }
     return sum;
 }
 
-void SVGToOTFFontConverter::appendTable(const char identifier[4], Vector<char>& output, FontAppendingFunction appendingFunction)
+void SVGToOTFFontConverter::appendTable(const char identifier[4], FontAppendingFunction appendingFunction)
 {
-    size_t offset = output.size();
+    size_t offset = m_result.size();
     ASSERT(isFourByteAligned(offset));
-    (this->*appendingFunction)(output);
-    size_t unpaddedSize = output.size() - offset;
-    while (!isFourByteAligned(output.size()))
-        output.append(0);
-    ASSERT(isFourByteAligned(output.size()));
+    (this->*appendingFunction)();
+    size_t unpaddedSize = m_result.size() - offset;
+    while (!isFourByteAligned(m_result.size()))
+        m_result.append(0);
+    ASSERT(isFourByteAligned(m_result.size()));
     size_t directoryEntryOffset = headerSize + m_tablesAppendedCount * directoryEntrySize;
-    output[directoryEntryOffset] = identifier[0];
-    output[directoryEntryOffset + 1] = identifier[1];
-    output[directoryEntryOffset + 2] = identifier[2];
-    output[directoryEntryOffset + 3] = identifier[3];
-    overwrite32(output, directoryEntryOffset + 4, calculateChecksum(output, offset, output.size()));
-    overwrite32(output, directoryEntryOffset + 8, offset);
-    overwrite32(output, directoryEntryOffset + 12, unpaddedSize);
+    m_result[directoryEntryOffset] = identifier[0];
+    m_result[directoryEntryOffset + 1] = identifier[1];
+    m_result[directoryEntryOffset + 2] = identifier[2];
+    m_result[directoryEntryOffset + 3] = identifier[3];
+    overwrite32(directoryEntryOffset + 4, calculateChecksum(offset, m_result.size()));
+    overwrite32(directoryEntryOffset + 8, offset);
+    overwrite32(directoryEntryOffset + 12, unpaddedSize);
     ++m_tablesAppendedCount;
 }
 
-Vector<char> SVGToOTFFontConverter::convertSVGToOTFFont()
+void SVGToOTFFontConverter::convertSVGToOTFFont()
 {
     Vector<char> result;
 
     if (m_glyphs.isEmpty())
-        return result;
+        return;
 
     uint16_t numTables = 13;
     uint16_t roundedNumTables = roundDownToPowerOfTwo(numTables);
     uint16_t searchRange = roundedNumTables * 16; // searchRange: "(Maximum power of 2 <= numTables) x 16."
 
-    result.append('O');
-    result.append('T');
-    result.append('T');
-    result.append('O');
-    write16(result, numTables);
-    write16(result, searchRange);
-    write16(result, integralLog2(roundedNumTables)); // entrySelector: "Log2(maximum power of 2 <= numTables)."
-    write16(result, numTables * 16 - searchRange); // rangeShift: "NumTables x 16-searchRange."
+    m_result.append('O');
+    m_result.append('T');
+    m_result.append('T');
+    m_result.append('O');
+    append16(numTables);
+    append16(searchRange);
+    append16(integralLog2(roundedNumTables)); // entrySelector: "Log2(maximum power of 2 <= numTables)."
+    append16(numTables * 16 - searchRange); // rangeShift: "NumTables x 16-searchRange."
 
     ASSERT(result.size() == headerSize);
 
     // Leave space for the directory entries.
     for (size_t i = 0; i < directoryEntrySize * numTables; ++i)
-        result.append(0);
+        m_result.append(0);
 
-    appendTable("CFF ", result, &SVGToOTFFontConverter::appendCFFTable);
-    appendTable("OS/2", result, &SVGToOTFFontConverter::appendOS2Table);
-    appendTable("VORG", result, &SVGToOTFFontConverter::appendVORGTable);
-    appendTable("cmap", result, &SVGToOTFFontConverter::appendCMAPTable);
-    auto headTableOffset = result.size();
-    appendTable("head", result, &SVGToOTFFontConverter::appendHEADTable);
-    appendTable("hhea", result, &SVGToOTFFontConverter::appendHHEATable);
-    appendTable("hmtx", result, &SVGToOTFFontConverter::appendHMTXTable);
-    appendTable("kern", result, &SVGToOTFFontConverter::appendKERNTable);
-    appendTable("maxp", result, &SVGToOTFFontConverter::appendMAXPTable);
-    appendTable("name", result, &SVGToOTFFontConverter::appendNAMETable);
-    appendTable("post", result, &SVGToOTFFontConverter::appendPOSTTable);
-    appendTable("vhea", result, &SVGToOTFFontConverter::appendVHEATable);
-    appendTable("vmtx", result, &SVGToOTFFontConverter::appendVMTXTable);
+    appendTable("CFF ", &SVGToOTFFontConverter::appendCFFTable);
+    appendTable("OS/2", &SVGToOTFFontConverter::appendOS2Table);
+    appendTable("VORG", &SVGToOTFFontConverter::appendVORGTable);
+    appendTable("cmap", &SVGToOTFFontConverter::appendCMAPTable);
+    auto headTableOffset = m_result.size();
+    appendTable("head", &SVGToOTFFontConverter::appendHEADTable);
+    appendTable("hhea", &SVGToOTFFontConverter::appendHHEATable);
+    appendTable("hmtx", &SVGToOTFFontConverter::appendHMTXTable);
+    appendTable("kern", &SVGToOTFFontConverter::appendKERNTable);
+    appendTable("maxp", &SVGToOTFFontConverter::appendMAXPTable);
+    appendTable("name", &SVGToOTFFontConverter::appendNAMETable);
+    appendTable("post", &SVGToOTFFontConverter::appendPOSTTable);
+    appendTable("vhea", &SVGToOTFFontConverter::appendVHEATable);
+    appendTable("vmtx", &SVGToOTFFontConverter::appendVMTXTable);
 
     ASSERT(numTables == m_tablesAppendedCount);
 
     // checksumAdjustment: "To compute: set it to 0, calculate the checksum for the 'head' table and put it in the table directory,
     // sum the entire font as uint32, then store B1B0AFBA - sum. The checksum for the 'head' table will now be wrong. That is OK."
-    overwrite32(result, headTableOffset + 8, 0xB1B0AFBAU - calculateChecksum(result, 0, result.size()));
-
-    return result;
+    overwrite32(headTableOffset + 8, 0xB1B0AFBAU - calculateChecksum(0, result.size()));
 }
 
 Vector<char> convertSVGToOTFFont(const SVGFontElement& element)
 {
-    return SVGToOTFFontConverter(element).convertSVGToOTFFont();
+    SVGToOTFFontConverter converter(element);
+    converter.convertSVGToOTFFont();
+    return converter.releaseResult();
 }
 
 }
_______________________________________________
webkit-changes mailing list
[email protected]
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to