• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2002, 2003 The Karbon Developers
3  * Copyright (C) 2006 Alexander Kellett <lypanov@kde.org>
4  * Copyright (C) 2006, 2007 Rob Buis <buis@kde.org>
5  * Copyright (C) 2007, 2009 Apple Inc. All rights reserved.
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Library General Public
9  * License as published by the Free Software Foundation; either
10  * version 2 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Library General Public License for more details.
16  *
17  * You should have received a copy of the GNU Library General Public License
18  * along with this library; see the file COPYING.LIB.  If not, write to
19  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
20  * Boston, MA 02110-1301, USA.
21  */
22 
23 #include "config.h"
24 
25 #if ENABLE(SVG)
26 #include "SVGParserUtilities.h"
27 
28 #include "Document.h"
29 #include "FloatPoint.h"
30 #include "SVGPointList.h"
31 
32 #include <limits>
33 #include <wtf/ASCIICType.h>
34 
35 namespace WebCore {
36 
isValidRange(const FloatType & x)37 template <typename FloatType> static inline bool isValidRange(const FloatType& x)
38 {
39     static const FloatType max = std::numeric_limits<FloatType>::max();
40     return x >= -max && x <= max;
41 }
42 
43 // We use this generic parseNumber function to allow the Path parsing code to work
44 // at a higher precision internally, without any unnecessary runtime cost or code
45 // complexity.
genericParseNumber(const UChar * & ptr,const UChar * end,FloatType & number,bool skip)46 template <typename FloatType> static bool genericParseNumber(const UChar*& ptr, const UChar* end, FloatType& number, bool skip)
47 {
48     FloatType integer, decimal, frac, exponent;
49     int sign, expsign;
50     const UChar* start = ptr;
51 
52     exponent = 0;
53     integer = 0;
54     frac = 1;
55     decimal = 0;
56     sign = 1;
57     expsign = 1;
58 
59     // read the sign
60     if (ptr < end && *ptr == '+')
61         ptr++;
62     else if (ptr < end && *ptr == '-') {
63         ptr++;
64         sign = -1;
65     }
66 
67     if (ptr == end || ((*ptr < '0' || *ptr > '9') && *ptr != '.'))
68         // The first character of a number must be one of [0-9+-.]
69         return false;
70 
71     // read the integer part, build right-to-left
72     const UChar* ptrStartIntPart = ptr;
73     while (ptr < end && *ptr >= '0' && *ptr <= '9')
74         ++ptr; // Advance to first non-digit.
75 
76     if (ptr != ptrStartIntPart) {
77         const UChar* ptrScanIntPart = ptr - 1;
78         FloatType multiplier = 1;
79         while (ptrScanIntPart >= ptrStartIntPart) {
80             integer += multiplier * static_cast<FloatType>(*(ptrScanIntPart--) - '0');
81             multiplier *= 10;
82         }
83         // Bail out early if this overflows.
84         if (!isValidRange(integer))
85             return false;
86     }
87 
88     if (ptr < end && *ptr == '.') { // read the decimals
89         ptr++;
90 
91         // There must be a least one digit following the .
92         if (ptr >= end || *ptr < '0' || *ptr > '9')
93             return false;
94 
95         while (ptr < end && *ptr >= '0' && *ptr <= '9')
96             decimal += (*(ptr++) - '0') * (frac *= static_cast<FloatType>(0.1));
97     }
98 
99     // read the exponent part
100     if (ptr != start && ptr + 1 < end && (*ptr == 'e' || *ptr == 'E')
101         && (ptr[1] != 'x' && ptr[1] != 'm')) {
102         ptr++;
103 
104         // read the sign of the exponent
105         if (*ptr == '+')
106             ptr++;
107         else if (*ptr == '-') {
108             ptr++;
109             expsign = -1;
110         }
111 
112         // There must be an exponent
113         if (ptr >= end || *ptr < '0' || *ptr > '9')
114             return false;
115 
116         while (ptr < end && *ptr >= '0' && *ptr <= '9') {
117             exponent *= static_cast<FloatType>(10);
118             exponent += *ptr - '0';
119             ptr++;
120         }
121         // Make sure exponent is valid.
122         if (!isValidRange(exponent) || exponent > std::numeric_limits<FloatType>::max_exponent)
123             return false;
124     }
125 
126     number = integer + decimal;
127     number *= sign;
128 
129     if (exponent)
130         number *= static_cast<FloatType>(pow(10.0, expsign * static_cast<int>(exponent)));
131 
132     // Don't return Infinity() or NaN().
133     if (!isValidRange(number))
134         return false;
135 
136     if (start == ptr)
137         return false;
138 
139     if (skip)
140         skipOptionalSpacesOrDelimiter(ptr, end);
141 
142     return true;
143 }
144 
parseNumber(const UChar * & ptr,const UChar * end,float & number,bool skip)145 bool parseNumber(const UChar*& ptr, const UChar* end, float& number, bool skip)
146 {
147     return genericParseNumber(ptr, end, number, skip);
148 }
149 
150 // only used to parse largeArcFlag and sweepFlag which must be a "0" or "1"
151 // and might not have any whitespace/comma after it
parseArcFlag(const UChar * & ptr,const UChar * end,bool & flag)152 bool parseArcFlag(const UChar*& ptr, const UChar* end, bool& flag)
153 {
154     const UChar flagChar = *ptr++;
155     if (flagChar == '0')
156         flag = false;
157     else if (flagChar == '1')
158         flag = true;
159     else
160         return false;
161 
162     skipOptionalSpacesOrDelimiter(ptr, end);
163 
164     return true;
165 }
166 
parseNumberOptionalNumber(const String & s,float & x,float & y)167 bool parseNumberOptionalNumber(const String& s, float& x, float& y)
168 {
169     if (s.isEmpty())
170         return false;
171     const UChar* cur = s.characters();
172     const UChar* end = cur + s.length();
173 
174     if (!parseNumber(cur, end, x))
175         return false;
176 
177     if (cur == end)
178         y = x;
179     else if (!parseNumber(cur, end, y, false))
180         return false;
181 
182     return cur == end;
183 }
184 
pointsListFromSVGData(SVGPointList & pointsList,const String & points)185 bool pointsListFromSVGData(SVGPointList& pointsList, const String& points)
186 {
187     if (points.isEmpty())
188         return true;
189     const UChar* cur = points.characters();
190     const UChar* end = cur + points.length();
191 
192     skipOptionalSpaces(cur, end);
193 
194     bool delimParsed = false;
195     while (cur < end) {
196         delimParsed = false;
197         float xPos = 0.0f;
198         if (!parseNumber(cur, end, xPos))
199            return false;
200 
201         float yPos = 0.0f;
202         if (!parseNumber(cur, end, yPos, false))
203             return false;
204 
205         skipOptionalSpaces(cur, end);
206 
207         if (cur < end && *cur == ',') {
208             delimParsed = true;
209             cur++;
210         }
211         skipOptionalSpaces(cur, end);
212 
213         pointsList.append(FloatPoint(xPos, yPos));
214     }
215     return cur == end && !delimParsed;
216 }
217 
parseGlyphName(const String & input,HashSet<String> & values)218 bool parseGlyphName(const String& input, HashSet<String>& values)
219 {
220     // FIXME: Parsing error detection is missing.
221     values.clear();
222 
223     const UChar* ptr = input.characters();
224     const UChar* end = ptr + input.length();
225     skipOptionalSpaces(ptr, end);
226 
227     while (ptr < end) {
228         // Leading and trailing white space, and white space before and after separators, will be ignored.
229         const UChar* inputStart = ptr;
230         while (ptr < end && *ptr != ',')
231             ++ptr;
232 
233         if (ptr == inputStart)
234             break;
235 
236         // walk backwards from the ; to ignore any whitespace
237         const UChar* inputEnd = ptr - 1;
238         while (inputStart < inputEnd && isWhitespace(*inputEnd))
239             --inputEnd;
240 
241         values.add(String(inputStart, inputEnd - inputStart + 1));
242         skipOptionalSpacesOrDelimiter(ptr, end, ',');
243     }
244 
245     return true;
246 }
247 
parseUnicodeRange(const UChar * characters,unsigned length,UnicodeRange & range)248 static bool parseUnicodeRange(const UChar* characters, unsigned length, UnicodeRange& range)
249 {
250     if (length < 2 || characters[0] != 'U' || characters[1] != '+')
251         return false;
252 
253     // Parse the starting hex number (or its prefix).
254     unsigned startRange = 0;
255     unsigned startLength = 0;
256 
257     const UChar* ptr = characters + 2;
258     const UChar* end = characters + length;
259     while (ptr < end) {
260         if (!isASCIIHexDigit(*ptr))
261             break;
262         ++startLength;
263         if (startLength > 6)
264             return false;
265         startRange = (startRange << 4) | toASCIIHexValue(*ptr);
266         ++ptr;
267     }
268 
269     // Handle the case of ranges separated by "-" sign.
270     if (2 + startLength < length && *ptr == '-') {
271         if (!startLength)
272             return false;
273 
274         // Parse the ending hex number (or its prefix).
275         unsigned endRange = 0;
276         unsigned endLength = 0;
277         ++ptr;
278         while (ptr < end) {
279             if (!isASCIIHexDigit(*ptr))
280                 break;
281             ++endLength;
282             if (endLength > 6)
283                 return false;
284             endRange = (endRange << 4) | toASCIIHexValue(*ptr);
285             ++ptr;
286         }
287 
288         if (!endLength)
289             return false;
290 
291         range.first = startRange;
292         range.second = endRange;
293         return true;
294     }
295 
296     // Handle the case of a number with some optional trailing question marks.
297     unsigned endRange = startRange;
298     while (ptr < end) {
299         if (*ptr != '?')
300             break;
301         ++startLength;
302         if (startLength > 6)
303             return false;
304         startRange <<= 4;
305         endRange = (endRange << 4) | 0xF;
306         ++ptr;
307     }
308 
309     if (!startLength)
310         return false;
311 
312     range.first = startRange;
313     range.second = endRange;
314     return true;
315 }
316 
parseKerningUnicodeString(const String & input,UnicodeRanges & rangeList,HashSet<String> & stringList)317 bool parseKerningUnicodeString(const String& input, UnicodeRanges& rangeList, HashSet<String>& stringList)
318 {
319     // FIXME: Parsing error detection is missing.
320     const UChar* ptr = input.characters();
321     const UChar* end = ptr + input.length();
322 
323     while (ptr < end) {
324         const UChar* inputStart = ptr;
325         while (ptr < end && *ptr != ',')
326             ++ptr;
327 
328         if (ptr == inputStart)
329             break;
330 
331         // Try to parse unicode range first
332         UnicodeRange range;
333         if (parseUnicodeRange(inputStart, ptr - inputStart, range))
334             rangeList.append(range);
335         else
336             stringList.add(String(inputStart, ptr - inputStart));
337         ++ptr;
338     }
339 
340     return true;
341 }
342 
parseDelimitedString(const String & input,const char seperator)343 Vector<String> parseDelimitedString(const String& input, const char seperator)
344 {
345     Vector<String> values;
346 
347     const UChar* ptr = input.characters();
348     const UChar* end = ptr + input.length();
349     skipOptionalSpaces(ptr, end);
350 
351     while (ptr < end) {
352         // Leading and trailing white space, and white space before and after semicolon separators, will be ignored.
353         const UChar* inputStart = ptr;
354         while (ptr < end && *ptr != seperator) // careful not to ignore whitespace inside inputs
355             ptr++;
356 
357         if (ptr == inputStart)
358             break;
359 
360         // walk backwards from the ; to ignore any whitespace
361         const UChar* inputEnd = ptr - 1;
362         while (inputStart < inputEnd && isWhitespace(*inputEnd))
363             inputEnd--;
364 
365         values.append(String(inputStart, inputEnd - inputStart + 1));
366         skipOptionalSpacesOrDelimiter(ptr, end, seperator);
367     }
368 
369     return values;
370 }
371 
372 }
373 
374 #endif // ENABLE(SVG)
375