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 "include/core/SkCanvas.h"
9 #include "include/core/SkFontMgr.h"
10 #include "include/core/SkString.h"
11 #include "include/private/SkTo.h"
12 #include "modules/svg/include/SkSVGAttributeParser.h"
13 #include "modules/svg/include/SkSVGCircle.h"
14 #include "modules/svg/include/SkSVGClipPath.h"
15 #include "modules/svg/include/SkSVGDOM.h"
16 #include "modules/svg/include/SkSVGDefs.h"
17 #include "modules/svg/include/SkSVGEllipse.h"
18 #include "modules/svg/include/SkSVGFeBlend.h"
19 #include "modules/svg/include/SkSVGFeColorMatrix.h"
20 #include "modules/svg/include/SkSVGFeComposite.h"
21 #include "modules/svg/include/SkSVGFeDisplacementMap.h"
22 #include "modules/svg/include/SkSVGFeFlood.h"
23 #include "modules/svg/include/SkSVGFeGaussianBlur.h"
24 #include "modules/svg/include/SkSVGFeImage.h"
25 #include "modules/svg/include/SkSVGFeLightSource.h"
26 #include "modules/svg/include/SkSVGFeLighting.h"
27 #include "modules/svg/include/SkSVGFeMorphology.h"
28 #include "modules/svg/include/SkSVGFeOffset.h"
29 #include "modules/svg/include/SkSVGFeTurbulence.h"
30 #include "modules/svg/include/SkSVGFilter.h"
31 #include "modules/svg/include/SkSVGG.h"
32 #include "modules/svg/include/SkSVGImage.h"
33 #include "modules/svg/include/SkSVGLine.h"
34 #include "modules/svg/include/SkSVGLinearGradient.h"
35 #include "modules/svg/include/SkSVGMask.h"
36 #include "modules/svg/include/SkSVGNode.h"
37 #include "modules/svg/include/SkSVGPath.h"
38 #include "modules/svg/include/SkSVGPattern.h"
39 #include "modules/svg/include/SkSVGPoly.h"
40 #include "modules/svg/include/SkSVGRadialGradient.h"
41 #include "modules/svg/include/SkSVGRect.h"
42 #include "modules/svg/include/SkSVGRenderContext.h"
43 #include "modules/svg/include/SkSVGSVG.h"
44 #include "modules/svg/include/SkSVGStop.h"
45 #include "modules/svg/include/SkSVGText.h"
46 #include "modules/svg/include/SkSVGTypes.h"
47 #include "modules/svg/include/SkSVGUse.h"
48 #include "modules/svg/include/SkSVGValue.h"
49 #include "src/core/SkTSearch.h"
50 #include "src/core/SkTraceEvent.h"
51 #include "src/xml/SkDOM.h"
52
53 namespace {
54
SetIRIAttribute(const sk_sp<SkSVGNode> & node,SkSVGAttribute attr,const char * stringValue)55 bool SetIRIAttribute(const sk_sp<SkSVGNode>& node, SkSVGAttribute attr,
56 const char* stringValue) {
57 auto parseResult = SkSVGAttributeParser::parse<SkSVGIRI>(stringValue);
58 if (!parseResult.isValid()) {
59 return false;
60 }
61
62 node->setAttribute(attr, SkSVGStringValue(parseResult->iri()));
63 return true;
64 }
65
SetStringAttribute(const sk_sp<SkSVGNode> & node,SkSVGAttribute attr,const char * stringValue)66 bool SetStringAttribute(const sk_sp<SkSVGNode>& node, SkSVGAttribute attr,
67 const char* stringValue) {
68 SkString str(stringValue, strlen(stringValue));
69 SkSVGStringType strType = SkSVGStringType(str);
70 node->setAttribute(attr, SkSVGStringValue(strType));
71 return true;
72 }
73
SetTransformAttribute(const sk_sp<SkSVGNode> & node,SkSVGAttribute attr,const char * stringValue)74 bool SetTransformAttribute(const sk_sp<SkSVGNode>& node, SkSVGAttribute attr,
75 const char* stringValue) {
76 auto parseResult = SkSVGAttributeParser::parse<SkSVGTransformType>(stringValue);
77 if (!parseResult.isValid()) {
78 return false;
79 }
80
81 node->setAttribute(attr, SkSVGTransformValue(*parseResult));
82 return true;
83 }
84
SetLengthAttribute(const sk_sp<SkSVGNode> & node,SkSVGAttribute attr,const char * stringValue)85 bool SetLengthAttribute(const sk_sp<SkSVGNode>& node, SkSVGAttribute attr,
86 const char* stringValue) {
87 auto parseResult = SkSVGAttributeParser::parse<SkSVGLength>(stringValue);
88 if (!parseResult.isValid()) {
89 return false;
90 }
91
92 node->setAttribute(attr, SkSVGLengthValue(*parseResult));
93 return true;
94 }
95
SetViewBoxAttribute(const sk_sp<SkSVGNode> & node,SkSVGAttribute attr,const char * stringValue)96 bool SetViewBoxAttribute(const sk_sp<SkSVGNode>& node, SkSVGAttribute attr,
97 const char* stringValue) {
98 SkSVGViewBoxType viewBox;
99 SkSVGAttributeParser parser(stringValue);
100 if (!parser.parseViewBox(&viewBox)) {
101 return false;
102 }
103
104 node->setAttribute(attr, SkSVGViewBoxValue(viewBox));
105 return true;
106 }
107
SetObjectBoundingBoxUnitsAttribute(const sk_sp<SkSVGNode> & node,SkSVGAttribute attr,const char * stringValue)108 bool SetObjectBoundingBoxUnitsAttribute(const sk_sp<SkSVGNode>& node,
109 SkSVGAttribute attr,
110 const char* stringValue) {
111 auto parseResult = SkSVGAttributeParser::parse<SkSVGObjectBoundingBoxUnits>(stringValue);
112 if (!parseResult.isValid()) {
113 return false;
114 }
115
116 node->setAttribute(attr, SkSVGObjectBoundingBoxUnitsValue(*parseResult));
117 return true;
118 }
119
SetPreserveAspectRatioAttribute(const sk_sp<SkSVGNode> & node,SkSVGAttribute attr,const char * stringValue)120 bool SetPreserveAspectRatioAttribute(const sk_sp<SkSVGNode>& node, SkSVGAttribute attr,
121 const char* stringValue) {
122 SkSVGPreserveAspectRatio par;
123 SkSVGAttributeParser parser(stringValue);
124 if (!parser.parsePreserveAspectRatio(&par)) {
125 return false;
126 }
127
128 node->setAttribute(attr, SkSVGPreserveAspectRatioValue(par));
129 return true;
130 }
131
TrimmedString(const char * first,const char * last)132 SkString TrimmedString(const char* first, const char* last) {
133 SkASSERT(first);
134 SkASSERT(last);
135 SkASSERT(first <= last);
136
137 while (first <= last && *first <= ' ') { first++; }
138 while (first <= last && *last <= ' ') { last--; }
139
140 SkASSERT(last - first + 1 >= 0);
141 return SkString(first, SkTo<size_t>(last - first + 1));
142 }
143
144 // Breaks a "foo: bar; baz: ..." string into key:value pairs.
145 class StyleIterator {
146 public:
StyleIterator(const char * str)147 StyleIterator(const char* str) : fPos(str) { }
148
next()149 std::tuple<SkString, SkString> next() {
150 SkString name, value;
151
152 if (fPos) {
153 const char* sep = this->nextSeparator();
154 SkASSERT(*sep == ';' || *sep == '\0');
155
156 const char* valueSep = strchr(fPos, ':');
157 if (valueSep && valueSep < sep) {
158 name = TrimmedString(fPos, valueSep - 1);
159 value = TrimmedString(valueSep + 1, sep - 1);
160 }
161
162 fPos = *sep ? sep + 1 : nullptr;
163 }
164
165 return std::make_tuple(name, value);
166 }
167
168 private:
nextSeparator() const169 const char* nextSeparator() const {
170 const char* sep = fPos;
171 while (*sep != ';' && *sep != '\0') {
172 sep++;
173 }
174 return sep;
175 }
176
177 const char* fPos;
178 };
179
180 bool set_string_attribute(const sk_sp<SkSVGNode>& node, const char* name, const char* value);
181
SetStyleAttributes(const sk_sp<SkSVGNode> & node,SkSVGAttribute,const char * stringValue)182 bool SetStyleAttributes(const sk_sp<SkSVGNode>& node, SkSVGAttribute,
183 const char* stringValue) {
184
185 SkString name, value;
186 StyleIterator iter(stringValue);
187 for (;;) {
188 std::tie(name, value) = iter.next();
189 if (name.isEmpty()) {
190 break;
191 }
192 set_string_attribute(node, name.c_str(), value.c_str());
193 }
194
195 return true;
196 }
197
198 template<typename T>
199 struct SortedDictionaryEntry {
200 const char* fKey;
201 const T fValue;
202 };
203
204 struct AttrParseInfo {
205 SkSVGAttribute fAttr;
206 bool (*fSetter)(const sk_sp<SkSVGNode>& node, SkSVGAttribute attr, const char* stringValue);
207 };
208
209 SortedDictionaryEntry<AttrParseInfo> gAttributeParseInfo[] = {
210 { "cx" , { SkSVGAttribute::kCx , SetLengthAttribute }},
211 { "cy" , { SkSVGAttribute::kCy , SetLengthAttribute }},
212 { "filterUnits" , { SkSVGAttribute::kFilterUnits ,
213 SetObjectBoundingBoxUnitsAttribute }},
214 // focal point x & y
215 { "fx" , { SkSVGAttribute::kFx , SetLengthAttribute }},
216 { "fy" , { SkSVGAttribute::kFy , SetLengthAttribute }},
217 { "height" , { SkSVGAttribute::kHeight , SetLengthAttribute }},
218 { "preserveAspectRatio", { SkSVGAttribute::kPreserveAspectRatio,
219 SetPreserveAspectRatioAttribute }},
220 { "r" , { SkSVGAttribute::kR , SetLengthAttribute }},
221 { "rx" , { SkSVGAttribute::kRx , SetLengthAttribute }},
222 { "ry" , { SkSVGAttribute::kRy , SetLengthAttribute }},
223 { "style" , { SkSVGAttribute::kUnknown , SetStyleAttributes }},
224 { "text" , { SkSVGAttribute::kText , SetStringAttribute }},
225 { "transform" , { SkSVGAttribute::kTransform , SetTransformAttribute }},
226 { "viewBox" , { SkSVGAttribute::kViewBox , SetViewBoxAttribute }},
227 { "width" , { SkSVGAttribute::kWidth , SetLengthAttribute }},
228 { "x" , { SkSVGAttribute::kX , SetLengthAttribute }},
229 { "x1" , { SkSVGAttribute::kX1 , SetLengthAttribute }},
230 { "x2" , { SkSVGAttribute::kX2 , SetLengthAttribute }},
231 { "xlink:href" , { SkSVGAttribute::kHref , SetIRIAttribute }},
232 { "y" , { SkSVGAttribute::kY , SetLengthAttribute }},
233 { "y1" , { SkSVGAttribute::kY1 , SetLengthAttribute }},
234 { "y2" , { SkSVGAttribute::kY2 , SetLengthAttribute }},
235 };
236
237 SortedDictionaryEntry<sk_sp<SkSVGNode>(*)()> gTagFactories[] = {
__anon51e47a760202() 238 { "a" , []() -> sk_sp<SkSVGNode> { return SkSVGG::Make(); }},
__anon51e47a760302() 239 { "circle" , []() -> sk_sp<SkSVGNode> { return SkSVGCircle::Make(); }},
__anon51e47a760402() 240 { "clipPath" , []() -> sk_sp<SkSVGNode> { return SkSVGClipPath::Make(); }},
__anon51e47a760502() 241 { "defs" , []() -> sk_sp<SkSVGNode> { return SkSVGDefs::Make(); }},
__anon51e47a760602() 242 { "ellipse" , []() -> sk_sp<SkSVGNode> { return SkSVGEllipse::Make(); }},
__anon51e47a760702() 243 { "feBlend" , []() -> sk_sp<SkSVGNode> { return SkSVGFeBlend::Make(); }},
__anon51e47a760802() 244 { "feColorMatrix" , []() -> sk_sp<SkSVGNode> { return SkSVGFeColorMatrix::Make(); }},
__anon51e47a760902() 245 { "feComposite" , []() -> sk_sp<SkSVGNode> { return SkSVGFeComposite::Make(); }},
__anon51e47a760a02() 246 { "feDiffuseLighting" , []() -> sk_sp<SkSVGNode> { return SkSVGFeDiffuseLighting::Make(); }},
__anon51e47a760b02() 247 { "feDisplacementMap" , []() -> sk_sp<SkSVGNode> { return SkSVGFeDisplacementMap::Make(); }},
__anon51e47a760c02() 248 { "feDistantLight" , []() -> sk_sp<SkSVGNode> { return SkSVGFeDistantLight::Make(); }},
__anon51e47a760d02() 249 { "feFlood" , []() -> sk_sp<SkSVGNode> { return SkSVGFeFlood::Make(); }},
__anon51e47a760e02() 250 { "feGaussianBlur" , []() -> sk_sp<SkSVGNode> { return SkSVGFeGaussianBlur::Make(); }},
__anon51e47a760f02() 251 { "feImage" , []() -> sk_sp<SkSVGNode> { return SkSVGFeImage::Make(); }},
__anon51e47a761002() 252 { "feMorphology" , []() -> sk_sp<SkSVGNode> { return SkSVGFeMorphology::Make(); }},
__anon51e47a761102() 253 { "feOffset" , []() -> sk_sp<SkSVGNode> { return SkSVGFeOffset::Make(); }},
__anon51e47a761202() 254 { "fePointLight" , []() -> sk_sp<SkSVGNode> { return SkSVGFePointLight::Make(); }},
__anon51e47a761302() 255 { "feSpecularLighting", []() -> sk_sp<SkSVGNode> { return SkSVGFeSpecularLighting::Make(); }},
__anon51e47a761402() 256 { "feSpotLight" , []() -> sk_sp<SkSVGNode> { return SkSVGFeSpotLight::Make(); }},
__anon51e47a761502() 257 { "feTurbulence" , []() -> sk_sp<SkSVGNode> { return SkSVGFeTurbulence::Make(); }},
__anon51e47a761602() 258 { "filter" , []() -> sk_sp<SkSVGNode> { return SkSVGFilter::Make(); }},
__anon51e47a761702() 259 { "g" , []() -> sk_sp<SkSVGNode> { return SkSVGG::Make(); }},
__anon51e47a761802() 260 { "image" , []() -> sk_sp<SkSVGNode> { return SkSVGImage::Make(); }},
__anon51e47a761902() 261 { "line" , []() -> sk_sp<SkSVGNode> { return SkSVGLine::Make(); }},
__anon51e47a761a02() 262 { "linearGradient" , []() -> sk_sp<SkSVGNode> { return SkSVGLinearGradient::Make(); }},
__anon51e47a761b02() 263 { "mask" , []() -> sk_sp<SkSVGNode> { return SkSVGMask::Make(); }},
__anon51e47a761c02() 264 { "path" , []() -> sk_sp<SkSVGNode> { return SkSVGPath::Make(); }},
__anon51e47a761d02() 265 { "pattern" , []() -> sk_sp<SkSVGNode> { return SkSVGPattern::Make(); }},
__anon51e47a761e02() 266 { "polygon" , []() -> sk_sp<SkSVGNode> { return SkSVGPoly::MakePolygon(); }},
__anon51e47a761f02() 267 { "polyline" , []() -> sk_sp<SkSVGNode> { return SkSVGPoly::MakePolyline(); }},
__anon51e47a762002() 268 { "radialGradient" , []() -> sk_sp<SkSVGNode> { return SkSVGRadialGradient::Make(); }},
__anon51e47a762102() 269 { "rect" , []() -> sk_sp<SkSVGNode> { return SkSVGRect::Make(); }},
__anon51e47a762202() 270 { "stop" , []() -> sk_sp<SkSVGNode> { return SkSVGStop::Make(); }},
271 // "svg" handled explicitly
__anon51e47a762302() 272 { "text" , []() -> sk_sp<SkSVGNode> { return SkSVGText::Make(); }},
__anon51e47a762402() 273 { "textPath" , []() -> sk_sp<SkSVGNode> { return SkSVGTextPath::Make(); }},
__anon51e47a762502() 274 { "tspan" , []() -> sk_sp<SkSVGNode> { return SkSVGTSpan::Make(); }},
__anon51e47a762602() 275 { "use" , []() -> sk_sp<SkSVGNode> { return SkSVGUse::Make(); }},
276 };
277
278 struct ConstructionContext {
ConstructionContext__anon51e47a760111::ConstructionContext279 ConstructionContext(SkSVGIDMapper* mapper) : fParent(nullptr), fIDMapper(mapper) {}
ConstructionContext__anon51e47a760111::ConstructionContext280 ConstructionContext(const ConstructionContext& other, const sk_sp<SkSVGNode>& newParent)
281 : fParent(newParent.get()), fIDMapper(other.fIDMapper) {}
282
283 SkSVGNode* fParent;
284 SkSVGIDMapper* fIDMapper;
285 };
286
set_string_attribute(const sk_sp<SkSVGNode> & node,const char * name,const char * value)287 bool set_string_attribute(const sk_sp<SkSVGNode>& node, const char* name, const char* value) {
288 if (node->parseAndSetAttribute(name, value)) {
289 // Handled by new code path
290 return true;
291 }
292
293 const int attrIndex = SkStrSearch(&gAttributeParseInfo[0].fKey,
294 SkTo<int>(SK_ARRAY_COUNT(gAttributeParseInfo)),
295 name, sizeof(gAttributeParseInfo[0]));
296 if (attrIndex < 0) {
297 #if defined(SK_VERBOSE_SVG_PARSING)
298 SkDebugf("unhandled attribute: %s\n", name);
299 #endif
300 return false;
301 }
302
303 SkASSERT(SkTo<size_t>(attrIndex) < SK_ARRAY_COUNT(gAttributeParseInfo));
304 const auto& attrInfo = gAttributeParseInfo[attrIndex].fValue;
305 if (!attrInfo.fSetter(node, attrInfo.fAttr, value)) {
306 #if defined(SK_VERBOSE_SVG_PARSING)
307 SkDebugf("could not parse attribute: '%s=\"%s\"'\n", name, value);
308 #endif
309 return false;
310 }
311
312 return true;
313 }
314
parse_node_attributes(const SkDOM & xmlDom,const SkDOM::Node * xmlNode,const sk_sp<SkSVGNode> & svgNode,SkSVGIDMapper * mapper)315 void parse_node_attributes(const SkDOM& xmlDom, const SkDOM::Node* xmlNode,
316 const sk_sp<SkSVGNode>& svgNode, SkSVGIDMapper* mapper) {
317 const char* name, *value;
318 SkDOM::AttrIter attrIter(xmlDom, xmlNode);
319 while ((name = attrIter.next(&value))) {
320 // We're handling id attributes out of band for now.
321 if (!strcmp(name, "id")) {
322 mapper->set(SkString(value), svgNode);
323 continue;
324 }
325 set_string_attribute(svgNode, name, value);
326 }
327 }
328
construct_svg_node(const SkDOM & dom,const ConstructionContext & ctx,const SkDOM::Node * xmlNode)329 sk_sp<SkSVGNode> construct_svg_node(const SkDOM& dom, const ConstructionContext& ctx,
330 const SkDOM::Node* xmlNode) {
331 const char* elem = dom.getName(xmlNode);
332 const SkDOM::Type elemType = dom.getType(xmlNode);
333
334 if (elemType == SkDOM::kText_Type) {
335 // Text literals require special handling.
336 SkASSERT(dom.countChildren(xmlNode) == 0);
337 auto txt = SkSVGTextLiteral::Make();
338 txt->setText(SkString(dom.getName(xmlNode)));
339 ctx.fParent->appendChild(std::move(txt));
340
341 return nullptr;
342 }
343
344 SkASSERT(elemType == SkDOM::kElement_Type);
345
346 auto make_node = [](const ConstructionContext& ctx, const char* elem) -> sk_sp<SkSVGNode> {
347 if (strcmp(elem, "svg") == 0) {
348 // Outermost SVG element must be tagged as such.
349 return SkSVGSVG::Make(ctx.fParent ? SkSVGSVG::Type::kInner
350 : SkSVGSVG::Type::kRoot);
351 }
352
353 const int tagIndex = SkStrSearch(&gTagFactories[0].fKey,
354 SkTo<int>(SK_ARRAY_COUNT(gTagFactories)),
355 elem, sizeof(gTagFactories[0]));
356 if (tagIndex < 0) {
357 #if defined(SK_VERBOSE_SVG_PARSING)
358 SkDebugf("unhandled element: <%s>\n", elem);
359 #endif
360 return nullptr;
361 }
362 SkASSERT(SkTo<size_t>(tagIndex) < SK_ARRAY_COUNT(gTagFactories));
363
364 return gTagFactories[tagIndex].fValue();
365 };
366
367 auto node = make_node(ctx, elem);
368 if (!node) {
369 return nullptr;
370 }
371
372 parse_node_attributes(dom, xmlNode, node, ctx.fIDMapper);
373
374 ConstructionContext localCtx(ctx, node);
375 for (auto* child = dom.getFirstChild(xmlNode, nullptr); child;
376 child = dom.getNextSibling(child)) {
377 sk_sp<SkSVGNode> childNode = construct_svg_node(dom, localCtx, child);
378 if (childNode) {
379 node->appendChild(std::move(childNode));
380 }
381 }
382
383 return node;
384 }
385
386 } // anonymous namespace
387
setFontManager(sk_sp<SkFontMgr> fmgr)388 SkSVGDOM::Builder& SkSVGDOM::Builder::setFontManager(sk_sp<SkFontMgr> fmgr) {
389 fFontMgr = std::move(fmgr);
390 return *this;
391 }
392
setResourceProvider(sk_sp<skresources::ResourceProvider> rp)393 SkSVGDOM::Builder& SkSVGDOM::Builder::setResourceProvider(sk_sp<skresources::ResourceProvider> rp) {
394 fResourceProvider = std::move(rp);
395 return *this;
396 }
397
make(SkStream & str) const398 sk_sp<SkSVGDOM> SkSVGDOM::Builder::make(SkStream& str) const {
399 TRACE_EVENT0("skia", TRACE_FUNC);
400 SkDOM xmlDom;
401 if (!xmlDom.build(str)) {
402 return nullptr;
403 }
404
405 SkSVGIDMapper mapper;
406 ConstructionContext ctx(&mapper);
407
408 auto root = construct_svg_node(xmlDom, ctx, xmlDom.getRootNode());
409 if (!root || root->tag() != SkSVGTag::kSvg) {
410 return nullptr;
411 }
412
413 class NullResourceProvider final : public skresources::ResourceProvider {
414 sk_sp<SkData> load(const char[], const char[]) const override { return nullptr; }
415 };
416
417 auto resource_provider = fResourceProvider ? fResourceProvider
418 : sk_make_sp<NullResourceProvider>();
419
420 return sk_sp<SkSVGDOM>(new SkSVGDOM(sk_sp<SkSVGSVG>(static_cast<SkSVGSVG*>(root.release())),
421 std::move(fFontMgr), std::move(resource_provider),
422 std::move(mapper)));
423 }
424
SkSVGDOM(sk_sp<SkSVGSVG> root,sk_sp<SkFontMgr> fmgr,sk_sp<skresources::ResourceProvider> rp,SkSVGIDMapper && mapper)425 SkSVGDOM::SkSVGDOM(sk_sp<SkSVGSVG> root, sk_sp<SkFontMgr> fmgr,
426 sk_sp<skresources::ResourceProvider> rp, SkSVGIDMapper&& mapper)
427 : fRoot(std::move(root))
428 , fFontMgr(std::move(fmgr))
429 , fResourceProvider(std::move(rp))
430 , fIDMapper(std::move(mapper))
431 , fContainerSize(fRoot->intrinsicSize(SkSVGLengthContext(SkSize::Make(0, 0))))
432 {
433 SkASSERT(fResourceProvider);
434 }
435
render(SkCanvas * canvas) const436 void SkSVGDOM::render(SkCanvas* canvas) const {
437 TRACE_EVENT0("skia", TRACE_FUNC);
438 if (fRoot) {
439 SkSVGLengthContext lctx(fContainerSize);
440 SkSVGPresentationContext pctx;
441 fRoot->render(SkSVGRenderContext(canvas, fFontMgr, fResourceProvider, fIDMapper, lctx, pctx,
442 {nullptr, nullptr}));
443 }
444 }
445
containerSize() const446 const SkSize& SkSVGDOM::containerSize() const {
447 return fContainerSize;
448 }
449
setContainerSize(const SkSize & containerSize)450 void SkSVGDOM::setContainerSize(const SkSize& containerSize) {
451 // TODO: inval
452 fContainerSize = containerSize;
453 }
454
findNodeById(const char * id)455 sk_sp<SkSVGNode>* SkSVGDOM::findNodeById(const char* id) {
456 SkString idStr(id);
457 return this->fIDMapper.find(idStr);
458 }
459
460 // TODO(fuego): move this to SkSVGNode or its own CU.
setAttribute(const char * attributeName,const char * attributeValue)461 bool SkSVGNode::setAttribute(const char* attributeName, const char* attributeValue) {
462 return set_string_attribute(sk_ref_sp(this), attributeName, attributeValue);
463 }
464