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 | |
32 | namespace WebCore { |
33 | |
34 | template <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. |
43 | template <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 | |
141 | template <typename CharacterType> |
142 | bool 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 |
150 | template bool parseSVGNumber(LChar* begin, size_t length, double&); |
151 | template bool parseSVGNumber(UChar* begin, size_t length, double&); |
152 | |
153 | bool parseNumber(const LChar*& ptr, const LChar* end, float& number, bool skip) |
154 | { |
155 | return genericParseNumber(ptr, end, number, skip); |
156 | } |
157 | |
158 | bool parseNumber(const UChar*& ptr, const UChar* end, float& number, bool skip) |
159 | { |
160 | return genericParseNumber(ptr, end, number, skip); |
161 | } |
162 | |
163 | bool 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 |
173 | template <typename CharacterType> |
174 | bool 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 | |
191 | bool parseArcFlag(const LChar*& ptr, const LChar* end, bool& flag) |
192 | { |
193 | return genericParseArcFlag(ptr, end, flag); |
194 | } |
195 | |
196 | bool parseArcFlag(const UChar*& ptr, const UChar* end, bool& flag) |
197 | { |
198 | return genericParseArcFlag(ptr, end, flag); |
199 | } |
200 | |
201 | bool 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 | |
221 | bool 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 | |
246 | bool 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 | |
262 | bool 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 | |
293 | static 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 | |
362 | bool 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 | |
389 | Vector<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 | |
419 | template <typename CharacterType> |
420 | bool 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 | |
431 | template bool parseFloatPoint(const LChar*& current, const LChar* end, FloatPoint& point1); |
432 | template bool parseFloatPoint(const UChar*& current, const UChar* end, FloatPoint& point1); |
433 | |
434 | template <typename CharacterType> |
435 | inline 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 | |
451 | template bool parseFloatPoint2(const LChar*& current, const LChar* end, FloatPoint& point1, FloatPoint& point2); |
452 | template bool parseFloatPoint2(const UChar*& current, const UChar* end, FloatPoint& point1, FloatPoint& point2); |
453 | |
454 | template <typename CharacterType> |
455 | bool 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 | |
476 | template bool parseFloatPoint3(const LChar*& current, const LChar* end, FloatPoint& point1, FloatPoint& point2, FloatPoint& point3); |
477 | template bool parseFloatPoint3(const UChar*& current, const UChar* end, FloatPoint& point1, FloatPoint& point2, FloatPoint& point3); |
478 | |
479 | } |
480 | |