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
invalidateGlyphCache()51 void SVGFontElement::invalidateGlyphCache()
52 {
53 if (m_isGlyphCacheValid) {
54 m_glyphMap.clear();
55 m_kerningPairs.clear();
56 }
57 m_isGlyphCacheValid = false;
58 }
59
firstMissingGlyphElement() const60 SVGMissingGlyphElement* SVGFontElement::firstMissingGlyphElement() const
61 {
62 for (Node* child = firstChild(); child; child = child->nextSibling()) {
63 if (child->hasTagName(missing_glyphTag))
64 return static_cast<SVGMissingGlyphElement*>(child);
65 }
66
67 return 0;
68 }
69
ensureGlyphCache() const70 void SVGFontElement::ensureGlyphCache() const
71 {
72 if (m_isGlyphCacheValid)
73 return;
74
75 for (Node* child = firstChild(); child; child = child->nextSibling()) {
76 if (child->hasTagName(glyphTag)) {
77 SVGGlyphElement* glyph = static_cast<SVGGlyphElement*>(child);
78 String unicode = glyph->getAttribute(unicodeAttr);
79 if (unicode.length())
80 m_glyphMap.add(unicode, glyph->buildGlyphIdentifier());
81 } else if (child->hasTagName(hkernTag)) {
82 SVGHKernElement* hkern = static_cast<SVGHKernElement*>(child);
83 SVGHorizontalKerningPair kerningPair = hkern->buildHorizontalKerningPair();
84 m_kerningPairs.append(kerningPair);
85 }
86 }
87
88 m_isGlyphCacheValid = true;
89 }
90
91 // Returns the number of characters consumed or 0 if no range was found.
parseUnicodeRange(const UChar * characters,unsigned length,pair<unsigned,unsigned> & range)92 static unsigned parseUnicodeRange(const UChar* characters, unsigned length, pair<unsigned, unsigned>& range)
93 {
94 if (length < 2)
95 return 0;
96 if (characters[0] != 'U')
97 return 0;
98 if (characters[1] != '+')
99 return 0;
100
101 // Parse the starting hex number (or its prefix).
102 unsigned start = 0;
103 unsigned startLength = 0;
104 for (unsigned i = 2; i < length; ++i) {
105 if (!isASCIIHexDigit(characters[i]))
106 break;
107 if (++startLength > 6)
108 return 0;
109 start = (start << 4) | toASCIIHexValue(characters[i]);
110 }
111
112 // Handle the case of ranges separated by "-" sign.
113 if (2 + startLength < length && characters[2 + startLength] == '-') {
114 if (!startLength)
115 return 0;
116
117 // Parse the ending hex number (or its prefix).
118 unsigned end = 0;
119 unsigned endLength = 0;
120 for (unsigned i = 2 + startLength + 1; i < length; ++i) {
121 if (!isASCIIHexDigit(characters[i]))
122 break;
123 if (++endLength > 6)
124 return 0;
125 end = (end << 4) | toASCIIHexValue(characters[i]);
126 }
127
128 if (!endLength)
129 return 0;
130
131 range.first = start;
132 range.second = end;
133 return 2 + startLength + 1 + endLength;
134 }
135
136 // Handle the case of a number with some optional trailing question marks.
137 unsigned end = start;
138 for (unsigned i = 2 + startLength; i < length; ++i) {
139 if (characters[i] != '?')
140 break;
141 if (++startLength > 6)
142 return 0;
143 start <<= 4;
144 end = (end << 4) | 0xF;
145 }
146
147 if (!startLength)
148 return 0;
149
150 range.first = start;
151 range.second = end;
152 return 2 + startLength;
153 }
154
parseUnicodeRangeList(const UChar * characters,unsigned length,Vector<pair<unsigned,unsigned>> & ranges)155 static bool parseUnicodeRangeList(const UChar* characters, unsigned length, Vector<pair<unsigned, unsigned> >& ranges)
156 {
157 ranges.clear();
158 if (!length)
159 return true;
160
161 const UChar* remainingCharacters = characters;
162 unsigned remainingLength = length;
163
164 while (1) {
165 pair<unsigned, unsigned> range;
166 unsigned charactersConsumed = parseUnicodeRange(remainingCharacters, remainingLength, range);
167 if (charactersConsumed) {
168 ranges.append(range);
169 remainingCharacters += charactersConsumed;
170 remainingLength -= charactersConsumed;
171 } else {
172 if (!remainingLength)
173 return false;
174 UChar character = remainingCharacters[0];
175 if (character == ',')
176 return false;
177 ranges.append(make_pair(character, character));
178 ++remainingCharacters;
179 --remainingLength;
180 }
181 if (!remainingLength)
182 return true;
183 if (remainingCharacters[0] != ',')
184 return false;
185 ++remainingCharacters;
186 --remainingLength;
187 }
188 }
189
stringMatchesUnicodeRange(const String & unicodeString,const String & unicodeRangeSpec)190 static bool stringMatchesUnicodeRange(const String& unicodeString, const String& unicodeRangeSpec)
191 {
192 Vector<pair<unsigned, unsigned> > ranges;
193 if (!parseUnicodeRangeList(unicodeRangeSpec.characters(), unicodeRangeSpec.length(), ranges))
194 return false;
195
196 if (unicodeString.length() != ranges.size())
197 return false;
198
199 for (size_t i = 0; i < unicodeString.length(); ++i) {
200 UChar c = unicodeString[i];
201 if (c < ranges[i].first || c > ranges[i].second)
202 return false;
203 }
204
205 return true;
206 }
207
matches(const String & u1,const String & g1,const String & u2,const String & g2,const SVGHorizontalKerningPair & kerningPair)208 static bool matches(const String& u1, const String& g1, const String& u2, const String& g2, const SVGHorizontalKerningPair& kerningPair)
209 {
210 if (kerningPair.unicode1.length() && !stringMatchesUnicodeRange(u1, kerningPair.unicode1))
211 return false;
212 if (kerningPair.glyphName1.length() && kerningPair.glyphName1 != g1)
213 return false;
214
215 if (kerningPair.unicode2.length() && !stringMatchesUnicodeRange(u2, kerningPair.unicode2))
216 return false;
217 if (kerningPair.glyphName2.length() && kerningPair.glyphName2 != g2)
218 return false;
219
220 return true;
221 }
222
getHorizontalKerningPairForStringsAndGlyphs(const String & u1,const String & g1,const String & u2,const String & g2,SVGHorizontalKerningPair & kerningPair) const223 bool SVGFontElement::getHorizontalKerningPairForStringsAndGlyphs(const String& u1, const String& g1, const String& u2, const String& g2, SVGHorizontalKerningPair& kerningPair) const
224 {
225 for (size_t i = 0; i < m_kerningPairs.size(); ++i) {
226 if (matches(u1, g1, u2, g2, m_kerningPairs[i])) {
227 kerningPair = m_kerningPairs[i];
228 return true;
229 }
230 }
231
232 return false;
233 }
234
getGlyphIdentifiersForString(const String & string,Vector<SVGGlyphIdentifier> & glyphs) const235 void SVGFontElement::getGlyphIdentifiersForString(const String& string, Vector<SVGGlyphIdentifier>& glyphs) const
236 {
237 ensureGlyphCache();
238 m_glyphMap.get(string, glyphs);
239 }
240
241 }
242
243 #endif // ENABLE(SVG_FONTS)
244