Width and height have no effect on <use> when referencing inline images - svg

I'm trying to inline some images into an SVG that uses xlink:href references pointed to fully qualified local paths. For reasons why see this GitHub issue. The example python does a reasonable job, but it will inline the same image many times in a single SVG which is not optimal.
So I tried a similar approach that converts something like this:
<svg xmlns="http://www.w3.org/2000/svg">
<image x="0" y="-144" width="101px" height="101px" xlink:href="/path/to/file.png" />
<image x="0" y="-144" width="101px" height="101px" xlink:href="/path/to/file.png" />
</svg>
To this
<svg xmlns="http://www.w3.org/2000/svg">
<defs>
<image id="file.png_1" href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgA..." />
</defs>
<use x="0" y="+144" width="101px" height="101px" href="#file.png_1" />
<use x="0" y="-144" width="101px" height="101px" href="#file.png_1" />
</svg>
The problem
The way this renders (Safari and Chrome), it seems that width and height have no effect on the <use>.
I can get the desired result to render identically to the original if I move the width and height onto the <defs><image/></defs>. But doing this makes the assumption that no SVG will EVER use the same image twice with different sizes.
Despite reading the SVG 1.1 Standard, I'm still no further forward on understanding how to size a <use> element without explicitly sizing it on the referenced <image> element.

In a comment the OP is asking for an example:
#enxaneta could you show a very short worked example as an answer.
In the next example I'm putting an image in a symbol. The image has a width and a height. Also the symbol has a viewBox. In this case the viewBox of the symbol has the same size as the image.
I'm using the symbol with use.The use element has a x and y attributes for the upper left corner. Also the use has a width and a height. Please observe that I'm preserving the same aspect ratio as the symbol's viewBox.
<svg viewBox="0 0 65 50">
<symbol id="i" viewBox="0 0 22 9.8">
<image width="22" height="9.8" href="https://assets.codepen.io/222579/bone300.svg"/>
</symbol>
<use href="#i" x="10" y="7" width="44" height="19.6" />
</svg>

According to the SVG specification width and height are ignored on a use element unless that use element points to a symbol or an svg element.
The width and height properties on the ‘use’ element override the values for the corresponding properties on a referenced ‘svg’ or ‘symbol’ element when determining the used value for that property on the instance root element.
So wrapping the image in a symbol element and then pointing the use to the symbol should work.

Related

Can I use "symbol" and "use" tags to preserve the aspect ratio of text in an SVG that has preserveAspectRatio="none"?

I have an inline SVG image. I want it to fill the available space, so I set the preserveAspectRatio attribute to "none". It represents a user selection, so the user coordinates can vary, but are typically quite large. The user coordinates are important, so I define them in the viewBox so that features are rendered at the "right" relative locations.
I need to put text labels at specific locations on this image, but I've always avoided preserveAspectRatio "none" because glyphs are deformed by the non-preserved aspect ratio. In this case I really need to make it work.
I thought that maybe I could create a symbol for each label on a different SVG that has a default preserveAspectRatio of "xMidYMid meet", and then reference those symbols on my image at the required user coordinates.
It looks like this:
<svg width="0" height="0">
<symbol id="label" width="225" height="50" viewBox="0 0 225 50">
<text text-anchor="middle">Label Text</text>
</symbol>
</svg>
<svg id="user_selection" width="100%" height="100%"
viewBox="${rtVB['min-x']} ${rtVB['min-y']} ${rtVB.width} ${rtVB.height}"
preserveAspectRatio="none">
<!-- lots elided -->
<use x="${xloc}" y="${yloc}" href="#label" class="labels" width="225" height="50"/>
</svg>
The viewBox width is typically between 1,000 and 10,000. The height is typically between 100 and 3,000. The result is either complete absence of a label, or a miniscule dot on the screen. If I get a dot, select it and right click I can see that 1 or sometimes 2 characters from the label have been rendered.
I tried looking at the MDN definition of the 'symbol' and 'use' elements, but I wasn't able to see how to use them to get the desired result.
I thank you for your assistance.

SVG symbol with stroke has wrong size in Illustrator

I'm generating an SVG file on a website and it's supposed to be imported in Ilustrator. I use <symbol /> element to store a shape definition and I reference it with the <use /> element on the "sheet". Users are able to set size of the shape and it's really crucial that it's exactly the same size when imported to Adobe Illustrator. It works unless I add a stroke.
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg id="SvgjsSvg1000" xmlns="http://www.w3.org/2000/svg" version="1.1" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:svgjs="http://svgjs.dev/svgjs" width="210mm" height="297mm" viewBox="0 0 210 297" viewbox="0 0 210 297">
<defs id="SvgjsDefs1001">
<symbol id="shape_id70" preserveAspectRatio="xMinYMin meet" viewBox="0 0 27.841039657592773 12.2083101272583">
<path id="SvgjsPath1030" d="M26.4405 13.067C25.685728 11.72066 22.49458 8.90142 20.73442 7.678030000000001C22.99088 7.6388854 23.85819 7.6146637 28.738950000000003 7.456081C26.298620000000003 6.628644 23.737080000000002 5.904501 21.418080000000003 4.973881C23.937200000000004 4.5081560000000005 26.519460000000002 4.085806000000001 28.376120000000004 3.7947010000000008C28.376120000000004 3.7946453179000006 19.370760000000004 2.7013810000000005 8.358420000000002 4.414499000000001L9.412540000000002 1.364679000000001L6.497860000000001 3.520859000000001L4.442800000000001 0.858699000000001L4.324531000000001 4.464059000000001L0.897911000000001 5.542179000000001L4.249861000000001 6.913239000000001L4.236664300000001 10.198599000000002L6.192894300000001 7.622079000000001L9.099554300000001 8.802649L8.143547300000002 6.432539C12.463087300000002 6.813516 22.5756473 8.818239 26.440547300000002 13.067009Z" fill="none"></path>
</symbol>
</defs>
<use id="SvgjsUse1034" xlink:href="#shape_id70" x="0" y="0" width="50"></use>
</svg>
This is fine in both browser and Illustrator. But when I add attributes stroke-width="0.1" stroke="#000". In Illustrator, the size of the shape changesto 48.951. It's still 50 in browser though. I tried to add these attributes to the <symbol />, <path /> and <use /> elements with the same result.
I know that the SVG standard doesn't have any attribute that would control how to render the stroke. I know there is a discussion about the stroke-alignment attribute for future versions of SVG. But browsers don't support that yet, and neither Adobe Illustrator.
So my question is: Is there any way how to adjust the SVG so that Illustrator would render the shape with the size that is set by the width attribute in the <use /> element regardless of the stroke settings
The width value on your <use> should be having no effect on your <symbol> because your symbol has no viewBox attribute. Without a viewBox, only the x and y attributes of the <use> will be doing anything.
Also, be aware that we've seen a few questions on S.O. in the past, complaining about bugs in Illustrator's SVG import filter. If <symbol> is working, then that's great. However, in general, you may find that keeping your SVG structure simple, and avoiding the more advanced SVG features, might be a good idea.

Change Part of Symbol Color SVG

I have an SVG symbol, basically three paths with an all black stroke. This symbol is used heavily across my SVG document using the use tag.
Sometimes i just want to change only one stroke of the instances of the symbol, like color variations, How can i achieve this using SVG+CSS, knowing that I used 'use' to create the symbol instances.
This actually is a very neat trick http://codepen.io/FWeinb/blog/quick-tip-svg-use-style-two-colors, Fabrice Weinberg here is using fill="currentColor" on the symbol so he can change it later using css.
<svg style="display:none;">
<symbol id="test">
<rect x="10" y="10" width="100" height="100" />
<rect x="100" y="10" width="100" height="100" fill="currentColor" />
</symbol>
</svg>
<svg class="icon icon--BlueBlack"><use xlink:href="#test" /></svg>
<svg class="icon icon--BlueGreen"><use xlink:href="#test" /></svg>
and
.icon--BlueBlack{
fill:blue;
}
.icon--BlueGreen{
fill:blue;
color:green;
}
According to the SVG 1.1 spec on the use element:
The effect of a ‘use’ element is as if the contents of the referenced
element were deeply cloned into a separate non-exposed DOM tree […]
The SVG DOM does not show the referenced element's contents as
children of ‘use’ element.
That implies that the referenced elements children are not accessible by traversing your DOM tree. This also holds true for access via css selectors as the spec goes on:
CSS2 selectors cannot be applied to the (conceptually) cloned DOM tree
because its contents are not part of the formal document structure.
It is possible. You can't style the dereferenced symbol contents directly with CSS. But you can style the parent <use> element and have that colour inherit into the symbol. See the answers to the following question for examples.
How to style one particular SVG path in CSS?

Scaling SVG image element when image is another SVG file

I am making a tool where I need a possibility to add an image into existing SVG. This image can be both Bitmap file and another SVG file. So I do it using element, like this:
<image x="100" y="100" width="200" height="100" preserveAspectRatio="none" xlink:href="image.jpg">
I need this image to fit to width/height I specified (I don't care about the original size of an image), that's why I set preserveAspectRatio to "none". And it works fine with bitmaps. However when I try the same code with another SVG image, it is not scaled. The preserverAspectRatio description says that viewBox should be set on this image element, however it doesn't help - the image is not scaled.
Here is the code which, as far as I understand should work:
<image x="100" y="100" width="200" height="100" viewBox="0 0 200 100" preserveAspectRatio="none" xlink:href="clock.svg">
And here is source of the clock.svg:
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" id="Map">
<path d="M8.179,20.115c-0.478,0.277-0.642,0.889-0.365,1.366c0.275,0.479,0.889,0.642,1.365,0.366c0.479-0.275,0.643-0.888,0.367-1.367C9.27,20.004,8.658,19.84,8.179,20.115zM9.18,12.239c-0.479-0.276-1.09-0.112-1.366,0.366s-0.111,1.09,0.365,1.366c0.479,0.276,1.09,0.113,1.367-0.366C9.821,13.126,9.657,12.516,9.18,12.239zM8.625,17.043c-0.001-0.552-0.448-0.999-1.001-1c-0.553,0-1,0.448-1,1c0,0.553,0.449,1,1,1C8.176,18.043,8.624,17.596,8.625,17.043zM16.312,3.957V3.031h1c0.275,0,0.5-0.225,0.5-0.5v-0.5c0-0.275-0.225-0.5-0.5-0.5h-3.625c-0.275,0-0.5,0.225-0.5,0.5v0.5c0,0.275,0.225,0.5,0.5,0.5h1v0.926C7.819,4.381,2.376,10.068,2.374,17.042C2.376,24.291,8.251,30.166,15.5,30.169c7.249-0.003,13.124-5.878,13.125-13.127C28.624,10.067,23.181,4.38,16.312,3.957zM15.5,27.166C9.909,27.157,5.385,22.633,5.375,17.042C5.385,11.451,9.909,6.927,15.5,6.917c5.59,0.01,10.115,4.535,10.124,10.125C25.615,22.633,21.091,27.157,15.5,27.166zM12.062,22.998c-0.478-0.275-1.089-0.111-1.366,0.367c-0.275,0.479-0.111,1.09,0.366,1.365c0.478,0.277,1.091,0.111,1.365-0.365C12.704,23.887,12.54,23.275,12.062,22.998zM12.062,11.088c0.479-0.276,0.642-0.888,0.366-1.366c-0.276-0.478-0.888-0.642-1.366-0.366s-0.642,0.888-0.366,1.366C10.973,11.2,11.584,11.364,12.062,11.088zM22.822,13.971c0.478-0.275,0.643-0.888,0.366-1.366c-0.275-0.478-0.89-0.642-1.366-0.366c-0.479,0.278-0.642,0.89-0.366,1.367C21.732,14.083,22.344,14.247,22.822,13.971zM15.501,23.92c-0.552,0-1,0.447-1,1c0,0.552,0.448,1,1,1s1-0.448,1-1C16.501,24.367,16.053,23.92,15.501,23.92zM19.938,9.355c-0.477-0.276-1.091-0.111-1.365,0.366c-0.275,0.48-0.111,1.091,0.366,1.367s1.089,0.112,1.366-0.366C20.581,10.245,20.418,9.632,19.938,9.355zM23.378,16.042c-0.554,0.002-1.001,0.45-1.001,1c0.001,0.552,0.448,1,1.001,1c0.551,0,1-0.447,1-1C24.378,16.492,23.929,16.042,23.378,16.042zM22.823,20.115c-0.48-0.275-1.092-0.111-1.367,0.365c-0.275,0.479-0.112,1.091,0.367,1.367c0.477,0.275,1.089,0.112,1.365-0.366C23.464,21.004,23.3,20.391,22.823,20.115zM15.501,8.167c-0.552,0-1,0.448-1,1l-0.466,7.343l-3.004,1.96c-0.478,0.277-0.642,0.889-0.365,1.366c0.275,0.479,0.889,0.642,1.365,0.366l3.305-1.676c0.055,0.006,0.109,0.017,0.166,0.017c0.828,0,1.5-0.672,1.5-1.5l-0.5-7.876C16.501,8.614,16.053,8.167,15.501,8.167zM18.939,22.998c-0.479,0.276-0.643,0.888-0.366,1.367c0.275,0.477,0.888,0.642,1.366,0.365c0.478-0.276,0.642-0.889,0.366-1.365C20.028,22.886,19.417,22.723,18.939,22.998zM11.197,3.593c-0.836-1.04-2.103-1.718-3.541-1.718c-2.52,0-4.562,2.042-4.562,4.562c0,0.957,0.297,1.843,0.8,2.576C5.649,6.484,8.206,4.553,11.197,3.593zM27.106,9.014c0.503-0.733,0.8-1.619,0.8-2.576c0-2.52-2.043-4.562-4.562-4.562c-1.438,0-2.704,0.678-3.541,1.717C22.794,4.553,25.351,6.484,27.106,9.014z" fill="#000000" fill-opacity="1" stroke="#DDDDDD" stroke-width="0.5" stroke-opacity="1"/>
I want this clock to be scaled and occupy all the 200x100 rectangle, but it is not.
I would be very grateful if anybody could help.
From the 'image' element definition in the SVG 1.1 spec:
The value of the ‘viewBox’ attribute to use when evaluating the
‘preserveAspectRatio’ attribute is defined by the referenced content.
For content that clearly identifies a viewBox (e.g. an SVG file with
the ‘viewBox’ attribute on the outermost svg element) that value
should be used. For most raster content (PNG, JPEG) the bounds of the
image should be used (i.e. the ‘image’ element has an implicit
‘viewBox’ of '0 0 raster-image-width raster-image-height'). Where no
value is readily available (e.g. an SVG file with no ‘viewBox’
attribute on the outermost svg element) the ‘preserveAspectRatio’
attribute is ignored, and only the translation due to the ‘x’ & ‘y’
attributes of the viewport is used to display the content.
What the spec tells you is that the referenced svg (your clock.svg) should define it's coordinate system (aka 'viewBox').

SVG Positioning

I'm having a play with SVG and am having a few problems with positioning. I have a series of shapes which are contained in the g group tag. I was hoping to use it like a container, so I could set its x position and then all the elements in that group would also move. But that doesn't seem to be possible.
How do most people go about positioning a group of elements which you wish to move in tandem?
Is there any concept of relative positioning? e.g. relative to its parent
Everything in the g element is positioned relative to the current transform matrix.
To move the content, just put the transformation in the g element:
<g transform="translate(20,2.5) rotate(10)">
<rect x="0" y="0" width="60" height="10"/>
</g>
Links: Example from the SVG 1.1 spec
There is a shorter alternative to the previous answer. SVG Elements can also be grouped by nesting svg elements:
<svg xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink">
<svg x="10">
<rect x="10" y="10" height="100" width="100" style="stroke:#ff0000;fill: #0000ff"/>
</svg>
<svg x="200">
<rect x="10" y="10" height="100" width="100" style="stroke:#009900;fill: #00cc00"/>
</svg>
</svg>
The two rectangles are identical (apart from the colors), but the parent svg elements have different x values.
See http://tutorials.jenkov.com/svg/svg-element.html.
As mentioned in the other comment, the transform attribute on the g element is what you want. Use transform="translate(x,y)" to move the g around and things within the g will move in relation to the g.
There are two ways to group multiple SVG shapes and position the group:
The first to use <g> with transform attribute as Aaron wrote. But you can't just use a x attribute on the <g> element.
The other way is to use nested <svg> element.
<svg id="parent">
<svg id="group1" x="10">
<!-- some shapes -->
</svg>
</svg>
In this way, the #group1 svg is nested in #parent, and the x=10 is relative to the parent svg. However, you can't use transform attribute on <svg> element, which is quite the contrary of <g> element.
I know this is old but neither an <svg> group tag nor a <g> fixed the issue I was facing. I needed to adjust the y position of a <g> tag which also had animation on it.
The solution was to use both the <svg> and <g> tag together:
<svg y="1190" x="235">
<g class="light-1">
<path />
</g>
</svg>

Resources