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