2011-06-10 Nikolas Zimmermann <nzimmermann@rim.com>
authorzimmermann@webkit.org <zimmermann@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Fri, 10 Jun 2011 20:15:10 +0000 (20:15 +0000)
committerzimmermann@webkit.org <zimmermann@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Fri, 10 Jun 2011 20:15:10 +0000 (20:15 +0000)
        Reviewed by Rob Buis.

        Integrate SVG Fonts within GlyphPage concept, removing the special SVG code paths from Font, making it possible to reuse the simple text code path for SVG Fonts
        https://bugs.webkit.org/show_bug.cgi?id=59085

        Add glyph table to SVGFontElement mapping between SVGGlyph <-> Glyph
        https://bugs.webkit.org/show_bug.cgi?id=62441

        Preparation patch 1: Introduce the internal glyph table in SVGGlyphMap that will be used to identify each
        SVGGlyph identifier with a Glyph (which is just an ushort). It will be used by follow-up patches.

        Doesn't affect any test so far.

        * platform/graphics/SVGGlyph.h:
        (WebCore::SVGGlyph::SVGGlyph):
        (WebCore::SVGGlyph::operator==):
        * rendering/svg/SVGTextRunRenderingContext.cpp:
        (WebCore::SVGTextRunWalker::walk):
        * svg/SVGFontData.cpp:
        (WebCore::SVGFontData::initializeFontData):
        * svg/SVGFontElement.cpp:
        (WebCore::SVGFontElement::SVGFontElement):
        (WebCore::SVGFontElement::registerLigaturesInGlyphCache):
        (WebCore::SVGFontElement::ensureGlyphCache):
        (WebCore::kerningForPairOfStringsAndGlyphs):
        (WebCore::SVGFontElement::horizontalKerningForPairOfStringsAndGlyphs):
        (WebCore::SVGFontElement::verticalKerningForPairOfStringsAndGlyphs):
        (WebCore::SVGFontElement::collectGlyphsForString):
        (WebCore::SVGFontElement::collectGlyphsForGlyphName):
        (WebCore::SVGFontElement::svgGlyphForGlyph):
        (WebCore::SVGFontElement::missingGlyph):
        * svg/SVGFontElement.h:
        (WebCore::SVGKerningPair::SVGKerningPair):
        * svg/SVGGlyphMap.h:
        (WebCore::SVGGlyphMap::addGlyphByUnicodeString):
        (WebCore::SVGGlyphMap::addGlyphByName):
        (WebCore::SVGGlyphMap::appendToGlyphTable):
        (WebCore::SVGGlyphMap::collectGlyphsForString):
        (WebCore::SVGGlyphMap::clear):
        (WebCore::SVGGlyphMap::svgGlyphForGlyph):
        (WebCore::SVGGlyphMap::glyphIdentifierForGlyphName):

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@88557 268f45cc-cd09-0410-ab3c-d52691b4dbfc

Source/WebCore/ChangeLog
Source/WebCore/platform/graphics/SVGGlyph.h
Source/WebCore/rendering/svg/SVGTextRunRenderingContext.cpp
Source/WebCore/svg/SVGFontData.cpp
Source/WebCore/svg/SVGFontElement.cpp
Source/WebCore/svg/SVGFontElement.h
Source/WebCore/svg/SVGGlyphMap.h

index e1964de..2fb9c46 100644 (file)
@@ -1,3 +1,47 @@
+2011-06-10  Nikolas Zimmermann  <nzimmermann@rim.com>
+
+        Reviewed by Rob Buis.
+
+        Integrate SVG Fonts within GlyphPage concept, removing the special SVG code paths from Font, making it possible to reuse the simple text code path for SVG Fonts
+        https://bugs.webkit.org/show_bug.cgi?id=59085
+
+        Add glyph table to SVGFontElement mapping between SVGGlyph <-> Glyph
+        https://bugs.webkit.org/show_bug.cgi?id=62441
+
+        Preparation patch 1: Introduce the internal glyph table in SVGGlyphMap that will be used to identify each
+        SVGGlyph identifier with a Glyph (which is just an ushort). It will be used by follow-up patches.
+
+        Doesn't affect any test so far.
+
+        * platform/graphics/SVGGlyph.h:
+        (WebCore::SVGGlyph::SVGGlyph):
+        (WebCore::SVGGlyph::operator==):
+        * rendering/svg/SVGTextRunRenderingContext.cpp:
+        (WebCore::SVGTextRunWalker::walk):
+        * svg/SVGFontData.cpp:
+        (WebCore::SVGFontData::initializeFontData):
+        * svg/SVGFontElement.cpp:
+        (WebCore::SVGFontElement::SVGFontElement):
+        (WebCore::SVGFontElement::registerLigaturesInGlyphCache):
+        (WebCore::SVGFontElement::ensureGlyphCache):
+        (WebCore::kerningForPairOfStringsAndGlyphs):
+        (WebCore::SVGFontElement::horizontalKerningForPairOfStringsAndGlyphs):
+        (WebCore::SVGFontElement::verticalKerningForPairOfStringsAndGlyphs):
+        (WebCore::SVGFontElement::collectGlyphsForString):
+        (WebCore::SVGFontElement::collectGlyphsForGlyphName):
+        (WebCore::SVGFontElement::svgGlyphForGlyph):
+        (WebCore::SVGFontElement::missingGlyph):
+        * svg/SVGFontElement.h:
+        (WebCore::SVGKerningPair::SVGKerningPair):
+        * svg/SVGGlyphMap.h:
+        (WebCore::SVGGlyphMap::addGlyphByUnicodeString):
+        (WebCore::SVGGlyphMap::addGlyphByName):
+        (WebCore::SVGGlyphMap::appendToGlyphTable):
+        (WebCore::SVGGlyphMap::collectGlyphsForString):
+        (WebCore::SVGGlyphMap::clear):
+        (WebCore::SVGGlyphMap::svgGlyphForGlyph):
+        (WebCore::SVGGlyphMap::glyphIdentifierForGlyphName):
+
 2011-06-10  Emil A Eklund  <eae@chromium.org>
 
         Reviewed by Eric Seidel.
index 600a05b..91960b6 100644 (file)
@@ -24,6 +24,7 @@
 #define SVGGlyph_h
 
 #if ENABLE(SVG_FONTS)
+#include "Glyph.h"
 #include "Path.h"
 
 #include <limits>
@@ -54,6 +55,7 @@ struct SVGGlyph {
         , orientation(Both)
         , arabicForm(None)
         , priority(0)
+        , tableEntry(0)
         , unicodeStringLength(0)
         , horizontalAdvanceX(0)
         , verticalOriginX(0)
@@ -74,6 +76,7 @@ struct SVGGlyph {
         return isValid == other.isValid
             && orientation == other.orientation
             && arabicForm == other.arabicForm
+            && tableEntry == other.tableEntry
             && unicodeStringLength == other.unicodeStringLength
             && glyphName == other.glyphName
             && horizontalAdvanceX == other.horizontalAdvanceX
@@ -88,6 +91,7 @@ struct SVGGlyph {
     unsigned orientation : 2; // Orientation
     unsigned arabicForm : 3; // ArabicForm
     int priority;
+    Glyph tableEntry;
     size_t unicodeStringLength;
     String glyphName;
 
index ec4e122..d73b846 100644 (file)
@@ -152,7 +152,7 @@ struct SVGTextRunWalker {
             if (haveAltGlyph)
                 glyphs.append(altGlyphIdentifier);
             else
-                m_fontElement->getGlyphIdentifiersForString(lookupString, glyphs);
+                m_fontElement->collectGlyphsForString(lookupString, glyphs);
 
             Vector<SVGGlyph>::iterator it = glyphs.begin();
             Vector<SVGGlyph>::iterator end = glyphs.end();
index 55b8d3d..e904d3d 100644 (file)
@@ -59,7 +59,7 @@ void SVGFontData::initializeFontData(SimpleFontData* fontData, int size)
     if (!xHeight) {
         // Fallback if x_heightAttr is not specified for the font element.
         Vector<SVGGlyph> letterXGlyphs;
-        associatedFontElement->getGlyphIdentifiersForString(String("x", 1), letterXGlyphs);
+        associatedFontElement->collectGlyphsForString(String("x", 1), letterXGlyphs);
         xHeight = letterXGlyphs.isEmpty() ? 2 * ascent / 3 : letterXGlyphs.first().horizontalAdvanceX * scale;
     }
 
@@ -72,15 +72,15 @@ void SVGFontData::initializeFontData(SimpleFontData* fontData, int size)
     fontMetrics.setXHeight(xHeight);
 
     Vector<SVGGlyph> spaceGlyphs;
-    associatedFontElement->getGlyphIdentifiersForString(String(" ", 1), spaceGlyphs);
+    associatedFontElement->collectGlyphsForString(String(" ", 1), spaceGlyphs);
     fontData->setSpaceWidth(spaceGlyphs.isEmpty() ? xHeight : spaceGlyphs.first().horizontalAdvanceX * scale);
 
     Vector<SVGGlyph> numeralZeroGlyphs;
-    associatedFontElement->getGlyphIdentifiersForString(String("0", 1), numeralZeroGlyphs);
+    associatedFontElement->collectGlyphsForString(String("0", 1), numeralZeroGlyphs);
     fontData->setAvgCharWidth(numeralZeroGlyphs.isEmpty() ? fontData->spaceWidth() : numeralZeroGlyphs.first().horizontalAdvanceX * scale);
 
     Vector<SVGGlyph> letterWGlyphs;
-    associatedFontElement->getGlyphIdentifiersForString(String("W", 1), letterWGlyphs);
+    associatedFontElement->collectGlyphsForString(String("W", 1), letterWGlyphs);
     fontData->setMaxCharWidth(letterWGlyphs.isEmpty() ? ascent : letterWGlyphs.first().horizontalAdvanceX * scale);
 
     // FIXME: is there a way we can get the space glyph from the SVGGlyph above?
index fb888a9..265dd16 100644 (file)
@@ -41,6 +41,7 @@ DEFINE_ANIMATED_BOOLEAN(SVGFontElement, SVGNames::externalResourcesRequiredAttr,
 
 inline SVGFontElement::SVGFontElement(const QualifiedName& tagName, Document* document)
     : SVGStyledElement(tagName, document) 
+    , m_missingGlyph(0)
     , m_isGlyphCacheValid(false)
 {
     ASSERT(hasTagName(SVGNames::fontTag));
@@ -79,26 +80,94 @@ SVGMissingGlyphElement* SVGFontElement::firstMissingGlyphElement() const
     return 0;
 }
 
-void SVGFontElement::ensureGlyphCache() const
+void SVGFontElement::registerLigaturesInGlyphCache(Vector<String>& ligatures)
+{
+    ASSERT(!ligatures.isEmpty());
+
+    // Register each character of a ligature in the map, if not present.
+    // Eg. If only a "fi" ligature is present, but not "f" and "i", the
+    // GlyphPage will not contain any entries for "f" and "i", so the
+    // SVGFont is not used to render the text "fi1234". Register an
+    // empty SVGGlyph with the character, so the SVG Font will be used
+    // to render the text. If someone tries to render "f2" the SVG Font
+    // will not be able to find a glyph for "f", but handles the fallback
+    // character substitution properly through glyphDataForCharacter().
+    Vector<SVGGlyph> glyphs;
+    size_t ligaturesSize = ligatures.size();
+    for (size_t i = 0; i < ligaturesSize; ++i) {
+        const String& unicode = ligatures[i];
+
+        unsigned unicodeLength = unicode.length();
+        ASSERT(unicodeLength > 1);
+
+        const UChar* characters = unicode.characters();
+        for (unsigned i = 0; i < unicodeLength; ++i) {
+            String lookupString(characters + i, 1);
+            m_glyphMap.collectGlyphsForString(lookupString, glyphs);
+            if (!glyphs.isEmpty()) {
+                glyphs.clear();
+                continue;
+            }
+                
+            // This glyph is never meant to be used for rendering, only as identifier as a part of a ligature.
+            SVGGlyph newGlyphPart;
+            /* FIXME: Enable this once with the next patch.
+            newGlyphPart.isPartOfLigature = true;
+            */
+            m_glyphMap.addGlyphByUnicodeString(lookupString, newGlyphPart);
+        }
+    }
+}
+
+void SVGFontElement::ensureGlyphCache()
 {
     if (m_isGlyphCacheValid)
         return;
 
+    SVGMissingGlyphElement* firstMissingGlyphElement = 0;
+    Vector<String> ligatures;
     for (Node* child = firstChild(); child; child = child->nextSibling()) {
         if (child->hasTagName(SVGNames::glyphTag)) {
             SVGGlyphElement* glyph = static_cast<SVGGlyphElement*>(child);
             String unicode = glyph->getAttribute(SVGNames::unicodeAttr);
-            if (unicode.length())
-                m_glyphMap.add(unicode, glyph->buildGlyphIdentifier());
+            SVGGlyph svgGlyph = glyph->buildGlyphIdentifier();
+            unsigned unicodeLength = unicode.length();
+
+            // Register named glyphs in the glyph table as well.
+            if (!unicodeLength) {
+                m_glyphMap.addGlyphByName(glyph->getIdAttribute(), svgGlyph);
+                continue;
+            }
+
+            // Register ligatures, if needed.
+            if (unicodeLength > 1)
+                ligatures.append(unicode);
+
+            m_glyphMap.addGlyphByUnicodeString(unicode, svgGlyph);
         } else if (child->hasTagName(SVGNames::hkernTag)) {
             SVGHKernElement* hkern = static_cast<SVGHKernElement*>(child);
             hkern->buildHorizontalKerningPair(m_horizontalKerningPairs);
         } else if (child->hasTagName(SVGNames::vkernTag)) {
             SVGVKernElement* vkern = static_cast<SVGVKernElement*>(child);
             vkern->buildVerticalKerningPair(m_verticalKerningPairs);
-        }
+        } else if (child->hasTagName(SVGNames::missing_glyphTag) && !firstMissingGlyphElement)
+            firstMissingGlyphElement = static_cast<SVGMissingGlyphElement*>(child);
     }
-        
+
+    /* FIXME: Register each character of each ligature, if needed.
+       This is not needed yet, turn it on with the next patch. With the current SVG Fonts code it would break fonts-glyph-04-t.svg
+    if (!ligatures.isEmpty())
+        registerLigaturesInGlyphCache(ligatures);
+    */
+
+    // Register missing-glyph element, if present.
+    if (firstMissingGlyphElement) {
+        SVGGlyph svgGlyph = SVGGlyphElement::buildGenericGlyphIdentifier(firstMissingGlyphElement);
+        m_glyphMap.appendToGlyphTable(svgGlyph);
+        m_missingGlyph = svgGlyph.tableEntry;
+        ASSERT(m_missingGlyph > 0);
+    }
+
     m_isGlyphCacheValid = true;
 }
 
@@ -132,7 +201,7 @@ static bool stringMatchesGlyphName(const String& glyphName, const HashSet<String
     
     return false;
 }
-    
+
 static bool matches(const String& u1, const String& g1, const String& u2, const String& g2, const SVGKerningPair& kerningPair)
 {
     if (!stringMatchesUnicodeRange(u1, kerningPair.unicodeRange1, kerningPair.unicodeName1)
@@ -146,7 +215,7 @@ static bool matches(const String& u1, const String& g1, const String& u2, const
     return true;
 }
 
-static float kerningForPairOfStringsAndGlyphs(KerningPairVector& kerningPairs, const String& u1, const String& g1, const String& u2, const String& g2)
+static float kerningForPairOfStringsAndGlyphs(const KerningPairVector& kerningPairs, const String& u1, const String& g1, const String& u2, const String& g2)
 {
     KerningPairVector::const_iterator it = kerningPairs.end() - 1;
     const KerningPairVector::const_iterator begin = kerningPairs.begin() - 1;
@@ -155,13 +224,13 @@ static float kerningForPairOfStringsAndGlyphs(KerningPairVector& kerningPairs, c
             return it->kerning;
     }
 
-    return 0.0f;
+    return 0;
 }
     
 float SVGFontElement::horizontalKerningForPairOfStringsAndGlyphs(const String& u1, const String& g1, const String& u2, const String& g2) const
 {
     if (m_horizontalKerningPairs.isEmpty())
-        return 0.0f;
+        return 0;
 
     return kerningForPairOfStringsAndGlyphs(m_horizontalKerningPairs, u1, g1, u2, g2);
 }
@@ -169,15 +238,34 @@ float SVGFontElement::horizontalKerningForPairOfStringsAndGlyphs(const String& u
 float SVGFontElement::verticalKerningForPairOfStringsAndGlyphs(const String& u1, const String& g1, const String& u2, const String& g2) const
 {
     if (m_verticalKerningPairs.isEmpty())
-        return 0.0f;
+        return 0;
 
     return kerningForPairOfStringsAndGlyphs(m_verticalKerningPairs, u1, g1, u2, g2);
 }
 
-void SVGFontElement::getGlyphIdentifiersForString(const String& string, Vector<SVGGlyph>& glyphs) const
+void SVGFontElement::collectGlyphsForString(const String& string, Vector<SVGGlyph>& glyphs)
+{
+    ensureGlyphCache();
+    m_glyphMap.collectGlyphsForString(string, glyphs);
+}
+
+void SVGFontElement::collectGlyphsForGlyphName(const String& glyphName, Vector<SVGGlyph>& glyphs)
+{
+    ensureGlyphCache();
+    // FIXME: We only support glyphName -> single glyph mapping so far.
+    glyphs.append(m_glyphMap.glyphIdentifierForGlyphName(glyphName));
+}
+
+SVGGlyph SVGFontElement::svgGlyphForGlyph(Glyph glyph)
+{
+    ensureGlyphCache();
+    return m_glyphMap.svgGlyphForGlyph(glyph);
+}
+    
+Glyph SVGFontElement::missingGlyph()
 {
     ensureGlyphCache();
-    m_glyphMap.get(string, glyphs);
+    return m_missingGlyph;
 }
 
 AttributeToPropertyTypeMap& SVGFontElement::attributeToPropertyTypeMap()
index 622766a..d44faa9 100644 (file)
@@ -43,7 +43,7 @@ struct SVGKerningPair {
     HashSet<String> glyphName2;
     
     SVGKerningPair()
-        : kerning(0.0f)
+        : kerning(0)
     {
     }
 };
@@ -58,12 +58,16 @@ public:
     static PassRefPtr<SVGFontElement> create(const QualifiedName&, Document*);
 
     void invalidateGlyphCache();
-
-    void getGlyphIdentifiersForString(const String&, Vector<SVGGlyph>&) const;
+    void collectGlyphsForString(const String&, Vector<SVGGlyph>&);
+    void collectGlyphsForGlyphName(const String&, Vector<SVGGlyph>&);
 
     float horizontalKerningForPairOfStringsAndGlyphs(const String& u1, const String& g1, const String& u2, const String& g2) const;
     float verticalKerningForPairOfStringsAndGlyphs(const String& u1, const String& g1, const String& u2, const String& g2) const;
-    
+
+    // Used by SimpleFontData/WidthIterator.
+    SVGGlyph svgGlyphForGlyph(Glyph);
+    Glyph missingGlyph();
+
     SVGMissingGlyphElement* firstMissingGlyphElement() const;
 
 private:
@@ -74,17 +78,19 @@ private:
     virtual void fillAttributeToPropertyTypeMap();
     virtual AttributeToPropertyTypeMap& attributeToPropertyTypeMap();
 
-    void ensureGlyphCache() const;
+    void ensureGlyphCache();
+    void registerLigaturesInGlyphCache(Vector<String>&);
 
     // Animated property declarations
 
     // SVGExternalResourcesRequired
     DECLARE_ANIMATED_BOOLEAN(ExternalResourcesRequired, externalResourcesRequired)
 
-    mutable KerningPairVector m_horizontalKerningPairs;
-    mutable KerningPairVector m_verticalKerningPairs;
-    mutable SVGGlyphMap m_glyphMap;
-    mutable bool m_isGlyphCacheValid;
+    KerningPairVector m_horizontalKerningPairs;
+    KerningPairVector m_verticalKerningPairs;
+    SVGGlyphMap m_glyphMap;
+    Glyph m_missingGlyph;
+    bool m_isGlyphCacheValid;
 };
 
 } // namespace WebCore
index 772ed08..ef9327f 100644 (file)
 #define SVGGlyphMap_h
 
 #if ENABLE(SVG_FONTS)
+#include "SVGGlyph.h"
 #include "SVGGlyphElement.h"
 
+#include <wtf/HashMap.h>
+#include <wtf/Vector.h>
+
 namespace WebCore {
 
 struct GlyphMapNode;
+class SVGFontData;
 
 typedef HashMap<UChar, RefPtr<GlyphMapNode> > GlyphMapLayer;
 
@@ -45,7 +50,7 @@ class SVGGlyphMap {
 public:
     SVGGlyphMap() : m_currentPriority(0) { }
 
-    void add(const String& string, const SVGGlyph& glyph) 
+    void addGlyphByUnicodeString(const String& string, const SVGGlyph& glyph)
     {
         size_t len = string.length();
         GlyphMapLayer* currentLayer = &m_rootLayer;
@@ -63,22 +68,44 @@ public:
 
         if (node) {
             node->glyphs.append(glyph);
-            node->glyphs.last().priority = m_currentPriority++;
-            node->glyphs.last().unicodeStringLength = len;
-            node->glyphs.last().isValid = true;
+
+            SVGGlyph& svgGlyph = node->glyphs.last();
+            svgGlyph.priority = m_currentPriority++;
+            svgGlyph.unicodeStringLength = len;
+            svgGlyph.isValid = true;
+            appendToGlyphTable(svgGlyph);
         }
     }
 
+    void addGlyphByName(const String& glyphName, SVGGlyph& glyph)
+    {
+        if (glyphName.isEmpty())
+            return;
+        appendToGlyphTable(glyph);
+        m_namedGlyphs.add(glyphName, glyph.tableEntry);
+    }
+
+    void appendToGlyphTable(SVGGlyph& glyph)
+    {
+        size_t tableEntry = m_glyphTable.size();
+        ASSERT(tableEntry < std::numeric_limits<unsigned short>::max());
+
+        // The first table entry starts with 1. 0 denotes an unknown glyph.
+        glyph.tableEntry = tableEntry + 1;
+        m_glyphTable.append(glyph);
+    }
+
     static inline bool compareGlyphPriority(const SVGGlyph& first, const SVGGlyph& second)
     {
         return first.priority < second.priority;
     }
 
-    void get(const String& string, Vector<SVGGlyph>& glyphs)
+    void collectGlyphsForString(const String& string, Vector<SVGGlyph>& glyphs)
     {
         GlyphMapLayer* currentLayer = &m_rootLayer;
 
-        for (size_t i = 0; i < string.length(); ++i) {
+        size_t length = string.length();
+        for (size_t i = 0; i < length; ++i) {
             UChar curChar = string[i];
             RefPtr<GlyphMapNode> node = currentLayer->get(curChar);
             if (!node)
@@ -88,21 +115,36 @@ public:
         }
         std::sort(glyphs.begin(), glyphs.end(), compareGlyphPriority);
     }
-
+    
     void clear() 
     { 
-        m_rootLayer.clear(); 
+        m_rootLayer.clear();
+        m_glyphTable.clear();
         m_currentPriority = 0;
     }
 
+    const SVGGlyph& svgGlyphForGlyph(Glyph glyph) const
+    {
+        if (!glyph || glyph > m_glyphTable.size()) {
+            DEFINE_STATIC_LOCAL(SVGGlyph, defaultGlyph, ());
+            return defaultGlyph;
+        }
+        return m_glyphTable[glyph - 1];
+    }
+
+    const SVGGlyph& glyphIdentifierForGlyphName(const String& glyphName) const
+    {
+        return svgGlyphForGlyph(m_namedGlyphs.get(glyphName));
+    }
+
 private:
     GlyphMapLayer m_rootLayer;
+    Vector<SVGGlyph, 256> m_glyphTable;
+    HashMap<String, Glyph> m_namedGlyphs;
     int m_currentPriority;
 };
 
 }
 
 #endif // ENABLE(SVG_FONTS)
-
-
 #endif // SVGGlyphMap_h