mirror of
https://github.com/thorvg/thorvg.git
synced 2025-06-15 12:34:30 +00:00
svg_loader: proper precedence of a styling
The attribute values should be copied from a style node only if they were set in the destination node using the attributes (in opocity to a style attribute). A proper copyCssStyleAttr() function is introduced.
This commit is contained in:
parent
1799cd9a78
commit
abd959bb54
1 changed files with 150 additions and 68 deletions
|
@ -81,9 +81,6 @@ typedef SvgNode* (*FactoryMethod)(SvgLoaderData* loader, SvgNode* parent, const
|
||||||
typedef SvgStyleGradient* (*GradientFactoryMethod)(SvgLoaderData* loader, const char* buf, unsigned bufLength);
|
typedef SvgStyleGradient* (*GradientFactoryMethod)(SvgLoaderData* loader, const char* buf, unsigned bufLength);
|
||||||
|
|
||||||
|
|
||||||
static void _copyAttr(SvgNode* to, const SvgNode* from, bool copyGeomAttrib = true);
|
|
||||||
|
|
||||||
|
|
||||||
static char* _skipSpace(const char* str, const char* end)
|
static char* _skipSpace(const char* str, const char* end)
|
||||||
{
|
{
|
||||||
while (((end && str < end) || (!end && *str != '\0')) && isspace(*str)) {
|
while (((end && str < end) || (!end && *str != '\0')) && isspace(*str)) {
|
||||||
|
@ -960,8 +957,95 @@ static SvgNode* _findCssStyleNode(const SvgNode* cssStyle, const char* title, Sv
|
||||||
if ((*child)->type == type && ((*child)->id) && !strcmp((*child)->id, title)) return (*child);
|
if ((*child)->type == type && ((*child)->id) && !strcmp((*child)->id, title)) return (*child);
|
||||||
}
|
}
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void _cssStyleCopy(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 && !((int)to->flags & (int)SvgStyleFlags::Color)) {
|
||||||
|
to->color = from->color;
|
||||||
|
to->curColorSet = true;
|
||||||
|
to->flags = (SvgStyleFlags)((int)to->flags | (int)SvgStyleFlags::Color);
|
||||||
|
}
|
||||||
|
//Fill
|
||||||
|
if (((int)from->fill.flags & (int)SvgFillFlags::Paint) && !((int)to->flags & (int)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;
|
||||||
|
if (from->fill.paint.url) to->fill.paint.url = _copyId(from->fill.paint.url);
|
||||||
|
to->fill.flags = (SvgFillFlags)((int)to->fill.flags | (int)SvgFillFlags::Paint);
|
||||||
|
to->flags = (SvgStyleFlags)((int)to->flags | (int)SvgStyleFlags::Fill);
|
||||||
|
}
|
||||||
|
if (((int)from->fill.flags & (int)SvgFillFlags::Opacity) && !((int)to->flags & (int)SvgStyleFlags::FillOpacity)) {
|
||||||
|
to->fill.opacity = from->fill.opacity;
|
||||||
|
to->flags = (SvgStyleFlags)((int)to->flags | (int)SvgStyleFlags::FillOpacity);
|
||||||
|
}
|
||||||
|
if (((int)from->fill.flags & (int)SvgFillFlags::FillRule) && !((int)to->flags & (int)SvgStyleFlags::FillRule)) {
|
||||||
|
to->fill.fillRule = from->fill.fillRule;
|
||||||
|
to->flags = (SvgStyleFlags)((int)to->flags | (int)SvgStyleFlags::FillRule);
|
||||||
|
}
|
||||||
|
//Stroke
|
||||||
|
if (((int)from->stroke.flags & (int)SvgStrokeFlags::Paint) && !((int)to->flags & (int)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;
|
||||||
|
to->stroke.paint.url = from->stroke.paint.url ? _copyId(from->stroke.paint.url) : nullptr;
|
||||||
|
to->stroke.flags = (SvgStrokeFlags)((int)to->stroke.flags | (int)SvgStrokeFlags::Paint);
|
||||||
|
to->flags = (SvgStyleFlags)((int)to->flags | (int)SvgStyleFlags::Stroke);
|
||||||
|
}
|
||||||
|
if (((int)from->stroke.flags & (int)SvgStrokeFlags::Opacity) && !((int)to->flags & (int)SvgStyleFlags::StrokeOpacity)) {
|
||||||
|
to->stroke.opacity = from->stroke.opacity;
|
||||||
|
to->flags = (SvgStyleFlags)((int)to->flags | (int)SvgStyleFlags::StrokeOpacity);
|
||||||
|
}
|
||||||
|
if (((int)from->stroke.flags & (int)SvgStrokeFlags::Width) && !((int)to->flags & (int)SvgStyleFlags::StrokeWidth)) {
|
||||||
|
to->stroke.width = from->stroke.width;
|
||||||
|
to->flags = (SvgStyleFlags)((int)to->flags | (int)SvgStyleFlags::StrokeWidth);
|
||||||
|
}
|
||||||
|
if (((int)from->stroke.flags & (int)SvgStrokeFlags::Dash) && !((int)to->flags & (int)SvgStyleFlags::StrokeDashArray)) {
|
||||||
|
if (from->stroke.dash.array.count > 0) {
|
||||||
|
to->stroke.dash.array.clear();
|
||||||
|
to->stroke.dash.array.reserve(from->stroke.dash.array.count);
|
||||||
|
for (uint32_t i = 0; i < from->stroke.dash.array.count; ++i) {
|
||||||
|
to->stroke.dash.array.push(from->stroke.dash.array.data[i]);
|
||||||
|
}
|
||||||
|
to->flags = (SvgStyleFlags)((int)to->flags | (int)SvgStyleFlags::StrokeDashArray);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (((int)from->stroke.flags & (int)SvgStrokeFlags::Cap) && !((int)to->flags & (int)SvgStyleFlags::StrokeLineCap)) {
|
||||||
|
to->stroke.cap = from->stroke.cap;
|
||||||
|
to->flags = (SvgStyleFlags)((int)to->flags | (int)SvgStyleFlags::StrokeLineCap);
|
||||||
|
}
|
||||||
|
if (((int)from->stroke.flags & (int)SvgStrokeFlags::Join) && !((int)to->flags & (int)SvgStyleFlags::StrokeLineJoin)) {
|
||||||
|
to->stroke.join = from->stroke.join;
|
||||||
|
to->flags = (SvgStyleFlags)((int)to->flags | (int)SvgStyleFlags::StrokeLineJoin);
|
||||||
|
}
|
||||||
|
//Opacity
|
||||||
|
//TODO: it can be set to be 255 and shouldn't be changed by attribute 'opacity'
|
||||||
|
if (from->opacity < 255 && !((int)to->flags & (int)SvgStyleFlags::Opacity)) {
|
||||||
|
to->opacity = from->opacity;
|
||||||
|
to->flags = (SvgStyleFlags)((int)to->flags | (int)SvgStyleFlags::Opacity);
|
||||||
|
}
|
||||||
|
//TODO: support clip-path, mask, mask-type, display
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void _copyCssStyleAttr(SvgNode* to, const SvgNode* from)
|
||||||
|
{
|
||||||
|
//Copy matrix attribute
|
||||||
|
if (from->transform && !((int)to->style->flags & (int)SvgStyleFlags::Transform)) {
|
||||||
|
to->transform = (Matrix*)malloc(sizeof(Matrix));
|
||||||
|
if (to->transform) {
|
||||||
|
*to->transform = *from->transform;
|
||||||
|
to->style->flags = (SvgStyleFlags)((int)to->style->flags | (int)SvgStyleFlags::Transform);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//Copy style attribute
|
||||||
|
_cssStyleCopy(to->style, from->style);
|
||||||
|
//TODO: clips and masks are not supported yet in css style
|
||||||
|
if (from->style->clipPath.url) to->style->clipPath.url = strdup(from->style->clipPath.url);
|
||||||
|
if (from->style->mask.url) to->style->mask.url = strdup(from->style->mask.url);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -975,7 +1059,7 @@ static void _handleCssClassAttr(SvgLoaderData* loader, SvgNode* node, const char
|
||||||
//TODO: works only if style was defined before it is used
|
//TODO: works only if style was defined before it is used
|
||||||
if (auto cssNode = _findCssStyleNode(loader->cssStyle, *cssClass, node->type)) {
|
if (auto cssNode = _findCssStyleNode(loader->cssStyle, *cssClass, node->type)) {
|
||||||
//TODO: check SVG2 standard - should the geometric properties be copied?
|
//TODO: check SVG2 standard - should the geometric properties be copied?
|
||||||
_copyAttr(node, cssNode, false);
|
_copyCssStyleAttr(node, cssNode);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1932,7 +2016,7 @@ static void _styleCopy(SvgStyleProperty* to, const SvgStyleProperty* from)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static void _copyAttr(SvgNode* to, const SvgNode* from, bool copyGeomAttrib)
|
static void _copyAttr(SvgNode* to, const SvgNode* from)
|
||||||
{
|
{
|
||||||
//Copy matrix attribute
|
//Copy matrix attribute
|
||||||
if (from->transform) {
|
if (from->transform) {
|
||||||
|
@ -1947,7 +2031,6 @@ static void _copyAttr(SvgNode* to, const SvgNode* from, bool copyGeomAttrib)
|
||||||
if (from->style->clipPath.url) to->style->clipPath.url = strdup(from->style->clipPath.url);
|
if (from->style->clipPath.url) to->style->clipPath.url = strdup(from->style->clipPath.url);
|
||||||
if (from->style->mask.url) to->style->mask.url = strdup(from->style->mask.url);
|
if (from->style->mask.url) to->style->mask.url = strdup(from->style->mask.url);
|
||||||
|
|
||||||
if (copyGeomAttrib) {
|
|
||||||
//Copy node attribute
|
//Copy node attribute
|
||||||
switch (from->type) {
|
switch (from->type) {
|
||||||
case SvgNodeType::Circle: {
|
case SvgNodeType::Circle: {
|
||||||
|
@ -2010,7 +2093,6 @@ static void _copyAttr(SvgNode* to, const SvgNode* from, bool copyGeomAttrib)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static void _cloneNode(SvgNode* from, SvgNode* parent, int depth)
|
static void _cloneNode(SvgNode* from, SvgNode* parent, int depth)
|
||||||
|
@ -2683,7 +2765,7 @@ static void _svgLoaderParserXmlOpen(SvgLoaderData* loader, const char* content,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static void _svgLoaderParserXmlStyle(SvgLoaderData* loader, const char* content, unsigned int length)
|
static void _svgLoaderParserXmlCssStyle(SvgLoaderData* loader, const char* content, unsigned int length)
|
||||||
{
|
{
|
||||||
char* tag;
|
char* tag;
|
||||||
char* name;
|
char* name;
|
||||||
|
@ -2738,7 +2820,7 @@ static bool _svgLoaderParser(void* data, SimpleXMLType type, const char* content
|
||||||
}
|
}
|
||||||
case SimpleXMLType::Data:
|
case SimpleXMLType::Data:
|
||||||
case SimpleXMLType::CData: {
|
case SimpleXMLType::CData: {
|
||||||
if (loader->style) _svgLoaderParserXmlStyle(loader, content, length);
|
if (loader->style) _svgLoaderParserXmlCssStyle(loader, content, length);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case SimpleXMLType::DoctypeChild: {
|
case SimpleXMLType::DoctypeChild: {
|
||||||
|
|
Loading…
Add table
Reference in a new issue