1/* 2 * Copyright 2016 Google Inc. 3 * 4 * Use of this source code is governed by a BSD-style license that can be 5 * found in the LICENSE file. 6 */ 7 8#include "SkParse.h" 9#include "SkSVGAttributeParser.h" 10#include "SkSVGTypes.h" 11 12namespace { 13 14// TODO: these should be shared with SkParse.cpp 15 16inline bool is_between(char c, char min, char max) { 17 SkASSERT(min <= max); 18 return (unsigned)(c - min) <= (unsigned)(max - min); 19} 20 21inline bool is_eos(char c) { 22 return !c; 23} 24 25inline bool is_ws(char c) { 26 return is_between(c, 1, 32); 27} 28 29inline bool is_sep(char c) { 30 return is_ws(c) || c == ',' || c == ';'; 31} 32 33} // anonymous ns 34 35SkSVGAttributeParser::SkSVGAttributeParser(const char attributeString[]) 36 : fCurPos(attributeString) {} 37 38template <typename F> 39inline bool SkSVGAttributeParser::advanceWhile(F f) { 40 auto initial = fCurPos; 41 while (f(*fCurPos)) { 42 fCurPos++; 43 } 44 return fCurPos != initial; 45} 46 47inline bool SkSVGAttributeParser::parseEOSToken() { 48 return is_eos(*fCurPos); 49} 50 51inline bool SkSVGAttributeParser::parseSepToken() { 52 return this->advanceWhile(is_sep); 53} 54 55inline bool SkSVGAttributeParser::parseWSToken() { 56 return this->advanceWhile(is_ws); 57} 58 59inline bool SkSVGAttributeParser::parseExpectedStringToken(const char* expected) { 60 const char* c = fCurPos; 61 62 while (*c && *expected && *c == *expected) { 63 c++; 64 expected++; 65 } 66 67 if (*expected) { 68 return false; 69 } 70 71 fCurPos = c; 72 return true; 73} 74 75bool SkSVGAttributeParser::parseScalarToken(SkScalar* res) { 76 if (const char* next = SkParse::FindScalar(fCurPos, res)) { 77 fCurPos = next; 78 return true; 79 } 80 return false; 81} 82 83bool SkSVGAttributeParser::parseHexToken(uint32_t* res) { 84 if (const char* next = SkParse::FindHex(fCurPos, res)) { 85 fCurPos = next; 86 return true; 87 } 88 return false; 89} 90 91bool SkSVGAttributeParser::parseLengthUnitToken(SkSVGLength::Unit* unit) { 92 static const struct { 93 const char* fUnitName; 94 SkSVGLength::Unit fUnit; 95 } gUnitInfo[] = { 96 { "%" , SkSVGLength::Unit::kPercentage }, 97 { "em", SkSVGLength::Unit::kEMS }, 98 { "ex", SkSVGLength::Unit::kEXS }, 99 { "px", SkSVGLength::Unit::kPX }, 100 { "cm", SkSVGLength::Unit::kCM }, 101 { "mm", SkSVGLength::Unit::kMM }, 102 { "in", SkSVGLength::Unit::kIN }, 103 { "pt", SkSVGLength::Unit::kPT }, 104 { "pc", SkSVGLength::Unit::kPC }, 105 }; 106 107 for (size_t i = 0; i < SK_ARRAY_COUNT(gUnitInfo); ++i) { 108 if (this->parseExpectedStringToken(gUnitInfo[i].fUnitName)) { 109 *unit = gUnitInfo[i].fUnit; 110 return true; 111 } 112 } 113 return false; 114} 115 116// https://www.w3.org/TR/SVG/types.html#DataTypeColor 117bool SkSVGAttributeParser::parseNamedColorToken(SkColor* c) { 118 if (const char* next = SkParse::FindNamedColor(fCurPos, strlen(fCurPos), c)) { 119 fCurPos = next; 120 return true; 121 } 122 return false; 123} 124 125bool SkSVGAttributeParser::parseHexColorToken(SkColor* c) { 126 uint32_t v; 127 const char* initial = fCurPos; 128 129 if (!this->parseExpectedStringToken("#") || !this->parseHexToken(&v)) { 130 return false; 131 } 132 133 switch (fCurPos - initial) { 134 case 7: 135 // matched #xxxxxxx 136 break; 137 case 4: 138 // matched '#xxx; 139 v = ((v << 12) & 0x00f00000) | 140 ((v << 8) & 0x000ff000) | 141 ((v << 4) & 0x00000ff0) | 142 ((v << 0) & 0x0000000f); 143 break; 144 default: 145 return false; 146 } 147 148 *c = v | 0xff000000; 149 return true; 150} 151 152bool SkSVGAttributeParser::parseColorComponentToken(int32_t* c) { 153 fCurPos = SkParse::FindS32(fCurPos, c); 154 if (!fCurPos) { 155 return false; 156 } 157 158 if (*fCurPos == '%') { 159 *c = SkScalarRoundToInt(*c * 255.0f / 100); 160 fCurPos++; 161 } 162 163 return true; 164} 165 166bool SkSVGAttributeParser::parseRGBColorToken(SkColor* c) { 167 return this->parseParenthesized("rgb", [this](SkColor* c) -> bool { 168 int32_t r, g, b; 169 if (this->parseColorComponentToken(&r) && 170 this->parseSepToken() && 171 this->parseColorComponentToken(&g) && 172 this->parseSepToken() && 173 this->parseColorComponentToken(&b)) { 174 175 *c = SkColorSetRGB(static_cast<uint8_t>(r), 176 static_cast<uint8_t>(g), 177 static_cast<uint8_t>(b)); 178 return true; 179 } 180 return false; 181 }, c); 182} 183 184bool SkSVGAttributeParser::parseColor(SkSVGColorType* color) { 185 SkColor c; 186 187 // consume preceding whitespace 188 this->parseWSToken(); 189 190 // TODO: rgb(...) 191 bool parsedValue = false; 192 if (this->parseHexColorToken(&c) 193 || this->parseNamedColorToken(&c) 194 || this->parseRGBColorToken(&c)) { 195 *color = SkSVGColorType(c); 196 parsedValue = true; 197 198 // consume trailing whitespace 199 this->parseWSToken(); 200 } 201 202 return parsedValue && this->parseEOSToken(); 203} 204 205// https://www.w3.org/TR/SVG/linking.html#IRIReference 206bool SkSVGAttributeParser::parseIRI(SkSVGStringType* iri) { 207 // consume preceding whitespace 208 this->parseWSToken(); 209 210 // we only support local fragments 211 if (!this->parseExpectedStringToken("#")) { 212 return false; 213 } 214 const auto* start = fCurPos; 215 this->advanceWhile([](char c) -> bool { return !is_eos(c) && c != ')'; }); 216 if (start == fCurPos) { 217 return false; 218 } 219 *iri = SkString(start, fCurPos - start); 220 return true; 221} 222 223// https://www.w3.org/TR/SVG/types.html#DataTypeFuncIRI 224bool SkSVGAttributeParser::parseFuncIRI(SkSVGStringType* iri) { 225 return this->parseParenthesized("url", [this](SkSVGStringType* iri) -> bool { 226 return this->parseIRI(iri); 227 }, iri); 228} 229 230// https://www.w3.org/TR/SVG/types.html#DataTypeNumber 231bool SkSVGAttributeParser::parseNumber(SkSVGNumberType* number) { 232 // consume WS 233 this->parseWSToken(); 234 235 SkScalar s; 236 if (this->parseScalarToken(&s)) { 237 *number = SkSVGNumberType(s); 238 // consume trailing separators 239 this->parseSepToken(); 240 return true; 241 } 242 243 return false; 244} 245 246// https://www.w3.org/TR/SVG/types.html#DataTypeLength 247bool SkSVGAttributeParser::parseLength(SkSVGLength* length) { 248 SkScalar s; 249 SkSVGLength::Unit u = SkSVGLength::Unit::kNumber; 250 251 if (this->parseScalarToken(&s) && 252 (this->parseLengthUnitToken(&u) || this->parseSepToken() || this->parseEOSToken())) { 253 *length = SkSVGLength(s, u); 254 // consume trailing separators 255 this->parseSepToken(); 256 return true; 257 } 258 259 return false; 260} 261 262// https://www.w3.org/TR/SVG/coords.html#ViewBoxAttribute 263bool SkSVGAttributeParser::parseViewBox(SkSVGViewBoxType* vb) { 264 SkScalar x, y, w, h; 265 this->parseWSToken(); 266 267 bool parsedValue = false; 268 if (this->parseScalarToken(&x) && this->parseSepToken() && 269 this->parseScalarToken(&y) && this->parseSepToken() && 270 this->parseScalarToken(&w) && this->parseSepToken() && 271 this->parseScalarToken(&h)) { 272 273 *vb = SkSVGViewBoxType(SkRect::MakeXYWH(x, y, w, h)); 274 parsedValue = true; 275 // consume trailing whitespace 276 this->parseWSToken(); 277 } 278 return parsedValue && this->parseEOSToken(); 279} 280 281template <typename Func, typename T> 282bool SkSVGAttributeParser::parseParenthesized(const char* prefix, Func f, T* result) { 283 this->parseWSToken(); 284 if (prefix && !this->parseExpectedStringToken(prefix)) { 285 return false; 286 } 287 this->parseWSToken(); 288 if (!this->parseExpectedStringToken("(")) { 289 return false; 290 } 291 this->parseWSToken(); 292 293 if (!f(result)) { 294 return false; 295 } 296 this->parseWSToken(); 297 298 return this->parseExpectedStringToken(")"); 299} 300 301bool SkSVGAttributeParser::parseMatrixToken(SkMatrix* matrix) { 302 return this->parseParenthesized("matrix", [this](SkMatrix* m) -> bool { 303 SkScalar scalars[6]; 304 for (int i = 0; i < 6; ++i) { 305 if (!(this->parseScalarToken(scalars + i) && 306 (i > 4 || this->parseSepToken()))) { 307 return false; 308 } 309 } 310 311 m->setAll(scalars[0], scalars[2], scalars[4], scalars[1], scalars[3], scalars[5], 0, 0, 1); 312 return true; 313 }, matrix); 314} 315 316bool SkSVGAttributeParser::parseTranslateToken(SkMatrix* matrix) { 317 return this->parseParenthesized("translate", [this](SkMatrix* m) -> bool { 318 SkScalar tx = 0.0, ty = 0.0; 319 this->parseWSToken(); 320 if (!this->parseScalarToken(&tx)) { 321 return false; 322 } 323 324 if (!(this->parseSepToken() && this->parseScalarToken(&ty))) { 325 ty = tx; 326 } 327 328 m->setTranslate(tx, ty); 329 return true; 330 }, matrix); 331} 332 333bool SkSVGAttributeParser::parseScaleToken(SkMatrix* matrix) { 334 return this->parseParenthesized("scale", [this](SkMatrix* m) -> bool { 335 SkScalar sx = 0.0, sy = 0.0; 336 if (!this->parseScalarToken(&sx)) { 337 return false; 338 } 339 340 if (!(this->parseSepToken() && this->parseScalarToken(&sy))) { 341 sy = sx; 342 } 343 344 m->setScale(sx, sy); 345 return true; 346 }, matrix); 347} 348 349bool SkSVGAttributeParser::parseRotateToken(SkMatrix* matrix) { 350 return this->parseParenthesized("rotate", [this](SkMatrix* m) -> bool { 351 SkScalar angle; 352 if (!this->parseScalarToken(&angle)) { 353 return false; 354 } 355 356 SkScalar cx = 0; 357 SkScalar cy = 0; 358 // optional [<cx> <cy>] 359 if (this->parseSepToken() && this->parseScalarToken(&cx)) { 360 if (!(this->parseSepToken() && this->parseScalarToken(&cy))) { 361 return false; 362 } 363 } 364 365 m->setRotate(angle, cx, cy); 366 return true; 367 }, matrix); 368} 369 370bool SkSVGAttributeParser::parseSkewXToken(SkMatrix* matrix) { 371 return this->parseParenthesized("skewX", [this](SkMatrix* m) -> bool { 372 SkScalar angle; 373 if (!this->parseScalarToken(&angle)) { 374 return false; 375 } 376 m->setSkewX(angle); 377 return true; 378 }, matrix); 379} 380 381bool SkSVGAttributeParser::parseSkewYToken(SkMatrix* matrix) { 382 return this->parseParenthesized("skewY", [this](SkMatrix* m) -> bool { 383 SkScalar angle; 384 if (!this->parseScalarToken(&angle)) { 385 return false; 386 } 387 m->setSkewY(angle); 388 return true; 389 }, matrix); 390} 391 392// https://www.w3.org/TR/SVG/coords.html#TransformAttribute 393bool SkSVGAttributeParser::parseTransform(SkSVGTransformType* t) { 394 SkMatrix matrix = SkMatrix::I(); 395 396 bool parsed = false; 397 while (true) { 398 SkMatrix m; 399 400 if (!( this->parseMatrixToken(&m) 401 || this->parseTranslateToken(&m) 402 || this->parseScaleToken(&m) 403 || this->parseRotateToken(&m) 404 || this->parseSkewXToken(&m) 405 || this->parseSkewYToken(&m))) { 406 break; 407 } 408 409 matrix.preConcat(m); 410 parsed = true; 411 } 412 413 this->parseWSToken(); 414 if (!parsed || !this->parseEOSToken()) { 415 return false; 416 } 417 418 *t = SkSVGTransformType(matrix); 419 return true; 420} 421 422// https://www.w3.org/TR/SVG/painting.html#SpecifyingPaint 423bool SkSVGAttributeParser::parsePaint(SkSVGPaint* paint) { 424 SkSVGColorType c; 425 SkSVGStringType iri; 426 bool parsedValue = false; 427 if (this->parseColor(&c)) { 428 *paint = SkSVGPaint(c); 429 parsedValue = true; 430 } else if (this->parseExpectedStringToken("none")) { 431 *paint = SkSVGPaint(SkSVGPaint::Type::kNone); 432 parsedValue = true; 433 } else if (this->parseExpectedStringToken("currentColor")) { 434 *paint = SkSVGPaint(SkSVGPaint::Type::kCurrentColor); 435 parsedValue = true; 436 } else if (this->parseExpectedStringToken("inherit")) { 437 *paint = SkSVGPaint(SkSVGPaint::Type::kInherit); 438 parsedValue = true; 439 } else if (this->parseFuncIRI(&iri)) { 440 *paint = SkSVGPaint(iri.value()); 441 parsedValue = true; 442 } 443 return parsedValue && this->parseEOSToken(); 444} 445 446// https://www.w3.org/TR/SVG/masking.html#ClipPathProperty 447bool SkSVGAttributeParser::parseClipPath(SkSVGClip* clip) { 448 SkSVGStringType iri; 449 bool parsedValue = false; 450 451 if (this->parseExpectedStringToken("none")) { 452 *clip = SkSVGClip(SkSVGClip::Type::kNone); 453 parsedValue = true; 454 } else if (this->parseExpectedStringToken("inherit")) { 455 *clip = SkSVGClip(SkSVGClip::Type::kInherit); 456 parsedValue = true; 457 } else if (this->parseFuncIRI(&iri)) { 458 *clip = SkSVGClip(iri.value()); 459 parsedValue = true; 460 } 461 462 return parsedValue && this->parseEOSToken(); 463} 464 465// https://www.w3.org/TR/SVG/painting.html#StrokeLinecapProperty 466bool SkSVGAttributeParser::parseLineCap(SkSVGLineCap* cap) { 467 static const struct { 468 SkSVGLineCap::Type fType; 469 const char* fName; 470 } gCapInfo[] = { 471 { SkSVGLineCap::Type::kButt , "butt" }, 472 { SkSVGLineCap::Type::kRound , "round" }, 473 { SkSVGLineCap::Type::kSquare , "square" }, 474 { SkSVGLineCap::Type::kInherit, "inherit" }, 475 }; 476 477 bool parsedValue = false; 478 for (size_t i = 0; i < SK_ARRAY_COUNT(gCapInfo); ++i) { 479 if (this->parseExpectedStringToken(gCapInfo[i].fName)) { 480 *cap = SkSVGLineCap(gCapInfo[i].fType); 481 parsedValue = true; 482 break; 483 } 484 } 485 486 return parsedValue && this->parseEOSToken(); 487} 488 489// https://www.w3.org/TR/SVG/painting.html#StrokeLinejoinProperty 490bool SkSVGAttributeParser::parseLineJoin(SkSVGLineJoin* join) { 491 static const struct { 492 SkSVGLineJoin::Type fType; 493 const char* fName; 494 } gJoinInfo[] = { 495 { SkSVGLineJoin::Type::kMiter , "miter" }, 496 { SkSVGLineJoin::Type::kRound , "round" }, 497 { SkSVGLineJoin::Type::kBevel , "bevel" }, 498 { SkSVGLineJoin::Type::kInherit, "inherit" }, 499 }; 500 501 bool parsedValue = false; 502 for (size_t i = 0; i < SK_ARRAY_COUNT(gJoinInfo); ++i) { 503 if (this->parseExpectedStringToken(gJoinInfo[i].fName)) { 504 *join = SkSVGLineJoin(gJoinInfo[i].fType); 505 parsedValue = true; 506 break; 507 } 508 } 509 510 return parsedValue && this->parseEOSToken(); 511} 512 513// https://www.w3.org/TR/SVG/pservers.html#LinearGradientElementSpreadMethodAttribute 514bool SkSVGAttributeParser::parseSpreadMethod(SkSVGSpreadMethod* spread) { 515 static const struct { 516 SkSVGSpreadMethod::Type fType; 517 const char* fName; 518 } gSpreadInfo[] = { 519 { SkSVGSpreadMethod::Type::kPad , "pad" }, 520 { SkSVGSpreadMethod::Type::kReflect, "reflect" }, 521 { SkSVGSpreadMethod::Type::kRepeat , "repeat" }, 522 }; 523 524 bool parsedValue = false; 525 for (size_t i = 0; i < SK_ARRAY_COUNT(gSpreadInfo); ++i) { 526 if (this->parseExpectedStringToken(gSpreadInfo[i].fName)) { 527 *spread = SkSVGSpreadMethod(gSpreadInfo[i].fType); 528 parsedValue = true; 529 break; 530 } 531 } 532 533 return parsedValue && this->parseEOSToken(); 534} 535 536// https://www.w3.org/TR/SVG/shapes.html#PolygonElementPointsAttribute 537bool SkSVGAttributeParser::parsePoints(SkSVGPointsType* points) { 538 SkTDArray<SkPoint> pts; 539 540 bool parsedValue = false; 541 for (;;) { 542 this->parseWSToken(); 543 544 SkScalar x, y; 545 if (!this->parseScalarToken(&x)) { 546 break; 547 } 548 549 // comma-wsp: 550 // (wsp+ comma? wsp*) | (comma wsp*) 551 bool wsp = this->parseWSToken(); 552 bool comma = this->parseExpectedStringToken(","); 553 if (!(wsp || comma)) { 554 break; 555 } 556 this->parseWSToken(); 557 558 if (!this->parseScalarToken(&y)) { 559 break; 560 } 561 562 pts.push(SkPoint::Make(x, y)); 563 parsedValue = true; 564 } 565 566 if (parsedValue && this->parseEOSToken()) { 567 *points = pts; 568 return true; 569 } 570 571 return false; 572} 573 574// https://www.w3.org/TR/SVG/painting.html#FillRuleProperty 575bool SkSVGAttributeParser::parseFillRule(SkSVGFillRule* fillRule) { 576 static const struct { 577 SkSVGFillRule::Type fType; 578 const char* fName; 579 } gFillRuleInfo[] = { 580 { SkSVGFillRule::Type::kNonZero, "nonzero" }, 581 { SkSVGFillRule::Type::kEvenOdd, "evenodd" }, 582 { SkSVGFillRule::Type::kInherit, "inherit" }, 583 }; 584 585 bool parsedValue = false; 586 for (size_t i = 0; i < SK_ARRAY_COUNT(gFillRuleInfo); ++i) { 587 if (this->parseExpectedStringToken(gFillRuleInfo[i].fName)) { 588 *fillRule = SkSVGFillRule(gFillRuleInfo[i].fType); 589 parsedValue = true; 590 break; 591 } 592 } 593 594 return parsedValue && this->parseEOSToken(); 595} 596 597// https://www.w3.org/TR/SVG/painting.html#VisibilityProperty 598bool SkSVGAttributeParser::parseVisibility(SkSVGVisibility* visibility) { 599 static const struct { 600 SkSVGVisibility::Type fType; 601 const char* fName; 602 } gVisibilityInfo[] = { 603 { SkSVGVisibility::Type::kVisible , "visible" }, 604 { SkSVGVisibility::Type::kHidden , "hidden" }, 605 { SkSVGVisibility::Type::kCollapse, "collapse" }, 606 { SkSVGVisibility::Type::kInherit , "inherit" }, 607 }; 608 609 bool parsedValue = false; 610 for (const auto& parseInfo : gVisibilityInfo) { 611 if (this->parseExpectedStringToken(parseInfo.fName)) { 612 *visibility = SkSVGVisibility(parseInfo.fType); 613 parsedValue = true; 614 break; 615 } 616 } 617 618 return parsedValue && this->parseEOSToken(); 619} 620 621// https://www.w3.org/TR/SVG/painting.html#StrokeDasharrayProperty 622bool SkSVGAttributeParser::parseDashArray(SkSVGDashArray* dashArray) { 623 bool parsedValue = false; 624 if (this->parseExpectedStringToken("none")) { 625 *dashArray = SkSVGDashArray(SkSVGDashArray::Type::kNone); 626 parsedValue = true; 627 } else if (this->parseExpectedStringToken("inherit")) { 628 *dashArray = SkSVGDashArray(SkSVGDashArray::Type::kInherit); 629 parsedValue = true; 630 } else { 631 SkTDArray<SkSVGLength> dashes; 632 for (;;) { 633 SkSVGLength dash; 634 // parseLength() also consumes trailing separators. 635 if (!this->parseLength(&dash)) { 636 break; 637 } 638 639 dashes.push(dash); 640 parsedValue = true; 641 } 642 643 if (parsedValue) { 644 *dashArray = SkSVGDashArray(std::move(dashes)); 645 } 646 } 647 648 return parsedValue && this->parseEOSToken(); 649} 650