1/*
2 * Copyright (c) 2022 - 2023 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 "tvgSvgCssStyle.h"
24
25#include <cstring>
26
27/************************************************************************/
28/* Internal Class Implementation */
29/************************************************************************/
30
31static bool _isImportanceApplicable(SvgStyleFlags &toFlagsImportance, SvgStyleFlags fromFlagsImportance, SvgStyleFlags flag)
32{
33 if (!(toFlagsImportance & flag) && (fromFlagsImportance & flag)) {
34 return true;
35 }
36 return false;
37}
38
39static void _copyStyle(SvgStyleProperty* to, const SvgStyleProperty* from)
40{
41 if (from == nullptr) return;
42 //Copy the properties of 'from' only if they were explicitly set (not the default ones).
43 if ((from->curColorSet && !(to->flags & SvgStyleFlags::Color)) ||
44 _isImportanceApplicable(to->flagsImportance, from->flagsImportance, SvgStyleFlags::Color)) {
45 to->color = from->color;
46 to->curColorSet = true;
47 to->flags = (to->flags | SvgStyleFlags::Color);
48 if (from->flagsImportance & SvgStyleFlags::Color) {
49 to->flagsImportance = (to->flagsImportance | SvgStyleFlags::Color);
50 }
51 }
52 //Fill
53 if (((from->fill.flags & SvgFillFlags::Paint) && !(to->flags & SvgStyleFlags::Fill)) ||
54 _isImportanceApplicable(to->flagsImportance, from->flagsImportance, SvgStyleFlags::Fill)) {
55 to->fill.paint.color = from->fill.paint.color;
56 to->fill.paint.none = from->fill.paint.none;
57 to->fill.paint.curColor = from->fill.paint.curColor;
58 if (from->fill.paint.url) {
59 if (to->fill.paint.url) free(to->fill.paint.url);
60 to->fill.paint.url = strdup(from->fill.paint.url);
61 }
62 to->fill.flags = (to->fill.flags | SvgFillFlags::Paint);
63 to->flags = (to->flags | SvgStyleFlags::Fill);
64 if (from->flagsImportance & SvgStyleFlags::Fill) {
65 to->flagsImportance = (to->flagsImportance | SvgStyleFlags::Fill);
66 }
67 }
68 if (((from->fill.flags & SvgFillFlags::Opacity) && !(to->flags & SvgStyleFlags::FillOpacity)) ||
69 _isImportanceApplicable(to->flagsImportance, from->flagsImportance, SvgStyleFlags::FillOpacity)) {
70 to->fill.opacity = from->fill.opacity;
71 to->fill.flags = (to->fill.flags | SvgFillFlags::Opacity);
72 to->flags = (to->flags | SvgStyleFlags::FillOpacity);
73 if (from->flagsImportance & SvgStyleFlags::FillOpacity) {
74 to->flagsImportance = (to->flagsImportance | SvgStyleFlags::FillOpacity);
75 }
76 }
77 if (((from->fill.flags & SvgFillFlags::FillRule) && !(to->flags & SvgStyleFlags::FillRule)) ||
78 _isImportanceApplicable(to->flagsImportance, from->flagsImportance, SvgStyleFlags::FillRule)) {
79 to->fill.fillRule = from->fill.fillRule;
80 to->fill.flags = (to->fill.flags | SvgFillFlags::FillRule);
81 to->flags = (to->flags | SvgStyleFlags::FillRule);
82 if (from->flagsImportance & SvgStyleFlags::FillRule) {
83 to->flagsImportance = (to->flagsImportance | SvgStyleFlags::FillRule);
84 }
85 }
86 //Stroke
87 if (((from->stroke.flags & SvgStrokeFlags::Paint) && !(to->flags & SvgStyleFlags::Stroke)) ||
88 _isImportanceApplicable(to->flagsImportance, from->flagsImportance, SvgStyleFlags::Stroke)) {
89 to->stroke.paint.color = from->stroke.paint.color;
90 to->stroke.paint.none = from->stroke.paint.none;
91 to->stroke.paint.curColor = from->stroke.paint.curColor;
92 if (from->stroke.paint.url) {
93 if (to->stroke.paint.url) free(to->stroke.paint.url);
94 to->stroke.paint.url = strdup(from->stroke.paint.url);
95 }
96 to->stroke.flags = (to->stroke.flags | SvgStrokeFlags::Paint);
97 to->flags = (to->flags | SvgStyleFlags::Stroke);
98 if (from->flagsImportance & SvgStyleFlags::Stroke) {
99 to->flagsImportance = (to->flagsImportance | SvgStyleFlags::Stroke);
100 }
101 }
102 if (((from->stroke.flags & SvgStrokeFlags::Opacity) && !(to->flags & SvgStyleFlags::StrokeOpacity)) ||
103 _isImportanceApplicable(to->flagsImportance, from->flagsImportance, SvgStyleFlags::StrokeOpacity)) {
104 to->stroke.opacity = from->stroke.opacity;
105 to->stroke.flags = (to->stroke.flags | SvgStrokeFlags::Opacity);
106 to->flags = (to->flags | SvgStyleFlags::StrokeOpacity);
107 if (from->flagsImportance & SvgStyleFlags::StrokeOpacity) {
108 to->flagsImportance = (to->flagsImportance | SvgStyleFlags::StrokeOpacity);
109 }
110 }
111 if (((from->stroke.flags & SvgStrokeFlags::Width) && !(to->flags & SvgStyleFlags::StrokeWidth)) ||
112 _isImportanceApplicable(to->flagsImportance, from->flagsImportance, SvgStyleFlags::StrokeWidth)) {
113 to->stroke.width = from->stroke.width;
114 to->stroke.flags = (to->stroke.flags | SvgStrokeFlags::Width);
115 to->flags = (to->flags | SvgStyleFlags::StrokeWidth);
116 if (from->flagsImportance & SvgStyleFlags::StrokeWidth) {
117 to->flagsImportance = (to->flagsImportance | SvgStyleFlags::StrokeWidth);
118 }
119 }
120 if (((from->stroke.flags & SvgStrokeFlags::Dash) && !(to->flags & SvgStyleFlags::StrokeDashArray)) ||
121 _isImportanceApplicable(to->flagsImportance, from->flagsImportance, SvgStyleFlags::StrokeDashArray)) {
122 if (from->stroke.dash.array.count > 0) {
123 to->stroke.dash.array.clear();
124 to->stroke.dash.array.reserve(from->stroke.dash.array.count);
125 for (uint32_t i = 0; i < from->stroke.dash.array.count; ++i) {
126 to->stroke.dash.array.push(from->stroke.dash.array.data[i]);
127 }
128 to->stroke.flags = (to->stroke.flags | SvgStrokeFlags::Dash);
129 to->flags = (to->flags | SvgStyleFlags::StrokeDashArray);
130 if (from->flagsImportance & SvgStyleFlags::StrokeDashArray) {
131 to->flagsImportance = (to->flagsImportance | SvgStyleFlags::StrokeDashArray);
132 }
133 }
134 }
135 if (((from->stroke.flags & SvgStrokeFlags::Cap) && !(to->flags & SvgStyleFlags::StrokeLineCap)) ||
136 _isImportanceApplicable(to->flagsImportance, from->flagsImportance, SvgStyleFlags::StrokeLineCap)) {
137 to->stroke.cap = from->stroke.cap;
138 to->stroke.flags = (to->stroke.flags | SvgStrokeFlags::Cap);
139 to->flags = (to->flags | SvgStyleFlags::StrokeLineCap);
140 if (from->flagsImportance & SvgStyleFlags::StrokeLineCap) {
141 to->flagsImportance = (to->flagsImportance | SvgStyleFlags::StrokeLineCap);
142 }
143 }
144 if (((from->stroke.flags & SvgStrokeFlags::Join) && !(to->flags & SvgStyleFlags::StrokeLineJoin)) ||
145 _isImportanceApplicable(to->flagsImportance, from->flagsImportance, SvgStyleFlags::StrokeLineJoin)) {
146 to->stroke.join = from->stroke.join;
147 to->stroke.flags = (to->stroke.flags | SvgStrokeFlags::Join);
148 to->flags = (to->flags | SvgStyleFlags::StrokeLineJoin);
149 if (from->flagsImportance & SvgStyleFlags::StrokeLineJoin) {
150 to->flagsImportance = (to->flagsImportance | SvgStyleFlags::StrokeLineJoin);
151 }
152 }
153 //Opacity
154 //TODO: it can be set to be 255 and shouldn't be changed by attribute 'opacity'
155 if ((from->opacity < 255 && !(to->flags & SvgStyleFlags::Opacity)) ||
156 _isImportanceApplicable(to->flagsImportance, from->flagsImportance, SvgStyleFlags::Opacity)) {
157 to->opacity = from->opacity;
158 to->flags = (to->flags | SvgStyleFlags::Opacity);
159 if (from->flagsImportance & SvgStyleFlags::Opacity) {
160 to->flagsImportance = (to->flagsImportance | SvgStyleFlags::Opacity);
161 }
162 }
163}
164
165
166/************************************************************************/
167/* External Class Implementation */
168/************************************************************************/
169
170void cssCopyStyleAttr(SvgNode* to, const SvgNode* from)
171{
172 //Copy matrix attribute
173 if (from->transform && !(to->style->flags & SvgStyleFlags::Transform)) {
174 to->transform = (Matrix*)malloc(sizeof(Matrix));
175 if (to->transform) {
176 *to->transform = *from->transform;
177 to->style->flags = (to->style->flags | SvgStyleFlags::Transform);
178 }
179 }
180 //Copy style attribute
181 _copyStyle(to->style, from->style);
182
183 if (from->style->clipPath.url) {
184 if (to->style->clipPath.url) free(to->style->clipPath.url);
185 to->style->clipPath.url = strdup(from->style->clipPath.url);
186 }
187 if (from->style->mask.url) {
188 if (to->style->mask.url) free(to->style->mask.url);
189 to->style->mask.url = strdup(from->style->mask.url);
190 }
191}
192
193
194SvgNode* cssFindStyleNode(const SvgNode* style, const char* title, SvgNodeType type)
195{
196 if (!style) return nullptr;
197
198 auto child = style->child.data;
199 for (uint32_t i = 0; i < style->child.count; ++i, ++child) {
200 if ((*child)->type == type) {
201 if ((!title && !(*child)->id) || (title && (*child)->id && !strcmp((*child)->id, title))) return (*child);
202 }
203 }
204 return nullptr;
205}
206
207
208SvgNode* cssFindStyleNode(const SvgNode* style, const char* title)
209{
210 if (!style || !title) return nullptr;
211
212 auto child = style->child.data;
213 for (uint32_t i = 0; i < style->child.count; ++i, ++child) {
214 if ((*child)->type == SvgNodeType::CssStyle) {
215 if ((*child)->id && !strcmp((*child)->id, title)) return (*child);
216 }
217 }
218 return nullptr;
219}
220
221
222void cssUpdateStyle(SvgNode* doc, SvgNode* style)
223{
224 if (doc->child.count > 0) {
225 auto child = doc->child.data;
226 for (uint32_t i = 0; i < doc->child.count; ++i, ++child) {
227 if (auto cssNode = cssFindStyleNode(style, nullptr, (*child)->type)) {
228 cssCopyStyleAttr(*child, cssNode);
229 }
230 cssUpdateStyle(*child, style);
231 }
232 }
233}
234
235
236void cssApplyStyleToPostponeds(Array<SvgNodeIdPair>& postponeds, SvgNode* style)
237{
238 for (uint32_t i = 0; i < postponeds.count; ++i) {
239 auto nodeIdPair = postponeds.data[i];
240
241 //css styling: tag.name has higher priority than .name
242 if (auto cssNode = cssFindStyleNode(style, nodeIdPair.id, nodeIdPair.node->type)) {
243 cssCopyStyleAttr(nodeIdPair.node, cssNode);
244 }
245 if (auto cssNode = cssFindStyleNode(style, nodeIdPair.id)) {
246 cssCopyStyleAttr(nodeIdPair.node, cssNode);
247 }
248 }
249}
250