• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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