1 /* 2 Copyright (C) 2007 Eric Seidel <eric (at) webkit.org> 3 Copyright (C) 2007 Nikolas Zimmermann <zimmermann (at) kde.org> 4 5 This library is free software; you can redistribute it and/or 6 modify it under the terms of the GNU Library General Public 7 License as published by the Free Software Foundation; either 8 version 2 of the License, or (at your option) any later version. 9 10 This library is distributed in the hope that it will be useful, 11 but WITHOUT ANY WARRANTY; without even the implied warranty of 12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13 Library General Public License for more details. 14 15 You should have received a copy of the GNU Library General Public License 16 along with this library; see the file COPYING.LIB. If not, write to 17 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 18 Boston, MA 02110-1301, USA. 19 */ 20 21 #include "config.h" 22 23 #if ENABLE(SVG_FONTS) 24 #include "SVGFontElement.h" 25 26 #include "Document.h" 27 #include "Font.h" 28 #include "GlyphPageTreeNode.h" 29 #include "SVGGlyphElement.h" 30 #include "SVGMissingGlyphElement.h" 31 #include "SVGNames.h" 32 #include "SVGParserUtilities.h" 33 #include <wtf/ASCIICType.h> 34 35 using namespace WTF; 36 37 namespace WebCore { 38 39 using namespace SVGNames; 40 41 SVGFontElement::SVGFontElement(const QualifiedName& tagName, Document* doc) 42 : SVGStyledElement(tagName, doc) 43 , m_isGlyphCacheValid(false) 44 { 45 } 46 47 SVGFontElement::~SVGFontElement() 48 { 49 } 50 51 void SVGFontElement::synchronizeProperty(const QualifiedName& attrName) 52 { 53 SVGStyledElement::synchronizeProperty(attrName); 54 55 if (attrName == anyQName() || SVGExternalResourcesRequired::isKnownAttribute(attrName)) 56 synchronizeExternalResourcesRequired(); 57 } 58 59 void SVGFontElement::invalidateGlyphCache() 60 { 61 if (m_isGlyphCacheValid) { 62 m_glyphMap.clear(); 63 m_kerningPairs.clear(); 64 } 65 m_isGlyphCacheValid = false; 66 } 67 68 SVGMissingGlyphElement* SVGFontElement::firstMissingGlyphElement() const 69 { 70 for (Node* child = firstChild(); child; child = child->nextSibling()) { 71 if (child->hasTagName(missing_glyphTag)) 72 return static_cast<SVGMissingGlyphElement*>(child); 73 } 74 75 return 0; 76 } 77 78 void SVGFontElement::ensureGlyphCache() const 79 { 80 if (m_isGlyphCacheValid) 81 return; 82 83 for (Node* child = firstChild(); child; child = child->nextSibling()) { 84 if (child->hasTagName(glyphTag)) { 85 SVGGlyphElement* glyph = static_cast<SVGGlyphElement*>(child); 86 String unicode = glyph->getAttribute(unicodeAttr); 87 if (unicode.length()) 88 m_glyphMap.add(unicode, glyph->buildGlyphIdentifier()); 89 } else if (child->hasTagName(hkernTag)) { 90 SVGHKernElement* hkern = static_cast<SVGHKernElement*>(child); 91 SVGHorizontalKerningPair kerningPair = hkern->buildHorizontalKerningPair(); 92 m_kerningPairs.append(kerningPair); 93 } 94 } 95 96 m_isGlyphCacheValid = true; 97 } 98 99 // Returns the number of characters consumed or 0 if no range was found. 100 static unsigned parseUnicodeRange(const UChar* characters, unsigned length, pair<unsigned, unsigned>& range) 101 { 102 if (length < 2) 103 return 0; 104 if (characters[0] != 'U') 105 return 0; 106 if (characters[1] != '+') 107 return 0; 108 109 // Parse the starting hex number (or its prefix). 110 unsigned start = 0; 111 unsigned startLength = 0; 112 for (unsigned i = 2; i < length; ++i) { 113 if (!isASCIIHexDigit(characters[i])) 114 break; 115 if (++startLength > 6) 116 return 0; 117 start = (start << 4) | toASCIIHexValue(characters[i]); 118 } 119 120 // Handle the case of ranges separated by "-" sign. 121 if (2 + startLength < length && characters[2 + startLength] == '-') { 122 if (!startLength) 123 return 0; 124 125 // Parse the ending hex number (or its prefix). 126 unsigned end = 0; 127 unsigned endLength = 0; 128 for (unsigned i = 2 + startLength + 1; i < length; ++i) { 129 if (!isASCIIHexDigit(characters[i])) 130 break; 131 if (++endLength > 6) 132 return 0; 133 end = (end << 4) | toASCIIHexValue(characters[i]); 134 } 135 136 if (!endLength) 137 return 0; 138 139 range.first = start; 140 range.second = end; 141 return 2 + startLength + 1 + endLength; 142 } 143 144 // Handle the case of a number with some optional trailing question marks. 145 unsigned end = start; 146 for (unsigned i = 2 + startLength; i < length; ++i) { 147 if (characters[i] != '?') 148 break; 149 if (++startLength > 6) 150 return 0; 151 start <<= 4; 152 end = (end << 4) | 0xF; 153 } 154 155 if (!startLength) 156 return 0; 157 158 range.first = start; 159 range.second = end; 160 return 2 + startLength; 161 } 162 163 static bool parseUnicodeRangeList(const UChar* characters, unsigned length, Vector<pair<unsigned, unsigned> >& ranges) 164 { 165 ranges.clear(); 166 if (!length) 167 return true; 168 169 const UChar* remainingCharacters = characters; 170 unsigned remainingLength = length; 171 172 while (1) { 173 pair<unsigned, unsigned> range; 174 unsigned charactersConsumed = parseUnicodeRange(remainingCharacters, remainingLength, range); 175 if (charactersConsumed) { 176 ranges.append(range); 177 remainingCharacters += charactersConsumed; 178 remainingLength -= charactersConsumed; 179 } else { 180 if (!remainingLength) 181 return false; 182 UChar character = remainingCharacters[0]; 183 if (character == ',') 184 return false; 185 ranges.append(make_pair(character, character)); 186 ++remainingCharacters; 187 --remainingLength; 188 } 189 if (!remainingLength) 190 return true; 191 if (remainingCharacters[0] != ',') 192 return false; 193 ++remainingCharacters; 194 --remainingLength; 195 } 196 } 197 198 static bool stringMatchesUnicodeRange(const String& unicodeString, const String& unicodeRangeSpec) 199 { 200 Vector<pair<unsigned, unsigned> > ranges; 201 if (!parseUnicodeRangeList(unicodeRangeSpec.characters(), unicodeRangeSpec.length(), ranges)) 202 return false; 203 204 if (unicodeString.length() != ranges.size()) 205 return false; 206 207 for (size_t i = 0; i < unicodeString.length(); ++i) { 208 UChar c = unicodeString[i]; 209 if (c < ranges[i].first || c > ranges[i].second) 210 return false; 211 } 212 213 return true; 214 } 215 216 static bool matches(const String& u1, const String& g1, const String& u2, const String& g2, const SVGHorizontalKerningPair& kerningPair) 217 { 218 if (kerningPair.unicode1.length() && !stringMatchesUnicodeRange(u1, kerningPair.unicode1)) 219 return false; 220 if (kerningPair.glyphName1.length() && kerningPair.glyphName1 != g1) 221 return false; 222 223 if (kerningPair.unicode2.length() && !stringMatchesUnicodeRange(u2, kerningPair.unicode2)) 224 return false; 225 if (kerningPair.glyphName2.length() && kerningPair.glyphName2 != g2) 226 return false; 227 228 return true; 229 } 230 231 bool SVGFontElement::getHorizontalKerningPairForStringsAndGlyphs(const String& u1, const String& g1, const String& u2, const String& g2, SVGHorizontalKerningPair& kerningPair) const 232 { 233 for (size_t i = 0; i < m_kerningPairs.size(); ++i) { 234 if (matches(u1, g1, u2, g2, m_kerningPairs[i])) { 235 kerningPair = m_kerningPairs[i]; 236 return true; 237 } 238 } 239 240 return false; 241 } 242 243 void SVGFontElement::getGlyphIdentifiersForString(const String& string, Vector<SVGGlyphIdentifier>& glyphs) const 244 { 245 ensureGlyphCache(); 246 m_glyphMap.get(string, glyphs); 247 } 248 249 } 250 251 #endif // ENABLE(SVG_FONTS) 252