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.
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.
26 #include "SVGParserUtilities.h"
29 #include "FloatRect.h"
30 #include "SVGPointList.h"
33 #include <wtf/ASCIICType.h>
37 template <typename FloatType> static inline bool isValidRange(const FloatType& x)
39 static const FloatType max = std::numeric_limits<FloatType>::max();
40 return x >= -max && x <= max;
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
46 template <typename FloatType> static bool genericParseNumber(const UChar*& ptr, const UChar* end, FloatType& number, bool skip)
48 FloatType integer, decimal, frac, exponent;
50 const UChar* start = ptr;
60 if (ptr < end && *ptr == '+')
62 else if (ptr < end && *ptr == '-') {
67 if (ptr == end || ((*ptr < '0' || *ptr > '9') && *ptr != '.'))
68 // The first character of a number must be one of [0-9+-.]
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.
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');
83 // Bail out early if this overflows.
84 if (!isValidRange(integer))
88 if (ptr < end && *ptr == '.') { // read the decimals
91 // There must be a least one digit following the .
92 if (ptr >= end || *ptr < '0' || *ptr > '9')
95 while (ptr < end && *ptr >= '0' && *ptr <= '9')
96 decimal += (*(ptr++) - '0') * (frac *= static_cast<FloatType>(0.1));
99 // read the exponent part
100 if (ptr != start && ptr + 1 < end && (*ptr == 'e' || *ptr == 'E')
101 && (ptr[1] != 'x' && ptr[1] != 'm')) {
104 // read the sign of the exponent
107 else if (*ptr == '-') {
112 // There must be an exponent
113 if (ptr >= end || *ptr < '0' || *ptr > '9')
116 while (ptr < end && *ptr >= '0' && *ptr <= '9') {
117 exponent *= static_cast<FloatType>(10);
118 exponent += *ptr - '0';
121 // Make sure exponent is valid.
122 if (!isValidRange(exponent) || exponent > std::numeric_limits<FloatType>::max_exponent)
126 number = integer + decimal;
130 number *= static_cast<FloatType>(pow(10.0, expsign * static_cast<int>(exponent)));
132 // Don't return Infinity() or NaN().
133 if (!isValidRange(number))
140 skipOptionalSVGSpacesOrDelimiter(ptr, end);
145 bool parseNumber(const UChar*& ptr, const UChar* end, float& number, bool skip)
147 return genericParseNumber(ptr, end, number, skip);
150 bool parseNumberFromString(const String& string, float& number, bool skip)
152 const UChar* ptr = string.characters();
153 const UChar* end = ptr + string.length();
154 return genericParseNumber(ptr, end, number, skip) && ptr == end;
157 // only used to parse largeArcFlag and sweepFlag which must be a "0" or "1"
158 // and might not have any whitespace/comma after it
159 bool parseArcFlag(const UChar*& ptr, const UChar* end, bool& flag)
163 const UChar flagChar = *ptr++;
166 else if (flagChar == '1')
171 skipOptionalSVGSpacesOrDelimiter(ptr, end);
176 bool parseNumberOptionalNumber(const String& s, float& x, float& y)
180 const UChar* cur = s.characters();
181 const UChar* end = cur + s.length();
183 if (!parseNumber(cur, end, x))
188 else if (!parseNumber(cur, end, y, false))
194 bool parseRect(const String& string, FloatRect& rect)
196 const UChar* ptr = string.characters();
197 const UChar* end = ptr + string.length();
198 skipOptionalSVGSpaces(ptr, end);
204 bool valid = parseNumber(ptr, end, x) && parseNumber(ptr, end, y) && parseNumber(ptr, end, width) && parseNumber(ptr, end, height, false);
205 rect = FloatRect(x, y, width, height);
209 bool pointsListFromSVGData(SVGPointList& pointsList, const String& points)
211 if (points.isEmpty())
213 const UChar* cur = points.characters();
214 const UChar* end = cur + points.length();
216 skipOptionalSVGSpaces(cur, end);
218 bool delimParsed = false;
222 if (!parseNumber(cur, end, xPos))
226 if (!parseNumber(cur, end, yPos, false))
229 skipOptionalSVGSpaces(cur, end);
231 if (cur < end && *cur == ',') {
235 skipOptionalSVGSpaces(cur, end);
237 pointsList.append(FloatPoint(xPos, yPos));
239 return cur == end && !delimParsed;
242 bool parseGlyphName(const String& input, HashSet<String>& values)
244 // FIXME: Parsing error detection is missing.
247 const UChar* ptr = input.characters();
248 const UChar* end = ptr + input.length();
249 skipOptionalSVGSpaces(ptr, end);
252 // Leading and trailing white space, and white space before and after separators, will be ignored.
253 const UChar* inputStart = ptr;
254 while (ptr < end && *ptr != ',')
257 if (ptr == inputStart)
260 // walk backwards from the ; to ignore any whitespace
261 const UChar* inputEnd = ptr - 1;
262 while (inputStart < inputEnd && isSVGSpace(*inputEnd))
265 values.add(String(inputStart, inputEnd - inputStart + 1));
266 skipOptionalSVGSpacesOrDelimiter(ptr, end, ',');
272 static bool parseUnicodeRange(const UChar* characters, unsigned length, UnicodeRange& range)
274 if (length < 2 || characters[0] != 'U' || characters[1] != '+')
277 // Parse the starting hex number (or its prefix).
278 unsigned startRange = 0;
279 unsigned startLength = 0;
281 const UChar* ptr = characters + 2;
282 const UChar* end = characters + length;
284 if (!isASCIIHexDigit(*ptr))
289 startRange = (startRange << 4) | toASCIIHexValue(*ptr);
293 // Handle the case of ranges separated by "-" sign.
294 if (2 + startLength < length && *ptr == '-') {
298 // Parse the ending hex number (or its prefix).
299 unsigned endRange = 0;
300 unsigned endLength = 0;
303 if (!isASCIIHexDigit(*ptr))
308 endRange = (endRange << 4) | toASCIIHexValue(*ptr);
315 range.first = startRange;
316 range.second = endRange;
320 // Handle the case of a number with some optional trailing question marks.
321 unsigned endRange = startRange;
329 endRange = (endRange << 4) | 0xF;
336 range.first = startRange;
337 range.second = endRange;
341 bool parseKerningUnicodeString(const String& input, UnicodeRanges& rangeList, HashSet<String>& stringList)
343 // FIXME: Parsing error detection is missing.
344 const UChar* ptr = input.characters();
345 const UChar* end = ptr + input.length();
348 const UChar* inputStart = ptr;
349 while (ptr < end && *ptr != ',')
352 if (ptr == inputStart)
355 // Try to parse unicode range first
357 if (parseUnicodeRange(inputStart, ptr - inputStart, range))
358 rangeList.append(range);
360 stringList.add(String(inputStart, ptr - inputStart));
367 Vector<String> parseDelimitedString(const String& input, const char seperator)
369 Vector<String> values;
371 const UChar* ptr = input.characters();
372 const UChar* end = ptr + input.length();
373 skipOptionalSVGSpaces(ptr, end);
376 // Leading and trailing white space, and white space before and after semicolon separators, will be ignored.
377 const UChar* inputStart = ptr;
378 while (ptr < end && *ptr != seperator) // careful not to ignore whitespace inside inputs
381 if (ptr == inputStart)
384 // walk backwards from the ; to ignore any whitespace
385 const UChar* inputEnd = ptr - 1;
386 while (inputStart < inputEnd && isSVGSpace(*inputEnd))
389 values.append(String(inputStart, inputEnd - inputStart + 1));
390 skipOptionalSVGSpacesOrDelimiter(ptr, end, seperator);
398 #endif // ENABLE(SVG)