Coverage Report

Created: 2021-08-22 09:07

/src/skia/modules/svg/src/SkSVGDOM.cpp
Line
Count
Source (jump to first uncovered line)
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
55
bool SetIRIAttribute(const sk_sp<SkSVGNode>& node, SkSVGAttribute attr,
56
77
                      const char* stringValue) {
57
77
    auto parseResult = SkSVGAttributeParser::parse<SkSVGIRI>(stringValue);
58
77
    if (!parseResult.isValid()) {
59
1
        return false;
60
1
    }
61
62
76
    node->setAttribute(attr, SkSVGStringValue(parseResult->iri()));
63
76
    return true;
64
76
}
65
66
bool SetStringAttribute(const sk_sp<SkSVGNode>& node, SkSVGAttribute attr,
67
166
                           const char* stringValue) {
68
166
    SkString str(stringValue, strlen(stringValue));
69
166
    SkSVGStringType strType = SkSVGStringType(str);
70
166
    node->setAttribute(attr, SkSVGStringValue(strType));
71
166
    return true;
72
166
}
73
74
bool SetTransformAttribute(const sk_sp<SkSVGNode>& node, SkSVGAttribute attr,
75
5.38k
                           const char* stringValue) {
76
5.38k
    auto parseResult = SkSVGAttributeParser::parse<SkSVGTransformType>(stringValue);
77
5.38k
    if (!parseResult.isValid()) {
78
2.20k
        return false;
79
2.20k
    }
80
81
3.17k
    node->setAttribute(attr, SkSVGTransformValue(*parseResult));
82
3.17k
    return true;
83
3.17k
}
84
85
bool SetLengthAttribute(const sk_sp<SkSVGNode>& node, SkSVGAttribute attr,
86
17.8k
                        const char* stringValue) {
87
17.8k
    auto parseResult = SkSVGAttributeParser::parse<SkSVGLength>(stringValue);
88
17.8k
    if (!parseResult.isValid()) {
89
14.4k
        return false;
90
14.4k
    }
91
92
3.45k
    node->setAttribute(attr, SkSVGLengthValue(*parseResult));
93
3.45k
    return true;
94
3.45k
}
95
96
bool SetViewBoxAttribute(const sk_sp<SkSVGNode>& node, SkSVGAttribute attr,
97
1.98k
                         const char* stringValue) {
98
1.98k
    SkSVGViewBoxType viewBox;
99
1.98k
    SkSVGAttributeParser parser(stringValue);
100
1.98k
    if (!parser.parseViewBox(&viewBox)) {
101
903
        return false;
102
903
    }
103
104
1.08k
    node->setAttribute(attr, SkSVGViewBoxValue(viewBox));
105
1.08k
    return true;
106
1.08k
}
107
108
bool SetObjectBoundingBoxUnitsAttribute(const sk_sp<SkSVGNode>& node,
109
                                        SkSVGAttribute attr,
110
67
                                        const char* stringValue) {
111
67
    auto parseResult = SkSVGAttributeParser::parse<SkSVGObjectBoundingBoxUnits>(stringValue);
112
67
    if (!parseResult.isValid()) {
113
67
        return false;
114
67
    }
115
116
0
    node->setAttribute(attr, SkSVGObjectBoundingBoxUnitsValue(*parseResult));
117
0
    return true;
118
0
}
119
120
bool SetPreserveAspectRatioAttribute(const sk_sp<SkSVGNode>& node, SkSVGAttribute attr,
121
0
                                     const char* stringValue) {
122
0
    SkSVGPreserveAspectRatio par;
123
0
    SkSVGAttributeParser parser(stringValue);
124
0
    if (!parser.parsePreserveAspectRatio(&par)) {
125
0
        return false;
126
0
    }
127
128
0
    node->setAttribute(attr, SkSVGPreserveAspectRatioValue(par));
129
0
    return true;
130
0
}
131
132
210k
SkString TrimmedString(const char* first, const char* last) {
133
210k
    SkASSERT(first);
134
210k
    SkASSERT(last);
135
210k
    SkASSERT(first <= last);
136
137
212k
    while (first <= last && *first <= ' ') { first++; }
138
220k
    while (first <= last && *last  <= ' ') { last--; }
139
140
210k
    SkASSERT(last - first + 1 >= 0);
141
210k
    return SkString(first, SkTo<size_t>(last - first + 1));
142
210k
}
143
144
// Breaks a "foo: bar; baz: ..." string into key:value pairs.
145
class StyleIterator {
146
public:
147
17.8k
    StyleIterator(const char* str) : fPos(str) { }
148
149
122k
    std::tuple<SkString, SkString> next() {
150
122k
        SkString name, value;
151
152
122k
        if (fPos) {
153
111k
            const char* sep = this->nextSeparator();
154
111k
            SkASSERT(*sep == ';' || *sep == '\0');
155
156
111k
            const char* valueSep = strchr(fPos, ':');
157
111k
            if (valueSep && valueSep < sep) {
158
105k
                name  = TrimmedString(fPos, valueSep - 1);
159
105k
                value = TrimmedString(valueSep + 1, sep - 1);
160
105k
            }
161
162
95.6k
            fPos = *sep ? sep + 1 : nullptr;
163
111k
        }
164
165
122k
        return std::make_tuple(name, value);
166
122k
    }
167
168
private:
169
111k
    const char* nextSeparator() const {
170
111k
        const char* sep = fPos;
171
2.86M
        while (*sep != ';' && *sep != '\0') {
172
2.75M
            sep++;
173
2.75M
        }
174
111k
        return sep;
175
111k
    }
176
177
    const char* fPos;
178
};
179
180
bool set_string_attribute(const sk_sp<SkSVGNode>& node, const char* name, const char* value);
181
182
bool SetStyleAttributes(const sk_sp<SkSVGNode>& node, SkSVGAttribute,
183
17.8k
                        const char* stringValue) {
184
185
17.8k
    SkString name, value;
186
17.8k
    StyleIterator iter(stringValue);
187
122k
    for (;;) {
188
122k
        std::tie(name, value) = iter.next();
189
122k
        if (name.isEmpty()) {
190
17.8k
            break;
191
17.8k
        }
192
105k
        set_string_attribute(node, name.c_str(), value.c_str());
193
105k
    }
194
195
17.8k
    return true;
196
17.8k
}
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[] = {
238
67
    { "a"                 , []() -> sk_sp<SkSVGNode> { return SkSVGG::Make();                  }},
239
3.19k
    { "circle"            , []() -> sk_sp<SkSVGNode> { return SkSVGCircle::Make();             }},
240
4.00k
    { "clipPath"          , []() -> sk_sp<SkSVGNode> { return SkSVGClipPath::Make();           }},
241
555
    { "defs"              , []() -> sk_sp<SkSVGNode> { return SkSVGDefs::Make();               }},
242
1.30k
    { "ellipse"           , []() -> sk_sp<SkSVGNode> { return SkSVGEllipse::Make();            }},
243
0
    { "feBlend"           , []() -> sk_sp<SkSVGNode> { return SkSVGFeBlend::Make();            }},
244
48
    { "feColorMatrix"     , []() -> sk_sp<SkSVGNode> { return SkSVGFeColorMatrix::Make();      }},
245
5.78k
    { "feComposite"       , []() -> sk_sp<SkSVGNode> { return SkSVGFeComposite::Make();        }},
246
0
    { "feDiffuseLighting" , []() -> sk_sp<SkSVGNode> { return SkSVGFeDiffuseLighting::Make();  }},
247
0
    { "feDisplacementMap" , []() -> sk_sp<SkSVGNode> { return SkSVGFeDisplacementMap::Make();  }},
248
0
    { "feDistantLight"    , []() -> sk_sp<SkSVGNode> { return SkSVGFeDistantLight::Make();     }},
249
4.05k
    { "feFlood"           , []() -> sk_sp<SkSVGNode> { return SkSVGFeFlood::Make();            }},
250
0
    { "feGaussianBlur"    , []() -> sk_sp<SkSVGNode> { return SkSVGFeGaussianBlur::Make();     }},
251
0
    { "feImage"           , []() -> sk_sp<SkSVGNode> { return SkSVGFeImage::Make();            }},
252
0
    { "feMorphology"      , []() -> sk_sp<SkSVGNode> { return SkSVGFeMorphology::Make();       }},
253
765
    { "feOffset"          , []() -> sk_sp<SkSVGNode> { return SkSVGFeOffset::Make();           }},
254
0
    { "fePointLight"      , []() -> sk_sp<SkSVGNode> { return SkSVGFePointLight::Make();       }},
255
0
    { "feSpecularLighting", []() -> sk_sp<SkSVGNode> { return SkSVGFeSpecularLighting::Make(); }},
256
39
    { "feSpotLight"       , []() -> sk_sp<SkSVGNode> { return SkSVGFeSpotLight::Make();        }},
257
20
    { "feTurbulence"      , []() -> sk_sp<SkSVGNode> { return SkSVGFeTurbulence::Make();       }},
258
491
    { "filter"            , []() -> sk_sp<SkSVGNode> { return SkSVGFilter::Make();             }},
259
33.2k
    { "g"                 , []() -> sk_sp<SkSVGNode> { return SkSVGG::Make();                  }},
260
86
    { "image"             , []() -> sk_sp<SkSVGNode> { return SkSVGImage::Make();              }},
261
4.36k
    { "line"              , []() -> sk_sp<SkSVGNode> { return SkSVGLine::Make();               }},
262
447
    { "linearGradient"    , []() -> sk_sp<SkSVGNode> { return SkSVGLinearGradient::Make();     }},
263
110
    { "mask"              , []() -> sk_sp<SkSVGNode> { return SkSVGMask::Make();               }},
264
96.0k
    { "path"              , []() -> sk_sp<SkSVGNode> { return SkSVGPath::Make();               }},
265
834
    { "pattern"           , []() -> sk_sp<SkSVGNode> { return SkSVGPattern::Make();            }},
266
1.88k
    { "polygon"           , []() -> sk_sp<SkSVGNode> { return SkSVGPoly::MakePolygon();        }},
267
42
    { "polyline"          , []() -> sk_sp<SkSVGNode> { return SkSVGPoly::MakePolyline();       }},
268
0
    { "radialGradient"    , []() -> sk_sp<SkSVGNode> { return SkSVGRadialGradient::Make();     }},
269
7.61k
    { "rect"              , []() -> sk_sp<SkSVGNode> { return SkSVGRect::Make();               }},
270
61
    { "stop"              , []() -> sk_sp<SkSVGNode> { return SkSVGStop::Make();               }},
271
//    "svg" handled explicitly
272
19.2k
    { "text"              , []() -> sk_sp<SkSVGNode> { return SkSVGText::Make();               }},
273
0
    { "textPath"          , []() -> sk_sp<SkSVGNode> { return SkSVGTextPath::Make();           }},
274
2.02k
    { "tspan"             , []() -> sk_sp<SkSVGNode> { return SkSVGTSpan::Make();              }},
275
377
    { "use"               , []() -> sk_sp<SkSVGNode> { return SkSVGUse::Make();                }},
276
};
277
278
struct ConstructionContext {
279
10.4k
    ConstructionContext(SkSVGIDMapper* mapper) : fParent(nullptr), fIDMapper(mapper) {}
280
    ConstructionContext(const ConstructionContext& other, const sk_sp<SkSVGNode>& newParent)
281
212k
        : fParent(newParent.get()), fIDMapper(other.fIDMapper) {}
282
283
    SkSVGNode*     fParent;
284
    SkSVGIDMapper* fIDMapper;
285
};
286
287
427k
bool set_string_attribute(const sk_sp<SkSVGNode>& node, const char* name, const char* value) {
288
427k
    if (node->parseAndSetAttribute(name, value)) {
289
        // Handled by new code path
290
200k
        return true;
291
200k
    }
292
293
226k
    const int attrIndex = SkStrSearch(&gAttributeParseInfo[0].fKey,
294
226k
                                      SkTo<int>(SK_ARRAY_COUNT(gAttributeParseInfo)),
295
226k
                                      name, sizeof(gAttributeParseInfo[0]));
296
226k
    if (attrIndex < 0) {
297
#if defined(SK_VERBOSE_SVG_PARSING)
298
        SkDebugf("unhandled attribute: %s\n", name);
299
#endif
300
183k
        return false;
301
183k
    }
302
303
43.4k
    SkASSERT(SkTo<size_t>(attrIndex) < SK_ARRAY_COUNT(gAttributeParseInfo));
304
43.4k
    const auto& attrInfo = gAttributeParseInfo[attrIndex].fValue;
305
43.4k
    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
17.6k
        return false;
310
17.6k
    }
311
312
25.7k
    return true;
313
25.7k
}
314
315
void parse_node_attributes(const SkDOM& xmlDom, const SkDOM::Node* xmlNode,
316
212k
                           const sk_sp<SkSVGNode>& svgNode, SkSVGIDMapper* mapper) {
317
212k
    const char* name, *value;
318
212k
    SkDOM::AttrIter attrIter(xmlDom, xmlNode);
319
616k
    while ((name = attrIter.next(&value))) {
320
        // We're handling id attributes out of band for now.
321
404k
        if (!strcmp(name, "id")) {
322
81.3k
            mapper->set(SkString(value), svgNode);
323
81.3k
            continue;
324
81.3k
        }
325
322k
        set_string_attribute(svgNode, name, value);
326
322k
    }
327
212k
}
328
329
sk_sp<SkSVGNode> construct_svg_node(const SkDOM& dom, const ConstructionContext& ctx,
330
343k
                                    const SkDOM::Node* xmlNode) {
331
343k
    const char* elem = dom.getName(xmlNode);
332
343k
    const SkDOM::Type elemType = dom.getType(xmlNode);
333
334
343k
    if (elemType == SkDOM::kText_Type) {
335
        // Text literals require special handling.
336
114k
        SkASSERT(dom.countChildren(xmlNode) == 0);
337
114k
        auto txt = SkSVGTextLiteral::Make();
338
114k
        txt->setText(SkString(dom.getName(xmlNode)));
339
114k
        ctx.fParent->appendChild(std::move(txt));
340
341
114k
        return nullptr;
342
114k
    }
343
344
228k
    SkASSERT(elemType == SkDOM::kElement_Type);
345
346
228k
    auto make_node = [](const ConstructionContext& ctx, const char* elem) -> sk_sp<SkSVGNode> {
347
228k
        if (strcmp(elem, "svg") == 0) {
348
            // Outermost SVG element must be tagged as such.
349
15.9k
            return SkSVGSVG::Make(ctx.fParent ? SkSVGSVG::Type::kInner
350
10.0k
                                              : SkSVGSVG::Type::kRoot);
351
26.0k
        }
352
353
202k
        const int tagIndex = SkStrSearch(&gTagFactories[0].fKey,
354
202k
                                         SkTo<int>(SK_ARRAY_COUNT(gTagFactories)),
355
202k
                                         elem, sizeof(gTagFactories[0]));
356
202k
        if (tagIndex < 0) {
357
#if defined(SK_VERBOSE_SVG_PARSING)
358
            SkDebugf("unhandled element: <%s>\n", elem);
359
#endif
360
16.1k
            return nullptr;
361
16.1k
        }
362
186k
        SkASSERT(SkTo<size_t>(tagIndex) < SK_ARRAY_COUNT(gTagFactories));
363
364
186k
        return gTagFactories[tagIndex].fValue();
365
186k
    };
366
367
228k
    auto node = make_node(ctx, elem);
368
228k
    if (!node) {
369
16.1k
        return nullptr;
370
16.1k
    }
371
372
212k
    parse_node_attributes(dom, xmlNode, node, ctx.fIDMapper);
373
374
212k
    ConstructionContext localCtx(ctx, node);
375
545k
    for (auto* child = dom.getFirstChild(xmlNode, nullptr); child;
376
332k
         child = dom.getNextSibling(child)) {
377
332k
        sk_sp<SkSVGNode> childNode = construct_svg_node(dom, localCtx, child);
378
332k
        if (childNode) {
379
202k
            node->appendChild(std::move(childNode));
380
202k
        }
381
332k
    }
382
383
212k
    return node;
384
212k
}
385
386
} // anonymous namespace
387
388
0
SkSVGDOM::Builder& SkSVGDOM::Builder::setFontManager(sk_sp<SkFontMgr> fmgr) {
389
0
    fFontMgr = std::move(fmgr);
390
0
    return *this;
391
0
}
392
393
0
SkSVGDOM::Builder& SkSVGDOM::Builder::setResourceProvider(sk_sp<skresources::ResourceProvider> rp) {
394
0
    fResourceProvider = std::move(rp);
395
0
    return *this;
396
0
}
397
398
17.7k
sk_sp<SkSVGDOM> SkSVGDOM::Builder::make(SkStream& str) const {
399
17.7k
    TRACE_EVENT0("skia", TRACE_FUNC);
400
17.7k
    SkDOM xmlDom;
401
17.7k
    if (!xmlDom.build(str)) {
402
7.38k
        return nullptr;
403
7.38k
    }
404
405
10.4k
    SkSVGIDMapper mapper;
406
10.4k
    ConstructionContext ctx(&mapper);
407
408
10.4k
    auto root = construct_svg_node(xmlDom, ctx, xmlDom.getRootNode());
409
10.4k
    if (!root || root->tag() != SkSVGTag::kSvg) {
410
366
        return nullptr;
411
366
    }
412
413
10.0k
    class NullResourceProvider final : public skresources::ResourceProvider {
414
0
        sk_sp<SkData> load(const char[], const char[]) const override { return nullptr; }
415
10.0k
    };
416
417
0
    auto resource_provider = fResourceProvider ? fResourceProvider
418
10.0k
                                               : sk_make_sp<NullResourceProvider>();
419
420
10.0k
    return sk_sp<SkSVGDOM>(new SkSVGDOM(sk_sp<SkSVGSVG>(static_cast<SkSVGSVG*>(root.release())),
421
10.0k
                                        std::move(fFontMgr), std::move(resource_provider),
422
10.0k
                                        std::move(mapper)));
423
10.0k
}
424
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
10.0k
{
433
10.0k
    SkASSERT(fResourceProvider);
434
10.0k
}
435
436
10.0k
void SkSVGDOM::render(SkCanvas* canvas) const {
437
10.0k
    TRACE_EVENT0("skia", TRACE_FUNC);
438
10.0k
    if (fRoot) {
439
10.0k
        SkSVGLengthContext       lctx(fContainerSize);
440
10.0k
        SkSVGPresentationContext pctx;
441
10.0k
        fRoot->render(SkSVGRenderContext(canvas, fFontMgr, fResourceProvider, fIDMapper, lctx, pctx,
442
10.0k
                                         {nullptr, nullptr}));
443
10.0k
    }
444
10.0k
}
445
446
10.0k
const SkSize& SkSVGDOM::containerSize() const {
447
10.0k
    return fContainerSize;
448
10.0k
}
449
450
10.0k
void SkSVGDOM::setContainerSize(const SkSize& containerSize) {
451
    // TODO: inval
452
10.0k
    fContainerSize = containerSize;
453
10.0k
}
454
455
0
sk_sp<SkSVGNode>* SkSVGDOM::findNodeById(const char* id) {
456
0
    SkString idStr(id);
457
0
    return this->fIDMapper.find(idStr);
458
0
}
459
460
// TODO(fuego): move this to SkSVGNode or its own CU.
461
0
bool SkSVGNode::setAttribute(const char* attributeName, const char* attributeValue) {
462
0
    return set_string_attribute(sk_ref_sp(this), attributeName, attributeValue);
463
0
}