blob: 04b050828a25b423d082c252b11c22df22b523f0 [file] [log] [blame]
fmalitabffc2562016-08-03 10:21:11 -07001/*
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
fmalita28d5b722016-09-12 17:06:47 -0700116// https://www.w3.org/TR/SVG/types.html#DataTypeColor
fmalitabffc2562016-08-03 10:21:11 -0700117bool 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
fmalita28d5b722016-09-12 17:06:47 -0700152bool 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
fmalita397a5172016-08-08 11:38:55 -0700184bool SkSVGAttributeParser::parseColor(SkSVGColorType* color) {
fmalitabffc2562016-08-03 10:21:11 -0700185 SkColor c;
186
fmalita61f36b32016-08-08 13:58:50 -0700187 // consume preceding whitespace
188 this->parseWSToken();
189
fmalitabffc2562016-08-03 10:21:11 -0700190 // TODO: rgb(...)
fmalita61f36b32016-08-08 13:58:50 -0700191 bool parsedValue = false;
fmalita28d5b722016-09-12 17:06:47 -0700192 if (this->parseHexColorToken(&c)
193 || this->parseNamedColorToken(&c)
194 || this->parseRGBColorToken(&c)) {
fmalita397a5172016-08-08 11:38:55 -0700195 *color = SkSVGColorType(c);
fmalita61f36b32016-08-08 13:58:50 -0700196 parsedValue = true;
197
198 // consume trailing whitespace
199 this->parseWSToken();
fmalitabffc2562016-08-03 10:21:11 -0700200 }
201
fmalita61f36b32016-08-08 13:58:50 -0700202 return parsedValue && this->parseEOSToken();
fmalitabffc2562016-08-03 10:21:11 -0700203}
204
fmalita28d5b722016-09-12 17:06:47 -0700205// 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
fmalitabffc2562016-08-03 10:21:11 -0700230// https://www.w3.org/TR/SVG/types.html#DataTypeNumber
fmalita397a5172016-08-08 11:38:55 -0700231bool SkSVGAttributeParser::parseNumber(SkSVGNumberType* number) {
fmalitabffc2562016-08-03 10:21:11 -0700232 // consume WS
233 this->parseWSToken();
234
235 SkScalar s;
236 if (this->parseScalarToken(&s)) {
fmalita397a5172016-08-08 11:38:55 -0700237 *number = SkSVGNumberType(s);
fmalitabffc2562016-08-03 10:21:11 -0700238 // 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}
fmalita397a5172016-08-08 11:38:55 -0700261
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}
fmalitac97796b2016-08-08 12:58:57 -0700280
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, ty;
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, sy;
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}
fmalita2d961e02016-08-11 09:16:29 -0700421
422// https://www.w3.org/TR/SVG/painting.html#SpecifyingPaint
423bool SkSVGAttributeParser::parsePaint(SkSVGPaint* paint) {
424 SkSVGColorType c;
fmalita28d5b722016-09-12 17:06:47 -0700425 SkSVGStringType iri;
fmalita2d961e02016-08-11 09:16:29 -0700426 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;
fmalita28d5b722016-09-12 17:06:47 -0700439 } else if (this->parseFuncIRI(&iri)) {
440 *paint = SkSVGPaint(iri.value());
441 parsedValue = true;
fmalita2d961e02016-08-11 09:16:29 -0700442 }
443 return parsedValue && this->parseEOSToken();
444}
445
446// https://www.w3.org/TR/SVG/painting.html#StrokeLinecapProperty
447bool SkSVGAttributeParser::parseLineCap(SkSVGLineCap* cap) {
448 static const struct {
449 SkSVGLineCap::Type fType;
450 const char* fName;
451 } gCapInfo[] = {
452 { SkSVGLineCap::Type::kButt , "butt" },
453 { SkSVGLineCap::Type::kRound , "round" },
454 { SkSVGLineCap::Type::kSquare , "square" },
455 { SkSVGLineCap::Type::kInherit, "inherit" },
456 };
457
458 bool parsedValue = false;
459 for (size_t i = 0; i < SK_ARRAY_COUNT(gCapInfo); ++i) {
460 if (this->parseExpectedStringToken(gCapInfo[i].fName)) {
461 *cap = SkSVGLineCap(gCapInfo[i].fType);
462 parsedValue = true;
463 break;
464 }
465 }
466
467 return parsedValue && this->parseEOSToken();
468}
469
470// https://www.w3.org/TR/SVG/painting.html#StrokeLinejoinProperty
471bool SkSVGAttributeParser::parseLineJoin(SkSVGLineJoin* join) {
472 static const struct {
473 SkSVGLineJoin::Type fType;
474 const char* fName;
475 } gJoinInfo[] = {
476 { SkSVGLineJoin::Type::kMiter , "miter" },
477 { SkSVGLineJoin::Type::kRound , "round" },
478 { SkSVGLineJoin::Type::kBevel , "bevel" },
479 { SkSVGLineJoin::Type::kInherit, "inherit" },
480 };
481
482 bool parsedValue = false;
483 for (size_t i = 0; i < SK_ARRAY_COUNT(gJoinInfo); ++i) {
484 if (this->parseExpectedStringToken(gJoinInfo[i].fName)) {
485 *join = SkSVGLineJoin(gJoinInfo[i].fType);
486 parsedValue = true;
487 break;
488 }
489 }
490
491 return parsedValue && this->parseEOSToken();
492}
fmalita5b31f322016-08-12 12:15:33 -0700493
fmalitacecd6172016-09-13 12:56:11 -0700494// https://www.w3.org/TR/SVG/pservers.html#LinearGradientElementSpreadMethodAttribute
495bool SkSVGAttributeParser::parseSpreadMethod(SkSVGSpreadMethod* spread) {
496 static const struct {
497 SkSVGSpreadMethod::Type fType;
498 const char* fName;
499 } gSpreadInfo[] = {
500 { SkSVGSpreadMethod::Type::kPad , "pad" },
501 { SkSVGSpreadMethod::Type::kReflect, "reflect" },
502 { SkSVGSpreadMethod::Type::kRepeat , "repeat" },
503 };
504
505 bool parsedValue = false;
506 for (size_t i = 0; i < SK_ARRAY_COUNT(gSpreadInfo); ++i) {
507 if (this->parseExpectedStringToken(gSpreadInfo[i].fName)) {
508 *spread = SkSVGSpreadMethod(gSpreadInfo[i].fType);
509 parsedValue = true;
510 break;
511 }
512 }
513
514 return parsedValue && this->parseEOSToken();
515}
516
fmalita5b31f322016-08-12 12:15:33 -0700517// https://www.w3.org/TR/SVG/shapes.html#PolygonElementPointsAttribute
518bool SkSVGAttributeParser::parsePoints(SkSVGPointsType* points) {
519 SkTDArray<SkPoint> pts;
520
521 bool parsedValue = false;
522 for (;;) {
523 this->parseWSToken();
524
525 SkScalar x, y;
526 if (!this->parseScalarToken(&x)) {
527 break;
528 }
529
530 // comma-wsp:
531 // (wsp+ comma? wsp*) | (comma wsp*)
532 bool wsp = this->parseWSToken();
533 bool comma = this->parseExpectedStringToken(",");
534 if (!(wsp || comma)) {
535 break;
536 }
537 this->parseWSToken();
538
539 if (!this->parseScalarToken(&y)) {
540 break;
541 }
542
543 pts.push(SkPoint::Make(x, y));
544 parsedValue = true;
545 }
546
547 if (parsedValue && this->parseEOSToken()) {
548 *points = pts;
549 return true;
550 }
551
552 return false;
553}