From b0e41b2b9fc49e1c6c9a224dcaa031b50f5a5ba4 Mon Sep 17 00:00:00 2001 From: Mira Grudzinska Date: Mon, 8 May 2023 00:25:10 +0200 Subject: [PATCH] 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 --- src/loaders/svg/tvgSvgCssStyle.cpp | 74 +++++++++++++++++++++++----- src/loaders/svg/tvgSvgLoader.cpp | 19 ++++++- src/loaders/svg/tvgSvgLoaderCommon.h | 1 + 3 files changed, 81 insertions(+), 13 deletions(-) diff --git a/src/loaders/svg/tvgSvgCssStyle.cpp b/src/loaders/svg/tvgSvgCssStyle.cpp index 694e6d1e..6fdb6412 100644 --- a/src/loaders/svg/tvgSvgCssStyle.cpp +++ b/src/loaders/svg/tvgSvgCssStyle.cpp @@ -28,17 +28,30 @@ /* 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) { if (from == nullptr) return; //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->curColorSet = true; to->flags = (to->flags | SvgStyleFlags::Color); + if (from->flagsImportance & SvgStyleFlags::Color) { + to->flagsImportance = (to->flagsImportance | SvgStyleFlags::Color); + } } //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.none = from->fill.paint.none; 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->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.flags = (to->fill.flags | SvgFillFlags::Opacity); 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.flags = (to->fill.flags | SvgFillFlags::FillRule); to->flags = (to->flags | SvgStyleFlags::FillRule); + if (from->flagsImportance & SvgStyleFlags::FillRule) { + to->flagsImportance = (to->flagsImportance | SvgStyleFlags::FillRule); + } } //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.none = from->stroke.paint.none; 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->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.flags = (to->stroke.flags | SvgStrokeFlags::Opacity); 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.flags = (to->stroke.flags | SvgStrokeFlags::Width); 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) { to->stroke.dash.array.clear(); 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->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.flags = (to->stroke.flags | SvgStrokeFlags::Cap); 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.flags = (to->stroke.flags | SvgStrokeFlags::Join); to->flags = (to->flags | SvgStyleFlags::StrokeLineJoin); + if (from->flagsImportance & SvgStyleFlags::StrokeLineJoin) { + to->flagsImportance = (to->flagsImportance | SvgStyleFlags::StrokeLineJoin); + } } //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->flags = (to->flags | SvgStyleFlags::Opacity); + if (from->flagsImportance & SvgStyleFlags::Opacity) { + to->flagsImportance = (to->flagsImportance | SvgStyleFlags::Opacity); + } } } diff --git a/src/loaders/svg/tvgSvgLoader.cpp b/src/loaders/svg/tvgSvgLoader.cpp index 6fe94c19..ec6a320e 100644 --- a/src/loaders/svg/tvgSvgLoader.cpp +++ b/src/loaders/svg/tvgSvgLoader.cpp @@ -1135,12 +1135,27 @@ static bool _parseStyleAttr(void* data, const char* key, const char* value, bool sz = strlen(key); for (unsigned int i = 0; i < sizeof(styleTags) / sizeof(styleTags[0]); i++) { 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) { - styleTags[i].tagHandler(loader, node, value); - node->style->flags = (node->style->flags | styleTags[i].flag); + if (importance || !(node->style->flagsImportance & 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)) { styleTags[i].tagHandler(loader, node, value); } + if (importance) { + node->style->flagsImportance = (node->style->flags | styleTags[i].flag); + free(const_cast(value)); + } return true; } } diff --git a/src/loaders/svg/tvgSvgLoaderCommon.h b/src/loaders/svg/tvgSvgLoaderCommon.h index b0392768..961438ff 100644 --- a/src/loaders/svg/tvgSvgLoaderCommon.h +++ b/src/loaders/svg/tvgSvgLoaderCommon.h @@ -484,6 +484,7 @@ struct SvgStyleProperty char* cssClass; bool paintOrder; //true if default (fill, stroke), false otherwise 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