• 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 
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