1 /*
2  * Copyright (c) 2022 - 2024 the ThorVG project. All rights reserved.
3 
4  * Permission is hereby granted, free of charge, to any person obtaining a copy
5  * of this software and associated documentation files (the "Software"), to deal
6  * in the Software without restriction, including without limitation the rights
7  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8  * copies of the Software, and to permit persons to whom the Software is
9  * furnished to do so, subject to the following conditions:
10 
11  * The above copyright notice and this permission notice shall be included in all
12  * copies or substantial portions of the Software.
13 
14  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20  * SOFTWARE.
21  */
22 
23 #include "../../lv_conf_internal.h"
24 #if LV_USE_THORVG_INTERNAL
25 
26 #include "tvgSvgCssStyle.h"
27 
28 #include <cstring>
29 
30 /************************************************************************/
31 /* Internal Class Implementation                                        */
32 /************************************************************************/
33 
_isImportanceApplicable(SvgStyleFlags & toFlagsImportance,SvgStyleFlags fromFlagsImportance,SvgStyleFlags flag)34 static bool _isImportanceApplicable(SvgStyleFlags &toFlagsImportance, SvgStyleFlags fromFlagsImportance, SvgStyleFlags flag)
35 {
36     if (!(toFlagsImportance & flag) && (fromFlagsImportance & flag)) {
37         return true;
38     }
39     return false;
40 }
41 
_copyStyle(SvgStyleProperty * to,const SvgStyleProperty * from)42 static void _copyStyle(SvgStyleProperty* to, const SvgStyleProperty* from)
43 {
44     if (from == nullptr) return;
45     //Copy the properties of 'from' only if they were explicitly set (not the default ones).
46     if ((from->curColorSet && !(to->flags & SvgStyleFlags::Color)) ||
47         _isImportanceApplicable(to->flagsImportance, from->flagsImportance, SvgStyleFlags::Color)) {
48         to->color = from->color;
49         to->curColorSet = true;
50         to->flags = (to->flags | SvgStyleFlags::Color);
51         if (from->flagsImportance & SvgStyleFlags::Color) {
52             to->flagsImportance = (to->flagsImportance | SvgStyleFlags::Color);
53         }
54     }
55     if (((from->flags & SvgStyleFlags::PaintOrder) && !(to->flags & SvgStyleFlags::PaintOrder)) ||
56         _isImportanceApplicable(to->flagsImportance, from->flagsImportance, SvgStyleFlags::PaintOrder)) {
57         to->paintOrder = from->paintOrder;
58         to->flags = (to->flags | SvgStyleFlags::PaintOrder);
59         if (from->flagsImportance & SvgStyleFlags::PaintOrder) {
60             to->flagsImportance = (to->flagsImportance | SvgStyleFlags::PaintOrder);
61         }
62     }
63     if (((from->flags & SvgStyleFlags::Display) && !(to->flags & SvgStyleFlags::Display)) ||
64         _isImportanceApplicable(to->flagsImportance, from->flagsImportance, SvgStyleFlags::Display)) {
65         to->display = from->display;
66         to->flags = (to->flags | SvgStyleFlags::Display);
67         if (from->flagsImportance & SvgStyleFlags::Display) {
68             to->flagsImportance = (to->flagsImportance | SvgStyleFlags::Display);
69         }
70     }
71     //Fill
72     if (((from->fill.flags & SvgFillFlags::Paint) && !(to->flags & SvgStyleFlags::Fill)) ||
73         _isImportanceApplicable(to->flagsImportance, from->flagsImportance, SvgStyleFlags::Fill)) {
74         to->fill.paint.color = from->fill.paint.color;
75         to->fill.paint.none = from->fill.paint.none;
76         to->fill.paint.curColor = from->fill.paint.curColor;
77         if (from->fill.paint.url) {
78             if (to->fill.paint.url) free(to->fill.paint.url);
79             to->fill.paint.url = strdup(from->fill.paint.url);
80         }
81         to->fill.flags = (to->fill.flags | SvgFillFlags::Paint);
82         to->flags = (to->flags | SvgStyleFlags::Fill);
83         if (from->flagsImportance & SvgStyleFlags::Fill) {
84             to->flagsImportance = (to->flagsImportance | SvgStyleFlags::Fill);
85         }
86     }
87     if (((from->fill.flags & SvgFillFlags::Opacity) && !(to->flags & SvgStyleFlags::FillOpacity)) ||
88         _isImportanceApplicable(to->flagsImportance, from->flagsImportance, SvgStyleFlags::FillOpacity)) {
89         to->fill.opacity = from->fill.opacity;
90         to->fill.flags = (to->fill.flags | SvgFillFlags::Opacity);
91         to->flags = (to->flags | SvgStyleFlags::FillOpacity);
92         if (from->flagsImportance & SvgStyleFlags::FillOpacity) {
93             to->flagsImportance = (to->flagsImportance | SvgStyleFlags::FillOpacity);
94         }
95     }
96     if (((from->fill.flags & SvgFillFlags::FillRule) && !(to->flags & SvgStyleFlags::FillRule)) ||
97         _isImportanceApplicable(to->flagsImportance, from->flagsImportance, SvgStyleFlags::FillRule)) {
98         to->fill.fillRule = from->fill.fillRule;
99         to->fill.flags = (to->fill.flags | SvgFillFlags::FillRule);
100         to->flags = (to->flags | SvgStyleFlags::FillRule);
101         if (from->flagsImportance & SvgStyleFlags::FillRule) {
102             to->flagsImportance = (to->flagsImportance | SvgStyleFlags::FillRule);
103         }
104     }
105     //Stroke
106     if (((from->stroke.flags & SvgStrokeFlags::Paint) && !(to->flags & SvgStyleFlags::Stroke)) ||
107         _isImportanceApplicable(to->flagsImportance, from->flagsImportance, SvgStyleFlags::Stroke)) {
108         to->stroke.paint.color = from->stroke.paint.color;
109         to->stroke.paint.none = from->stroke.paint.none;
110         to->stroke.paint.curColor = from->stroke.paint.curColor;
111         if (from->stroke.paint.url) {
112             if (to->stroke.paint.url) free(to->stroke.paint.url);
113             to->stroke.paint.url = strdup(from->stroke.paint.url);
114         }
115         to->stroke.flags = (to->stroke.flags | SvgStrokeFlags::Paint);
116         to->flags = (to->flags | SvgStyleFlags::Stroke);
117         if (from->flagsImportance & SvgStyleFlags::Stroke) {
118             to->flagsImportance = (to->flagsImportance | SvgStyleFlags::Stroke);
119         }
120     }
121     if (((from->stroke.flags & SvgStrokeFlags::Opacity) && !(to->flags & SvgStyleFlags::StrokeOpacity)) ||
122         _isImportanceApplicable(to->flagsImportance, from->flagsImportance, SvgStyleFlags::StrokeOpacity)) {
123         to->stroke.opacity = from->stroke.opacity;
124         to->stroke.flags = (to->stroke.flags | SvgStrokeFlags::Opacity);
125         to->flags = (to->flags | SvgStyleFlags::StrokeOpacity);
126         if (from->flagsImportance & SvgStyleFlags::StrokeOpacity) {
127             to->flagsImportance = (to->flagsImportance | SvgStyleFlags::StrokeOpacity);
128         }
129     }
130     if (((from->stroke.flags & SvgStrokeFlags::Width) && !(to->flags & SvgStyleFlags::StrokeWidth)) ||
131         _isImportanceApplicable(to->flagsImportance, from->flagsImportance, SvgStyleFlags::StrokeWidth)) {
132         to->stroke.width = from->stroke.width;
133         to->stroke.flags = (to->stroke.flags | SvgStrokeFlags::Width);
134         to->flags = (to->flags | SvgStyleFlags::StrokeWidth);
135         if (from->flagsImportance & SvgStyleFlags::StrokeWidth) {
136             to->flagsImportance = (to->flagsImportance | SvgStyleFlags::StrokeWidth);
137         }
138     }
139     if (((from->stroke.flags & SvgStrokeFlags::Dash) && !(to->flags & SvgStyleFlags::StrokeDashArray)) ||
140         _isImportanceApplicable(to->flagsImportance, from->flagsImportance, SvgStyleFlags::StrokeDashArray)) {
141         if (from->stroke.dash.array.count > 0) {
142             to->stroke.dash.array.clear();
143             to->stroke.dash.array.reserve(from->stroke.dash.array.count);
144             for (uint32_t i = 0; i < from->stroke.dash.array.count; ++i) {
145                 to->stroke.dash.array.push(from->stroke.dash.array[i]);
146             }
147             to->stroke.flags = (to->stroke.flags | SvgStrokeFlags::Dash);
148             to->flags = (to->flags | SvgStyleFlags::StrokeDashArray);
149             if (from->flagsImportance & SvgStyleFlags::StrokeDashArray) {
150                 to->flagsImportance = (to->flagsImportance | SvgStyleFlags::StrokeDashArray);
151             }
152         }
153     }
154     if (((from->stroke.flags & SvgStrokeFlags::Cap) && !(to->flags & SvgStyleFlags::StrokeLineCap)) ||
155         _isImportanceApplicable(to->flagsImportance, from->flagsImportance, SvgStyleFlags::StrokeLineCap)) {
156         to->stroke.cap = from->stroke.cap;
157         to->stroke.flags = (to->stroke.flags | SvgStrokeFlags::Cap);
158         to->flags = (to->flags | SvgStyleFlags::StrokeLineCap);
159         if (from->flagsImportance & SvgStyleFlags::StrokeLineCap) {
160             to->flagsImportance = (to->flagsImportance | SvgStyleFlags::StrokeLineCap);
161         }
162     }
163     if (((from->stroke.flags & SvgStrokeFlags::Join) && !(to->flags & SvgStyleFlags::StrokeLineJoin)) ||
164         _isImportanceApplicable(to->flagsImportance, from->flagsImportance, SvgStyleFlags::StrokeLineJoin)) {
165         to->stroke.join = from->stroke.join;
166         to->stroke.flags = (to->stroke.flags | SvgStrokeFlags::Join);
167         to->flags = (to->flags | SvgStyleFlags::StrokeLineJoin);
168         if (from->flagsImportance & SvgStyleFlags::StrokeLineJoin) {
169             to->flagsImportance = (to->flagsImportance | SvgStyleFlags::StrokeLineJoin);
170         }
171     }
172     //Opacity
173     //TODO: it can be set to be 255 and shouldn't be changed by attribute 'opacity'
174     if ((from->opacity < 255 && !(to->flags & SvgStyleFlags::Opacity)) ||
175         _isImportanceApplicable(to->flagsImportance, from->flagsImportance, SvgStyleFlags::Opacity)) {
176         to->opacity = from->opacity;
177         to->flags = (to->flags | SvgStyleFlags::Opacity);
178         if (from->flagsImportance & SvgStyleFlags::Opacity) {
179             to->flagsImportance = (to->flagsImportance | SvgStyleFlags::Opacity);
180         }
181     }
182 }
183 
184 
185 /************************************************************************/
186 /* External Class Implementation                                        */
187 /************************************************************************/
188 
cssCopyStyleAttr(SvgNode * to,const SvgNode * from)189 void cssCopyStyleAttr(SvgNode* to, const SvgNode* from)
190 {
191     //Copy matrix attribute
192     if (from->transform && !(to->style->flags & SvgStyleFlags::Transform)) {
193         to->transform = (Matrix*)malloc(sizeof(Matrix));
194         if (to->transform) {
195             *to->transform = *from->transform;
196             to->style->flags = (to->style->flags | SvgStyleFlags::Transform);
197         }
198     }
199     //Copy style attribute
200     _copyStyle(to->style, from->style);
201 
202     if (from->style->clipPath.url) {
203         if (to->style->clipPath.url) free(to->style->clipPath.url);
204         to->style->clipPath.url = strdup(from->style->clipPath.url);
205     }
206     if (from->style->mask.url) {
207         if (to->style->mask.url) free(to->style->mask.url);
208         to->style->mask.url = strdup(from->style->mask.url);
209     }
210 }
211 
212 
cssFindStyleNode(const SvgNode * style,const char * title,SvgNodeType type)213 SvgNode* cssFindStyleNode(const SvgNode* style, const char* title, SvgNodeType type)
214 {
215     if (!style) return nullptr;
216 
217     auto child = style->child.data;
218     for (uint32_t i = 0; i < style->child.count; ++i, ++child) {
219         if ((*child)->type == type) {
220             if ((!title && !(*child)->id) || (title && (*child)->id && !strcmp((*child)->id, title))) return (*child);
221         }
222     }
223     return nullptr;
224 }
225 
226 
cssFindStyleNode(const SvgNode * style,const char * title)227 SvgNode* cssFindStyleNode(const SvgNode* style, const char* title)
228 {
229     if (!style || !title) return nullptr;
230 
231     auto child = style->child.data;
232     for (uint32_t i = 0; i < style->child.count; ++i, ++child) {
233         if ((*child)->type == SvgNodeType::CssStyle) {
234             if ((*child)->id && !strcmp((*child)->id, title)) return (*child);
235         }
236     }
237     return nullptr;
238 }
239 
240 
cssUpdateStyle(SvgNode * doc,SvgNode * style)241 void cssUpdateStyle(SvgNode* doc, SvgNode* style)
242 {
243     if (doc->child.count > 0) {
244         auto child = doc->child.data;
245         for (uint32_t i = 0; i < doc->child.count; ++i, ++child) {
246             if (auto cssNode = cssFindStyleNode(style, nullptr, (*child)->type)) {
247                 cssCopyStyleAttr(*child, cssNode);
248             }
249             cssUpdateStyle(*child, style);
250         }
251     }
252 }
253 
254 
cssApplyStyleToPostponeds(Array<SvgNodeIdPair> & postponeds,SvgNode * style)255 void cssApplyStyleToPostponeds(Array<SvgNodeIdPair>& postponeds, SvgNode* style)
256 {
257     for (uint32_t i = 0; i < postponeds.count; ++i) {
258         auto nodeIdPair = postponeds[i];
259 
260         //css styling: tag.name has higher priority than .name
261         if (auto cssNode = cssFindStyleNode(style, nodeIdPair.id, nodeIdPair.node->type)) {
262             cssCopyStyleAttr(nodeIdPair.node, cssNode);
263         }
264         if (auto cssNode = cssFindStyleNode(style, nodeIdPair.id)) {
265             cssCopyStyleAttr(nodeIdPair.node, cssNode);
266         }
267     }
268 }
269 
270 #endif /* LV_USE_THORVG_INTERNAL */
271 
272