1 /*
2 Copyright (C) 2007 Eric Seidel <eric@webkit.org>
3 Copyright (C) 2007 Nikolas Zimmermann <zimmermann@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
SVGFontElement(const QualifiedName & tagName,Document * doc)41 SVGFontElement::SVGFontElement(const QualifiedName& tagName, Document* doc)
42 : SVGStyledElement(tagName, doc)
43 , m_isGlyphCacheValid(false)
44 {
45 }
46
~SVGFontElement()47 SVGFontElement::~SVGFontElement()
48 {
49 }
50
synchronizeProperty(const QualifiedName & attrName)51 void SVGFontElement::synchronizeProperty(const QualifiedName& attrName)
52 {
53 SVGStyledElement::synchronizeProperty(attrName);
54
55 if (attrName == anyQName() || SVGExternalResourcesRequired::isKnownAttribute(attrName))
56 synchronizeExternalResourcesRequired();
57 }
58
invalidateGlyphCache()59 void SVGFontElement::invalidateGlyphCache()
60 {
61 if (m_isGlyphCacheValid) {
62 m_glyphMap.clear();
63 m_kerningPairs.clear();
64 }
65 m_isGlyphCacheValid = false;
66 }
67
firstMissingGlyphElement() const68 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
ensureGlyphCache() const78 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.
parseUnicodeRange(const UChar * characters,unsigned length,pair<unsigned,unsigned> & range)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
parseUnicodeRangeList(const UChar * characters,unsigned length,Vector<pair<unsigned,unsigned>> & ranges)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
stringMatchesUnicodeRange(const String & unicodeString,const String & unicodeRangeSpec)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
matches(const String & u1,const String & g1,const String & u2,const String & g2,const SVGHorizontalKerningPair & kerningPair)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
getHorizontalKerningPairForStringsAndGlyphs(const String & u1,const String & g1,const String & u2,const String & g2,SVGHorizontalKerningPair & kerningPair) const231 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
getGlyphIdentifiersForString(const String & string,Vector<SVGGlyphIdentifier> & glyphs) const243 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