• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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