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-2018 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#include "SVGParserUtilities.h"
25
26#include "Document.h"
27#include "FloatRect.h"
28#include <limits>
29#include <wtf/ASCIICType.h>
30#include <wtf/text/StringView.h>
31
32namespace WebCore {
33
34template <typename FloatType> static inline bool isValidRange(const FloatType& x)
35{
36 static const FloatType max = std::numeric_limits<FloatType>::max();
37 return x >= -max && x <= max;
38}
39
40// We use this generic parseNumber function to allow the Path parsing code to work
41// at a higher precision internally, without any unnecessary runtime cost or code
42// complexity.
43template <typename CharacterType, typename FloatType> static bool genericParseNumber(const CharacterType*& ptr, const CharacterType* end, FloatType& number, bool skip)
44{
45 FloatType integer, decimal, frac, exponent;
46 int sign, expsign;
47 const CharacterType* start = ptr;
48
49 exponent = 0;
50 integer = 0;
51 frac = 1;
52 decimal = 0;
53 sign = 1;
54 expsign = 1;
55
56 // read the sign
57 if (ptr < end && *ptr == '+')
58 ptr++;
59 else if (ptr < end && *ptr == '-') {
60 ptr++;
61 sign = -1;
62 }
63
64 if (ptr == end || (!isASCIIDigit(*ptr) && *ptr != '.'))
65 return false;
66
67 // read the integer part, build right-to-left
68 const CharacterType* ptrStartIntPart = ptr;
69 while (ptr < end && isASCIIDigit(*ptr))
70 ++ptr; // Advance to first non-digit.
71
72 if (ptr != ptrStartIntPart) {
73 const CharacterType* ptrScanIntPart = ptr - 1;
74 FloatType multiplier = 1;
75 while (ptrScanIntPart >= ptrStartIntPart) {
76 integer += multiplier * static_cast<FloatType>(*(ptrScanIntPart--) - '0');
77 multiplier *= 10;
78 }
79 // Bail out early if this overflows.
80 if (!isValidRange(integer))
81 return false;
82 }
83
84 if (ptr < end && *ptr == '.') { // read the decimals
85 ptr++;
86
87 // There must be a least one digit following the .
88 if (ptr >= end || !isASCIIDigit(*ptr))
89 return false;
90
91 while (ptr < end && isASCIIDigit(*ptr))
92 decimal += (*(ptr++) - '0') * (frac *= static_cast<FloatType>(0.1));
93 }
94
95 // read the exponent part
96 if (ptr != start && ptr + 1 < end && (*ptr == 'e' || *ptr == 'E')
97 && (ptr[1] != 'x' && ptr[1] != 'm')) {
98 ptr++;
99
100 // read the sign of the exponent
101 if (*ptr == '+')
102 ptr++;
103 else if (*ptr == '-') {
104 ptr++;
105 expsign = -1;
106 }
107
108 // There must be an exponent
109 if (ptr >= end || !isASCIIDigit(*ptr))
110 return false;
111
112 while (ptr < end && isASCIIDigit(*ptr)) {
113 exponent *= static_cast<FloatType>(10);
114 exponent += *ptr - '0';
115 ptr++;
116 }
117 // Make sure exponent is valid.
118 if (!isValidRange(exponent) || exponent > std::numeric_limits<FloatType>::max_exponent)
119 return false;
120 }
121
122 number = integer + decimal;
123 number *= sign;
124
125 if (exponent)
126 number *= static_cast<FloatType>(pow(10.0, expsign * static_cast<int>(exponent)));
127
128 // Don't return Infinity() or NaN().
129 if (!isValidRange(number))
130 return false;
131
132 if (start == ptr)
133 return false;
134
135 if (skip)
136 skipOptionalSVGSpacesOrDelimiter(ptr, end);
137
138 return true;
139}
140
141template <typename CharacterType>
142bool parseSVGNumber(CharacterType* begin, size_t length, double& number)
143{
144 const CharacterType* ptr = begin;
145 const CharacterType* end = ptr + length;
146 return genericParseNumber(ptr, end, number, false);
147}
148
149// Explicitly instantiate the two flavors of parseSVGNumber() to satisfy external callers
150template bool parseSVGNumber(LChar* begin, size_t length, double&);
151template bool parseSVGNumber(UChar* begin, size_t length, double&);
152
153bool parseNumber(const LChar*& ptr, const LChar* end, float& number, bool skip)
154{
155 return genericParseNumber(ptr, end, number, skip);
156}
157
158bool parseNumber(const UChar*& ptr, const UChar* end, float& number, bool skip)
159{
160 return genericParseNumber(ptr, end, number, skip);
161}
162
163bool parseNumberFromString(const String& string, float& number, bool skip)
164{
165 auto upconvertedCharacters = StringView(string).upconvertedCharacters();
166 const UChar* ptr = upconvertedCharacters;
167 const UChar* end = ptr + string.length();
168 return genericParseNumber(ptr, end, number, skip) && ptr == end;
169}
170
171// only used to parse largeArcFlag and sweepFlag which must be a "0" or "1"
172// and might not have any whitespace/comma after it
173template <typename CharacterType>
174bool genericParseArcFlag(const CharacterType*& ptr, const CharacterType* end, bool& flag)
175{
176 if (ptr >= end)
177 return false;
178 const CharacterType flagChar = *ptr++;
179 if (flagChar == '0')
180 flag = false;
181 else if (flagChar == '1')
182 flag = true;
183 else
184 return false;
185
186 skipOptionalSVGSpacesOrDelimiter(ptr, end);
187
188 return true;
189}
190
191bool parseArcFlag(const LChar*& ptr, const LChar* end, bool& flag)
192{
193 return genericParseArcFlag(ptr, end, flag);
194}
195
196bool parseArcFlag(const UChar*& ptr, const UChar* end, bool& flag)
197{
198 return genericParseArcFlag(ptr, end, flag);
199}
200
201bool parseNumberOptionalNumber(const String& s, float& x, float& y)
202{
203 if (s.isEmpty())
204 return false;
205
206 auto upconvertedCharacters = StringView(s).upconvertedCharacters();
207 const UChar* cur = upconvertedCharacters;
208 const UChar* end = cur + s.length();
209
210 if (!parseNumber(cur, end, x))
211 return false;
212
213 if (cur == end)
214 y = x;
215 else if (!parseNumber(cur, end, y, false))
216 return false;
217
218 return cur == end;
219}
220
221bool parsePoint(const String& s, FloatPoint& point)
222{
223 if (s.isEmpty())
224 return false;
225 auto upconvertedCharacters = StringView(s).upconvertedCharacters();
226 const UChar* cur = upconvertedCharacters;
227 const UChar* end = cur + s.length();
228
229 if (!skipOptionalSVGSpaces(cur, end))
230 return false;
231
232 float x = 0;
233 if (!parseNumber(cur, end, x))
234 return false;
235
236 float y = 0;
237 if (!parseNumber(cur, end, y))
238 return false;
239
240 point = FloatPoint(x, y);
241
242 // Disallow anything except spaces at the end.
243 return !skipOptionalSVGSpaces(cur, end);
244}
245
246bool parseRect(const String& string, FloatRect& rect)
247{
248 auto upconvertedCharacters = StringView(string).upconvertedCharacters();
249 const UChar* ptr = upconvertedCharacters;
250 const UChar* end = ptr + string.length();
251 skipOptionalSVGSpaces(ptr, end);
252
253 float x = 0;
254 float y = 0;
255 float width = 0;
256 float height = 0;
257 bool valid = parseNumber(ptr, end, x) && parseNumber(ptr, end, y) && parseNumber(ptr, end, width) && parseNumber(ptr, end, height, false);
258 rect = FloatRect(x, y, width, height);
259 return valid;
260}
261
262bool parseGlyphName(const String& input, HashSet<String>& values)
263{
264 // FIXME: Parsing error detection is missing.
265 values.clear();
266
267 auto upconvertedCharacters = StringView(input).upconvertedCharacters();
268 const UChar* ptr = upconvertedCharacters;
269 const UChar* end = ptr + input.length();
270 skipOptionalSVGSpaces(ptr, end);
271
272 while (ptr < end) {
273 // Leading and trailing white space, and white space before and after separators, will be ignored.
274 const UChar* inputStart = ptr;
275 while (ptr < end && *ptr != ',')
276 ++ptr;
277
278 if (ptr == inputStart)
279 break;
280
281 // walk backwards from the ; to ignore any whitespace
282 const UChar* inputEnd = ptr - 1;
283 while (inputStart < inputEnd && isSVGSpace(*inputEnd))
284 --inputEnd;
285
286 values.add(String(inputStart, inputEnd - inputStart + 1));
287 skipOptionalSVGSpacesOrDelimiter(ptr, end, ',');
288 }
289
290 return true;
291}
292
293static bool parseUnicodeRange(const UChar* characters, unsigned length, UnicodeRange& range)
294{
295 if (length < 2 || characters[0] != 'U' || characters[1] != '+')
296 return false;
297
298 // Parse the starting hex number (or its prefix).
299 unsigned startRange = 0;
300 unsigned startLength = 0;
301
302 const UChar* ptr = characters + 2;
303 const UChar* end = characters + length;
304 while (ptr < end) {
305 if (!isASCIIHexDigit(*ptr))
306 break;
307 ++startLength;
308 if (startLength > 6)
309 return false;
310 startRange = (startRange << 4) | toASCIIHexValue(*ptr);
311 ++ptr;
312 }
313
314 // Handle the case of ranges separated by "-" sign.
315 if (2 + startLength < length && *ptr == '-') {
316 if (!startLength)
317 return false;
318
319 // Parse the ending hex number (or its prefix).
320 unsigned endRange = 0;
321 unsigned endLength = 0;
322 ++ptr;
323 while (ptr < end) {
324 if (!isASCIIHexDigit(*ptr))
325 break;
326 ++endLength;
327 if (endLength > 6)
328 return false;
329 endRange = (endRange << 4) | toASCIIHexValue(*ptr);
330 ++ptr;
331 }
332
333 if (!endLength)
334 return false;
335
336 range.first = startRange;
337 range.second = endRange;
338 return true;
339 }
340
341 // Handle the case of a number with some optional trailing question marks.
342 unsigned endRange = startRange;
343 while (ptr < end) {
344 if (*ptr != '?')
345 break;
346 ++startLength;
347 if (startLength > 6)
348 return false;
349 startRange <<= 4;
350 endRange = (endRange << 4) | 0xF;
351 ++ptr;
352 }
353
354 if (!startLength)
355 return false;
356
357 range.first = startRange;
358 range.second = endRange;
359 return true;
360}
361
362bool parseKerningUnicodeString(const String& input, UnicodeRanges& rangeList, HashSet<String>& stringList)
363{
364 // FIXME: Parsing error detection is missing.
365 auto upconvertedCharacters = StringView(input).upconvertedCharacters();
366 const UChar* ptr = upconvertedCharacters;
367 const UChar* end = ptr + input.length();
368
369 while (ptr < end) {
370 const UChar* inputStart = ptr;
371 while (ptr < end && *ptr != ',')
372 ++ptr;
373
374 if (ptr == inputStart)
375 break;
376
377 // Try to parse unicode range first
378 UnicodeRange range;
379 if (parseUnicodeRange(inputStart, ptr - inputStart, range))
380 rangeList.append(range);
381 else
382 stringList.add(String(inputStart, ptr - inputStart));
383 ++ptr;
384 }
385
386 return true;
387}
388
389Vector<String> parseDelimitedString(const String& input, const char seperator)
390{
391 Vector<String> values;
392
393 auto upconvertedCharacters = StringView(input).upconvertedCharacters();
394 const UChar* ptr = upconvertedCharacters;
395 const UChar* end = ptr + input.length();
396 skipOptionalSVGSpaces(ptr, end);
397
398 while (ptr < end) {
399 // Leading and trailing white space, and white space before and after semicolon separators, will be ignored.
400 const UChar* inputStart = ptr;
401 while (ptr < end && *ptr != seperator) // careful not to ignore whitespace inside inputs
402 ptr++;
403
404 if (ptr == inputStart)
405 break;
406
407 // walk backwards from the ; to ignore any whitespace
408 const UChar* inputEnd = ptr - 1;
409 while (inputStart < inputEnd && isSVGSpace(*inputEnd))
410 inputEnd--;
411
412 values.append(String(inputStart, inputEnd - inputStart + 1));
413 skipOptionalSVGSpacesOrDelimiter(ptr, end, seperator);
414 }
415
416 return values;
417}
418
419template <typename CharacterType>
420bool parseFloatPoint(const CharacterType*& current, const CharacterType* end, FloatPoint& point)
421{
422 float x;
423 float y;
424 if (!parseNumber(current, end, x)
425 || !parseNumber(current, end, y))
426 return false;
427 point = FloatPoint(x, y);
428 return true;
429}
430
431template bool parseFloatPoint(const LChar*& current, const LChar* end, FloatPoint& point1);
432template bool parseFloatPoint(const UChar*& current, const UChar* end, FloatPoint& point1);
433
434template <typename CharacterType>
435inline bool parseFloatPoint2(const CharacterType*& current, const CharacterType* end, FloatPoint& point1, FloatPoint& point2)
436{
437 float x1;
438 float y1;
439 float x2;
440 float y2;
441 if (!parseNumber(current, end, x1)
442 || !parseNumber(current, end, y1)
443 || !parseNumber(current, end, x2)
444 || !parseNumber(current, end, y2))
445 return false;
446 point1 = FloatPoint(x1, y1);
447 point2 = FloatPoint(x2, y2);
448 return true;
449}
450
451template bool parseFloatPoint2(const LChar*& current, const LChar* end, FloatPoint& point1, FloatPoint& point2);
452template bool parseFloatPoint2(const UChar*& current, const UChar* end, FloatPoint& point1, FloatPoint& point2);
453
454template <typename CharacterType>
455bool parseFloatPoint3(const CharacterType*& current, const CharacterType* end, FloatPoint& point1, FloatPoint& point2, FloatPoint& point3)
456{
457 float x1;
458 float y1;
459 float x2;
460 float y2;
461 float x3;
462 float y3;
463 if (!parseNumber(current, end, x1)
464 || !parseNumber(current, end, y1)
465 || !parseNumber(current, end, x2)
466 || !parseNumber(current, end, y2)
467 || !parseNumber(current, end, x3)
468 || !parseNumber(current, end, y3))
469 return false;
470 point1 = FloatPoint(x1, y1);
471 point2 = FloatPoint(x2, y2);
472 point3 = FloatPoint(x3, y3);
473 return true;
474}
475
476template bool parseFloatPoint3(const LChar*& current, const LChar* end, FloatPoint& point1, FloatPoint& point2, FloatPoint& point3);
477template bool parseFloatPoint3(const UChar*& current, const UChar* end, FloatPoint& point1, FloatPoint& point2, FloatPoint& point3);
478
479}
480