• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 "core/svg/SVGParserUtilities.h"
25 
26 #include "core/dom/Document.h"
27 #include "core/svg/SVGPointList.h"
28 #include "platform/geometry/FloatRect.h"
29 #include "platform/transforms/AffineTransform.h"
30 #include "wtf/ASCIICType.h"
31 #include <limits>
32 
33 namespace WebCore {
34 
35 template <typename FloatType>
isValidRange(const FloatType & x)36 static inline bool isValidRange(const FloatType& x)
37 {
38     static const FloatType max = std::numeric_limits<FloatType>::max();
39     return x >= -max && x <= max;
40 }
41 
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
44 // complexity.
45 template <typename CharType, typename FloatType>
genericParseNumber(const CharType * & ptr,const CharType * end,FloatType & number,WhitespaceMode mode)46 static bool genericParseNumber(const CharType*& ptr, const CharType* end, FloatType& number, WhitespaceMode mode)
47 {
48     FloatType integer, decimal, frac, exponent;
49     int sign, expsign;
50     const CharType* start = ptr;
51 
52     exponent = 0;
53     integer = 0;
54     frac = 1;
55     decimal = 0;
56     sign = 1;
57     expsign = 1;
58 
59     if (mode & AllowLeadingWhitespace)
60         skipOptionalSVGSpaces(ptr, end);
61 
62     // read the sign
63     if (ptr < end && *ptr == '+')
64         ptr++;
65     else if (ptr < end && *ptr == '-') {
66         ptr++;
67         sign = -1;
68     }
69 
70     if (ptr == end || ((*ptr < '0' || *ptr > '9') && *ptr != '.'))
71         // The first character of a number must be one of [0-9+-.]
72         return false;
73 
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.
78 
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');
84             multiplier *= 10;
85         }
86         // Bail out early if this overflows.
87         if (!isValidRange(integer))
88             return false;
89     }
90 
91     if (ptr < end && *ptr == '.') { // read the decimals
92         ptr++;
93 
94         // There must be a least one digit following the .
95         if (ptr >= end || *ptr < '0' || *ptr > '9')
96             return false;
97 
98         while (ptr < end && *ptr >= '0' && *ptr <= '9')
99             decimal += (*(ptr++) - '0') * (frac *= static_cast<FloatType>(0.1));
100     }
101 
102     // read the exponent part
103     if (ptr != start && ptr + 1 < end && (*ptr == 'e' || *ptr == 'E')
104         && (ptr[1] != 'x' && ptr[1] != 'm')) {
105         ptr++;
106 
107         // read the sign of the exponent
108         if (*ptr == '+')
109             ptr++;
110         else if (*ptr == '-') {
111             ptr++;
112             expsign = -1;
113         }
114 
115         // There must be an exponent
116         if (ptr >= end || *ptr < '0' || *ptr > '9')
117             return false;
118 
119         while (ptr < end && *ptr >= '0' && *ptr <= '9') {
120             exponent *= static_cast<FloatType>(10);
121             exponent += *ptr - '0';
122             ptr++;
123         }
124         // Make sure exponent is valid.
125         if (!isValidRange(exponent) || exponent > std::numeric_limits<FloatType>::max_exponent)
126             return false;
127     }
128 
129     number = integer + decimal;
130     number *= sign;
131 
132     if (exponent)
133         number *= static_cast<FloatType>(pow(10.0, expsign * static_cast<int>(exponent)));
134 
135     // Don't return Infinity() or NaN().
136     if (!isValidRange(number))
137         return false;
138 
139     if (start == ptr)
140         return false;
141 
142     if (mode & AllowTrailingWhitespace)
143         skipOptionalSVGSpacesOrDelimiter(ptr, end);
144 
145     return true;
146 }
147 
148 template <typename CharType>
parseSVGNumber(CharType * begin,size_t length,double & number)149 bool parseSVGNumber(CharType* begin, size_t length, double& number)
150 {
151     const CharType* ptr = begin;
152     const CharType* end = ptr + length;
153     return genericParseNumber(ptr, end, number, AllowLeadingAndTrailingWhitespace);
154 }
155 
156 // Explicitly instantiate the two flavors of parseSVGNumber() to satisfy external callers
157 template bool parseSVGNumber(LChar* begin, size_t length, double&);
158 template bool parseSVGNumber(UChar* begin, size_t length, double&);
159 
parseNumber(const LChar * & ptr,const LChar * end,float & number,WhitespaceMode mode)160 bool parseNumber(const LChar*& ptr, const LChar* end, float& number, WhitespaceMode mode)
161 {
162     return genericParseNumber(ptr, end, number, mode);
163 }
164 
parseNumber(const UChar * & ptr,const UChar * end,float & number,WhitespaceMode mode)165 bool parseNumber(const UChar*& ptr, const UChar* end, float& number, WhitespaceMode mode)
166 {
167     return genericParseNumber(ptr, end, number, mode);
168 }
169 
170 // only used to parse largeArcFlag and sweepFlag which must be a "0" or "1"
171 // and might not have any whitespace/comma after it
172 template <typename CharType>
genericParseArcFlag(const CharType * & ptr,const CharType * end,bool & flag)173 bool genericParseArcFlag(const CharType*& ptr, const CharType* end, bool& flag)
174 {
175     if (ptr >= end)
176         return false;
177     const CharType flagChar = *ptr++;
178     if (flagChar == '0')
179         flag = false;
180     else if (flagChar == '1')
181         flag = true;
182     else
183         return false;
184 
185     skipOptionalSVGSpacesOrDelimiter(ptr, end);
186 
187     return true;
188 }
189 
parseArcFlag(const LChar * & ptr,const LChar * end,bool & flag)190 bool parseArcFlag(const LChar*& ptr, const LChar* end, bool& flag)
191 {
192     return genericParseArcFlag(ptr, end, flag);
193 }
194 
parseArcFlag(const UChar * & ptr,const UChar * end,bool & flag)195 bool parseArcFlag(const UChar*& ptr, const UChar* end, bool& flag)
196 {
197     return genericParseArcFlag(ptr, end, flag);
198 }
199 
200 template<typename CharType>
genericParseNumberOptionalNumber(const CharType * & ptr,const CharType * end,float & x,float & y)201 static bool genericParseNumberOptionalNumber(const CharType*& ptr, const CharType* end, float& x, float& y)
202 {
203     if (!parseNumber(ptr, end, x))
204         return false;
205 
206     if (ptr == end)
207         y = x;
208     else if (!parseNumber(ptr, end, y, AllowLeadingAndTrailingWhitespace))
209         return false;
210 
211     return ptr == end;
212 }
213 
parseNumberOptionalNumber(const String & string,float & x,float & y)214 bool parseNumberOptionalNumber(const String& string, float& x, float& y)
215 {
216     if (string.isEmpty())
217         return false;
218 
219     if (string.is8Bit()) {
220         const LChar* ptr = string.characters8();
221         const LChar* end = ptr + string.length();
222         return genericParseNumberOptionalNumber(ptr, end, x, y);
223     }
224     const UChar* ptr = string.characters16();
225     const UChar* end = ptr + string.length();
226     return genericParseNumberOptionalNumber(ptr, end, x, y);
227 }
228 
229 template<typename CharType>
genericParseNumberOrPercentage(const CharType * & ptr,const CharType * end,float & number)230 bool genericParseNumberOrPercentage(const CharType*& ptr, const CharType* end, float& number)
231 {
232     if (genericParseNumber(ptr, end, number, AllowLeadingWhitespace)) {
233         if (ptr == end)
234             return true;
235 
236         bool isPercentage = (*ptr == '%');
237         if (isPercentage)
238             ptr++;
239 
240         skipOptionalSVGSpaces(ptr, end);
241 
242         if (isPercentage)
243             number /= 100.f;
244 
245         return ptr == end;
246     }
247 
248     return false;
249 }
250 
parseNumberOrPercentage(const String & string,float & number)251 bool parseNumberOrPercentage(const String& string, float& number)
252 {
253     if (string.isEmpty())
254         return false;
255 
256     if (string.is8Bit()) {
257         const LChar* ptr = string.characters8();
258         const LChar* end = ptr + string.length();
259         return genericParseNumberOrPercentage(ptr, end, number);
260     }
261     const UChar* ptr = string.characters16();
262     const UChar* end = ptr + string.length();
263     return genericParseNumberOrPercentage(ptr, end, number);
264 }
265 
266 template<typename CharType>
parseGlyphName(const CharType * & ptr,const CharType * end,HashSet<String> & values)267 static bool parseGlyphName(const CharType*& ptr, const CharType* end, HashSet<String>& values)
268 {
269     skipOptionalSVGSpaces(ptr, end);
270 
271     while (ptr < end) {
272         // Leading and trailing white space, and white space before and after separators, will be ignored.
273         const CharType* inputStart = ptr;
274         while (ptr < end && *ptr != ',')
275             ++ptr;
276 
277         if (ptr == inputStart)
278             break;
279 
280         // walk backwards from the ; to ignore any whitespace
281         const CharType* inputEnd = ptr - 1;
282         while (inputStart < inputEnd && isHTMLSpace<CharType>(*inputEnd))
283             --inputEnd;
284 
285         values.add(String(inputStart, inputEnd - inputStart + 1));
286         skipOptionalSVGSpacesOrDelimiter(ptr, end, ',');
287     }
288 
289     return true;
290 }
291 
parseGlyphName(const String & input,HashSet<String> & values)292 bool parseGlyphName(const String& input, HashSet<String>& values)
293 {
294     // FIXME: Parsing error detection is missing.
295     values.clear();
296     if (input.isEmpty())
297         return true;
298     if (input.is8Bit()) {
299         const LChar* ptr = input.characters8();
300         const LChar* end = ptr + input.length();
301         return parseGlyphName(ptr, end, values);
302     }
303     const UChar* ptr = input.characters16();
304     const UChar* end = ptr + input.length();
305     return parseGlyphName(ptr, end, values);
306 }
307 
308 template<typename CharType>
parseUnicodeRange(const CharType * characters,unsigned length,UnicodeRange & range)309 static bool parseUnicodeRange(const CharType* characters, unsigned length, UnicodeRange& range)
310 {
311     if (length < 2 || characters[0] != 'U' || characters[1] != '+')
312         return false;
313 
314     // Parse the starting hex number (or its prefix).
315     unsigned startRange = 0;
316     unsigned startLength = 0;
317 
318     const CharType* ptr = characters + 2;
319     const CharType* end = characters + length;
320     while (ptr < end) {
321         if (!isASCIIHexDigit(*ptr))
322             break;
323         ++startLength;
324         if (startLength > 6)
325             return false;
326         startRange = (startRange << 4) | toASCIIHexValue(*ptr);
327         ++ptr;
328     }
329 
330     // Handle the case of ranges separated by "-" sign.
331     if (2 + startLength < length && *ptr == '-') {
332         if (!startLength)
333             return false;
334 
335         // Parse the ending hex number (or its prefix).
336         unsigned endRange = 0;
337         unsigned endLength = 0;
338         ++ptr;
339         while (ptr < end) {
340             if (!isASCIIHexDigit(*ptr))
341                 break;
342             ++endLength;
343             if (endLength > 6)
344                 return false;
345             endRange = (endRange << 4) | toASCIIHexValue(*ptr);
346             ++ptr;
347         }
348 
349         if (!endLength)
350             return false;
351 
352         range.first = startRange;
353         range.second = endRange;
354         return true;
355     }
356 
357     // Handle the case of a number with some optional trailing question marks.
358     unsigned endRange = startRange;
359     while (ptr < end) {
360         if (*ptr != '?')
361             break;
362         ++startLength;
363         if (startLength > 6)
364             return false;
365         startRange <<= 4;
366         endRange = (endRange << 4) | 0xF;
367         ++ptr;
368     }
369 
370     if (!startLength)
371         return false;
372 
373     range.first = startRange;
374     range.second = endRange;
375     return true;
376 }
377 
378 template<typename CharType>
genericParseKerningUnicodeString(const CharType * & ptr,const CharType * end,UnicodeRanges & rangeList,HashSet<String> & stringList)379 static bool genericParseKerningUnicodeString(const CharType*& ptr, const CharType* end, UnicodeRanges& rangeList, HashSet<String>& stringList)
380 {
381     while (ptr < end) {
382         const CharType* inputStart = ptr;
383         while (ptr < end && *ptr != ',')
384             ++ptr;
385 
386         if (ptr == inputStart)
387             break;
388 
389         // Try to parse unicode range first
390         UnicodeRange range;
391         if (parseUnicodeRange(inputStart, ptr - inputStart, range))
392             rangeList.append(range);
393         else
394             stringList.add(String(inputStart, ptr - inputStart));
395         ++ptr;
396     }
397 
398     return true;
399 }
400 
parseKerningUnicodeString(const String & input,UnicodeRanges & rangeList,HashSet<String> & stringList)401 bool parseKerningUnicodeString(const String& input, UnicodeRanges& rangeList, HashSet<String>& stringList)
402 {
403     // FIXME: Parsing error detection is missing.
404     if (input.isEmpty())
405         return true;
406     if (input.is8Bit()) {
407         const LChar* ptr = input.characters8();
408         const LChar* end = ptr + input.length();
409         return genericParseKerningUnicodeString(ptr, end, rangeList, stringList);
410     }
411     const UChar* ptr = input.characters16();
412     const UChar* end = ptr + input.length();
413     return genericParseKerningUnicodeString(ptr, end, rangeList, stringList);
414 }
415 
416 template<typename CharType>
genericParseDelimitedString(const CharType * & ptr,const CharType * end,const char seperator)417 static Vector<String> genericParseDelimitedString(const CharType*& ptr, const CharType* end, const char seperator)
418 {
419     Vector<String> values;
420 
421     skipOptionalSVGSpaces(ptr, end);
422 
423     while (ptr < end) {
424         // Leading and trailing white space, and white space before and after semicolon separators, will be ignored.
425         const CharType* inputStart = ptr;
426         while (ptr < end && *ptr != seperator) // careful not to ignore whitespace inside inputs
427             ptr++;
428 
429         if (ptr == inputStart)
430             break;
431 
432         // walk backwards from the ; to ignore any whitespace
433         const CharType* inputEnd = ptr - 1;
434         while (inputStart < inputEnd && isHTMLSpace<CharType>(*inputEnd))
435             inputEnd--;
436 
437         values.append(String(inputStart, inputEnd - inputStart + 1));
438         skipOptionalSVGSpacesOrDelimiter(ptr, end, seperator);
439     }
440 
441     return values;
442 }
443 
parseDelimitedString(const String & input,const char seperator)444 Vector<String> parseDelimitedString(const String& input, const char seperator)
445 {
446     if (input.isEmpty())
447         return Vector<String>();
448     if (input.is8Bit()) {
449         const LChar* ptr = input.characters8();
450         const LChar* end = ptr + input.length();
451         return genericParseDelimitedString(ptr, end, seperator);
452     }
453     const UChar* ptr = input.characters16();
454     const UChar* end = ptr + input.length();
455     return genericParseDelimitedString(ptr, end, seperator);
456 }
457 
458 template <typename CharType>
parseFloatPoint(const CharType * & current,const CharType * end,FloatPoint & point)459 bool parseFloatPoint(const CharType*& current, const CharType* end, FloatPoint& point)
460 {
461     float x;
462     float y;
463     if (!parseNumber(current, end, x)
464         || !parseNumber(current, end, y))
465         return false;
466     point = FloatPoint(x, y);
467     return true;
468 }
469 
470 template bool parseFloatPoint(const LChar*& current, const LChar* end, FloatPoint& point1);
471 template bool parseFloatPoint(const UChar*& current, const UChar* end, FloatPoint& point1);
472 
473 template <typename CharType>
parseFloatPoint2(const CharType * & current,const CharType * end,FloatPoint & point1,FloatPoint & point2)474 inline bool parseFloatPoint2(const CharType*& current, const CharType* end, FloatPoint& point1, FloatPoint& point2)
475 {
476     float x1;
477     float y1;
478     float x2;
479     float y2;
480     if (!parseNumber(current, end, x1)
481         || !parseNumber(current, end, y1)
482         || !parseNumber(current, end, x2)
483         || !parseNumber(current, end, y2))
484         return false;
485     point1 = FloatPoint(x1, y1);
486     point2 = FloatPoint(x2, y2);
487     return true;
488 }
489 
490 template bool parseFloatPoint2(const LChar*& current, const LChar* end, FloatPoint& point1, FloatPoint& point2);
491 template bool parseFloatPoint2(const UChar*& current, const UChar* end, FloatPoint& point1, FloatPoint& point2);
492 
493 template <typename CharType>
parseFloatPoint3(const CharType * & current,const CharType * end,FloatPoint & point1,FloatPoint & point2,FloatPoint & point3)494 bool parseFloatPoint3(const CharType*& current, const CharType* end, FloatPoint& point1, FloatPoint& point2, FloatPoint& point3)
495 {
496     float x1;
497     float y1;
498     float x2;
499     float y2;
500     float x3;
501     float y3;
502     if (!parseNumber(current, end, x1)
503         || !parseNumber(current, end, y1)
504         || !parseNumber(current, end, x2)
505         || !parseNumber(current, end, y2)
506         || !parseNumber(current, end, x3)
507         || !parseNumber(current, end, y3))
508         return false;
509     point1 = FloatPoint(x1, y1);
510     point2 = FloatPoint(x2, y2);
511     point3 = FloatPoint(x3, y3);
512     return true;
513 }
514 
515 template bool parseFloatPoint3(const LChar*& current, const LChar* end, FloatPoint& point1, FloatPoint& point2, FloatPoint& point3);
516 template bool parseFloatPoint3(const UChar*& current, const UChar* end, FloatPoint& point1, FloatPoint& point2, FloatPoint& point3);
517 
518 static const LChar skewXDesc[] =  {'s', 'k', 'e', 'w', 'X'};
519 static const LChar skewYDesc[] =  {'s', 'k', 'e', 'w', 'Y'};
520 static const LChar scaleDesc[] =  {'s', 'c', 'a', 'l', 'e'};
521 static const LChar translateDesc[] =  {'t', 'r', 'a', 'n', 's', 'l', 'a', 't', 'e'};
522 static const LChar rotateDesc[] =  {'r', 'o', 't', 'a', 't', 'e'};
523 static const LChar matrixDesc[] =  {'m', 'a', 't', 'r', 'i', 'x'};
524 
525 template<typename CharType>
parseAndSkipTransformType(const CharType * & ptr,const CharType * end,SVGTransformType & type)526 bool parseAndSkipTransformType(const CharType*& ptr, const CharType* end, SVGTransformType& type)
527 {
528     if (ptr >= end)
529         return false;
530 
531     if (*ptr == 's') {
532         if (skipString(ptr, end, skewXDesc, WTF_ARRAY_LENGTH(skewXDesc)))
533             type = SVG_TRANSFORM_SKEWX;
534         else if (skipString(ptr, end, skewYDesc, WTF_ARRAY_LENGTH(skewYDesc)))
535             type = SVG_TRANSFORM_SKEWY;
536         else if (skipString(ptr, end, scaleDesc, WTF_ARRAY_LENGTH(scaleDesc)))
537             type = SVG_TRANSFORM_SCALE;
538         else
539             return false;
540     } else if (skipString(ptr, end, translateDesc, WTF_ARRAY_LENGTH(translateDesc)))
541         type = SVG_TRANSFORM_TRANSLATE;
542     else if (skipString(ptr, end, rotateDesc, WTF_ARRAY_LENGTH(rotateDesc)))
543         type = SVG_TRANSFORM_ROTATE;
544     else if (skipString(ptr, end, matrixDesc, WTF_ARRAY_LENGTH(matrixDesc)))
545         type = SVG_TRANSFORM_MATRIX;
546     else
547         return false;
548 
549     return true;
550 }
551 
552 template bool parseAndSkipTransformType(const UChar*& current, const UChar* end, SVGTransformType&);
553 template bool parseAndSkipTransformType(const LChar*& current, const LChar* end, SVGTransformType&);
554 
parseTransformType(const String & string)555 SVGTransformType parseTransformType(const String& string)
556 {
557     if (string.isEmpty())
558         return SVG_TRANSFORM_UNKNOWN;
559     SVGTransformType type = SVG_TRANSFORM_UNKNOWN;
560     if (string.is8Bit()) {
561         const LChar* ptr = string.characters8();
562         const LChar* end = ptr + string.length();
563         parseAndSkipTransformType(ptr, end, type);
564     } else {
565         const UChar* ptr = string.characters16();
566         const UChar* end = ptr + string.length();
567         parseAndSkipTransformType(ptr, end, type);
568     }
569     return type;
570 }
571 
572 }
573