Resolution for issue Fill or stroke attributes on a use element - #162
Resolution for issue Fill or stroke attributes on a use element #162tjindal wants to merge 6 commits into
Conversation
…ges as paint implementation is not getting transfered from reference to graphic element
dirkschulze
left a comment
There was a problem hiding this comment.
What I get from the issue, you try to fix the following:
<circle id="myCircle" cx="50" cy="50" r="40" stroke="blue" stroke-width="10"/>
<use href="#myCircle" x="100" fill="blue"/>Right now I see 2 issues:
- You may overwrite all properties and reset other properties of the referenced object. See the stroke on above's example. It might get reset with this PR. I suggest that you merge styles into a new object and not replace them.
- This only covers cases where Reference is referencing a Graphic. It does not cover Groups. The example below might not work. So maybe not limit it to Graphics.
<svg>
<g id="myCircle">
<circle cx="50" cy="50" r="40" stroke="blue" stroke-width="10"/>
</g>
<use href="#myCircle" x="100" fill="blue"/>
</svg>| } | ||
|
|
||
| #ifndef STYLE_SUPPORT | ||
| #ifndef STYLE_SUPPORTn |
There was a problem hiding this comment.
yes its typo! and i have fixed in latest one @dirkschulze
| }; | ||
|
|
||
| struct Reference : public Element | ||
| auto hrefAttr = child->GetAttribute(kHrefAttr, kXlinkNS); | ||
| if (!hrefAttr.found || !hrefAttr.value || hrefAttr.value[0] != '#') | ||
| return; | ||
|
|
There was a problem hiding this comment.
I fell that those newlines are useful for readability. Can we keep them?
There was a problem hiding this comment.
yes, i have kept those newlines in latest one. Mistakenly removed last time. @dirkschulze
dirkschulze
left a comment
There was a problem hiding this comment.
The current object needs to take the graphic style of the parent into account to compute the resolved style for the current objet. For that we have a stack of graphic styles. The graphic style of the referencing object should be on the top of this stack.
In general the code should work as is already. Just like the group element, only that we know that we will just have one object. The question is why the style object of the reference object is not taken into account correctly when rendering the object that gets referenced. Could you debug please which style the object that gets referenced is taking into account? Is it taking the graphic style of its own parent? Do we accidentally break the inheritance instead of taking the graphic style of the parent (the reference object)?
Example (https://codepen.io/krit/pen/MWEYvYK):
<g fill="yellow">
<rect id="r" width="100" height="100"/>
</g>
<use xlink:href="#r" x="120" fill="green"/>Will the second rect be
- yellow
- black
- green?
The expectation is that the first rect will be yellow and the second green. Should the second rect instead be:
- yellow, the object that gets referenced takes the style of its parent in DOM into account
- black, it means the style chain is broken and we take the default.
- (green, everything works as expected.)
I'd suggest that you start with adding a new test with all the use cases we discussed earlier in this PR. The patch can not land w/o proper testing anyway. Then you can debug it based on the test.
| { | ||
| if (refObj.fillStyle.hasFill) | ||
| { | ||
| this->fillStyle.hasFill = refObj.fillStyle.hasFill; |
There was a problem hiding this comment.
in above example you mentioned , by doing recent changes output is showing 'green' which is desired one. As style object of reference object is taken into account by defining assignment operator , which pass on the fill and stroke properties if any from reference object to object that gets referenced.
i have removed the space aligning.
There was a problem hiding this comment.
<g id ="q" fill="yellow">
<rect id="r" width="100" height="100"/>
</g>
<use xlink:href="#r" x="120" />
in above example ,with the original code (not including any recent changes) the output is first rect is yellow and second is also yellow. even our expectation is that the first rect will be yellow and the second is black.
we have a stack of fill and stroke styles. the first rect takes fill style from parent . That FillStyle will be passed as a reference to function “ParseGraphicProperties” as it modifies FillStyle if any fill attribute found.currently in this scenario, it did not able to modify because no fill attribute is present. Then “Graphic” element with its FillStyle is pushed in stack. There is no way to distinguish its parent FillStyle and current one.
when reference("use") element is pointing to any graphic element, simply it will look up into map "mIdToElementMap" and if graphic element is found, it will further draw graphic element. inheritance is broken there after lookup from above map, all styling attributes which are applied to reference object they are ignored.
simply graphic element will be drawn as it was previously stored in stack. so “yellow” rect will be drawn.
i have also debug the area you pointed in discussion
auto saveRestore = SaveRestoreHelper{mRenderer, reference.graphicStyle};
it simple saves the graphic style , but fill and stroke properties are not stored anywhere which actually fills the rect.
There was a problem hiding this comment.
@dirkschulze can you pls share your thoughts on above comment ?
| // it will call assignment operator and pass fillStyle from reference to graphic | ||
| graphic = reference; | ||
| } | ||
| else if((*(refIt->second)).Type() == ElementType::kGroup) |
There was a problem hiding this comment.
This group handling will create another chain of issues. Especially with inherited properties that you don't handle. It also assumes that you just need to pass style information to the first child. However, there might be multiple nested groups and the styles need to get inherited through those groups as well.
<g id="ref">
<rect width="100" height="100"/>
</g>
<use xlink:href="#ref" x="120" fill="green"/>In general, the <use> element is like another group with shifting its content to position x/y. Actually, the shifting should be identical to a translation via transform. The referenced content gets "copied in" here. I am quoting because we do not want to actually copy.
The intention was the following:
- We have a reference object and the object that gets referenced by it
- The reference object has styling applied the styling gets passed to the object that it references like we do for the group object.
- The reference object triggers rendering just like the group does. So it renders the referenced object as normal.... the difference is the style object of the group.
So the code should already work as is. See overall comment.
There was a problem hiding this comment.
Thanks Dirk, i get your point of multiple nested groups if given, and i modified changes recently again to make code more generalised.
|
For what ever reason, I am not detected as reviewer of this PR anymore. I can't even give inline comments. Here my comments: I see the issue now. We have 2 phases:
I thought that we moved the style (inheritance) resolution to phase 2 but we didn't. Inheritance resolution still happens in phase 1. This was ok in the beginning since we replaced the SVGUseElement with a SVGGElement and re-parsed the element in question. This required that all nodes that get referenced were parsed already, which might not be the case. So we moved from literally copying sub-trees to the Reference object. Now, we just keep a reference string. On rendering, we see if the element was found during parse time. If yes, we render it. Apparently we did not change the inheritance resolving with it. As a result, the inheritance information is lost at render time. The referenced element's style was resolved to the root element in DOM tree and not in render tree already. As a result, the following example shows both rectangles as yellow even though the 2nd should be green: <g fill="yellow">
<rect id="r" width="100" height="100"/>
</g>
<use xlink:href="#r" x="120" fill="green"/>@tjindal You are trying to fix the symptoms but that will just cause even more issues since you don't have all information. From what I get, it seems that you check if the SVGUseElement's (Reference object's) style matches the style of the referenced object and determine by that if the style needs to get overridden or not. This doesn't even work for simple examples: <rect id="r" width="100" height="100" fill="green"/>
<use x="120" fill="red" xlink:href="#r"/>With your changes, the 2nd rect would turn red but it should stay green. Inherited properties should only take the style of the ancestor if the value is not set. One way that I see is moving inheritance resolution to the rendering phase (from phase 1 to phase 2). Should we go with An issue with that approach is rendering only sub-trees via A slightly different approach is traversing the parsed document right after parsing directly and resolving all styles then. In that case,
We could optimize style resolving to certain subtrees:
I am open to ideas :) |
Resolution for issue Fill or stroke attributes on a use element changes as paint implementation is not getting transfered from reference to graphic element
Description
Related Issue
Motivation and Context
How Has This Been Tested?
Screenshots (if appropriate):
Types of changes
Checklist: