1 /* 2 * Copyright (C) 2002, 2003 The Karbon Developers 3 * Copyright (C) 2006 Alexander Kellett <lypanov (at) kde.org> 4 * Copyright (C) 2006, 2007 Rob Buis <buis (at) 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> 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> 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> 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 158 bool parseNumber(const LChar*& ptr, const LChar* end, float& number, bool skip) 159 { 160 return genericParseNumber(ptr, end, number, skip); 161 } 162 163 bool parseNumber(const UChar*& ptr, const UChar* end, float& number, bool skip) 164 { 165 return genericParseNumber(ptr, end, number, skip); 166 } 167 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> 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 202 bool parseArcFlag(const LChar*& ptr, const LChar* end, bool& flag) 203 { 204 return genericParseArcFlag(ptr, end, flag); 205 } 206 207 bool parseArcFlag(const UChar*& ptr, const UChar* end, bool& flag) 208 { 209 return genericParseArcFlag(ptr, end, flag); 210 } 211 212 template<typename CharType> 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 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> 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 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> 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? 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> 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 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> 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> 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 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> 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 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> 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> 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> 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> 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> 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 665 bool parseTransformValue(unsigned type, const LChar*& ptr, const LChar* end, SVGTransform& transform) 666 { 667 return parseTransformValueInternal(type, ptr, end, transform); 668 } 669 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> 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 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> 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 757 bool parseTransformAttribute(SVGTransformList& list, const LChar*& ptr, const LChar* end, TransformParsingMode mode) 758 { 759 return parseTransformAttributeInternal(list, ptr, end, mode); 760 } 761 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