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