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, 2013 Apple Inc. All rights reserved.
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.
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.
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.
24 #include "core/svg/SVGParserUtilities.h"
26 #include "core/svg/SVGPointList.h"
27 #include "platform/geometry/FloatRect.h"
28 #include "platform/transforms/AffineTransform.h"
29 #include "wtf/ASCIICType.h"
30 #include "wtf/text/StringHash.h"
35 template <typename FloatType>
36 static inline bool isValidRange(const FloatType& x)
38 static const FloatType max = std::numeric_limits<FloatType>::max();
39 return x >= -max && x <= max;
42 // We use this generic parseNumber function to allow the Path parsing code to work
43 // at a higher precision internally, without any unnecessary runtime cost or code
45 template <typename CharType, typename FloatType>
46 static bool genericParseNumber(const CharType*& ptr, const CharType* end, FloatType& number, WhitespaceMode mode)
48 FloatType integer, decimal, frac, exponent;
50 const CharType* start = ptr;
59 if (mode & AllowLeadingWhitespace)
60 skipOptionalSVGSpaces(ptr, end);
63 if (ptr < end && *ptr == '+')
65 else if (ptr < end && *ptr == '-') {
70 if (ptr == end || ((*ptr < '0' || *ptr > '9') && *ptr != '.'))
71 // The first character of a number must be one of [0-9+-.]
74 // read the integer part, build right-to-left
75 const CharType* ptrStartIntPart = ptr;
76 while (ptr < end && *ptr >= '0' && *ptr <= '9')
77 ++ptr; // Advance to first non-digit.
79 if (ptr != ptrStartIntPart) {
80 const CharType* ptrScanIntPart = ptr - 1;
81 FloatType multiplier = 1;
82 while (ptrScanIntPart >= ptrStartIntPart) {
83 integer += multiplier * static_cast<FloatType>(*(ptrScanIntPart--) - '0');
86 // Bail out early if this overflows.
87 if (!isValidRange(integer))
91 if (ptr < end && *ptr == '.') { // read the decimals
94 // There must be a least one digit following the .
95 if (ptr >= end || *ptr < '0' || *ptr > '9')
98 while (ptr < end && *ptr >= '0' && *ptr <= '9')
99 decimal += (*(ptr++) - '0') * (frac *= static_cast<FloatType>(0.1));
102 // read the exponent part
103 if (ptr != start && ptr + 1 < end && (*ptr == 'e' || *ptr == 'E')
104 && (ptr[1] != 'x' && ptr[1] != 'm')) {
107 // read the sign of the exponent
110 else if (*ptr == '-') {
115 // There must be an exponent
116 if (ptr >= end || *ptr < '0' || *ptr > '9')
119 while (ptr < end && *ptr >= '0' && *ptr <= '9') {
120 exponent *= static_cast<FloatType>(10);
121 exponent += *ptr - '0';
124 // Make sure exponent is valid.
125 if (!isValidRange(exponent) || exponent > std::numeric_limits<FloatType>::max_exponent)
129 number = integer + decimal;
133 number *= static_cast<FloatType>(pow(10.0, expsign * static_cast<int>(exponent)));
135 // Don't return Infinity() or NaN().
136 if (!isValidRange(number))
142 if (mode & AllowTrailingWhitespace)
143 skipOptionalSVGSpacesOrDelimiter(ptr, end);
148 bool parseNumber(const LChar*& ptr, const LChar* end, float& number, WhitespaceMode mode)
150 return genericParseNumber(ptr, end, number, mode);
153 bool parseNumber(const UChar*& ptr, const UChar* end, float& number, WhitespaceMode mode)
155 return genericParseNumber(ptr, end, number, mode);
158 // only used to parse largeArcFlag and sweepFlag which must be a "0" or "1"
159 // and might not have any whitespace/comma after it
160 template <typename CharType>
161 bool genericParseArcFlag(const CharType*& ptr, const CharType* end, bool& flag)
165 const CharType flagChar = *ptr++;
168 else if (flagChar == '1')
173 skipOptionalSVGSpacesOrDelimiter(ptr, end);
178 bool parseArcFlag(const LChar*& ptr, const LChar* end, bool& flag)
180 return genericParseArcFlag(ptr, end, flag);
183 bool parseArcFlag(const UChar*& ptr, const UChar* end, bool& flag)
185 return genericParseArcFlag(ptr, end, flag);
188 template<typename CharType>
189 static bool genericParseNumberOptionalNumber(const CharType*& ptr, const CharType* end, float& x, float& y)
191 if (!parseNumber(ptr, end, x))
196 else if (!parseNumber(ptr, end, y, AllowLeadingAndTrailingWhitespace))
202 bool parseNumberOptionalNumber(const String& string, float& x, float& y)
204 if (string.isEmpty())
207 if (string.is8Bit()) {
208 const LChar* ptr = string.characters8();
209 const LChar* end = ptr + string.length();
210 return genericParseNumberOptionalNumber(ptr, end, x, y);
212 const UChar* ptr = string.characters16();
213 const UChar* end = ptr + string.length();
214 return genericParseNumberOptionalNumber(ptr, end, x, y);
217 template<typename CharType>
218 bool genericParseNumberOrPercentage(const CharType*& ptr, const CharType* end, float& number)
220 if (genericParseNumber(ptr, end, number, AllowLeadingWhitespace)) {
224 bool isPercentage = (*ptr == '%');
228 skipOptionalSVGSpaces(ptr, end);
239 bool parseNumberOrPercentage(const String& string, float& number)
241 if (string.isEmpty())
244 if (string.is8Bit()) {
245 const LChar* ptr = string.characters8();
246 const LChar* end = ptr + string.length();
247 return genericParseNumberOrPercentage(ptr, end, number);
249 const UChar* ptr = string.characters16();
250 const UChar* end = ptr + string.length();
251 return genericParseNumberOrPercentage(ptr, end, number);
254 #if ENABLE(SVG_FONTS)
255 template<typename CharType>
256 static bool parseGlyphName(const CharType*& ptr, const CharType* end, HashSet<String>& values)
258 skipOptionalSVGSpaces(ptr, end);
261 // Leading and trailing white space, and white space before and after separators, will be ignored.
262 const CharType* inputStart = ptr;
263 while (ptr < end && *ptr != ',')
266 if (ptr == inputStart)
269 // walk backwards from the ; to ignore any whitespace
270 const CharType* inputEnd = ptr - 1;
271 while (inputStart < inputEnd && isHTMLSpace<CharType>(*inputEnd))
274 values.add(String(inputStart, inputEnd - inputStart + 1));
275 skipOptionalSVGSpacesOrDelimiter(ptr, end, ',');
281 bool parseGlyphName(const String& input, HashSet<String>& values)
283 // FIXME: Parsing error detection is missing.
287 if (input.is8Bit()) {
288 const LChar* ptr = input.characters8();
289 const LChar* end = ptr + input.length();
290 return parseGlyphName(ptr, end, values);
292 const UChar* ptr = input.characters16();
293 const UChar* end = ptr + input.length();
294 return parseGlyphName(ptr, end, values);
297 template<typename CharType>
298 static bool parseUnicodeRange(const CharType* characters, unsigned length, UnicodeRange& range)
300 if (length < 2 || characters[0] != 'U' || characters[1] != '+')
303 // Parse the starting hex number (or its prefix).
304 unsigned startRange = 0;
305 unsigned startLength = 0;
307 const CharType* ptr = characters + 2;
308 const CharType* end = characters + length;
310 if (!isASCIIHexDigit(*ptr))
315 startRange = (startRange << 4) | toASCIIHexValue(*ptr);
319 // Handle the case of ranges separated by "-" sign.
320 if (2 + startLength < length && *ptr == '-') {
324 // Parse the ending hex number (or its prefix).
325 unsigned endRange = 0;
326 unsigned endLength = 0;
329 if (!isASCIIHexDigit(*ptr))
334 endRange = (endRange << 4) | toASCIIHexValue(*ptr);
341 range.first = startRange;
342 range.second = endRange;
346 // Handle the case of a number with some optional trailing question marks.
347 unsigned endRange = startRange;
355 endRange = (endRange << 4) | 0xF;
362 range.first = startRange;
363 range.second = endRange;
367 template<typename CharType>
368 static bool genericParseKerningUnicodeString(const CharType*& ptr, const CharType* end, UnicodeRanges& rangeList, HashSet<String>& stringList)
371 const CharType* inputStart = ptr;
372 while (ptr < end && *ptr != ',')
375 if (ptr == inputStart)
378 // Try to parse unicode range first
380 if (parseUnicodeRange(inputStart, ptr - inputStart, range))
381 rangeList.append(range);
383 stringList.add(String(inputStart, ptr - inputStart));
390 bool parseKerningUnicodeString(const String& input, UnicodeRanges& rangeList, HashSet<String>& stringList)
392 // FIXME: Parsing error detection is missing.
395 if (input.is8Bit()) {
396 const LChar* ptr = input.characters8();
397 const LChar* end = ptr + input.length();
398 return genericParseKerningUnicodeString(ptr, end, rangeList, stringList);
400 const UChar* ptr = input.characters16();
401 const UChar* end = ptr + input.length();
402 return genericParseKerningUnicodeString(ptr, end, rangeList, stringList);
405 template<typename CharType>
406 static Vector<String> genericParseDelimitedString(const CharType*& ptr, const CharType* end, const char seperator)
408 Vector<String> values;
410 skipOptionalSVGSpaces(ptr, end);
413 // Leading and trailing white space, and white space before and after semicolon separators, will be ignored.
414 const CharType* inputStart = ptr;
415 while (ptr < end && *ptr != seperator) // careful not to ignore whitespace inside inputs
418 if (ptr == inputStart)
421 // walk backwards from the ; to ignore any whitespace
422 const CharType* inputEnd = ptr - 1;
423 while (inputStart < inputEnd && isHTMLSpace<CharType>(*inputEnd))
426 values.append(String(inputStart, inputEnd - inputStart + 1));
427 skipOptionalSVGSpacesOrDelimiter(ptr, end, seperator);
433 Vector<String> parseDelimitedString(const String& input, const char seperator)
436 return Vector<String>();
437 if (input.is8Bit()) {
438 const LChar* ptr = input.characters8();
439 const LChar* end = ptr + input.length();
440 return genericParseDelimitedString(ptr, end, seperator);
442 const UChar* ptr = input.characters16();
443 const UChar* end = ptr + input.length();
444 return genericParseDelimitedString(ptr, end, seperator);
448 template <typename CharType>
449 bool parseFloatPoint(const CharType*& current, const CharType* end, FloatPoint& point)
453 if (!parseNumber(current, end, x)
454 || !parseNumber(current, end, y))
456 point = FloatPoint(x, y);
460 template bool parseFloatPoint(const LChar*& current, const LChar* end, FloatPoint& point1);
461 template bool parseFloatPoint(const UChar*& current, const UChar* end, FloatPoint& point1);
463 template <typename CharType>
464 inline bool parseFloatPoint2(const CharType*& current, const CharType* end, FloatPoint& point1, FloatPoint& point2)
470 if (!parseNumber(current, end, x1)
471 || !parseNumber(current, end, y1)
472 || !parseNumber(current, end, x2)
473 || !parseNumber(current, end, y2))
475 point1 = FloatPoint(x1, y1);
476 point2 = FloatPoint(x2, y2);
480 template bool parseFloatPoint2(const LChar*& current, const LChar* end, FloatPoint& point1, FloatPoint& point2);
481 template bool parseFloatPoint2(const UChar*& current, const UChar* end, FloatPoint& point1, FloatPoint& point2);
483 template <typename CharType>
484 bool parseFloatPoint3(const CharType*& current, const CharType* end, FloatPoint& point1, FloatPoint& point2, FloatPoint& point3)
492 if (!parseNumber(current, end, x1)
493 || !parseNumber(current, end, y1)
494 || !parseNumber(current, end, x2)
495 || !parseNumber(current, end, y2)
496 || !parseNumber(current, end, x3)
497 || !parseNumber(current, end, y3))
499 point1 = FloatPoint(x1, y1);
500 point2 = FloatPoint(x2, y2);
501 point3 = FloatPoint(x3, y3);
505 template bool parseFloatPoint3(const LChar*& current, const LChar* end, FloatPoint& point1, FloatPoint& point2, FloatPoint& point3);
506 template bool parseFloatPoint3(const UChar*& current, const UChar* end, FloatPoint& point1, FloatPoint& point2, FloatPoint& point3);
508 static const LChar skewXDesc[] = {'s', 'k', 'e', 'w', 'X'};
509 static const LChar skewYDesc[] = {'s', 'k', 'e', 'w', 'Y'};
510 static const LChar scaleDesc[] = {'s', 'c', 'a', 'l', 'e'};
511 static const LChar translateDesc[] = {'t', 'r', 'a', 'n', 's', 'l', 'a', 't', 'e'};
512 static const LChar rotateDesc[] = {'r', 'o', 't', 'a', 't', 'e'};
513 static const LChar matrixDesc[] = {'m', 'a', 't', 'r', 'i', 'x'};
515 template<typename CharType>
516 bool parseAndSkipTransformType(const CharType*& ptr, const CharType* end, SVGTransformType& type)
522 if (skipString(ptr, end, skewXDesc, WTF_ARRAY_LENGTH(skewXDesc)))
523 type = SVG_TRANSFORM_SKEWX;
524 else if (skipString(ptr, end, skewYDesc, WTF_ARRAY_LENGTH(skewYDesc)))
525 type = SVG_TRANSFORM_SKEWY;
526 else if (skipString(ptr, end, scaleDesc, WTF_ARRAY_LENGTH(scaleDesc)))
527 type = SVG_TRANSFORM_SCALE;
530 } else if (skipString(ptr, end, translateDesc, WTF_ARRAY_LENGTH(translateDesc)))
531 type = SVG_TRANSFORM_TRANSLATE;
532 else if (skipString(ptr, end, rotateDesc, WTF_ARRAY_LENGTH(rotateDesc)))
533 type = SVG_TRANSFORM_ROTATE;
534 else if (skipString(ptr, end, matrixDesc, WTF_ARRAY_LENGTH(matrixDesc)))
535 type = SVG_TRANSFORM_MATRIX;
542 template bool parseAndSkipTransformType(const UChar*& current, const UChar* end, SVGTransformType&);
543 template bool parseAndSkipTransformType(const LChar*& current, const LChar* end, SVGTransformType&);
545 SVGTransformType parseTransformType(const String& string)
547 if (string.isEmpty())
548 return SVG_TRANSFORM_UNKNOWN;
549 SVGTransformType type = SVG_TRANSFORM_UNKNOWN;
550 if (string.is8Bit()) {
551 const LChar* ptr = string.characters8();
552 const LChar* end = ptr + string.length();
553 parseAndSkipTransformType(ptr, end, type);
555 const UChar* ptr = string.characters16();
556 const UChar* end = ptr + string.length();
557 parseAndSkipTransformType(ptr, end, type);