SVG <defs> not included from external target of <use> [duplicate] - svg

I'm trying to hack together a sprite sheet from a set of icons. I know almost nothing about SVG. I can get the simple icons to work, but an icon with a clip path isn't displaying properly. From what I can tell it seems like it's not using the clip path.
The sprite works in jsfilddle and it works if I just load the svg on it's own and include a < use > statement in the SVG. But if I have a separate < use > it doesn't work.
All my testing has been done in Chrome (50.0.2661.94)
<svg xmlns:xlink="http://www.w3.org/1999/xlink" xmlns="http://www.w3.org/2000/svg" version="1.1">
<defs>
<clipPath id="folder-clip-0">
<path d="..." />
</clipPath>
<symbol id="folder" viewBox="0 0 32 32">
<g class="container" data-width="32" data-height="27" transform="translate(0 2)">
<path d="..." class="..." />
<path class="..." d="..." />
<path clip-path="url(#folder-clip-0)" d="..." class="..." />
</g>
</symbol>
</defs>
</svg>
I'm using it like so:
<svg>
<use
xmlns:xlink="http://www.w3.org/1999/xlink"
xlink:href="/img/path/sprite.svg#folder">
</use>
</svg>
When I use the separate statement it looks like this:
But it should look like this:
The color difference is not relevant, it's just the background when the image was taken.
Edit:
I just discovered that if I dump the whole sprite sheet into the page HTML and reference it locally instead of an external file it works. So I don't know what's wrong with my external reference.
e.g.
<svg>
<use xlinkHref={"/img/path/not/work/sprite.svg#folder"}></use>
</svg>
vs.
<svg>
<symbol id="folder"></symbol>
</svg>
<svg>
<use xlinkHref={"#folder"}></use>
</svg>
This works for me as a fallback, but I'd rather have an external SVG file instead of embedding it in my HTML.
Edit 2:
If the SVG sprite sheet is embeded in the HTML directly using the external link shows the icon correctly.

This seems to be a browser support issue. Using the external reference works as expected in Firefox. Chrome doesn't handle clip paths and some other functions in external references. There's an outstanding bug report filed. Safari also doesn't support it.
Related StackOverflow ticket: Why can't I reference an SVG linear gradient defined in an external file (paint server)?
Open bugs:
https://code.google.com/p/chromium/issues/detail?id=109212
https://bugs.webkit.org/show_bug.cgi?id=105904

Related

Images in nested <defs> do not show up in chromium

I have some SVG files whereby one includes the other by using <use /> elements. To merge all those files into one resulting one, I have written some code that transforms ./main.svg from below:
#file: ./svg/shared.svg
<svg>
<image xlink:href="../img/bg.png" id="bg" />
</svg>
#file: ./svg/index.svg
<svg id="index">
<use xlink:href="./shared.svg#bg" />
</svg>
#file: ./main.svg
<svg>
<use xlink:href="./svg/index.svg#index" />
</svg>
into:
<svg>
<defs>
<svg id="svg-index">
<defs>
<image xlink:href="./img/bg.png" id="img-bg" />
</defs>
<use xlink:href="#img-bg" />
</svg>
</defs>
<use xlink:href="#svg-index" />
</svg>
So basically all urls and Ids are resolved and referenced nodes are imported. The script works fine, in Firefox and the result works as well in Inkscape. BUT in chromium the images do not show up. They are loaded, what I can see from the networking tab.
If I modify the images' xlink:href (inspector panel) by adding a little #qwertz at the end of one image, all of them show up, so that seams to be browser related, since the result works in FF and Inkscape.
Any Idea how to fix that?
UPDATE
One thing that may have some effect here is whether the manipulated SVG is already inserted into the dom or not. So this code makes the images appear:
setTimeout(function () {
[...document.querySelectorAll('image')].forEach(
img => img.setAttribute('xlink:href',
img.getAttribute('xlink:href')
)
);
}, 0)
But that feels really hackish and I am really interested in a »cleaner« solution here…

SVG patterns rasterised when printing to PDF

I'm trying to create a SVG file what when printed to PDF maintains all of its parts in vector. This file uses a pattern as a fill to a path, or in the example bellow as the fill to the rect element:
<svg
width="200"
height="200"
viewBox="0 0 200 200"
version="1.1"
xmlns="http://www.w3.org/2000/svg"
xmlnsXlink="http://www.w3.org/1999/xlink"
className="productWrapper"
>
<defs>
<pattern id="pattern" x="0" y="0" width="1" height="1">
<path
fill="#fcc"
d="[vector path here]"
/>
</pattern>
</defs>
<rect fill="url(#pattern)" stroke="black" width="200" height="200" />
</svg>
The issue I'm facing is that when printed to PDF the rect element or any path at the top level of the SVG file stays vector, but all vector content inside the pattern is rasterized.
A very minimal example of this can be found here:
https://6j8953j8rn.codesandbox.io/
Any way to get the SVG to render in vector when printing to PDF?
One tool that kept vector info after converting from SVG (including <pattern>) to PDF (I used it as CLI) is https://github.com/typst/svg2pdf
Some online converters also could do it while the most of other tools I tried make content inside <pattern> raster.

Clip path not displaying properly in SVG sprite when using "use"

I'm trying to hack together a sprite sheet from a set of icons. I know almost nothing about SVG. I can get the simple icons to work, but an icon with a clip path isn't displaying properly. From what I can tell it seems like it's not using the clip path.
The sprite works in jsfilddle and it works if I just load the svg on it's own and include a < use > statement in the SVG. But if I have a separate < use > it doesn't work.
All my testing has been done in Chrome (50.0.2661.94)
<svg xmlns:xlink="http://www.w3.org/1999/xlink" xmlns="http://www.w3.org/2000/svg" version="1.1">
<defs>
<clipPath id="folder-clip-0">
<path d="..." />
</clipPath>
<symbol id="folder" viewBox="0 0 32 32">
<g class="container" data-width="32" data-height="27" transform="translate(0 2)">
<path d="..." class="..." />
<path class="..." d="..." />
<path clip-path="url(#folder-clip-0)" d="..." class="..." />
</g>
</symbol>
</defs>
</svg>
I'm using it like so:
<svg>
<use
xmlns:xlink="http://www.w3.org/1999/xlink"
xlink:href="/img/path/sprite.svg#folder">
</use>
</svg>
When I use the separate statement it looks like this:
But it should look like this:
The color difference is not relevant, it's just the background when the image was taken.
Edit:
I just discovered that if I dump the whole sprite sheet into the page HTML and reference it locally instead of an external file it works. So I don't know what's wrong with my external reference.
e.g.
<svg>
<use xlinkHref={"/img/path/not/work/sprite.svg#folder"}></use>
</svg>
vs.
<svg>
<symbol id="folder"></symbol>
</svg>
<svg>
<use xlinkHref={"#folder"}></use>
</svg>
This works for me as a fallback, but I'd rather have an external SVG file instead of embedding it in my HTML.
Edit 2:
If the SVG sprite sheet is embeded in the HTML directly using the external link shows the icon correctly.
This seems to be a browser support issue. Using the external reference works as expected in Firefox. Chrome doesn't handle clip paths and some other functions in external references. There's an outstanding bug report filed. Safari also doesn't support it.
Related StackOverflow ticket: Why can't I reference an SVG linear gradient defined in an external file (paint server)?
Open bugs:
https://code.google.com/p/chromium/issues/detail?id=109212
https://bugs.webkit.org/show_bug.cgi?id=105904

Responsive SVG sprites

I am building an icon system for a site and moving away from png and font-icon sprites to SVG.
I need it to work across all major browsers (IE9+) and ideally want to use fragment identifiers or, as a second best alternative, inline. I've done extensive research, and there isn't a great deal out there, and from what I have read SVG sprites aren't particularly responsive.
For fragment identifiers, using img tag, I have to set the width/height to the same size as the viewbox. Increasing the dimensions on the img tag ends up revealing some of the next sprite. I'd like to be able to use percentage values so the sprite fragment fills the parent container.
I just want confirmation that this isn't possible, I can't find anything that suggests I am wrong to think this.
This is very much possible and is relatively easy to accomplish. SVG's are vector graphics and therefore, if done correctly, will be the most responsive graphics on your website.
Set your SVG file up as expected but put each sprite into a <g> tag with its own identifier.
<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg">
<g class="sprite" id="circle">
<ellipse cy="50" cx="50" ry="45" rx="45" stroke-width="5" stroke="#00ff00" fill="#00ff00" fill-opacity="0.75" />
</g>
<g class="sprite" id="square">
<rect y="5" x="5" width="90" height="90" stroke-width="5" stroke="#ff0000" fill="#ff0000" fill-opacity="0.75" />
</g>
<g class="sprite" id="triangle">
<path d="M20,7 L92,50 L6,93 z" stroke-width="5" stroke="#0000ff" fill="#0000ff" fill-opacity="0.75" />
</g>
</svg>
Add some CSS to say only the rargeted one needs to be displayed
<defs>
<style><![CDATA[
.sprite { display: none; }
.sprite:target { display: inline; }
]]></style>
</defs>
Then you can just call these out whenever required using an img tag or background element etc.
You can find the fully explained writeup here:
How to Use SVG Image Sprites

Can you combine multiple svgs with different viewBoxes and customize each in Polymer?

I have multiple svgs that I'd like to use in my application and was hoping to put them in a single custom-svg element to reference individually by id, however, the viewBoxes are different. One svg is defined as
<iron-iconset-svg name="club-icon" size="512">
<svg>
<defs>
<g id="club-icon">
<path d="bunch of numbers"></path>
</g>
</defs>
</svg>
</iron-iconset-svg>
The other svg is defined as
<iron-iconset-svg name="club-icon" size="300">
<svg>
<defs>
<g id="book-icon">
<path d="bunch of numbers"></path>
</g>
</defs>
</svg>
</iron-iconset-svg>
Is there a way for each custom icon to define its own viewBox, or must every svg defined within a single iconset share the same properties. For now I have multiple custom element html files, but each custom element is an http request (which I'm trying to minimize).
It can be done using symbols.
<svg xmlns="http://www.w3.org/2000/svg" style="display: none;">
<symbol id="beaker" viewBox="214.7 0 182.6 792">
<!-- <path>s and whatever other shapes in here -->
</symbol>
<symbol id="shape-icon-2" viewBox="0 26 100 48">
<!-- <path>s and whatever other shapes in here -->
</symbol>
</svg>
See this article on CSS tricks for a further explanation.
https://css-tricks.com/svg-symbol-good-choice-icons/
you might try a transform scale on you g element?
<g id="book-icon" transform="scale(1.71)">
1.71 = 512/300
or if you are using gulp/grunt, you could resize the SVGs to be the same using svg-scaler or the like

Resources