svg_loader: support the '!important' directive

The directive is used to give a specific style property
higher priority, ensuring that it overrides other style
declarations for the same property.

@Issue: https://github.com/thorvg/thorvg/issues/1255
This commit is contained in:
Mira Grudzinska 2023-05-08 00:25:10 +02:00 committed by Hermet Park
parent f4717fb4c3
commit b0e41b2b9f
3 changed files with 81 additions and 13 deletions

View file

@ -28,17 +28,30 @@
/* Internal Class Implementation */ /* Internal Class Implementation */
/************************************************************************/ /************************************************************************/
static bool _isImportanceApplicable(SvgStyleFlags &toFlagsImportance, SvgStyleFlags fromFlagsImportance, SvgStyleFlags flag)
{
if (!(toFlagsImportance & flag) && (fromFlagsImportance & flag)) {
return true;
}
return false;
}
static void _copyStyle(SvgStyleProperty* to, const SvgStyleProperty* from) static void _copyStyle(SvgStyleProperty* to, const SvgStyleProperty* from)
{ {
if (from == nullptr) return; if (from == nullptr) return;
//Copy the properties of 'from' only if they were explicitly set (not the default ones). //Copy the properties of 'from' only if they were explicitly set (not the default ones).
if (from->curColorSet && !(to->flags & SvgStyleFlags::Color)) { if ((from->curColorSet && !(to->flags & SvgStyleFlags::Color)) ||
_isImportanceApplicable(to->flagsImportance, from->flagsImportance, SvgStyleFlags::Color)) {
to->color = from->color; to->color = from->color;
to->curColorSet = true; to->curColorSet = true;
to->flags = (to->flags | SvgStyleFlags::Color); to->flags = (to->flags | SvgStyleFlags::Color);
if (from->flagsImportance & SvgStyleFlags::Color) {
to->flagsImportance = (to->flagsImportance | SvgStyleFlags::Color);
}
} }
//Fill //Fill
if ((from->fill.flags & SvgFillFlags::Paint) && !(to->flags & SvgStyleFlags::Fill)) { if (((from->fill.flags & SvgFillFlags::Paint) && !(to->flags & SvgStyleFlags::Fill)) ||
_isImportanceApplicable(to->flagsImportance, from->flagsImportance, SvgStyleFlags::Fill)) {
to->fill.paint.color = from->fill.paint.color; to->fill.paint.color = from->fill.paint.color;
to->fill.paint.none = from->fill.paint.none; to->fill.paint.none = from->fill.paint.none;
to->fill.paint.curColor = from->fill.paint.curColor; to->fill.paint.curColor = from->fill.paint.curColor;
@ -48,19 +61,31 @@ static void _copyStyle(SvgStyleProperty* to, const SvgStyleProperty* from)
} }
to->fill.flags = (to->fill.flags | SvgFillFlags::Paint); to->fill.flags = (to->fill.flags | SvgFillFlags::Paint);
to->flags = (to->flags | SvgStyleFlags::Fill); to->flags = (to->flags | SvgStyleFlags::Fill);
if (from->flagsImportance & SvgStyleFlags::Fill) {
to->flagsImportance = (to->flagsImportance | SvgStyleFlags::Fill);
}
} }
if ((from->fill.flags & SvgFillFlags::Opacity) && !(to->flags & SvgStyleFlags::FillOpacity)) { if (((from->fill.flags & SvgFillFlags::Opacity) && !(to->flags & SvgStyleFlags::FillOpacity)) ||
_isImportanceApplicable(to->flagsImportance, from->flagsImportance, SvgStyleFlags::FillOpacity)) {
to->fill.opacity = from->fill.opacity; to->fill.opacity = from->fill.opacity;
to->fill.flags = (to->fill.flags | SvgFillFlags::Opacity); to->fill.flags = (to->fill.flags | SvgFillFlags::Opacity);
to->flags = (to->flags | SvgStyleFlags::FillOpacity); to->flags = (to->flags | SvgStyleFlags::FillOpacity);
if (from->flagsImportance & SvgStyleFlags::FillOpacity) {
to->flagsImportance = (to->flagsImportance | SvgStyleFlags::FillOpacity);
}
} }
if ((from->fill.flags & SvgFillFlags::FillRule) && !(to->flags & SvgStyleFlags::FillRule)) { if (((from->fill.flags & SvgFillFlags::FillRule) && !(to->flags & SvgStyleFlags::FillRule)) ||
_isImportanceApplicable(to->flagsImportance, from->flagsImportance, SvgStyleFlags::FillRule)) {
to->fill.fillRule = from->fill.fillRule; to->fill.fillRule = from->fill.fillRule;
to->fill.flags = (to->fill.flags | SvgFillFlags::FillRule); to->fill.flags = (to->fill.flags | SvgFillFlags::FillRule);
to->flags = (to->flags | SvgStyleFlags::FillRule); to->flags = (to->flags | SvgStyleFlags::FillRule);
if (from->flagsImportance & SvgStyleFlags::FillRule) {
to->flagsImportance = (to->flagsImportance | SvgStyleFlags::FillRule);
}
} }
//Stroke //Stroke
if ((from->stroke.flags & SvgStrokeFlags::Paint) && !(to->flags & SvgStyleFlags::Stroke)) { if (((from->stroke.flags & SvgStrokeFlags::Paint) && !(to->flags & SvgStyleFlags::Stroke)) ||
_isImportanceApplicable(to->flagsImportance, from->flagsImportance, SvgStyleFlags::Stroke)) {
to->stroke.paint.color = from->stroke.paint.color; to->stroke.paint.color = from->stroke.paint.color;
to->stroke.paint.none = from->stroke.paint.none; to->stroke.paint.none = from->stroke.paint.none;
to->stroke.paint.curColor = from->stroke.paint.curColor; to->stroke.paint.curColor = from->stroke.paint.curColor;
@ -70,18 +95,30 @@ static void _copyStyle(SvgStyleProperty* to, const SvgStyleProperty* from)
} }
to->stroke.flags = (to->stroke.flags | SvgStrokeFlags::Paint); to->stroke.flags = (to->stroke.flags | SvgStrokeFlags::Paint);
to->flags = (to->flags | SvgStyleFlags::Stroke); to->flags = (to->flags | SvgStyleFlags::Stroke);
if (from->flagsImportance & SvgStyleFlags::Stroke) {
to->flagsImportance = (to->flagsImportance | SvgStyleFlags::Stroke);
}
} }
if ((from->stroke.flags & SvgStrokeFlags::Opacity) && !(to->flags & SvgStyleFlags::StrokeOpacity)) { if (((from->stroke.flags & SvgStrokeFlags::Opacity) && !(to->flags & SvgStyleFlags::StrokeOpacity)) ||
_isImportanceApplicable(to->flagsImportance, from->flagsImportance, SvgStyleFlags::StrokeOpacity)) {
to->stroke.opacity = from->stroke.opacity; to->stroke.opacity = from->stroke.opacity;
to->stroke.flags = (to->stroke.flags | SvgStrokeFlags::Opacity); to->stroke.flags = (to->stroke.flags | SvgStrokeFlags::Opacity);
to->flags = (to->flags | SvgStyleFlags::StrokeOpacity); to->flags = (to->flags | SvgStyleFlags::StrokeOpacity);
if (from->flagsImportance & SvgStyleFlags::StrokeOpacity) {
to->flagsImportance = (to->flagsImportance | SvgStyleFlags::StrokeOpacity);
}
} }
if ((from->stroke.flags & SvgStrokeFlags::Width) && !(to->flags & SvgStyleFlags::StrokeWidth)) { if (((from->stroke.flags & SvgStrokeFlags::Width) && !(to->flags & SvgStyleFlags::StrokeWidth)) ||
_isImportanceApplicable(to->flagsImportance, from->flagsImportance, SvgStyleFlags::StrokeWidth)) {
to->stroke.width = from->stroke.width; to->stroke.width = from->stroke.width;
to->stroke.flags = (to->stroke.flags | SvgStrokeFlags::Width); to->stroke.flags = (to->stroke.flags | SvgStrokeFlags::Width);
to->flags = (to->flags | SvgStyleFlags::StrokeWidth); to->flags = (to->flags | SvgStyleFlags::StrokeWidth);
if (from->flagsImportance & SvgStyleFlags::StrokeWidth) {
to->flagsImportance = (to->flagsImportance | SvgStyleFlags::StrokeWidth);
}
} }
if ((from->stroke.flags & SvgStrokeFlags::Dash) && !(to->flags & SvgStyleFlags::StrokeDashArray)) { if (((from->stroke.flags & SvgStrokeFlags::Dash) && !(to->flags & SvgStyleFlags::StrokeDashArray)) ||
_isImportanceApplicable(to->flagsImportance, from->flagsImportance, SvgStyleFlags::StrokeDashArray)) {
if (from->stroke.dash.array.count > 0) { if (from->stroke.dash.array.count > 0) {
to->stroke.dash.array.clear(); to->stroke.dash.array.clear();
to->stroke.dash.array.reserve(from->stroke.dash.array.count); to->stroke.dash.array.reserve(from->stroke.dash.array.count);
@ -90,23 +127,38 @@ static void _copyStyle(SvgStyleProperty* to, const SvgStyleProperty* from)
} }
to->stroke.flags = (to->stroke.flags | SvgStrokeFlags::Dash); to->stroke.flags = (to->stroke.flags | SvgStrokeFlags::Dash);
to->flags = (to->flags | SvgStyleFlags::StrokeDashArray); to->flags = (to->flags | SvgStyleFlags::StrokeDashArray);
if (from->flagsImportance & SvgStyleFlags::StrokeDashArray) {
to->flagsImportance = (to->flagsImportance | SvgStyleFlags::StrokeDashArray);
}
} }
} }
if ((from->stroke.flags & SvgStrokeFlags::Cap) && !(to->flags & SvgStyleFlags::StrokeLineCap)) { if (((from->stroke.flags & SvgStrokeFlags::Cap) && !(to->flags & SvgStyleFlags::StrokeLineCap)) ||
_isImportanceApplicable(to->flagsImportance, from->flagsImportance, SvgStyleFlags::StrokeLineCap)) {
to->stroke.cap = from->stroke.cap; to->stroke.cap = from->stroke.cap;
to->stroke.flags = (to->stroke.flags | SvgStrokeFlags::Cap); to->stroke.flags = (to->stroke.flags | SvgStrokeFlags::Cap);
to->flags = (to->flags | SvgStyleFlags::StrokeLineCap); to->flags = (to->flags | SvgStyleFlags::StrokeLineCap);
if (from->flagsImportance & SvgStyleFlags::StrokeLineCap) {
to->flagsImportance = (to->flagsImportance | SvgStyleFlags::StrokeLineCap);
}
} }
if ((from->stroke.flags & SvgStrokeFlags::Join) && !(to->flags & SvgStyleFlags::StrokeLineJoin)) { if (((from->stroke.flags & SvgStrokeFlags::Join) && !(to->flags & SvgStyleFlags::StrokeLineJoin)) ||
_isImportanceApplicable(to->flagsImportance, from->flagsImportance, SvgStyleFlags::StrokeLineJoin)) {
to->stroke.join = from->stroke.join; to->stroke.join = from->stroke.join;
to->stroke.flags = (to->stroke.flags | SvgStrokeFlags::Join); to->stroke.flags = (to->stroke.flags | SvgStrokeFlags::Join);
to->flags = (to->flags | SvgStyleFlags::StrokeLineJoin); to->flags = (to->flags | SvgStyleFlags::StrokeLineJoin);
if (from->flagsImportance & SvgStyleFlags::StrokeLineJoin) {
to->flagsImportance = (to->flagsImportance | SvgStyleFlags::StrokeLineJoin);
}
} }
//Opacity //Opacity
//TODO: it can be set to be 255 and shouldn't be changed by attribute 'opacity' //TODO: it can be set to be 255 and shouldn't be changed by attribute 'opacity'
if (from->opacity < 255 && !(to->flags & SvgStyleFlags::Opacity)) { if ((from->opacity < 255 && !(to->flags & SvgStyleFlags::Opacity)) ||
_isImportanceApplicable(to->flagsImportance, from->flagsImportance, SvgStyleFlags::Opacity)) {
to->opacity = from->opacity; to->opacity = from->opacity;
to->flags = (to->flags | SvgStyleFlags::Opacity); to->flags = (to->flags | SvgStyleFlags::Opacity);
if (from->flagsImportance & SvgStyleFlags::Opacity) {
to->flagsImportance = (to->flagsImportance | SvgStyleFlags::Opacity);
}
} }
} }

View file

@ -1135,12 +1135,27 @@ static bool _parseStyleAttr(void* data, const char* key, const char* value, bool
sz = strlen(key); sz = strlen(key);
for (unsigned int i = 0; i < sizeof(styleTags) / sizeof(styleTags[0]); i++) { for (unsigned int i = 0; i < sizeof(styleTags) / sizeof(styleTags[0]); i++) {
if (styleTags[i].sz - 1 == sz && !strncmp(styleTags[i].tag, key, sz)) { if (styleTags[i].sz - 1 == sz && !strncmp(styleTags[i].tag, key, sz)) {
bool importance = false;
if (auto ptr = strstr(value, "!important")) {
size_t size = ptr - value;
while (size > 0 && isspace(value[size - 1])) {
size--;
}
value = svgUtilStrndup(value, size);
importance = true;
}
if (style) { if (style) {
styleTags[i].tagHandler(loader, node, value); if (importance || !(node->style->flagsImportance & styleTags[i].flag)) {
node->style->flags = (node->style->flags | styleTags[i].flag); styleTags[i].tagHandler(loader, node, value);
node->style->flags = (node->style->flags | styleTags[i].flag);
}
} else if (!(node->style->flags & styleTags[i].flag)) { } else if (!(node->style->flags & styleTags[i].flag)) {
styleTags[i].tagHandler(loader, node, value); styleTags[i].tagHandler(loader, node, value);
} }
if (importance) {
node->style->flagsImportance = (node->style->flags | styleTags[i].flag);
free(const_cast<char*>(value));
}
return true; return true;
} }
} }

View file

@ -484,6 +484,7 @@ struct SvgStyleProperty
char* cssClass; char* cssClass;
bool paintOrder; //true if default (fill, stroke), false otherwise bool paintOrder; //true if default (fill, stroke), false otherwise
SvgStyleFlags flags; SvgStyleFlags flags;
SvgStyleFlags flagsImportance; //indicates the importance of the flag - if set, higher priority is applied (https://drafts.csswg.org/css-cascade-4/#importance)
}; };
struct SvgNode struct SvgNode