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 "core/svg/SVGTransformList.h"
29 #include "platform/geometry/FloatRect.h"
30 #include "platform/transforms/AffineTransform.h"
31 #include "wtf/ASCIICType.h"
32 #include <limits>
33
34 namespace WebCore {
35
36 template <typename FloatType>
isValidRange(const FloatType & x)37 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.
46 template <typename CharType, typename FloatType>
genericParseNumber(const CharType * & ptr,const CharType * end,FloatType & number,bool skip)47 static bool genericParseNumber(const CharType*& ptr, const CharType* end, FloatType& number, bool skip)
48 {
49 FloatType integer, decimal, frac, exponent;
50 int sign, expsign;
51 const CharType* start = ptr;
52
53 exponent = 0;
54 integer = 0;
55 frac = 1;
56 decimal = 0;
57 sign = 1;
58 expsign = 1;
59
60 // read the sign
61 if (ptr < end && *ptr == '+')
62 ptr++;
63 else if (ptr < end && *ptr == '-') {
64 ptr++;
65 sign = -1;
66 }
67
68 if (ptr == end || ((*ptr < '0' || *ptr > '9') && *ptr != '.'))
69 // The first character of a number must be one of [0-9+-.]
70 return false;
71
72 // read the integer part, build right-to-left
73 const CharType* ptrStartIntPart = ptr;
74 while (ptr < end && *ptr >= '0' && *ptr <= '9')
75 ++ptr; // Advance to first non-digit.
76
77 if (ptr != ptrStartIntPart) {
78 const CharType* ptrScanIntPart = ptr - 1;
79 FloatType multiplier = 1;
80 while (ptrScanIntPart >= ptrStartIntPart) {
81 integer += multiplier * static_cast<FloatType>(*(ptrScanIntPart--) - '0');
82 multiplier *= 10;
83 }
84 // Bail out early if this overflows.
85 if (!isValidRange(integer))
86 return false;
87 }
88
89 if (ptr < end && *ptr == '.') { // read the decimals
90 ptr++;
91
92 // There must be a least one digit following the .
93 if (ptr >= end || *ptr < '0' || *ptr > '9')
94 return false;
95
96 while (ptr < end && *ptr >= '0' && *ptr <= '9')
97 decimal += (*(ptr++) - '0') * (frac *= static_cast<FloatType>(0.1));
98 }
99
100 // read the exponent part
101 if (ptr != start && ptr + 1 < end && (*ptr == 'e' || *ptr == 'E')
102 && (ptr[1] != 'x' && ptr[1] != 'm')) {
103 ptr++;
104
105 // read the sign of the exponent
106 if (*ptr == '+')
107 ptr++;
108 else if (*ptr == '-') {
109 ptr++;
110 expsign = -1;
111 }
112
113 // There must be an exponent
114 if (ptr >= end || *ptr < '0' || *ptr > '9')
115 return false;
116
117 while (ptr < end && *ptr >= '0' && *ptr <= '9') {
118 exponent *= static_cast<FloatType>(10);
119 exponent += *ptr - '0';
120 ptr++;
121 }
122 // Make sure exponent is valid.
123 if (!isValidRange(exponent) || exponent > std::numeric_limits<FloatType>::max_exponent)
124 return false;
125 }
126
127 number = integer + decimal;
128 number *= sign;
129
130 if (exponent)
131 number *= static_cast<FloatType>(pow(10.0, expsign * static_cast<int>(exponent)));
132
133 // Don't return Infinity() or NaN().
134 if (!isValidRange(number))
135 return false;
136
137 if (start == ptr)
138 return false;
139
140 if (skip)
141 skipOptionalSVGSpacesOrDelimiter(ptr, end);
142
143 return true;
144 }
145
146 template <typename CharType>
parseSVGNumber(CharType * begin,size_t length,double & number)147 bool parseSVGNumber(CharType* begin, size_t length, double& number)
148 {
149 const CharType* ptr = begin;
150 const CharType* end = ptr + length;
151 return genericParseNumber(ptr, end, number, false);
152 }
153
154 // Explicitly instantiate the two flavors of parseSVGNumber() to satisfy external callers
155 template bool parseSVGNumber(LChar* begin, size_t length, double&);
156 template bool parseSVGNumber(UChar* begin, size_t length, double&);
157
parseNumber(const LChar * & ptr,const LChar * end,float & number,bool skip)158 bool parseNumber(const LChar*& ptr, const LChar* end, float& number, bool skip)
159 {
160 return genericParseNumber(ptr, end, number, skip);
161 }
162
parseNumber(const UChar * & ptr,const UChar * end,float & number,bool skip)163 bool parseNumber(const UChar*& ptr, const UChar* end, float& number, bool skip)
164 {
165 return genericParseNumber(ptr, end, number, skip);
166 }
167
parseNumberFromString(const String & string,float & number,bool skip)168 bool parseNumberFromString(const String& string, float& number, bool skip)
169 {
170 if (string.isEmpty())
171 return false;
172 if (string.is8Bit()) {
173 const LChar* ptr = string.characters8();
174 const LChar* end = ptr + string.length();
175 return genericParseNumber(ptr, end, number, skip) && ptr == end;
176 }
177 const UChar* ptr = string.characters16();
178 const UChar* end = ptr + string.length();
179 return genericParseNumber(ptr, end, number, skip) && ptr == end;
180 }
181
182 // only used to parse largeArcFlag and sweepFlag which must be a "0" or "1"
183 // and might not have any whitespace/comma after it
184 template <typename CharType>
genericParseArcFlag(const CharType * & ptr,const CharType * end,bool & flag)185 bool genericParseArcFlag(const CharType*& ptr, const CharType* end, bool& flag)
186 {
187 if (ptr >= end)
188 return false;
189 const CharType flagChar = *ptr++;
190 if (flagChar == '0')
191 flag = false;
192 else if (flagChar == '1')
193 flag = true;
194 else
195 return false;
196
197 skipOptionalSVGSpacesOrDelimiter(ptr, end);
198
199 return true;
200 }
201
parseArcFlag(const LChar * & ptr,const LChar * end,bool & flag)202 bool parseArcFlag(const LChar*& ptr, const LChar* end, bool& flag)
203 {
204 return genericParseArcFlag(ptr, end, flag);
205 }
206
parseArcFlag(const UChar * & ptr,const UChar * end,bool & flag)207 bool parseArcFlag(const UChar*& ptr, const UChar* end, bool& flag)
208 {
209 return genericParseArcFlag(ptr, end, flag);
210 }
211
212 template<typename CharType>
genericParseNumberOptionalNumber(const CharType * & ptr,const CharType * end,float & x,float & y)213 static bool genericParseNumberOptionalNumber(const CharType*& ptr, const CharType* end, float& x, float& y)
214 {
215 if (!parseNumber(ptr, end, x))
216 return false;
217
218 if (ptr == end)
219 y = x;
220 else if (!parseNumber(ptr, end, y, false))
221 return false;
222
223 return ptr == end;
224 }
225
parseNumberOptionalNumber(const String & string,float & x,float & y)226 bool parseNumberOptionalNumber(const String& string, float& x, float& y)
227 {
228 if (string.isEmpty())
229 return false;
230 if (string.is8Bit()) {
231 const LChar* ptr = string.characters8();
232 const LChar* end = ptr + string.length();
233 return genericParseNumberOptionalNumber(ptr, end, x, y);
234 }
235 const UChar* ptr = string.characters16();
236 const UChar* end = ptr + string.length();
237 return genericParseNumberOptionalNumber(ptr, end, x, y);
238 }
239
240 template<typename CharType>
genericParseRect(const CharType * & ptr,const CharType * end,FloatRect & rect)241 static bool genericParseRect(const CharType*& ptr, const CharType* end, FloatRect& rect)
242 {
243 skipOptionalSVGSpaces(ptr, end);
244
245 float x = 0;
246 float y = 0;
247 float width = 0;
248 float height = 0;
249 bool valid = parseNumber(ptr, end, x) && parseNumber(ptr, end, y) && parseNumber(ptr, end, width) && parseNumber(ptr, end, height, false);
250 rect = FloatRect(x, y, width, height);
251 return valid;
252 }
253
parseRect(const String & string,FloatRect & rect)254 bool parseRect(const String& string, FloatRect& rect)
255 {
256 if (string.isEmpty())
257 return false;
258 if (string.is8Bit()) {
259 const LChar* ptr = string.characters8();
260 const LChar* end = ptr + string.length();
261 return genericParseRect(ptr, end, rect);
262 }
263 const UChar* ptr = string.characters16();
264 const UChar* end = ptr + string.length();
265 return genericParseRect(ptr, end, rect);
266 }
267
268 template<typename CharType>
genericParsePointsList(SVGPointList & pointsList,const CharType * & ptr,const CharType * end)269 static bool genericParsePointsList(SVGPointList& pointsList, const CharType*& ptr, const CharType* end)
270 {
271 skipOptionalSVGSpaces(ptr, end);
272
273 bool delimParsed = false;
274 while (ptr < end) {
275 delimParsed = false;
276 float xPos = 0.0f;
277 if (!parseNumber(ptr, end, xPos))
278 return false;
279
280 float yPos = 0.0f;
281 if (!parseNumber(ptr, end, yPos, false))
282 return false;
283
284 skipOptionalSVGSpaces(ptr, end);
285
286 if (ptr < end && *ptr == ',') {
287 delimParsed = true;
288 ptr++;
289 }
290 skipOptionalSVGSpaces(ptr, end);
291
292 pointsList.append(FloatPoint(xPos, yPos));
293 }
294 return ptr == end && !delimParsed;
295 }
296
297 // FIXME: Why is the out parameter first?
pointsListFromSVGData(SVGPointList & pointsList,const String & points)298 bool pointsListFromSVGData(SVGPointList& pointsList, const String& points)
299 {
300 if (points.isEmpty())
301 return true;
302 if (points.is8Bit()) {
303 const LChar* ptr = points.characters8();
304 const LChar* end = ptr + points.length();
305 return genericParsePointsList(pointsList, ptr, end);
306 }
307 const UChar* ptr = points.characters16();
308 const UChar* end = ptr + points.length();
309 return genericParsePointsList(pointsList, ptr, end);
310 }
311
312 template<typename CharType>
parseGlyphName(const CharType * & ptr,const CharType * end,HashSet<String> & values)313 static bool parseGlyphName(const CharType*& ptr, const CharType* end, HashSet<String>& values)
314 {
315 skipOptionalSVGSpaces(ptr, end);
316
317 while (ptr < end) {
318 // Leading and trailing white space, and white space before and after separators, will be ignored.
319 const CharType* inputStart = ptr;
320 while (ptr < end && *ptr != ',')
321 ++ptr;
322
323 if (ptr == inputStart)
324 break;
325
326 // walk backwards from the ; to ignore any whitespace
327 const CharType* inputEnd = ptr - 1;
328 while (inputStart < inputEnd && isSVGSpace(*inputEnd))
329 --inputEnd;
330
331 values.add(String(inputStart, inputEnd - inputStart + 1));
332 skipOptionalSVGSpacesOrDelimiter(ptr, end, ',');
333 }
334
335 return true;
336 }
337
parseGlyphName(const String & input,HashSet<String> & values)338 bool parseGlyphName(const String& input, HashSet<String>& values)
339 {
340 // FIXME: Parsing error detection is missing.
341 values.clear();
342 if (input.isEmpty())
343 return true;
344 if (input.is8Bit()) {
345 const LChar* ptr = input.characters8();
346 const LChar* end = ptr + input.length();
347 return parseGlyphName(ptr, end, values);
348 }
349 const UChar* ptr = input.characters16();
350 const UChar* end = ptr + input.length();
351 return parseGlyphName(ptr, end, values);
352 }
353
354 template<typename CharType>
parseUnicodeRange(const CharType * characters,unsigned length,UnicodeRange & range)355 static bool parseUnicodeRange(const CharType* characters, unsigned length, UnicodeRange& range)
356 {
357 if (length < 2 || characters[0] != 'U' || characters[1] != '+')
358 return false;
359
360 // Parse the starting hex number (or its prefix).
361 unsigned startRange = 0;
362 unsigned startLength = 0;
363
364 const CharType* ptr = characters + 2;
365 const CharType* end = characters + length;
366 while (ptr < end) {
367 if (!isASCIIHexDigit(*ptr))
368 break;
369 ++startLength;
370 if (startLength > 6)
371 return false;
372 startRange = (startRange << 4) | toASCIIHexValue(*ptr);
373 ++ptr;
374 }
375
376 // Handle the case of ranges separated by "-" sign.
377 if (2 + startLength < length && *ptr == '-') {
378 if (!startLength)
379 return false;
380
381 // Parse the ending hex number (or its prefix).
382 unsigned endRange = 0;
383 unsigned endLength = 0;
384 ++ptr;
385 while (ptr < end) {
386 if (!isASCIIHexDigit(*ptr))
387 break;
388 ++endLength;
389 if (endLength > 6)
390 return false;
391 endRange = (endRange << 4) | toASCIIHexValue(*ptr);
392 ++ptr;
393 }
394
395 if (!endLength)
396 return false;
397
398 range.first = startRange;
399 range.second = endRange;
400 return true;
401 }
402
403 // Handle the case of a number with some optional trailing question marks.
404 unsigned endRange = startRange;
405 while (ptr < end) {
406 if (*ptr != '?')
407 break;
408 ++startLength;
409 if (startLength > 6)
410 return false;
411 startRange <<= 4;
412 endRange = (endRange << 4) | 0xF;
413 ++ptr;
414 }
415
416 if (!startLength)
417 return false;
418
419 range.first = startRange;
420 range.second = endRange;
421 return true;
422 }
423
424 template<typename CharType>
genericParseKerningUnicodeString(const CharType * & ptr,const CharType * end,UnicodeRanges & rangeList,HashSet<String> & stringList)425 static bool genericParseKerningUnicodeString(const CharType*& ptr, const CharType* end, UnicodeRanges& rangeList, HashSet<String>& stringList)
426 {
427 while (ptr < end) {
428 const CharType* inputStart = ptr;
429 while (ptr < end && *ptr != ',')
430 ++ptr;
431
432 if (ptr == inputStart)
433 break;
434
435 // Try to parse unicode range first
436 UnicodeRange range;
437 if (parseUnicodeRange(inputStart, ptr - inputStart, range))
438 rangeList.append(range);
439 else
440 stringList.add(String(inputStart, ptr - inputStart));
441 ++ptr;
442 }
443
444 return true;
445 }
446
parseKerningUnicodeString(const String & input,UnicodeRanges & rangeList,HashSet<String> & stringList)447 bool parseKerningUnicodeString(const String& input, UnicodeRanges& rangeList, HashSet<String>& stringList)
448 {
449 // FIXME: Parsing error detection is missing.
450 if (input.isEmpty())
451 return true;
452 if (input.is8Bit()) {
453 const LChar* ptr = input.characters8();
454 const LChar* end = ptr + input.length();
455 return genericParseKerningUnicodeString(ptr, end, rangeList, stringList);
456 }
457 const UChar* ptr = input.characters16();
458 const UChar* end = ptr + input.length();
459 return genericParseKerningUnicodeString(ptr, end, rangeList, stringList);
460 }
461
462 template<typename CharType>
genericParseDelimitedString(const CharType * & ptr,const CharType * end,const char seperator)463 static Vector<String> genericParseDelimitedString(const CharType*& ptr, const CharType* end, const char seperator)
464 {
465 Vector<String> values;
466
467 skipOptionalSVGSpaces(ptr, end);
468
469 while (ptr < end) {
470 // Leading and trailing white space, and white space before and after semicolon separators, will be ignored.
471 const CharType* inputStart = ptr;
472 while (ptr < end && *ptr != seperator) // careful not to ignore whitespace inside inputs
473 ptr++;
474
475 if (ptr == inputStart)
476 break;
477
478 // walk backwards from the ; to ignore any whitespace
479 const CharType* inputEnd = ptr - 1;
480 while (inputStart < inputEnd && isSVGSpace(*inputEnd))
481 inputEnd--;
482
483 values.append(String(inputStart, inputEnd - inputStart + 1));
484 skipOptionalSVGSpacesOrDelimiter(ptr, end, seperator);
485 }
486
487 return values;
488 }
489
parseDelimitedString(const String & input,const char seperator)490 Vector<String> parseDelimitedString(const String& input, const char seperator)
491 {
492 if (input.isEmpty())
493 return Vector<String>();
494 if (input.is8Bit()) {
495 const LChar* ptr = input.characters8();
496 const LChar* end = ptr + input.length();
497 return genericParseDelimitedString(ptr, end, seperator);
498 }
499 const UChar* ptr = input.characters16();
500 const UChar* end = ptr + input.length();
501 return genericParseDelimitedString(ptr, end, seperator);
502 }
503
504 template <typename CharType>
parseFloatPoint(const CharType * & current,const CharType * end,FloatPoint & point)505 bool parseFloatPoint(const CharType*& current, const CharType* end, FloatPoint& point)
506 {
507 float x;
508 float y;
509 if (!parseNumber(current, end, x)
510 || !parseNumber(current, end, y))
511 return false;
512 point = FloatPoint(x, y);
513 return true;
514 }
515
516 template bool parseFloatPoint(const LChar*& current, const LChar* end, FloatPoint& point1);
517 template bool parseFloatPoint(const UChar*& current, const UChar* end, FloatPoint& point1);
518
519 template <typename CharType>
parseFloatPoint2(const CharType * & current,const CharType * end,FloatPoint & point1,FloatPoint & point2)520 inline bool parseFloatPoint2(const CharType*& current, const CharType* end, FloatPoint& point1, FloatPoint& point2)
521 {
522 float x1;
523 float y1;
524 float x2;
525 float y2;
526 if (!parseNumber(current, end, x1)
527 || !parseNumber(current, end, y1)
528 || !parseNumber(current, end, x2)
529 || !parseNumber(current, end, y2))
530 return false;
531 point1 = FloatPoint(x1, y1);
532 point2 = FloatPoint(x2, y2);
533 return true;
534 }
535
536 template bool parseFloatPoint2(const LChar*& current, const LChar* end, FloatPoint& point1, FloatPoint& point2);
537 template bool parseFloatPoint2(const UChar*& current, const UChar* end, FloatPoint& point1, FloatPoint& point2);
538
539 template <typename CharType>
parseFloatPoint3(const CharType * & current,const CharType * end,FloatPoint & point1,FloatPoint & point2,FloatPoint & point3)540 bool parseFloatPoint3(const CharType*& current, const CharType* end, FloatPoint& point1, FloatPoint& point2, FloatPoint& point3)
541 {
542 float x1;
543 float y1;
544 float x2;
545 float y2;
546 float x3;
547 float y3;
548 if (!parseNumber(current, end, x1)
549 || !parseNumber(current, end, y1)
550 || !parseNumber(current, end, x2)
551 || !parseNumber(current, end, y2)
552 || !parseNumber(current, end, x3)
553 || !parseNumber(current, end, y3))
554 return false;
555 point1 = FloatPoint(x1, y1);
556 point2 = FloatPoint(x2, y2);
557 point3 = FloatPoint(x3, y3);
558 return true;
559 }
560
561 template bool parseFloatPoint3(const LChar*& current, const LChar* end, FloatPoint& point1, FloatPoint& point2, FloatPoint& point3);
562 template bool parseFloatPoint3(const UChar*& current, const UChar* end, FloatPoint& point1, FloatPoint& point2, FloatPoint& point3);
563
564 template<typename CharType>
parseTransformParamList(const CharType * & ptr,const CharType * end,float * values,int required,int optional)565 static int parseTransformParamList(const CharType*& ptr, const CharType* end, float* values, int required, int optional)
566 {
567 int optionalParams = 0, requiredParams = 0;
568
569 if (!skipOptionalSVGSpaces(ptr, end) || *ptr != '(')
570 return -1;
571
572 ptr++;
573
574 skipOptionalSVGSpaces(ptr, end);
575
576 while (requiredParams < required) {
577 if (ptr >= end || !parseNumber(ptr, end, values[requiredParams], false))
578 return -1;
579 requiredParams++;
580 if (requiredParams < required)
581 skipOptionalSVGSpacesOrDelimiter(ptr, end);
582 }
583 if (!skipOptionalSVGSpaces(ptr, end))
584 return -1;
585
586 bool delimParsed = skipOptionalSVGSpacesOrDelimiter(ptr, end);
587
588 if (ptr >= end)
589 return -1;
590
591 if (*ptr == ')') { // skip optionals
592 ptr++;
593 if (delimParsed)
594 return -1;
595 } else {
596 while (optionalParams < optional) {
597 if (ptr >= end || !parseNumber(ptr, end, values[requiredParams + optionalParams], false))
598 return -1;
599 optionalParams++;
600 if (optionalParams < optional)
601 skipOptionalSVGSpacesOrDelimiter(ptr, end);
602 }
603
604 if (!skipOptionalSVGSpaces(ptr, end))
605 return -1;
606
607 delimParsed = skipOptionalSVGSpacesOrDelimiter(ptr, end);
608
609 if (ptr >= end || *ptr != ')' || delimParsed)
610 return -1;
611 ptr++;
612 }
613
614 return requiredParams + optionalParams;
615 }
616
617 // These should be kept in sync with enum SVGTransformType
618 static const int requiredValuesForType[] = {0, 6, 1, 1, 1, 1, 1};
619 static const int optionalValuesForType[] = {0, 0, 1, 1, 2, 0, 0};
620
621 template<typename CharType>
parseTransformValueInternal(unsigned type,const CharType * & ptr,const CharType * end,SVGTransform & transform)622 static bool parseTransformValueInternal(unsigned type, const CharType*& ptr, const CharType* end, SVGTransform& transform)
623 {
624 if (type == SVGTransform::SVG_TRANSFORM_UNKNOWN)
625 return false;
626
627 int valueCount = 0;
628 float values[] = {0, 0, 0, 0, 0, 0};
629 if ((valueCount = parseTransformParamList(ptr, end, values, requiredValuesForType[type], optionalValuesForType[type])) < 0)
630 return false;
631
632 switch (type) {
633 case SVGTransform::SVG_TRANSFORM_SKEWX:
634 transform.setSkewX(values[0]);
635 break;
636 case SVGTransform::SVG_TRANSFORM_SKEWY:
637 transform.setSkewY(values[0]);
638 break;
639 case SVGTransform::SVG_TRANSFORM_SCALE:
640 if (valueCount == 1) // Spec: if only one param given, assume uniform scaling
641 transform.setScale(values[0], values[0]);
642 else
643 transform.setScale(values[0], values[1]);
644 break;
645 case SVGTransform::SVG_TRANSFORM_TRANSLATE:
646 if (valueCount == 1) // Spec: if only one param given, assume 2nd param to be 0
647 transform.setTranslate(values[0], 0);
648 else
649 transform.setTranslate(values[0], values[1]);
650 break;
651 case SVGTransform::SVG_TRANSFORM_ROTATE:
652 if (valueCount == 1)
653 transform.setRotate(values[0], 0, 0);
654 else
655 transform.setRotate(values[0], values[1], values[2]);
656 break;
657 case SVGTransform::SVG_TRANSFORM_MATRIX:
658 transform.setMatrix(AffineTransform(values[0], values[1], values[2], values[3], values[4], values[5]));
659 break;
660 }
661
662 return true;
663 }
664
parseTransformValue(unsigned type,const LChar * & ptr,const LChar * end,SVGTransform & transform)665 bool parseTransformValue(unsigned type, const LChar*& ptr, const LChar* end, SVGTransform& transform)
666 {
667 return parseTransformValueInternal(type, ptr, end, transform);
668 }
669
parseTransformValue(unsigned type,const UChar * & ptr,const UChar * end,SVGTransform & transform)670 bool parseTransformValue(unsigned type, const UChar*& ptr, const UChar* end, SVGTransform& transform)
671 {
672 return parseTransformValueInternal(type, ptr, end, transform);
673 }
674
675 static const LChar skewXDesc[] = {'s', 'k', 'e', 'w', 'X'};
676 static const LChar skewYDesc[] = {'s', 'k', 'e', 'w', 'Y'};
677 static const LChar scaleDesc[] = {'s', 'c', 'a', 'l', 'e'};
678 static const LChar translateDesc[] = {'t', 'r', 'a', 'n', 's', 'l', 'a', 't', 'e'};
679 static const LChar rotateDesc[] = {'r', 'o', 't', 'a', 't', 'e'};
680 static const LChar matrixDesc[] = {'m', 'a', 't', 'r', 'i', 'x'};
681
682 template<typename CharType>
parseAndSkipType(const CharType * & ptr,const CharType * end,unsigned short & type)683 static inline bool parseAndSkipType(const CharType*& ptr, const CharType* end, unsigned short& type)
684 {
685 if (ptr >= end)
686 return false;
687
688 if (*ptr == 's') {
689 if (skipString(ptr, end, skewXDesc, WTF_ARRAY_LENGTH(skewXDesc)))
690 type = SVGTransform::SVG_TRANSFORM_SKEWX;
691 else if (skipString(ptr, end, skewYDesc, WTF_ARRAY_LENGTH(skewYDesc)))
692 type = SVGTransform::SVG_TRANSFORM_SKEWY;
693 else if (skipString(ptr, end, scaleDesc, WTF_ARRAY_LENGTH(scaleDesc)))
694 type = SVGTransform::SVG_TRANSFORM_SCALE;
695 else
696 return false;
697 } else if (skipString(ptr, end, translateDesc, WTF_ARRAY_LENGTH(translateDesc)))
698 type = SVGTransform::SVG_TRANSFORM_TRANSLATE;
699 else if (skipString(ptr, end, rotateDesc, WTF_ARRAY_LENGTH(rotateDesc)))
700 type = SVGTransform::SVG_TRANSFORM_ROTATE;
701 else if (skipString(ptr, end, matrixDesc, WTF_ARRAY_LENGTH(matrixDesc)))
702 type = SVGTransform::SVG_TRANSFORM_MATRIX;
703 else
704 return false;
705
706 return true;
707 }
708
parseTransformType(const String & string)709 SVGTransform::SVGTransformType parseTransformType(const String& string)
710 {
711 if (string.isEmpty())
712 return SVGTransform::SVG_TRANSFORM_UNKNOWN;
713 unsigned short type = SVGTransform::SVG_TRANSFORM_UNKNOWN;
714 if (string.is8Bit()) {
715 const LChar* ptr = string.characters8();
716 const LChar* end = ptr + string.length();
717 parseAndSkipType(ptr, end, type);
718 } else {
719 const UChar* ptr = string.characters16();
720 const UChar* end = ptr + string.length();
721 parseAndSkipType(ptr, end, type);
722 }
723 return static_cast<SVGTransform::SVGTransformType>(type);
724 }
725
726 template<typename CharType>
parseTransformAttributeInternal(SVGTransformList & list,const CharType * & ptr,const CharType * end,TransformParsingMode mode)727 bool parseTransformAttributeInternal(SVGTransformList& list, const CharType*& ptr, const CharType* end, TransformParsingMode mode)
728 {
729 if (mode == ClearList)
730 list.clear();
731
732 bool delimParsed = false;
733 while (ptr < end) {
734 delimParsed = false;
735 unsigned short type = SVGTransform::SVG_TRANSFORM_UNKNOWN;
736 skipOptionalSVGSpaces(ptr, end);
737
738 if (!parseAndSkipType(ptr, end, type))
739 return false;
740
741 SVGTransform transform;
742 if (!parseTransformValue(type, ptr, end, transform))
743 return false;
744
745 list.append(transform);
746 skipOptionalSVGSpaces(ptr, end);
747 if (ptr < end && *ptr == ',') {
748 delimParsed = true;
749 ++ptr;
750 }
751 skipOptionalSVGSpaces(ptr, end);
752 }
753
754 return !delimParsed;
755 }
756
parseTransformAttribute(SVGTransformList & list,const LChar * & ptr,const LChar * end,TransformParsingMode mode)757 bool parseTransformAttribute(SVGTransformList& list, const LChar*& ptr, const LChar* end, TransformParsingMode mode)
758 {
759 return parseTransformAttributeInternal(list, ptr, end, mode);
760 }
761
parseTransformAttribute(SVGTransformList & list,const UChar * & ptr,const UChar * end,TransformParsingMode mode)762 bool parseTransformAttribute(SVGTransformList& list, const UChar*& ptr, const UChar* end, TransformParsingMode mode)
763 {
764 return parseTransformAttributeInternal(list, ptr, end, mode);
765 }
766
767 }
768