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