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
35namespace WebCore {
36
37template <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.
46template <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
145bool 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
152bool 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
167bool 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
185bool 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
218bool 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
248static 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
317bool 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
343Vector<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