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