1 /* 2 * Copyright (C) 2007 Eric Seidel <eric (at) webkit.org> 3 * Copyright (C) 2007 Nikolas Zimmermann <zimmermann (at) 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 35 namespace blink { 36 37 inline SVGFontElement::SVGFontElement(Document& document) 38 : SVGElement(SVGNames::fontTag, document) 39 , m_missingGlyph(0) 40 , m_isGlyphCacheValid(false) 41 { 42 UseCounter::count(document, UseCounter::SVGFontElement); 43 } 44 45 DEFINE_NODE_FACTORY(SVGFontElement) 46 47 void SVGFontElement::invalidateGlyphCache() 48 { 49 if (m_isGlyphCacheValid) { 50 m_glyphMap.clear(); 51 m_horizontalKerningTable.clear(); 52 m_verticalKerningTable.clear(); 53 } 54 m_isGlyphCacheValid = false; 55 } 56 57 void SVGFontElement::registerLigaturesInGlyphCache(Vector<String>& ligatures) 58 { 59 ASSERT(!ligatures.isEmpty()); 60 61 // Register each character of a ligature in the map, if not present. 62 // Eg. If only a "fi" ligature is present, but not "f" and "i", the 63 // GlyphPage will not contain any entries for "f" and "i", so the 64 // SVGFont is not used to render the text "fi1234". Register an 65 // empty SVGGlyph with the character, so the SVG Font will be used 66 // to render the text. If someone tries to render "f2" the SVG Font 67 // will not be able to find a glyph for "f", but handles the fallback 68 // character substitution properly through glyphDataForCharacter(). 69 Vector<SVGGlyph> glyphs; 70 size_t ligaturesSize = ligatures.size(); 71 for (size_t i = 0; i < ligaturesSize; ++i) { 72 const String& unicode = ligatures[i]; 73 74 unsigned unicodeLength = unicode.length(); 75 ASSERT(unicodeLength > 1); 76 77 for (unsigned i = 0; i < unicodeLength; ++i) { 78 String lookupString = unicode.substring(i, 1); 79 m_glyphMap.collectGlyphsForString(lookupString, glyphs); 80 if (!glyphs.isEmpty()) { 81 glyphs.clear(); 82 continue; 83 } 84 85 // This glyph is never meant to be used for rendering, only as identifier as a part of a ligature. 86 SVGGlyph newGlyphPart; 87 newGlyphPart.isPartOfLigature = true; 88 m_glyphMap.addGlyph(String(), lookupString, newGlyphPart); 89 } 90 } 91 } 92 93 static inline KerningPairKey makeKerningPairKey(Glyph glyphId1, Glyph glyphId2) 94 { 95 return glyphId1 << 16 | glyphId2; 96 } 97 98 Vector<SVGGlyph> SVGFontElement::buildGlyphList(const UnicodeRanges& unicodeRanges, const HashSet<String>& unicodeNames, const HashSet<String>& glyphNames) const 99 { 100 Vector<SVGGlyph> glyphs; 101 if (!unicodeRanges.isEmpty()) { 102 const UnicodeRanges::const_iterator end = unicodeRanges.end(); 103 for (UnicodeRanges::const_iterator it = unicodeRanges.begin(); it != end; ++it) 104 m_glyphMap.collectGlyphsForUnicodeRange(*it, glyphs); 105 } 106 if (!unicodeNames.isEmpty()) { 107 const HashSet<String>::const_iterator end = unicodeNames.end(); 108 for (HashSet<String>::const_iterator it = unicodeNames.begin(); it != end; ++it) 109 m_glyphMap.collectGlyphsForStringExact(*it, glyphs); 110 } 111 if (!glyphNames.isEmpty()) { 112 const HashSet<String>::const_iterator end = glyphNames.end(); 113 for (HashSet<String>::const_iterator it = glyphNames.begin(); it != end; ++it) { 114 const SVGGlyph& glyph = m_glyphMap.glyphIdentifierForGlyphName(*it); 115 if (glyph.tableEntry) 116 glyphs.append(glyph); 117 } 118 } 119 return glyphs; 120 } 121 122 void SVGFontElement::addPairsToKerningTable(const SVGKerningPair& kerningPair, KerningTable& kerningTable) 123 { 124 Vector<SVGGlyph> glyphsLhs = buildGlyphList(kerningPair.unicodeRange1, kerningPair.unicodeName1, kerningPair.glyphName1); 125 Vector<SVGGlyph> glyphsRhs = buildGlyphList(kerningPair.unicodeRange2, kerningPair.unicodeName2, kerningPair.glyphName2); 126 if (glyphsLhs.isEmpty() || glyphsRhs.isEmpty()) 127 return; 128 size_t glyphsLhsSize = glyphsLhs.size(); 129 size_t glyphsRhsSize = glyphsRhs.size(); 130 // Enumerate all the valid kerning pairs, and add them to the table. 131 for (size_t lhsIndex = 0; lhsIndex < glyphsLhsSize; ++lhsIndex) { 132 for (size_t rhsIndex = 0; rhsIndex < glyphsRhsSize; ++rhsIndex) { 133 Glyph glyph1 = glyphsLhs[lhsIndex].tableEntry; 134 Glyph glyph2 = glyphsRhs[rhsIndex].tableEntry; 135 ASSERT(glyph1 && glyph2); 136 kerningTable.add(makeKerningPairKey(glyph1, glyph2), kerningPair.kerning); 137 } 138 } 139 } 140 141 void SVGFontElement::buildKerningTable(const KerningPairVector& kerningPairs, KerningTable& kerningTable) 142 { 143 size_t kerningPairsSize = kerningPairs.size(); 144 for (size_t i = 0; i < kerningPairsSize; ++i) 145 addPairsToKerningTable(kerningPairs[i], kerningTable); 146 } 147 148 void SVGFontElement::ensureGlyphCache() 149 { 150 if (m_isGlyphCacheValid) 151 return; 152 153 KerningPairVector horizontalKerningPairs; 154 KerningPairVector verticalKerningPairs; 155 156 SVGMissingGlyphElement* firstMissingGlyphElement = 0; 157 Vector<String> ligatures; 158 for (SVGElement* element = Traversal<SVGElement>::firstChild(*this); element; element = Traversal<SVGElement>::nextSibling(*element)) { 159 if (isSVGGlyphElement(*element)) { 160 SVGGlyphElement& glyph = toSVGGlyphElement(*element); 161 AtomicString unicode = glyph.fastGetAttribute(SVGNames::unicodeAttr); 162 AtomicString glyphId = glyph.getIdAttribute(); 163 if (glyphId.isEmpty() && unicode.isEmpty()) 164 continue; 165 166 m_glyphMap.addGlyph(glyphId, unicode, glyph.buildGlyphIdentifier()); 167 168 // Register ligatures, if needed, don't mix up with surrogate pairs though! 169 if (unicode.length() > 1 && !U16_IS_SURROGATE(unicode[0])) 170 ligatures.append(unicode.string()); 171 } else if (isSVGHKernElement(*element)) { 172 toSVGHKernElement(*element).buildHorizontalKerningPair(horizontalKerningPairs); 173 } else if (isSVGVKernElement(*element)) { 174 toSVGVKernElement(*element).buildVerticalKerningPair(verticalKerningPairs); 175 } else if (isSVGMissingGlyphElement(*element) && !firstMissingGlyphElement) { 176 firstMissingGlyphElement = toSVGMissingGlyphElement(element); 177 } 178 } 179 180 // Build the kerning tables. 181 buildKerningTable(horizontalKerningPairs, m_horizontalKerningTable); 182 buildKerningTable(verticalKerningPairs, m_verticalKerningTable); 183 184 // The glyph-name->glyph-id map won't be needed/used after having built the kerning table(s). 185 m_glyphMap.dropNamedGlyphMap(); 186 187 // Register each character of each ligature, if needed. 188 if (!ligatures.isEmpty()) 189 registerLigaturesInGlyphCache(ligatures); 190 191 // Register missing-glyph element, if present. 192 if (firstMissingGlyphElement) { 193 SVGGlyph svgGlyph = SVGGlyphElement::buildGenericGlyphIdentifier(firstMissingGlyphElement); 194 m_glyphMap.appendToGlyphTable(svgGlyph); 195 m_missingGlyph = svgGlyph.tableEntry; 196 ASSERT(m_missingGlyph > 0); 197 } 198 199 m_isGlyphCacheValid = true; 200 } 201 202 static float kerningForPairOfGlyphs(const KerningTable& kerningTable, Glyph glyphId1, Glyph glyphId2) 203 { 204 KerningTable::const_iterator result = kerningTable.find(makeKerningPairKey(glyphId1, glyphId2)); 205 if (result != kerningTable.end()) 206 return result->value; 207 208 return 0; 209 } 210 211 float SVGFontElement::horizontalKerningForPairOfGlyphs(Glyph glyphId1, Glyph glyphId2) const 212 { 213 if (m_horizontalKerningTable.isEmpty()) 214 return 0; 215 216 return kerningForPairOfGlyphs(m_horizontalKerningTable, glyphId1, glyphId2); 217 } 218 219 float SVGFontElement::verticalKerningForPairOfGlyphs(Glyph glyphId1, Glyph glyphId2) const 220 { 221 if (m_verticalKerningTable.isEmpty()) 222 return 0; 223 224 return kerningForPairOfGlyphs(m_verticalKerningTable, glyphId1, glyphId2); 225 } 226 227 void SVGFontElement::collectGlyphsForString(const String& string, Vector<SVGGlyph>& glyphs) 228 { 229 ensureGlyphCache(); 230 m_glyphMap.collectGlyphsForString(string, glyphs); 231 } 232 233 void SVGFontElement::collectGlyphsForAltGlyphReference(const String& glyphIdentifier, Vector<SVGGlyph>& glyphs) 234 { 235 ensureGlyphCache(); 236 // FIXME: We only support glyphName -> single glyph mapping so far. 237 glyphs.append(m_glyphMap.glyphIdentifierForAltGlyphReference(glyphIdentifier)); 238 } 239 240 SVGGlyph SVGFontElement::svgGlyphForGlyph(Glyph glyph) 241 { 242 ensureGlyphCache(); 243 return m_glyphMap.svgGlyphForGlyph(glyph); 244 } 245 246 Glyph SVGFontElement::missingGlyph() 247 { 248 ensureGlyphCache(); 249 return m_missingGlyph; 250 } 251 252 } // namespace blink 253 254 #endif // ENABLE(SVG_FONTS) 255