weisJ/jsvg

Does not support <image> tags linking to SVG images

Closed this issue · 11 comments

Consider the following image that Chrome renders correctly:

<svg width="100" height="100" version="1.1" xmlns="http://www.w3.org/2000/svg">
      <image xmlns:foo="http://www.w3.org/1999/xlink" foo:href="https://dev.w3.org/SVG/tools/svgweb/samples/svg-files/android.svg" width="96" height="105" />
</svg>

There are two issues here for jsvg, one is the namespace alias (it looks explicitly for href or xlink:href), this to me is a minor issue.

The other is that it does not support rendering linked SVGs.

Do you think this is something that could be reasonably supported by jsvg?

The use case that I ran into was making a composite SVG out of multiple others, for example to show different layers of a building floor plan.

weisJ commented

Are in your scenario the other svg files located locally on the file system or on a web server? The latter might complicate things?

For a first step I could support this only with the restriction that the references svg is first rendered to a BufferedImage.
The implementation would just be to complete #1 (which I wanted to postpone until it is needed :D).

More complicated would be to render the referenced svg as a SVGDocument. Though the current architecture should accommodate it with a few adjustments.

Supporting namespaces properly is a bigger can of worms, which I am not too sure about whether the effort is worth it considering the svg 2 spec wants to move away from them for the href attribute.

I didn't spot that issue, nice!

They are locally on the filesystem, I used an external URL for demonstration purposes.

If the SVG spec says to treat SVG as raster images then that sounds good to me. If there turns out to be a need to handle them as SVGDocuments, I guess the ParserProvider could be extended to create an image parser that could somehow transform an Image element to an SVG one, for example.

Fair enough about the namespaces, in this case I'm able to "fix" them through the ParserProvider.

weisJ commented

Actually looking into it I think for now it is actually easier to paint it as a SVGDocument if one want to respect the width and height attribute of the <image> tag 😅

Thank you for looking into it!

At least for my particular use case, both approaches work. 🙂

weisJ commented

The implementation wasn't actually that difficult. I still need to work out configuration of the used SVGLoader. Though the basic use case should be covered with the latest snapshot version.

I tried it out, works perfectly for my use case! Very much appreciated!

To contribute to this discussion, here a svg file with local images not renderer by jsvg (1.6.1)

scatter_image vl json15150623028041014879

the referenced png images :

gimp
ffox

weisJ commented

Are you enabling external images as described above? Also could you please put the xml code for the svg into your comment (makes it easier to follow the discussion without having to open it in an external editor)

Are you enabling external images as described above?

not sure what you mean

Also could you please put the xml code for the svg into your comment

Yes sorry

<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" class="marks"
     width="274" height="260" viewBox="0 0 274 260">
    <rect width="274" height="260" fill="white"/>
    <g fill="none" stroke-miterlimit="10" transform="translate(51,23)">
        <g class="mark-group role-frame root" role="graphics-object" aria-roledescription="group mark container">
            <g transform="translate(0,0)">
                <path class="background" aria-hidden="true" d="M0.5,0.5h200v200h-200Z" stroke="#ddd"/>
                <g>
                    <g class="mark-group role-axis" aria-hidden="true">
                        <g transform="translate(0.5,200.5)">
                            <path class="background" aria-hidden="true" d="M0,0h0v0h0Z" pointer-events="none"/>
                            <g>
                                <g class="mark-rule role-axis-grid" pointer-events="none">
                                    <line transform="translate(0,-200)" x2="0" y2="200" stroke="#ddd" stroke-width="1"
                                          opacity="1"/>
                                    <line transform="translate(38,-200)" x2="0" y2="200" stroke="#ddd" stroke-width="1"
                                          opacity="1"/>
                                    <line transform="translate(77,-200)" x2="0" y2="200" stroke="#ddd" stroke-width="1"
                                          opacity="1"/>
                                    <line transform="translate(115,-200)" x2="0" y2="200" stroke="#ddd" stroke-width="1"
                                          opacity="1"/>
                                    <line transform="translate(154,-200)" x2="0" y2="200" stroke="#ddd" stroke-width="1"
                                          opacity="1"/>
                                    <line transform="translate(192,-200)" x2="0" y2="200" stroke="#ddd" stroke-width="1"
                                          opacity="1"/>
                                </g>
                            </g>
                            <path class="foreground" aria-hidden="true" d="" pointer-events="none" display="none"/>
                        </g>
                    </g>
                    <g class="mark-group role-axis" aria-hidden="true">
                        <g transform="translate(0.5,0.5)">
                            <path class="background" aria-hidden="true" d="M0,0h0v0h0Z" pointer-events="none"/>
                            <g>
                                <g class="mark-rule role-axis-grid" pointer-events="none">
                                    <line transform="translate(0,200)" x2="200" y2="0" stroke="#ddd" stroke-width="1"
                                          opacity="1"/>
                                    <line transform="translate(0,162)" x2="200" y2="0" stroke="#ddd" stroke-width="1"
                                          opacity="1"/>
                                    <line transform="translate(0,123)" x2="200" y2="0" stroke="#ddd" stroke-width="1"
                                          opacity="1"/>
                                    <line transform="translate(0,85)" x2="200" y2="0" stroke="#ddd" stroke-width="1"
                                          opacity="1"/>
                                    <line transform="translate(0,46)" x2="200" y2="0" stroke="#ddd" stroke-width="1"
                                          opacity="1"/>
                                    <line transform="translate(0,8)" x2="200" y2="0" stroke="#ddd" stroke-width="1"
                                          opacity="1"/>
                                </g>
                            </g>
                            <path class="foreground" aria-hidden="true" d="" pointer-events="none" display="none"/>
                        </g>
                    </g>
                    <g class="mark-group role-axis" role="graphics-symbol" aria-roledescription="axis"
                       aria-label="X-axis titled 'x' for a linear scale with values from 0.0 to 2.6">
                        <g transform="translate(0.5,200.5)">
                            <path class="background" aria-hidden="true" d="M0,0h0v0h0Z" pointer-events="none"/>
                            <g>
                                <g class="mark-rule role-axis-tick" pointer-events="none">
                                    <line transform="translate(0,0)" x2="0" y2="5" stroke="#888" stroke-width="1"
                                          opacity="1"/>
                                    <line transform="translate(38,0)" x2="0" y2="5" stroke="#888" stroke-width="1"
                                          opacity="1"/>
                                    <line transform="translate(77,0)" x2="0" y2="5" stroke="#888" stroke-width="1"
                                          opacity="1"/>
                                    <line transform="translate(115,0)" x2="0" y2="5" stroke="#888" stroke-width="1"
                                          opacity="1"/>
                                    <line transform="translate(154,0)" x2="0" y2="5" stroke="#888" stroke-width="1"
                                          opacity="1"/>
                                    <line transform="translate(192,0)" x2="0" y2="5" stroke="#888" stroke-width="1"
                                          opacity="1"/>
                                </g>
                                <g class="mark-text role-axis-label" pointer-events="none">
                                    <text text-anchor="start" transform="translate(0,15)" font-family="sans-serif"
                                          font-size="10px" fill="#000" opacity="1">0.0
                                    </text>
                                    <text text-anchor="middle" transform="translate(38.46153846153846,15)"
                                          font-family="sans-serif" font-size="10px" fill="#000" opacity="1">0.5
                                    </text>
                                    <text text-anchor="middle" transform="translate(76.92307692307692,15)"
                                          font-family="sans-serif" font-size="10px" fill="#000" opacity="1">1.0
                                    </text>
                                    <text text-anchor="middle" transform="translate(115.38461538461537,15)"
                                          font-family="sans-serif" font-size="10px" fill="#000" opacity="1">1.5
                                    </text>
                                    <text text-anchor="middle" transform="translate(153.84615384615384,15)"
                                          font-family="sans-serif" font-size="10px" fill="#000" opacity="1">2.0
                                    </text>
                                    <text text-anchor="middle" transform="translate(192.3076923076923,15)"
                                          font-family="sans-serif" font-size="10px" fill="#000" opacity="1">2.5
                                    </text>
                                </g>
                                <g class="mark-rule role-axis-domain" pointer-events="none">
                                    <line transform="translate(0,0)" x2="200" y2="0" stroke="#888" stroke-width="1"
                                          opacity="1"/>
                                </g>
                                <g class="mark-text role-axis-title" pointer-events="none">
                                    <text text-anchor="middle" transform="translate(100,30)" font-family="sans-serif"
                                          font-size="11px" font-weight="bold" fill="#000" opacity="1">x
                                    </text>
                                </g>
                            </g>
                            <path class="foreground" aria-hidden="true" d="" pointer-events="none" display="none"/>
                        </g>
                    </g>
                    <g class="mark-group role-axis" role="graphics-symbol" aria-roledescription="axis"
                       aria-label="Y-axis titled 'y' for a linear scale with values from 0.0 to 2.6">
                        <g transform="translate(0.5,0.5)">
                            <path class="background" aria-hidden="true" d="M0,0h0v0h0Z" pointer-events="none"/>
                            <g>
                                <g class="mark-rule role-axis-tick" pointer-events="none">
                                    <line transform="translate(0,200)" x2="-5" y2="0" stroke="#888" stroke-width="1"
                                          opacity="1"/>
                                    <line transform="translate(0,162)" x2="-5" y2="0" stroke="#888" stroke-width="1"
                                          opacity="1"/>
                                    <line transform="translate(0,123)" x2="-5" y2="0" stroke="#888" stroke-width="1"
                                          opacity="1"/>
                                    <line transform="translate(0,85)" x2="-5" y2="0" stroke="#888" stroke-width="1"
                                          opacity="1"/>
                                    <line transform="translate(0,46)" x2="-5" y2="0" stroke="#888" stroke-width="1"
                                          opacity="1"/>
                                    <line transform="translate(0,8)" x2="-5" y2="0" stroke="#888" stroke-width="1"
                                          opacity="1"/>
                                </g>
                                <g class="mark-text role-axis-label" pointer-events="none">
                                    <text text-anchor="end" transform="translate(-7,203)" font-family="sans-serif"
                                          font-size="10px" fill="#000" opacity="1">0.0
                                    </text>
                                    <text text-anchor="end" transform="translate(-7,164.53846153846155)"
                                          font-family="sans-serif" font-size="10px" fill="#000" opacity="1">0.5
                                    </text>
                                    <text text-anchor="end" transform="translate(-7,126.07692307692308)"
                                          font-family="sans-serif" font-size="10px" fill="#000" opacity="1">1.0
                                    </text>
                                    <text text-anchor="end" transform="translate(-7,87.61538461538463)"
                                          font-family="sans-serif" font-size="10px" fill="#000" opacity="1">1.5
                                    </text>
                                    <text text-anchor="end" transform="translate(-7,49.15384615384617)"
                                          font-family="sans-serif" font-size="10px" fill="#000" opacity="1">2.0
                                    </text>
                                    <text text-anchor="end" transform="translate(-7,10.69230769230771)"
                                          font-family="sans-serif" font-size="10px" fill="#000" opacity="1">2.5
                                    </text>
                                </g>
                                <g class="mark-rule role-axis-domain" pointer-events="none">
                                    <line transform="translate(0,200)" x2="0" y2="-200" stroke="#888" stroke-width="1"
                                          opacity="1"/>
                                </g>
                                <g class="mark-text role-axis-title" pointer-events="none">
                                    <text text-anchor="middle"
                                          transform="translate(-35,100) rotate(-90) translate(0,-2)"
                                          font-family="sans-serif" font-size="11px" font-weight="bold" fill="#000"
                                          opacity="1">y
                                    </text>
                                </g>
                            </g>
                            <path class="foreground" aria-hidden="true" d="" pointer-events="none" display="none"/>
                        </g>
                    </g>
                    <g class="mark-image role-mark marks" role="graphics-object"
                       aria-roledescription="image mark container">
                        <image aria-label="x: 0.5; y: 0.5; img: data/ffox.png" role="graphics-symbol"
                               aria-roledescription="image mark" xlink:href="data/ffox.png"
                               transform="translate(13.46153846153846,136.53846153846155)" width="50" height="50"
                               preserveAspectRatio="xMidYMid"/>
                        <image aria-label="x: 1.5; y: 1.5; img: data/gimp.png" role="graphics-symbol"
                               aria-roledescription="image mark" xlink:href="data/gimp.png"
                               transform="translate(90.38461538461537,59.61538461538463)" width="50" height="50"
                               preserveAspectRatio="xMidYMid"/>
                        <image aria-label="x: 2.5; y: 2.5; img: data/7zip.png" role="graphics-symbol"
                               aria-roledescription="image mark" xlink:href="data/7zip.png"
                               transform="translate(167.3076923076923,-17.307692307692292)" width="50" height="50"
                               preserveAspectRatio="xMidYMid"/>
                    </g>
                </g>
                <path class="foreground" aria-hidden="true" d="" display="none"/>
            </g>
        </g>
    </g>
</svg>
weisJ commented

Please try the latest snapshot version. To enable loading of external resources you have to specify so during loading using the LoaderContext e.g. LoaderContext.builder().externalResourcePolicy(ExternalResourcePolicy.ALLOW_ALL).

It rocks !
scatter_image vl json1689549094688264319