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