Changeset 156393 in webkit
- Timestamp:
- Sep 25, 2013 7:19:19 AM (11 years ago)
- Location:
- trunk
- Files:
-
- 3 added
- 13 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/LayoutTests/ChangeLog
r156392 r156393 1 2013-09-25 Allan Sandfeld Jensen <allan.jensen@digia.com> 2 3 Support kerning with SVG web fonts 4 https://bugs.webkit.org/show_bug.cgi?id=117540 5 6 Added test of SVG web font kerning. 7 8 Reviewed by Stephen Chenney. 9 10 * fast/text/svg-font-face-with-kerning-expected.png: Added. 11 * fast/text/svg-font-face-with-kerning-expected.txt: Added. 12 * fast/text/svg-font-face-with-kerning.html: Added. 13 1 14 2013-09-25 Anton Obzhirov <a.obzhirov@samsung.com> 2 15 -
trunk/Source/WebCore/ChangeLog
r156391 r156393 1 2013-09-25 Allan Sandfeld Jensen <allan.jensen@digia.com> 2 3 Support kerning with SVG web fonts 4 https://bugs.webkit.org/show_bug.cgi?id=117540 5 6 Reviewed by Stephen Chenney. 7 8 Adds the glue to WidthIterator to take advantage of kerning in SVG web fonts. 9 To supply SVG font kerning with its required text input, the signature of 10 applyFontTransforms has been extended. 11 12 Since SVG font kerning was extremely slow, it has been sped up by replacing 13 the iteration over all possible kerning definitions with a hash-map based 14 lookup of the leading symbol to be kerned. The new algorithm provides a 15 roughly 100x speed-up in SVG font kerning. 16 17 Test: fast/text/svg-font-face-with-kerning.html 18 19 * platform/graphics/TextRun.h: 20 (WebCore::TextRun::string): 21 * platform/graphics/WidthIterator.cpp: 22 (WebCore::applyFontTransforms): 23 (WebCore::WidthIterator::advanceInternal): 24 * rendering/svg/SVGTextRunRenderingContext.cpp: 25 (WebCore::SVGTextRunRenderingContext::applySVGKerning): 26 * rendering/svg/SVGTextRunRenderingContext.h: 27 * svg/SVGFontElement.cpp: 28 (WebCore::SVGFontElement::invalidateGlyphCache): 29 (WebCore::SVGFontElement::ensureGlyphCache): 30 (WebCore::SVGKerningMap::clear): 31 (WebCore::SVGKerningMap::insert): 32 (WebCore::stringMatchesUnicodeRange): 33 (WebCore::stringMatchesGlyphName): 34 (WebCore::stringMatchesUnicodeName): 35 (WebCore::matches): 36 (WebCore::kerningForPairOfStringsAndGlyphs): 37 (WebCore::SVGFontElement::horizontalKerningForPairOfStringsAndGlyphs): 38 (WebCore::SVGFontElement::verticalKerningForPairOfStringsAndGlyphs): 39 * svg/SVGFontElement.h: 40 (WebCore::SVGKerning::SVGKerning): 41 (WebCore::SVGKerningMap::isEmpty): 42 * svg/SVGHKernElement.cpp: 43 (WebCore::SVGHKernElement::buildHorizontalKerningPair): 44 * svg/SVGHKernElement.h: 45 * svg/SVGVKernElement.cpp: 46 (WebCore::SVGVKernElement::buildVerticalKerningPair): 47 * svg/SVGVKernElement.h: 48 1 49 2013-09-25 Andrei Parvu <parvu@adobe.com> 2 50 -
trunk/Source/WebCore/platform/graphics/TextRun.h
r141816 r156393 166 166 int length() const { return m_len; } 167 167 int charactersLength() const { return m_charactersLength; } 168 String string() const 169 { 170 if (is8Bit()) 171 return String(m_data.characters8, m_len); 172 return String(m_data.characters16, m_len); 173 } 168 174 169 175 #if ENABLE(8BIT_TEXTRUN) … … 210 216 virtual void drawSVGGlyphs(GraphicsContext*, const TextRun&, const SimpleFontData*, const GlyphBuffer&, int from, int to, const FloatPoint&) const = 0; 211 217 virtual float floatWidthUsingSVGFont(const Font&, const TextRun&, int& charsConsumed, String& glyphName) const = 0; 218 virtual bool applySVGKerning(const SimpleFontData*, WidthIterator&, GlyphBuffer*, int from) const = 0; 212 219 #endif 213 220 }; -
trunk/Source/WebCore/platform/graphics/WidthIterator.cpp
r154674 r156393 103 103 typedef Vector<pair<int, OriginalAdvancesForCharacterTreatedAsSpace>, 64> CharactersTreatedAsSpace; 104 104 105 static inline float applyFontTransforms(GlyphBuffer* glyphBuffer, bool ltr, int& lastGlyphCount, const SimpleFontData* fontData, TypesettingFeatures typesettingFeatures, CharactersTreatedAsSpace& charactersTreatedAsSpace)105 static inline float applyFontTransforms(GlyphBuffer* glyphBuffer, bool ltr, int& lastGlyphCount, const SimpleFontData* fontData, WidthIterator& iterator, TypesettingFeatures typesettingFeatures, CharactersTreatedAsSpace& charactersTreatedAsSpace) 106 106 { 107 107 ASSERT(typesettingFeatures & (Kerning | Ligatures)); … … 122 122 glyphBuffer->reverse(lastGlyphCount, glyphBufferSize - lastGlyphCount); 123 123 124 fontData->applyTransforms(glyphBuffer->glyphs(lastGlyphCount), advances + lastGlyphCount, glyphBufferSize - lastGlyphCount, typesettingFeatures); 124 // We need to handle transforms on SVG fonts internally, since they are rendered internally. 125 if (fontData->isSVGFont()) { 126 ASSERT(iterator.run().renderingContext()); 127 // SVG font ligatures are handled during glyph selection, only kerning remaining. 128 if (typesettingFeatures & Kerning) 129 iterator.run().renderingContext()->applySVGKerning(fontData, iterator, glyphBuffer, lastGlyphCount); 130 } else 131 fontData->applyTransforms(glyphBuffer->glyphs(lastGlyphCount), advances + lastGlyphCount, glyphBufferSize - lastGlyphCount, typesettingFeatures); 125 132 126 133 if (!ltr) … … 194 201 if (fontData != lastFontData && width) { 195 202 if (shouldApplyFontTransforms()) { 196 m_runWidthSoFar += applyFontTransforms(glyphBuffer, m_run.ltr(), lastGlyphCount, lastFontData, m_typesettingFeatures, charactersTreatedAsSpace);203 m_runWidthSoFar += applyFontTransforms(glyphBuffer, m_run.ltr(), lastGlyphCount, lastFontData, *this, m_typesettingFeatures, charactersTreatedAsSpace); 197 204 lastGlyphCount = glyphBuffer->size(); // applyFontTransforms doesn't update when there had been only one glyph. 198 205 } … … 316 323 317 324 if (shouldApplyFontTransforms()) 318 m_runWidthSoFar += applyFontTransforms(glyphBuffer, m_run.ltr(), lastGlyphCount, lastFontData, m_typesettingFeatures, charactersTreatedAsSpace);325 m_runWidthSoFar += applyFontTransforms(glyphBuffer, m_run.ltr(), lastGlyphCount, lastFontData, *this, m_typesettingFeatures, charactersTreatedAsSpace); 319 326 320 327 unsigned consumedCharacters = textIterator.currentCharacter() - m_currentCharacter; -
trunk/Source/WebCore/platform/graphics/texmap/TextureMapperLayer.cpp
r149813 r156393 73 73 if (m_state.replicaLayer) 74 74 m_state.replicaLayer->computeTransformsRecursive(); 75 for (size_t i = 0; i < m_children.size(); ++i) 75 for (size_t i = 0; i < m_children.size(); ++i) { 76 RELEASE_ASSERT(m_children[i]->m_parent == this); 76 77 m_children[i]->computeTransformsRecursive(); 78 } 77 79 78 80 // Reorder children if needed on the way back up. -
trunk/Source/WebCore/rendering/svg/SVGTextRunRenderingContext.cpp
r150730 r156393 82 82 return it.runWidthSoFar(); 83 83 } 84 84 85 bool SVGTextRunRenderingContext::applySVGKerning(const SimpleFontData* fontData, WidthIterator& iterator, GlyphBuffer* glyphBuffer, int from) const 86 { 87 ASSERT(glyphBuffer); 88 ASSERT(glyphBuffer->size() > 1); 89 SVGFontElement* fontElement = 0; 90 SVGFontFaceElement* fontFaceElement = 0; 91 92 svgFontAndFontFaceElementForFontData(fontData, fontFaceElement, fontElement); 93 if (!fontElement || !fontFaceElement) 94 return false; 95 96 float scale = scaleEmToUnits(fontData->platformData().size(), fontFaceElement->unitsPerEm()); 97 98 String lastGlyphName; 99 String lastUnicodeString; 100 int characterOffset = iterator.m_currentCharacter; 101 String text = iterator.run().string(); 102 const int glyphCount = glyphBuffer->size() - from; 103 GlyphBufferAdvance* advances = glyphBuffer->advances(from); 104 105 for (int i = 0; i < glyphCount; ++i) { 106 Glyph glyph = glyphBuffer->glyphAt(from + i); 107 if (!glyph) 108 continue; 109 float kerning = 0; 110 SVGGlyph svgGlyph = fontElement->svgGlyphForGlyph(glyph); 111 String unicodeString = text.substring(characterOffset, svgGlyph.unicodeStringLength); 112 if (i >= 1) { 113 // FIXME: Support vertical text. 114 kerning = fontElement->horizontalKerningForPairOfStringsAndGlyphs(lastUnicodeString, lastGlyphName, unicodeString, svgGlyph.glyphName); 115 advances[i - 1].setWidth(advances[i - 1].width() - kerning * scale); 116 } 117 lastGlyphName = svgGlyph.glyphName; 118 lastUnicodeString = unicodeString; 119 characterOffset += svgGlyph.unicodeStringLength; 120 } 121 122 return true; 123 } 124 85 125 void SVGTextRunRenderingContext::drawSVGGlyphs(GraphicsContext* context, const TextRun& run, const SimpleFontData* fontData, const GlyphBuffer& glyphBuffer, int from, int numGlyphs, const FloatPoint& point) const 86 126 { -
trunk/Source/WebCore/rendering/svg/SVGTextRunRenderingContext.h
r95901 r156393 46 46 virtual void drawSVGGlyphs(GraphicsContext*, const TextRun&, const SimpleFontData*, const GlyphBuffer&, int from, int to, const FloatPoint&) const; 47 47 virtual float floatWidthUsingSVGFont(const Font&, const TextRun&, int& charsConsumed, String& glyphName) const; 48 virtual bool applySVGKerning(const SimpleFontData*, WidthIterator&, GlyphBuffer*, int from) const; 48 49 #endif 49 50 -
trunk/Source/WebCore/svg/SVGFontElement.cpp
r155815 r156393 64 64 if (m_isGlyphCacheValid) { 65 65 m_glyphMap.clear(); 66 m_horizontalKerning Pairs.clear();67 m_verticalKerning Pairs.clear();66 m_horizontalKerningMap.clear(); 67 m_verticalKerningMap.clear(); 68 68 } 69 69 m_isGlyphCacheValid = false; … … 140 140 } else if (isSVGHKernElement(element)) { 141 141 SVGHKernElement* hkern = toSVGHKernElement(element); 142 hkern->buildHorizontalKerningPair(m_horizontalKerning Pairs);142 hkern->buildHorizontalKerningPair(m_horizontalKerningMap); 143 143 } else if (isSVGVKernElement(element)) { 144 144 SVGVKernElement* vkern = toSVGVKernElement(element); 145 vkern->buildVerticalKerningPair(m_verticalKerning Pairs);145 vkern->buildVerticalKerningPair(m_verticalKerningMap); 146 146 } else if (isSVGMissingGlyphElement(element) && !firstMissingGlyphElement) 147 147 firstMissingGlyphElement = toSVGMissingGlyphElement(element); … … 163 163 } 164 164 165 static bool stringMatchesUnicodeRange(const String& unicodeString, const UnicodeRanges& ranges, const HashSet<String>& unicodeValues) 165 void SVGKerningMap::clear() 166 { 167 unicodeMap.clear(); 168 glyphMap.clear(); 169 kerningUnicodeRangeMap.clear(); 170 } 171 172 void SVGKerningMap::insert(const SVGKerningPair& kerningPair) 173 { 174 SVGKerning svgKerning; 175 svgKerning.kerning = kerningPair.kerning; 176 svgKerning.unicodeRange2 = kerningPair.unicodeRange2; 177 svgKerning.unicodeName2 = kerningPair.unicodeName2; 178 svgKerning.glyphName2 = kerningPair.glyphName2; 179 180 HashSet<String>::const_iterator uIt = kerningPair.unicodeName1.begin(); 181 const HashSet<String>::const_iterator uEnd = kerningPair.unicodeName1.end(); 182 for (; uIt != uEnd; ++uIt) { 183 if (unicodeMap.contains(*uIt)) 184 unicodeMap.get(*uIt)->append(svgKerning); 185 else { 186 OwnPtr<SVGKerningVector> newVector = adoptPtr(new SVGKerningVector); 187 newVector->append(svgKerning); 188 unicodeMap.add(*uIt, newVector.release()); 189 } 190 } 191 192 HashSet<String>::const_iterator gIt = kerningPair.glyphName1.begin(); 193 const HashSet<String>::const_iterator gEnd = kerningPair.glyphName1.end(); 194 for (; gIt != gEnd; ++gIt) { 195 if (glyphMap.contains(*gIt)) 196 glyphMap.get(*gIt)->append(svgKerning); 197 else { 198 OwnPtr<SVGKerningVector> newVector = adoptPtr(new SVGKerningVector); 199 newVector->append(svgKerning); 200 glyphMap.add(*gIt, newVector.release()); 201 } 202 } 203 204 if (!kerningPair.unicodeRange1.isEmpty()) 205 kerningUnicodeRangeMap.append(kerningPair); 206 } 207 208 static inline bool stringMatchesUnicodeRange(const String& unicodeString, const UnicodeRanges& ranges) 166 209 { 167 210 if (unicodeString.isEmpty()) … … 177 220 } 178 221 179 if (!unicodeValues.isEmpty())180 return unicodeValues.contains(unicodeString);181 182 222 return false; 183 223 } 184 224 185 static bool stringMatchesGlyphName(const String& glyphName, const HashSet<String>& glyphValues)225 static inline bool stringMatchesGlyphName(const String& glyphName, const HashSet<String>& glyphValues) 186 226 { 187 227 if (glyphName.isEmpty()) 188 228 return false; 189 229 190 if (!glyphValues.isEmpty()) 191 return glyphValues.contains(glyphName); 192 193 return false; 194 } 195 196 static bool matches(const String& u1, const String& g1, const String& u2, const String& g2, const SVGKerningPair& kerningPair) 197 { 198 if (!stringMatchesUnicodeRange(u1, kerningPair.unicodeRange1, kerningPair.unicodeName1) 199 && !stringMatchesGlyphName(g1, kerningPair.glyphName1)) 230 return glyphValues.contains(glyphName); 231 } 232 233 static inline bool stringMatchesUnicodeName(const String& unicodeName, const HashSet<String>& unicodeValues) 234 { 235 if (unicodeName.isEmpty()) 200 236 return false; 201 237 202 if (!stringMatchesUnicodeRange(u2, kerningPair.unicodeRange2, kerningPair.unicodeName2) 203 && !stringMatchesGlyphName(g2, kerningPair.glyphName2)) 204 return false; 205 206 return true; 207 } 208 209 static float kerningForPairOfStringsAndGlyphs(const KerningPairVector& kerningPairs, const String& u1, const String& g1, const String& u2, const String& g2) 210 { 211 KerningPairVector::const_iterator it = kerningPairs.end() - 1; 212 const KerningPairVector::const_iterator begin = kerningPairs.begin() - 1; 213 for (; it != begin; --it) { 214 if (matches(u1, g1, u2, g2, *it)) 215 return it->kerning; 238 return unicodeValues.contains(unicodeName); 239 } 240 241 static inline bool matches(const String& u2, const String& g2, const SVGKerning& svgKerning) 242 { 243 return stringMatchesGlyphName(g2, svgKerning.glyphName2) 244 || stringMatchesUnicodeName(u2, svgKerning.unicodeName2) 245 || stringMatchesUnicodeRange(u2, svgKerning.unicodeRange2); 246 } 247 248 static inline bool matches(const String& u1, const String& u2, const String& g2, const SVGKerningPair& svgKerningPair) 249 { 250 return stringMatchesUnicodeRange(u1, svgKerningPair.unicodeRange1) && matches(u2, g2, svgKerningPair); 251 } 252 253 static inline float kerningForPairOfStringsAndGlyphs(const SVGKerningMap& kerningMap, const String& u1, const String& g1, const String& u2, const String& g2) 254 { 255 if (!g1.isEmpty() && kerningMap.glyphMap.contains(g1)) { 256 SVGKerningVector* kerningVector = kerningMap.glyphMap.get(g1); 257 SVGKerningVector::const_iterator it = kerningVector->end() - 1; 258 const SVGKerningVector::const_iterator begin = kerningVector->begin() - 1; 259 for (; it != begin; --it) { 260 if (matches(u2, g2, *it)) 261 return it->kerning; 262 } 263 } 264 265 if (!u1.isEmpty()) { 266 if (kerningMap.unicodeMap.contains(u1)) { 267 SVGKerningVector* kerningVector = kerningMap.unicodeMap.get(u1); 268 SVGKerningVector::const_iterator it = kerningVector->end() - 1; 269 const SVGKerningVector::const_iterator begin = kerningVector->begin() - 1; 270 for (; it != begin; --it) { 271 if (matches(u2, g2, *it)) 272 return it->kerning; 273 } 274 } 275 276 if (!kerningMap.kerningUnicodeRangeMap.isEmpty()) { 277 Vector<SVGKerningPair>::const_iterator it = kerningMap.kerningUnicodeRangeMap.end() - 1; 278 const Vector<SVGKerningPair>::const_iterator begin = kerningMap.kerningUnicodeRangeMap.begin() - 1; 279 for (; it != begin; --it) { 280 if (matches(u1, u2, g2, *it)) 281 return it->kerning; 282 } 283 } 216 284 } 217 285 … … 221 289 float SVGFontElement::horizontalKerningForPairOfStringsAndGlyphs(const String& u1, const String& g1, const String& u2, const String& g2) const 222 290 { 223 if (m_horizontalKerning Pairs.isEmpty())291 if (m_horizontalKerningMap.isEmpty()) 224 292 return 0; 225 293 226 return kerningForPairOfStringsAndGlyphs(m_horizontalKerning Pairs, u1, g1, u2, g2);294 return kerningForPairOfStringsAndGlyphs(m_horizontalKerningMap, u1, g1, u2, g2); 227 295 } 228 296 229 297 float SVGFontElement::verticalKerningForPairOfStringsAndGlyphs(const String& u1, const String& g1, const String& u2, const String& g2) const 230 298 { 231 if (m_verticalKerning Pairs.isEmpty())299 if (m_verticalKerningMap.isEmpty()) 232 300 return 0; 233 301 234 return kerningForPairOfStringsAndGlyphs(m_verticalKerning Pairs, u1, g1, u2, g2);302 return kerningForPairOfStringsAndGlyphs(m_verticalKerningMap, u1, g1, u2, g2); 235 303 } 236 304 -
trunk/Source/WebCore/svg/SVGFontElement.h
r155815 r156393 32 32 namespace WebCore { 33 33 34 // Describe an SVG <hkern>/<vkern> element 35 struct SVGKerning Pair{34 // Describe an SVG <hkern>/<vkern> element already matched on the first symbol. 35 struct SVGKerning { 36 36 float kerning; 37 UnicodeRanges unicodeRange1;38 37 UnicodeRanges unicodeRange2; 39 HashSet<String> unicodeName1;40 38 HashSet<String> unicodeName2; 41 HashSet<String> glyphName1;42 39 HashSet<String> glyphName2; 43 44 SVGKerning Pair()40 41 SVGKerning() 45 42 : kerning(0) 46 { 47 } 43 { } 48 44 }; 49 45 50 typedef Vector<SVGKerningPair> KerningPairVector; 46 // Describe an SVG <hkern>/<vkern> element 47 struct SVGKerningPair : public SVGKerning { 48 UnicodeRanges unicodeRange1; 49 HashSet<String> unicodeName1; 50 HashSet<String> glyphName1; 51 }; 52 53 typedef Vector<SVGKerning> SVGKerningVector; 54 55 struct SVGKerningMap { 56 HashMap<String, OwnPtr<SVGKerningVector> > unicodeMap; 57 HashMap<String, OwnPtr<SVGKerningVector> > glyphMap; 58 Vector<SVGKerningPair> kerningUnicodeRangeMap; 59 60 bool isEmpty() const { return unicodeMap.isEmpty() && glyphMap.isEmpty() && kerningUnicodeRangeMap.isEmpty(); } 61 void clear(); 62 void insert(const SVGKerningPair&); 63 }; 51 64 52 65 class SVGMissingGlyphElement; … … 82 95 END_DECLARE_ANIMATED_PROPERTIES 83 96 84 KerningPairVector m_horizontalKerningPairs;85 KerningPairVector m_verticalKerningPairs;97 SVGKerningMap m_horizontalKerningMap; 98 SVGKerningMap m_verticalKerningMap; 86 99 SVGGlyphMap m_glyphMap; 87 100 Glyph m_missingGlyph; -
trunk/Source/WebCore/svg/SVGHKernElement.cpp
r155815 r156393 60 60 } 61 61 62 void SVGHKernElement::buildHorizontalKerningPair( KerningPairVector& kerningPairs)62 void SVGHKernElement::buildHorizontalKerningPair(SVGKerningMap& kerningMap) 63 63 { 64 64 String u1 = fastGetAttribute(SVGNames::u1Attr); … … 75 75 && parseKerningUnicodeString(u2, kerningPair.unicodeRange2, kerningPair.unicodeName2)) { 76 76 kerningPair.kerning = fastGetAttribute(SVGNames::kAttr).string().toFloat(); 77 kerning Pairs.append(kerningPair);77 kerningMap.insert(kerningPair); 78 78 } 79 79 } -
trunk/Source/WebCore/svg/SVGHKernElement.h
r155815 r156393 32 32 static PassRefPtr<SVGHKernElement> create(const QualifiedName&, Document&); 33 33 34 void buildHorizontalKerningPair( KerningPairVector&);34 void buildHorizontalKerningPair(SVGKerningMap&); 35 35 36 36 private: -
trunk/Source/WebCore/svg/SVGVKernElement.cpp
r155815 r156393 61 61 } 62 62 63 void SVGVKernElement::buildVerticalKerningPair( KerningPairVector& kerningPairs)63 void SVGVKernElement::buildVerticalKerningPair(SVGKerningMap& kerningMap) 64 64 { 65 65 String u1 = fastGetAttribute(SVGNames::u1Attr); … … 76 76 && parseKerningUnicodeString(u2, kerningPair.unicodeRange2, kerningPair.unicodeName2)) { 77 77 kerningPair.kerning = fastGetAttribute(SVGNames::kAttr).string().toFloat(); 78 kerning Pairs.append(kerningPair);78 kerningMap.insert(kerningPair); 79 79 } 80 80 } -
trunk/Source/WebCore/svg/SVGVKernElement.h
r155815 r156393 31 31 static PassRefPtr<SVGVKernElement> create(const QualifiedName&, Document&); 32 32 33 void buildVerticalKerningPair( KerningPairVector&);33 void buildVerticalKerningPair(SVGKerningMap&); 34 34 35 35 private:
Note: See TracChangeset
for help on using the changeset viewer.