1 /*
2 * Copyright 2006 The Android Open Source Project
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 "SkSVGXMLDOM.h"
9
10 #include <cstdlib>
11
12 #include "CssStyleParser.h"
13 #include "include/core/SkStream.h"
14 #include "include/private/SkTo.h"
15 #include "src/xml/SkDOM.cpp"
16 #include "src/xml/SkXMLParser.h"
17 #include "src/xml/SkXMLWriter.h"
18
19 union SkColorEx {
20 struct {
21 SkColor color : 32;
22 bool valid : 1;
23 uint32_t reserved : 31; // reserved
24 };
25 uint64_t value = 0;
26 };
27
28 class SkSVGDOMParser : public SkDOMParser {
29 public:
SkSVGDOMParser(SkArenaAlloc * chunk)30 SkSVGDOMParser(SkArenaAlloc* chunk) : SkDOMParser(chunk) {}
31 /** Returns true for success
32 */
parse(SkStream & docStream,uint64_t svgThemeColor)33 bool parse(SkStream& docStream, uint64_t svgThemeColor) {
34 fSvgThemeColor = svgThemeColor;
35 return SkXMLParser::parse(docStream);
36 };
37
38 protected:
flushAttributes()39 void flushAttributes() override {
40 SkASSERT(fLevel > 0);
41 SkDOM::Node* node = fAlloc->make<SkSVGXMLDOM::Node>();
42 flushAttributesWithNode(node);
43 }
44
onStartElement(const char elem[])45 bool onStartElement(const char elem[]) override {
46 this->startCommon(elem, SkDOM::kElement_Type);
47 if (!strcmp(elem, "style")) {
48 fProcessingStyle = true;
49 }
50 return false;
51 }
52
setSVGColorAndOpacity(SkDOM::Attr * attr,const char name[],const char value[],const SkColorEx & svgThemeColor)53 bool setSVGColorAndOpacity(
54 SkDOM::Attr* attr, const char name[], const char value[], const SkColorEx& svgThemeColor) {
55 if (svgThemeColor.valid && (((strcmp(name, "fill") == 0) && (strcmp(value, "none") != 0)) ||
56 ((strcmp(name, "stroke") == 0) && (strcmp(value, "none") != 0))) && isPureColor(value)) {
57 char colorBuffer[8];
58 int res = snprintf(colorBuffer, sizeof(colorBuffer), "#%06x", (svgThemeColor.color & 0xFFFFFF));
59 attr->fValue = dupstr(fAlloc, (res < 0) ? value : colorBuffer);
60 return false;
61 }
62 if ((svgThemeColor.valid == 1) && (strcmp(name, "opacity") == 0)) {
63 char opacityBuffer[4];
64 // the opacity is stored in svgThemeColor[24:31], so shift right by 24 bits after extracting it,
65 // for e.g., (0x33FFFFFF & 0xFF000000) >> 24 = 0x33.
66 // the target string of opacity is like "0.1", so normalize 0x33 to 1, for e.g., 0x33 / 255 = 0.13.
67 int res = snprintf(
68 opacityBuffer, sizeof(opacityBuffer), "%2.1f", ((svgThemeColor.color & 0xFF000000) >> 24) / 255.0);
69 attr->fValue = dupstr(fAlloc, (res < 0) ? value : opacityBuffer);
70 return false;
71 }
72 return true;
73 }
74
onAddAttribute(const char name[],const char value[])75 bool onAddAttribute(const char name[], const char value[]) override {
76 SkDOM::Attr* attr = fAttrs.append();
77 attr->fName = dupstr(fAlloc, name);
78 SkColorEx svgThemeColor;
79 svgThemeColor.value = fSvgThemeColor;
80 if (!setSVGColorAndOpacity(attr, name, value, svgThemeColor)) {
81 return false;
82 }
83 attr->fValue = dupstr(fAlloc, value);
84 // add attributes in style classes.
85 if (!strcmp(attr->fName, "class")) {
86 auto styleClassMap = fStyleParser.getArributesMap(attr->fValue);
87 if (!styleClassMap.empty()) {
88 for (auto& arr: styleClassMap) {
89 SkDOM::Attr* attr = fAttrs.append();
90 attr->fName = dupstr(fAlloc, arr.first.c_str());
91 if (!setSVGColorAndOpacity(attr, attr->fName, arr.second.c_str(), svgThemeColor)) {
92 continue;
93 }
94 attr->fValue = dupstr(fAlloc, arr.second.c_str());
95 }
96 }
97 }
98 return false;
99 }
100
onEndElement(const char elem[])101 bool onEndElement(const char elem[]) override {
102 if (SkDOMParser::onEndElement(elem)) {
103 return true;
104 }
105 if (!strcmp(elem, "style")) {
106 fProcessingStyle = false;
107 }
108 return false;
109 }
110
onText(const char text[],int len)111 bool onText(const char text[], int len) override {
112 SkString str(text, len);
113 this->startCommon(str.c_str(), SkDOM::kText_Type);
114 this->SkSVGDOMParser::onEndElement(str.c_str());
115 if (fProcessingStyle) {
116 std::string style(str.c_str());
117 if (!style.empty() && style.front() == '.') {
118 fStyleParser.parseCssStyle(style);
119 }
120 }
121
122 return false;
123 }
124
isPureColor(const char value[]) const125 bool isPureColor(const char value[]) const {
126 std::string color(value);
127 if (color.empty()) {
128 return true;
129 }
130
131 auto pos = color.find_first_not_of(' ');
132 if (pos != std::string::npos) {
133 color = color.substr(pos);
134 }
135
136 if (color.length() > urlLength && color.substr(0, urlLength - 1) == "url(#") {
137 return false;
138 }
139 return true;
140 }
141
142 private:
143 // for parse css style svg files.
144 bool fProcessingStyle = false;
145 CssStyleParser fStyleParser;
146 uint64_t fSvgThemeColor = 0;
147 static const int urlLength = 6;
148 };
149
150
151 SkSVGXMLDOM::SkSVGXMLDOM() = default;
152 SkSVGXMLDOM::~SkSVGXMLDOM() = default;
153
build(SkStream & docStream)154 const SkDOM::Node* SkSVGXMLDOM::build(SkStream& docStream) {
155 SkSVGDOMParser parser(&fAlloc);
156 if (!parser.parse(docStream, fSvgThemeColor)) {
157 SkDEBUGCODE(SkDebugf("xml parse error, line %d\n", parser.fParserError.getLineNumber());)
158 fRoot = nullptr;
159 fAlloc.reset();
160 return nullptr;
161 }
162 fRoot = parser.getRoot();
163 return fRoot;
164 }
165
build(SkStream & docStream,uint64_t svgThemeColor)166 const SkDOM::Node* SkSVGXMLDOM::build(SkStream& docStream, uint64_t svgThemeColor) {
167 fSvgThemeColor = svgThemeColor;
168 return SkSVGXMLDOM::build(docStream);
169 }
170
copy(const SkDOM & dom,const SkDOM::Node * node)171 const SkDOM::Node* SkSVGXMLDOM::copy(const SkDOM& dom, const SkDOM::Node* node) {
172 SkSVGDOMParser parser(&fAlloc);
173
174 walk_dom(dom, node, &parser);
175
176 fRoot = parser.getRoot();
177 return fRoot;
178 }
179
beginParsing()180 SkXMLParser* SkSVGXMLDOM::beginParsing() {
181 SkASSERT(!fParser);
182 fParser.reset(new SkSVGDOMParser(&fAlloc));
183
184 return fParser.get();
185 }