1/* 2 * Copyright (C) 2007 Eric Seidel <eric@webkit.org> 3 * Copyright (C) 2007 Nikolas Zimmermann <zimmermann@kde.org> 4 * Copyright (C) Research In Motion Limited 2010. All rights reserved. 5 * 6 * This library is free software; you can redistribute it and/or 7 * modify it under the terms of the GNU Library General Public 8 * License as published by the Free Software Foundation; either 9 * version 2 of the License, or (at your option) any later version. 10 * 11 * This library is distributed in the hope that it will be useful, 12 * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 * Library General Public License for more details. 15 * 16 * You should have received a copy of the GNU Library General Public License 17 * along with this library; see the file COPYING.LIB. If not, write to 18 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 19 * Boston, MA 02110-1301, USA. 20 */ 21 22#include "config.h" 23 24#if ENABLE(SVG_FONTS) 25#include "core/svg/SVGFontElement.h" 26 27#include "core/dom/ElementTraversal.h" 28#include "core/frame/UseCounter.h" 29#include "core/svg/SVGGlyphElement.h" 30#include "core/svg/SVGHKernElement.h" 31#include "core/svg/SVGMissingGlyphElement.h" 32#include "core/svg/SVGVKernElement.h" 33#include "wtf/ASCIICType.h" 34 35namespace WebCore { 36 37inline SVGFontElement::SVGFontElement(Document& document) 38 : SVGElement(SVGNames::fontTag, document) 39 , m_missingGlyph(0) 40 , m_isGlyphCacheValid(false) 41{ 42 ScriptWrappable::init(this); 43 44 UseCounter::count(document, UseCounter::SVGFontElement); 45} 46 47DEFINE_NODE_FACTORY(SVGFontElement) 48 49void SVGFontElement::invalidateGlyphCache() 50{ 51 if (m_isGlyphCacheValid) { 52 m_glyphMap.clear(); 53 m_horizontalKerningTable.clear(); 54 m_verticalKerningTable.clear(); 55 } 56 m_isGlyphCacheValid = false; 57} 58 59void SVGFontElement::registerLigaturesInGlyphCache(Vector<String>& ligatures) 60{ 61 ASSERT(!ligatures.isEmpty()); 62 63 // Register each character of a ligature in the map, if not present. 64 // Eg. If only a "fi" ligature is present, but not "f" and "i", the 65 // GlyphPage will not contain any entries for "f" and "i", so the 66 // SVGFont is not used to render the text "fi1234". Register an 67 // empty SVGGlyph with the character, so the SVG Font will be used 68 // to render the text. If someone tries to render "f2" the SVG Font 69 // will not be able to find a glyph for "f", but handles the fallback 70 // character substitution properly through glyphDataForCharacter(). 71 Vector<SVGGlyph> glyphs; 72 size_t ligaturesSize = ligatures.size(); 73 for (size_t i = 0; i < ligaturesSize; ++i) { 74 const String& unicode = ligatures[i]; 75 76 unsigned unicodeLength = unicode.length(); 77 ASSERT(unicodeLength > 1); 78 79 for (unsigned i = 0; i < unicodeLength; ++i) { 80 String lookupString = unicode.substring(i, 1); 81 m_glyphMap.collectGlyphsForString(lookupString, glyphs); 82 if (!glyphs.isEmpty()) { 83 glyphs.clear(); 84 continue; 85 } 86 87 // This glyph is never meant to be used for rendering, only as identifier as a part of a ligature. 88 SVGGlyph newGlyphPart; 89 newGlyphPart.isPartOfLigature = true; 90 m_glyphMap.addGlyph(String(), lookupString, newGlyphPart); 91 } 92 } 93} 94 95static inline KerningPairKey makeKerningPairKey(Glyph glyphId1, Glyph glyphId2) 96{ 97 return glyphId1 << 16 | glyphId2; 98} 99 100Vector<SVGGlyph> SVGFontElement::buildGlyphList(const UnicodeRanges& unicodeRanges, const HashSet<String>& unicodeNames, const HashSet<String>& glyphNames) const 101{ 102 Vector<SVGGlyph> glyphs; 103 if (!unicodeRanges.isEmpty()) { 104 const UnicodeRanges::const_iterator end = unicodeRanges.end(); 105 for (UnicodeRanges::const_iterator it = unicodeRanges.begin(); it != end; ++it) 106 m_glyphMap.collectGlyphsForUnicodeRange(*it, glyphs); 107 } 108 if (!unicodeNames.isEmpty()) { 109 const HashSet<String>::const_iterator end = unicodeNames.end(); 110 for (HashSet<String>::const_iterator it = unicodeNames.begin(); it != end; ++it) 111 m_glyphMap.collectGlyphsForStringExact(*it, glyphs); 112 } 113 if (!glyphNames.isEmpty()) { 114 const HashSet<String>::const_iterator end = glyphNames.end(); 115 for (HashSet<String>::const_iterator it = glyphNames.begin(); it != end; ++it) { 116 const SVGGlyph& glyph = m_glyphMap.glyphIdentifierForGlyphName(*it); 117 if (glyph.tableEntry) 118 glyphs.append(glyph); 119 } 120 } 121 return glyphs; 122} 123 124void SVGFontElement::addPairsToKerningTable(const SVGKerningPair& kerningPair, KerningTable& kerningTable) 125{ 126 Vector<SVGGlyph> glyphsLhs = buildGlyphList(kerningPair.unicodeRange1, kerningPair.unicodeName1, kerningPair.glyphName1); 127 Vector<SVGGlyph> glyphsRhs = buildGlyphList(kerningPair.unicodeRange2, kerningPair.unicodeName2, kerningPair.glyphName2); 128 if (glyphsLhs.isEmpty() || glyphsRhs.isEmpty()) 129 return; 130 size_t glyphsLhsSize = glyphsLhs.size(); 131 size_t glyphsRhsSize = glyphsRhs.size(); 132 // Enumerate all the valid kerning pairs, and add them to the table. 133 for (size_t lhsIndex = 0; lhsIndex < glyphsLhsSize; ++lhsIndex) { 134 for (size_t rhsIndex = 0; rhsIndex < glyphsRhsSize; ++rhsIndex) { 135 Glyph glyph1 = glyphsLhs[lhsIndex].tableEntry; 136 Glyph glyph2 = glyphsRhs[rhsIndex].tableEntry; 137 ASSERT(glyph1 && glyph2); 138 kerningTable.add(makeKerningPairKey(glyph1, glyph2), kerningPair.kerning); 139 } 140 } 141} 142 143void SVGFontElement::buildKerningTable(const KerningPairVector& kerningPairs, KerningTable& kerningTable) 144{ 145 size_t kerningPairsSize = kerningPairs.size(); 146 for (size_t i = 0; i < kerningPairsSize; ++i) 147 addPairsToKerningTable(kerningPairs[i], kerningTable); 148} 149 150void SVGFontElement::ensureGlyphCache() 151{ 152 if (m_isGlyphCacheValid) 153 return; 154 155 KerningPairVector horizontalKerningPairs; 156 KerningPairVector verticalKerningPairs; 157 158 SVGMissingGlyphElement* firstMissingGlyphElement = 0; 159 Vector<String> ligatures; 160 for (SVGElement* element = Traversal<SVGElement>::firstChild(*this); element; element = Traversal<SVGElement>::nextSibling(*element)) { 161 if (isSVGGlyphElement(*element)) { 162 SVGGlyphElement& glyph = toSVGGlyphElement(*element); 163 AtomicString unicode = glyph.fastGetAttribute(SVGNames::unicodeAttr); 164 AtomicString glyphId = glyph.getIdAttribute(); 165 if (glyphId.isEmpty() && unicode.isEmpty()) 166 continue; 167 168 m_glyphMap.addGlyph(glyphId, unicode, glyph.buildGlyphIdentifier()); 169 170 // Register ligatures, if needed, don't mix up with surrogate pairs though! 171 if (unicode.length() > 1 && !U16_IS_SURROGATE(unicode[0])) 172 ligatures.append(unicode.string()); 173 } else if (isSVGHKernElement(*element)) { 174 toSVGHKernElement(*element).buildHorizontalKerningPair(horizontalKerningPairs); 175 } else if (isSVGVKernElement(*element)) { 176 toSVGVKernElement(*element).buildVerticalKerningPair(verticalKerningPairs); 177 } else if (isSVGMissingGlyphElement(*element) && !firstMissingGlyphElement) { 178 firstMissingGlyphElement = toSVGMissingGlyphElement(element); 179 } 180 } 181 182 // Build the kerning tables. 183 buildKerningTable(horizontalKerningPairs, m_horizontalKerningTable); 184 buildKerningTable(verticalKerningPairs, m_verticalKerningTable); 185 186 // The glyph-name->glyph-id map won't be needed/used after having built the kerning table(s). 187 m_glyphMap.dropNamedGlyphMap(); 188 189 // Register each character of each ligature, if needed. 190 if (!ligatures.isEmpty()) 191 registerLigaturesInGlyphCache(ligatures); 192 193 // Register missing-glyph element, if present. 194 if (firstMissingGlyphElement) { 195 SVGGlyph svgGlyph = SVGGlyphElement::buildGenericGlyphIdentifier(firstMissingGlyphElement); 196 m_glyphMap.appendToGlyphTable(svgGlyph); 197 m_missingGlyph = svgGlyph.tableEntry; 198 ASSERT(m_missingGlyph > 0); 199 } 200 201 m_isGlyphCacheValid = true; 202} 203 204static float kerningForPairOfGlyphs(const KerningTable& kerningTable, Glyph glyphId1, Glyph glyphId2) 205{ 206 KerningTable::const_iterator result = kerningTable.find(makeKerningPairKey(glyphId1, glyphId2)); 207 if (result != kerningTable.end()) 208 return result->value; 209 210 return 0; 211} 212 213float SVGFontElement::horizontalKerningForPairOfGlyphs(Glyph glyphId1, Glyph glyphId2) const 214{ 215 if (m_horizontalKerningTable.isEmpty()) 216 return 0; 217 218 return kerningForPairOfGlyphs(m_horizontalKerningTable, glyphId1, glyphId2); 219} 220 221float SVGFontElement::verticalKerningForPairOfGlyphs(Glyph glyphId1, Glyph glyphId2) const 222{ 223 if (m_verticalKerningTable.isEmpty()) 224 return 0; 225 226 return kerningForPairOfGlyphs(m_verticalKerningTable, glyphId1, glyphId2); 227} 228 229void SVGFontElement::collectGlyphsForString(const String& string, Vector<SVGGlyph>& glyphs) 230{ 231 ensureGlyphCache(); 232 m_glyphMap.collectGlyphsForString(string, glyphs); 233} 234 235void SVGFontElement::collectGlyphsForAltGlyphReference(const String& glyphIdentifier, Vector<SVGGlyph>& glyphs) 236{ 237 ensureGlyphCache(); 238 // FIXME: We only support glyphName -> single glyph mapping so far. 239 glyphs.append(m_glyphMap.glyphIdentifierForAltGlyphReference(glyphIdentifier)); 240} 241 242SVGGlyph SVGFontElement::svgGlyphForGlyph(Glyph glyph) 243{ 244 ensureGlyphCache(); 245 return m_glyphMap.svgGlyphForGlyph(glyph); 246} 247 248Glyph SVGFontElement::missingGlyph() 249{ 250 ensureGlyphCache(); 251 return m_missingGlyph; 252} 253 254} 255 256#endif // ENABLE(SVG_FONTS) 257