blob: c9745f1730d65c1c43447cdb05c30ebe5093e0d4 [file] [log] [blame]
fmalita6ceef3d2016-07-26 18:46:34 -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 "SkCanvas.h"
9#include "SkDOM.h"
fmalita6ceef3d2016-07-26 18:46:34 -070010#include "SkParsePath.h"
fmalita58649cc2016-07-29 08:52:03 -070011#include "SkString.h"
fmalitabffc2562016-08-03 10:21:11 -070012#include "SkSVGAttributeParser.h"
fmalitadc4c2a92016-08-16 15:38:51 -070013#include "SkSVGCircle.h"
fmalita28d5b722016-09-12 17:06:47 -070014#include "SkSVGDefs.h"
fmalita6ceef3d2016-07-26 18:46:34 -070015#include "SkSVGDOM.h"
fmalitadc4c2a92016-08-16 15:38:51 -070016#include "SkSVGEllipse.h"
fmalita6ceef3d2016-07-26 18:46:34 -070017#include "SkSVGG.h"
fmalitad24ee142016-08-17 08:38:15 -070018#include "SkSVGLine.h"
fmalita28d5b722016-09-12 17:06:47 -070019#include "SkSVGLinearGradient.h"
fmalita6ceef3d2016-07-26 18:46:34 -070020#include "SkSVGNode.h"
21#include "SkSVGPath.h"
fmalita5b31f322016-08-12 12:15:33 -070022#include "SkSVGPoly.h"
fmalitabffc2562016-08-03 10:21:11 -070023#include "SkSVGRect.h"
24#include "SkSVGRenderContext.h"
fmalita28d5b722016-09-12 17:06:47 -070025#include "SkSVGStop.h"
fmalita6ceef3d2016-07-26 18:46:34 -070026#include "SkSVGSVG.h"
fmalitabffc2562016-08-03 10:21:11 -070027#include "SkSVGTypes.h"
fmalita6ceef3d2016-07-26 18:46:34 -070028#include "SkSVGValue.h"
29#include "SkTSearch.h"
30
31namespace {
32
fmalita6ceef3d2016-07-26 18:46:34 -070033bool SetPaintAttribute(const sk_sp<SkSVGNode>& node, SkSVGAttribute attr,
34 const char* stringValue) {
fmalita2d961e02016-08-11 09:16:29 -070035 SkSVGPaint paint;
fmalitabffc2562016-08-03 10:21:11 -070036 SkSVGAttributeParser parser(stringValue);
fmalita2d961e02016-08-11 09:16:29 -070037 if (!parser.parsePaint(&paint)) {
fmalita28d5b722016-09-12 17:06:47 -070038 return false;
fmalitabffc2562016-08-03 10:21:11 -070039 }
40
fmalita2d961e02016-08-11 09:16:29 -070041 node->setAttribute(attr, SkSVGPaintValue(paint));
fmalita6ceef3d2016-07-26 18:46:34 -070042 return true;
43}
44
fmalita28d5b722016-09-12 17:06:47 -070045bool SetColorAttribute(const sk_sp<SkSVGNode>& node, SkSVGAttribute attr,
46 const char* stringValue) {
47 SkSVGColorType color;
48 SkSVGAttributeParser parser(stringValue);
49 if (!parser.parseColor(&color)) {
50 return false;
51 }
52
53 node->setAttribute(attr, SkSVGColorValue(color));
54 return true;
55}
56
57bool SetIRIAttribute(const sk_sp<SkSVGNode>& node, SkSVGAttribute attr,
58 const char* stringValue) {
59 SkSVGStringType iri;
60 SkSVGAttributeParser parser(stringValue);
61 if (!parser.parseIRI(&iri)) {
62 return false;
63 }
64
65 node->setAttribute(attr, SkSVGStringValue(iri));
66 return true;
67}
68
fmalita6ceef3d2016-07-26 18:46:34 -070069bool SetPathDataAttribute(const sk_sp<SkSVGNode>& node, SkSVGAttribute attr,
70 const char* stringValue) {
71 SkPath path;
72 if (!SkParsePath::FromSVGString(stringValue, &path)) {
73 return false;
74 }
75
76 node->setAttribute(attr, SkSVGPathValue(path));
77 return true;
78}
79
80bool SetTransformAttribute(const sk_sp<SkSVGNode>& node, SkSVGAttribute attr,
81 const char* stringValue) {
fmalitac97796b2016-08-08 12:58:57 -070082 SkSVGTransformType transform;
83 SkSVGAttributeParser parser(stringValue);
84 if (!parser.parseTransform(&transform)) {
85 return false;
86 }
87
88 node->setAttribute(attr, SkSVGTransformValue(transform));
fmalita6ceef3d2016-07-26 18:46:34 -070089 return true;
90}
91
fmalitabffc2562016-08-03 10:21:11 -070092bool SetLengthAttribute(const sk_sp<SkSVGNode>& node, SkSVGAttribute attr,
93 const char* stringValue) {
94 SkSVGLength length;
95 SkSVGAttributeParser parser(stringValue);
96 if (!parser.parseLength(&length)) {
97 return false;
98 }
99
100 node->setAttribute(attr, SkSVGLengthValue(length));
101 return true;
102}
103
fmalita2d961e02016-08-11 09:16:29 -0700104bool SetNumberAttribute(const sk_sp<SkSVGNode>& node, SkSVGAttribute attr,
105 const char* stringValue) {
106 SkSVGNumberType number;
107 SkSVGAttributeParser parser(stringValue);
108 if (!parser.parseNumber(&number)) {
109 return false;
110 }
111
112 node->setAttribute(attr, SkSVGNumberValue(number));
113 return true;
114}
115
fmalita397a5172016-08-08 11:38:55 -0700116bool SetViewBoxAttribute(const sk_sp<SkSVGNode>& node, SkSVGAttribute attr,
117 const char* stringValue) {
118 SkSVGViewBoxType viewBox;
119 SkSVGAttributeParser parser(stringValue);
120 if (!parser.parseViewBox(&viewBox)) {
121 return false;
122 }
123
124 node->setAttribute(attr, SkSVGViewBoxValue(viewBox));
125 return true;
126}
127
fmalita2d961e02016-08-11 09:16:29 -0700128bool SetLineCapAttribute(const sk_sp<SkSVGNode>& node, SkSVGAttribute attr,
129 const char* stringValue) {
130 SkSVGLineCap lineCap;
131 SkSVGAttributeParser parser(stringValue);
132 if (!parser.parseLineCap(&lineCap)) {
133 return false;
134 }
135
136 node->setAttribute(attr, SkSVGLineCapValue(lineCap));
137 return true;
138}
139
140bool SetLineJoinAttribute(const sk_sp<SkSVGNode>& node, SkSVGAttribute attr,
141 const char* stringValue) {
142 SkSVGLineJoin lineJoin;
143 SkSVGAttributeParser parser(stringValue);
144 if (!parser.parseLineJoin(&lineJoin)) {
145 return false;
146 }
147
148 node->setAttribute(attr, SkSVGLineJoinValue(lineJoin));
149 return true;
150}
151
fmalitacecd6172016-09-13 12:56:11 -0700152bool SetSpreadMethodAttribute(const sk_sp<SkSVGNode>& node, SkSVGAttribute attr,
153 const char* stringValue) {
154 SkSVGSpreadMethod spread;
155 SkSVGAttributeParser parser(stringValue);
156 if (!parser.parseSpreadMethod(&spread)) {
157 return false;
158 }
159
160 node->setAttribute(attr, SkSVGSpreadMethodValue(spread));
161 return true;
162}
163
fmalita5b31f322016-08-12 12:15:33 -0700164bool SetPointsAttribute(const sk_sp<SkSVGNode>& node, SkSVGAttribute attr,
165 const char* stringValue) {
166 SkSVGPointsType points;
167 SkSVGAttributeParser parser(stringValue);
168 if (!parser.parsePoints(&points)) {
169 return false;
170 }
171
172 node->setAttribute(attr, SkSVGPointsValue(points));
173 return true;
174}
175
fmalita61f36b32016-08-08 13:58:50 -0700176SkString TrimmedString(const char* first, const char* last) {
177 SkASSERT(first);
178 SkASSERT(last);
179 SkASSERT(first <= last);
180
181 while (first <= last && *first <= ' ') { first++; }
182 while (first <= last && *last <= ' ') { last--; }
183
184 SkASSERT(last - first + 1 >= 0);
185 return SkString(first, SkTo<size_t>(last - first + 1));
186}
187
fmalita58649cc2016-07-29 08:52:03 -0700188// Breaks a "foo: bar; baz: ..." string into key:value pairs.
189class StyleIterator {
190public:
191 StyleIterator(const char* str) : fPos(str) { }
192
193 std::tuple<SkString, SkString> next() {
194 SkString name, value;
195
196 if (fPos) {
197 const char* sep = this->nextSeparator();
198 SkASSERT(*sep == ';' || *sep == '\0');
199
200 const char* valueSep = strchr(fPos, ':');
201 if (valueSep && valueSep < sep) {
fmalita61f36b32016-08-08 13:58:50 -0700202 name = TrimmedString(fPos, valueSep - 1);
203 value = TrimmedString(valueSep + 1, sep - 1);
fmalita58649cc2016-07-29 08:52:03 -0700204 }
205
206 fPos = *sep ? sep + 1 : nullptr;
207 }
208
209 return std::make_tuple(name, value);
210 }
211
212private:
213 const char* nextSeparator() const {
214 const char* sep = fPos;
215 while (*sep != ';' && *sep != '\0') {
216 sep++;
217 }
218 return sep;
219 }
220
221 const char* fPos;
222};
223
224void set_string_attribute(const sk_sp<SkSVGNode>& node, const char* name, const char* value);
225
226bool SetStyleAttributes(const sk_sp<SkSVGNode>& node, SkSVGAttribute,
227 const char* stringValue) {
228
229 SkString name, value;
230 StyleIterator iter(stringValue);
231 for (;;) {
232 std::tie(name, value) = iter.next();
233 if (name.isEmpty()) {
234 break;
235 }
236 set_string_attribute(node, name.c_str(), value.c_str());
237 }
238
239 return true;
240}
241
fmalita6ceef3d2016-07-26 18:46:34 -0700242template<typename T>
243struct SortedDictionaryEntry {
244 const char* fKey;
245 const T fValue;
246};
247
248struct AttrParseInfo {
249 SkSVGAttribute fAttr;
250 bool (*fSetter)(const sk_sp<SkSVGNode>& node, SkSVGAttribute attr, const char* stringValue);
251};
252
253SortedDictionaryEntry<AttrParseInfo> gAttributeParseInfo[] = {
fmalitaceb93ab2016-09-13 13:59:05 -0700254 { "cx" , { SkSVGAttribute::kCx , SetLengthAttribute }},
255 { "cy" , { SkSVGAttribute::kCy , SetLengthAttribute }},
256 { "d" , { SkSVGAttribute::kD , SetPathDataAttribute }},
257 { "fill" , { SkSVGAttribute::kFill , SetPaintAttribute }},
258 { "fill-opacity" , { SkSVGAttribute::kFillOpacity , SetNumberAttribute }},
259 { "gradientTransform", { SkSVGAttribute::kGradientTransform, SetTransformAttribute }},
260 { "height" , { SkSVGAttribute::kHeight , SetLengthAttribute }},
261 { "offset" , { SkSVGAttribute::kOffset , SetLengthAttribute }},
262 { "opacity" , { SkSVGAttribute::kOpacity , SetNumberAttribute }},
263 { "points" , { SkSVGAttribute::kPoints , SetPointsAttribute }},
264 { "r" , { SkSVGAttribute::kR , SetLengthAttribute }},
265 { "rx" , { SkSVGAttribute::kRx , SetLengthAttribute }},
266 { "ry" , { SkSVGAttribute::kRy , SetLengthAttribute }},
267 { "spreadMethod" , { SkSVGAttribute::kSpreadMethod , SetSpreadMethodAttribute }},
268 { "stop-color" , { SkSVGAttribute::kStopColor , SetColorAttribute }},
269 { "stop-opacity" , { SkSVGAttribute::kStopOpacity , SetNumberAttribute }},
270 { "stroke" , { SkSVGAttribute::kStroke , SetPaintAttribute }},
271 { "stroke-linecap" , { SkSVGAttribute::kStrokeLineCap , SetLineCapAttribute }},
272 { "stroke-linejoin" , { SkSVGAttribute::kStrokeLineJoin , SetLineJoinAttribute }},
273 { "stroke-opacity" , { SkSVGAttribute::kStrokeOpacity , SetNumberAttribute }},
274 { "stroke-width" , { SkSVGAttribute::kStrokeWidth , SetLengthAttribute }},
275 { "style" , { SkSVGAttribute::kUnknown , SetStyleAttributes }},
276 { "transform" , { SkSVGAttribute::kTransform , SetTransformAttribute }},
277 { "viewBox" , { SkSVGAttribute::kViewBox , SetViewBoxAttribute }},
278 { "width" , { SkSVGAttribute::kWidth , SetLengthAttribute }},
279 { "x" , { SkSVGAttribute::kX , SetLengthAttribute }},
280 { "x1" , { SkSVGAttribute::kX1 , SetLengthAttribute }},
281 { "x2" , { SkSVGAttribute::kX2 , SetLengthAttribute }},
282 { "xlink:href" , { SkSVGAttribute::kHref , SetIRIAttribute }},
283 { "y" , { SkSVGAttribute::kY , SetLengthAttribute }},
284 { "y1" , { SkSVGAttribute::kY1 , SetLengthAttribute }},
285 { "y2" , { SkSVGAttribute::kY2 , SetLengthAttribute }},
fmalita6ceef3d2016-07-26 18:46:34 -0700286};
287
288SortedDictionaryEntry<sk_sp<SkSVGNode>(*)()> gTagFactories[] = {
fmalita28d5b722016-09-12 17:06:47 -0700289 { "circle" , []() -> sk_sp<SkSVGNode> { return SkSVGCircle::Make(); }},
290 { "defs" , []() -> sk_sp<SkSVGNode> { return SkSVGDefs::Make(); }},
291 { "ellipse" , []() -> sk_sp<SkSVGNode> { return SkSVGEllipse::Make(); }},
292 { "g" , []() -> sk_sp<SkSVGNode> { return SkSVGG::Make(); }},
293 { "line" , []() -> sk_sp<SkSVGNode> { return SkSVGLine::Make(); }},
294 { "linearGradient", []() -> sk_sp<SkSVGNode> { return SkSVGLinearGradient::Make(); }},
295 { "path" , []() -> sk_sp<SkSVGNode> { return SkSVGPath::Make(); }},
296 { "polygon" , []() -> sk_sp<SkSVGNode> { return SkSVGPoly::MakePolygon(); }},
297 { "polyline" , []() -> sk_sp<SkSVGNode> { return SkSVGPoly::MakePolyline(); }},
298 { "rect" , []() -> sk_sp<SkSVGNode> { return SkSVGRect::Make(); }},
299 { "stop" , []() -> sk_sp<SkSVGNode> { return SkSVGStop::Make(); }},
300 { "svg" , []() -> sk_sp<SkSVGNode> { return SkSVGSVG::Make(); }},
fmalita6ceef3d2016-07-26 18:46:34 -0700301};
302
303struct ConstructionContext {
fmalita28d5b722016-09-12 17:06:47 -0700304 ConstructionContext(SkSVGIDMapper* mapper) : fParent(nullptr), fIDMapper(mapper) {}
fmalita6ceef3d2016-07-26 18:46:34 -0700305 ConstructionContext(const ConstructionContext& other, const sk_sp<SkSVGNode>& newParent)
fmalita28d5b722016-09-12 17:06:47 -0700306 : fParent(newParent.get()), fIDMapper(other.fIDMapper) {}
fmalita6ceef3d2016-07-26 18:46:34 -0700307
308 const SkSVGNode* fParent;
fmalita28d5b722016-09-12 17:06:47 -0700309 SkSVGIDMapper* fIDMapper;
fmalita6ceef3d2016-07-26 18:46:34 -0700310};
311
fmalita58649cc2016-07-29 08:52:03 -0700312void set_string_attribute(const sk_sp<SkSVGNode>& node, const char* name, const char* value) {
313 const int attrIndex = SkStrSearch(&gAttributeParseInfo[0].fKey,
314 SkTo<int>(SK_ARRAY_COUNT(gAttributeParseInfo)),
315 name, sizeof(gAttributeParseInfo[0]));
316 if (attrIndex < 0) {
fmalitafea704e2016-08-10 16:25:32 -0700317#if defined(SK_VERBOSE_SVG_PARSING)
fmalita58649cc2016-07-29 08:52:03 -0700318 SkDebugf("unhandled attribute: %s\n", name);
fmalitafea704e2016-08-10 16:25:32 -0700319#endif
fmalita58649cc2016-07-29 08:52:03 -0700320 return;
321 }
322
323 SkASSERT(SkTo<size_t>(attrIndex) < SK_ARRAY_COUNT(gAttributeParseInfo));
324 const auto& attrInfo = gAttributeParseInfo[attrIndex].fValue;
325 if (!attrInfo.fSetter(node, attrInfo.fAttr, value)) {
fmalitafea704e2016-08-10 16:25:32 -0700326#if defined(SK_VERBOSE_SVG_PARSING)
fmalita58649cc2016-07-29 08:52:03 -0700327 SkDebugf("could not parse attribute: '%s=\"%s\"'\n", name, value);
fmalitafea704e2016-08-10 16:25:32 -0700328#endif
fmalita58649cc2016-07-29 08:52:03 -0700329 }
330}
331
fmalita6ceef3d2016-07-26 18:46:34 -0700332void parse_node_attributes(const SkDOM& xmlDom, const SkDOM::Node* xmlNode,
fmalita28d5b722016-09-12 17:06:47 -0700333 const sk_sp<SkSVGNode>& svgNode, SkSVGIDMapper* mapper) {
fmalita6ceef3d2016-07-26 18:46:34 -0700334 const char* name, *value;
335 SkDOM::AttrIter attrIter(xmlDom, xmlNode);
336 while ((name = attrIter.next(&value))) {
fmalita28d5b722016-09-12 17:06:47 -0700337 // We're handling id attributes out of band for now.
338 if (!strcmp(name, "id")) {
339 mapper->set(SkString(value), svgNode);
340 continue;
341 }
fmalita58649cc2016-07-29 08:52:03 -0700342 set_string_attribute(svgNode, name, value);
fmalita6ceef3d2016-07-26 18:46:34 -0700343 }
344}
345
346sk_sp<SkSVGNode> construct_svg_node(const SkDOM& dom, const ConstructionContext& ctx,
347 const SkDOM::Node* xmlNode) {
348 const char* elem = dom.getName(xmlNode);
349 const SkDOM::Type elemType = dom.getType(xmlNode);
350
351 if (elemType == SkDOM::kText_Type) {
352 SkASSERT(dom.countChildren(xmlNode) == 0);
353 // TODO: text handling
354 return nullptr;
355 }
356
357 SkASSERT(elemType == SkDOM::kElement_Type);
358
359 const int tagIndex = SkStrSearch(&gTagFactories[0].fKey,
360 SkTo<int>(SK_ARRAY_COUNT(gTagFactories)),
361 elem, sizeof(gTagFactories[0]));
362 if (tagIndex < 0) {
fmalitafea704e2016-08-10 16:25:32 -0700363#if defined(SK_VERBOSE_SVG_PARSING)
fmalita6ceef3d2016-07-26 18:46:34 -0700364 SkDebugf("unhandled element: <%s>\n", elem);
fmalitafea704e2016-08-10 16:25:32 -0700365#endif
fmalita6ceef3d2016-07-26 18:46:34 -0700366 return nullptr;
367 }
368
369 SkASSERT(SkTo<size_t>(tagIndex) < SK_ARRAY_COUNT(gTagFactories));
370 sk_sp<SkSVGNode> node = gTagFactories[tagIndex].fValue();
fmalita28d5b722016-09-12 17:06:47 -0700371 parse_node_attributes(dom, xmlNode, node, ctx.fIDMapper);
fmalita6ceef3d2016-07-26 18:46:34 -0700372
373 ConstructionContext localCtx(ctx, node);
374 for (auto* child = dom.getFirstChild(xmlNode, nullptr); child;
375 child = dom.getNextSibling(child)) {
376 sk_sp<SkSVGNode> childNode = construct_svg_node(dom, localCtx, child);
377 if (childNode) {
378 node->appendChild(std::move(childNode));
379 }
380 }
381
382 return node;
383}
384
385} // anonymous namespace
386
fmalitae1baa7c2016-09-14 12:04:30 -0700387SkSVGDOM::SkSVGDOM()
388 : fContainerSize(SkSize::Make(0, 0)) {
fmalita6ceef3d2016-07-26 18:46:34 -0700389}
390
fmalitae1baa7c2016-09-14 12:04:30 -0700391sk_sp<SkSVGDOM> SkSVGDOM::MakeFromDOM(const SkDOM& xmlDom) {
392 sk_sp<SkSVGDOM> dom = sk_make_sp<SkSVGDOM>();
fmalita6ceef3d2016-07-26 18:46:34 -0700393
fmalita28d5b722016-09-12 17:06:47 -0700394 ConstructionContext ctx(&dom->fIDMapper);
fmalita6ceef3d2016-07-26 18:46:34 -0700395 dom->fRoot = construct_svg_node(xmlDom, ctx, xmlDom.getRootNode());
396
fmalitae1baa7c2016-09-14 12:04:30 -0700397 // Reset the default container size to match the intrinsic SVG size.
398 dom->setContainerSize(dom->intrinsicSize());
399
fmalita6ceef3d2016-07-26 18:46:34 -0700400 return dom;
401}
402
fmalitae1baa7c2016-09-14 12:04:30 -0700403sk_sp<SkSVGDOM> SkSVGDOM::MakeFromStream(SkStream& svgStream) {
fmalita6ceef3d2016-07-26 18:46:34 -0700404 SkDOM xmlDom;
405 if (!xmlDom.build(svgStream)) {
406 return nullptr;
407 }
408
fmalitae1baa7c2016-09-14 12:04:30 -0700409 return MakeFromDOM(xmlDom);
fmalita6ceef3d2016-07-26 18:46:34 -0700410}
411
412void SkSVGDOM::render(SkCanvas* canvas) const {
413 if (fRoot) {
fmalita397a5172016-08-08 11:38:55 -0700414 SkSVGRenderContext ctx(canvas,
fmalita28d5b722016-09-12 17:06:47 -0700415 fIDMapper,
fmalita397a5172016-08-08 11:38:55 -0700416 SkSVGLengthContext(fContainerSize),
417 SkSVGPresentationContext());
418 fRoot->render(ctx);
fmalita6ceef3d2016-07-26 18:46:34 -0700419 }
420}
421
fmalitae1baa7c2016-09-14 12:04:30 -0700422SkSize SkSVGDOM::intrinsicSize() const {
423 if (!fRoot || fRoot->tag() != SkSVGTag::kSvg) {
424 return SkSize::Make(0, 0);
425 }
426
427 // Intrinsic sizes are never relative, so the viewport size is irrelevant.
428 const SkSVGLengthContext lctx(SkSize::Make(0, 0));
429 return static_cast<const SkSVGSVG*>(fRoot.get())->intrinsicSize(lctx);
430}
431
432const SkSize& SkSVGDOM::containerSize() const {
433 return fContainerSize;
434}
435
fmalita6ceef3d2016-07-26 18:46:34 -0700436void SkSVGDOM::setContainerSize(const SkSize& containerSize) {
437 // TODO: inval
438 fContainerSize = containerSize;
439}
fmalitaca39d712016-08-12 13:17:11 -0700440
441void SkSVGDOM::setRoot(sk_sp<SkSVGNode> root) {
442 fRoot = std::move(root);
443}