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