resvg icon indicating copy to clipboard operation
resvg copied to clipboard

`usvg --keep-named-groups` also keeps transforms

Open snoyer opened this issue 1 year ago • 4 comments

Given the following input:

<svg viewBox="0 0 10 10" xmlns="http://www.w3.org/2000/svg">
  <g id="grp" transform="translate(2,2)" fill="blue">
    <rect id="sq" x="1" y="1" width="4" height="4"/>
  </g>
</svg>

Running usvg resolves attributes as expected:

<svg width="10" height="10" viewBox="0 0 10 10" xmlns="http://www.w3.org/2000/svg">
    <defs/>
    <path id="sq" fill="#0000ff" stroke="none" transform="matrix(1 0 0 1 2 2)" d="M 1 1 L 5 1 L 5 5 L 1 5 Z"/>
</svg>

However, when running usvg --keep-named-groups the transform from the group is translated to its matrix form but not propagated to the descendants, leaving the path element with an implicit/inherited transform attribute:

<svg width="10" height="10" viewBox="0 0 10 10" xmlns="http://www.w3.org/2000/svg">
    <defs/>
    <g id="grp" transform="matrix(1 0 0 1 2 2)">
        <path id="sq" fill="#0000ff" stroke="none" d="M 1 1 L 5 1 L 5 5 L 1 5 Z"/>
    </g>
</svg>

It feels like we would expect the following output in order to comply with the Micro SVG spec:

<svg width="10" height="10" viewBox="0 0 10 10" xmlns="http://www.w3.org/2000/svg">
    <defs/>
    <g id="grp">
        <path id="sq" fill="#0000ff" stroke="none" transform="matrix(1 0 0 1 2 2)" d="M 1 1 L 5 1 L 5 5 L 1 5 Z"/>
    </g>
</svg>

snoyer avatar Sep 11 '22 01:09 snoyer

Micro SVG allows transform on groups. So the output is technically correct, but could be optimized further.

I'm not sure why I'm exposing --keep-named-groups in usvg to begin with. This feature is strictly for resvg.

RazrFalcon avatar Sep 11 '22 14:09 RazrFalcon

Micro SVG allows transform on groups. So the output is technically correct, but could be optimized further.

Alright, I assumed that would fall under inheritable/implicit argument as you would need to lookup ancestor elements of a <path> to compute its effective transform in a way that is not necessary for fill/stroke/... but indeed it is allowed in the spec.

I'm not sure why I'm exposing --keep-named-groups in usvg to begin with. This feature is strictly for resvg.

I would argue that this feature is very useful for some use-cases where the end-goal of the "renderer" is not purely graphical, for example "mining" some geometric data from an input that has labeled elements. In such case I would use usvg to "avoid dealing with most of SVG's complexity" (as per the readme) but I would need to have the original group ids to be able to navigate through the output.

A minimal example would be retrieving the simplified <path>s for the anonymous shapes from the rectangles and circles groups given this input:

<svg viewBox="0 0 10 10" xmlns="http://www.w3.org/2000/svg">
  <g id="rectangles" transform="translate(2,2)" fill="blue">
    <rect x="2" y="1" width="2" height="2"/>
    <rect x="5" y="2" width="2" height="2"/>
    <rect x="-1" y="-1" width="2" height="2"/>
  </g>
  <g id="circles" transform="translate(.5,5)" fill="blue">
    <circle cx="2" cy="0" r="1"/>
    <circle cx="5" cy="2" r="1"/>
    <circle cx="3" cy="3" r="1"/>
  </g>
</svg>

Using --keep-named-groups provides an output in which I can easily cssselect/xpath/... elements based on the original group ids:

<svg width="10" height="10" viewBox="0 0 10 10" xmlns="http://www.w3.org/2000/svg">
    <defs/>
    <g id="rectangles" transform="matrix(1 0 0 1 2 2)">
        <path fill="#0000ff" stroke="none" d="M 2 1 L 4 1 L 4 3 L 2 3 Z"/>
        <path fill="#0000ff" stroke="none" d="M 5 2 L 7 2 L 7 4 L 5 4 Z"/>
        <path fill="#0000ff" stroke="none" d="M -1 -1 L 1 -1 L 1 1 L -1 1 Z"/>
    </g>
    <g id="circles" transform="matrix(1 0 0 1 0.5 5)">
        <path fill="#0000ff" stroke="none" d="M 3 0 C 3 0.55228474983 2.55228474983 1 2 1 C 1.44771525017 1 1 0.55228474983 1 0 C 1 -0.55228474983 1.44771525017 -1 2 -1 C 2.55228474983 -1 3 -0.55228474983 3 0 Z"/>
        <path fill="#0000ff" stroke="none" d="M 6 2 C 6 2.55228474983 5.55228474983 3 5 3 C 4.44771525017 3 4 2.55228474983 4 2 C 4 1.44771525017 4.44771525017 1 5 1 C 5.55228474983 1 6 1.44771525017 6 2 Z"/>
        <path fill="#0000ff" stroke="none" d="M 4 3 C 4 3.55228474983 3.55228474983 4 3 4 C 2.44771525017 4 2 3.55228474983 2 3 C 2 2.44771525017 2.44771525017 2 3 2 C 3.55228474983 2 4 2.44771525017 4 3 Z"/>
    </g>
</svg>

snoyer avatar Sep 11 '22 15:09 snoyer

Yes, I understand the use case, but it's actually tremendously hard to preserved id's. There are so many edge-cases. It would be way easier to simply ignore them.

Anyway, I do admit that this is bug and it falls under the "no inherited properties" rule. Will fix it eventually.

RazrFalcon avatar Sep 11 '22 16:09 RazrFalcon

Thanks! I hope usvg can remain a general-purpose svg preprocessor and you don't eventually make it graphics-oriented only

snoyer avatar Sep 11 '22 16:09 snoyer