why does <g> not work in <clipPath> in svg? - svg

The following code does not work:
<svg>
<defs>
<clipPath id="test">
<g>
<rect x="0" y="0" width="10" height="10"></rect>
</g>
</clipPath>
</defs>
<rect x="0" y="0" width="100" height="100" clip-path="url(#test)"></rect>
</svg>
But this does work:
<svg>
<defs>
<clipPath id="test">
<rect x="0" y="0" width="10" height="10"></rect>
</clipPath>
</defs>
<rect x="0" y="0" width="100" height="100" clip-path="url(#test)"></rect>
</svg>
In my project, I have some paths in the group tag and I want to reuse the group as a clipPath target and show the path at the same time. For example:
<svg>
<defs>
<g id="group">
<path d="..."></path>
<path d="..."></path>
<path d="..."></path>
<path d="..."></path>
</g>
<clipPath id="test">
<use xlink:href="#group" fill="#000"></use>
</clipPath>
</defs>
<!-- show the stroke of the group -->
<use xlink:href="#group" stroke="#000"></use>
<!-- at same time, clip the rect to make some animation as the background -->
<rect x="0" y="0" width="100" height="100" clip-path="url(#test)"></rect>
</svg>

Because the SVG specification says so
Content model:
Any number of the following elements, in any order:
    descriptive elements
    animation elements
    shape elements
    ‘text’
    ‘use’
Unfortunately <g> elements are not in that list.
Firefox used to support clipping <g> elements a long time ago till we noticed that we weren't a) acting per the specification above and b) compatible with other implementations so we restricted our clipPath implementation to be consistent. So if you got Chrome and Safari on board for a specification change we'd likely be OK with that.
Note that you can work around this by clipping a <use> element that points to a <g> element.

Related

(SVG) Transform origin of grouped path element is not getting applied

I want to define grouped shapes in the <def> section and display them via the <use> tag with the transformation-origin being the group's center. Following is a minimal example illustrating my issue:
<svg xmlns="http://www.w3.org/2000/svg" width="200" height="200" viewBox="0 0 200 200">
<defs>
<g id="triangle">
<path fill="green" d="m 24 0 l 24 41.569 l -48 0 z" />
<circle cx="24" cy="24" r="1" fill="red" />
<rect width="48" height="41.569" fill="purple" fill-opacity="20%" />
</g>
</defs>
<rect width="100" height="100" />
<rect fill="gray" x="100" width="100" height="100" />
<rect width="100" height="100" />
<rect fill="gray" y="100" width="100" height="100" />
<rect width="100" height="100" x="100" y="100" />
<use href="#triangle" transform-origin="50% 50%" transform="translate(100 100)" />
</svg>
As it is my understanding, the triangle should be centered in the background. But in actuality, the top left corner of the group is centered.
I have tested my example against the MDN transform-origin one. The MDN one works well in the same environment (Edge Browser & VSCode). This lets me believe I am missing some side effects of the used tags or attributes
Observed behavior
Expected behavior
A scale transform has a centre point i.e. a point that doesn't change position.
So does a rotation - there's a centre of rotation.
A translate transform does not, every location moves. There's therefore no origin so transform-origin does nothing.

SVG how to set max width for group

I have some SVG elements grouped together in a <g> element (exactly the barcode 1D, PHP generates a barcode).
<g
style="fill:#000000;stroke:none"
id="barcode1D"
transform="matrix(1.2083333,0,0,0.8247805,62.027778,573.54235)">
<rect
x="0"
y="0"
width="4"
height="30"
id="xyz" />
....
<rect
x="224"
y="0"
width="0"
height="30"
id="xyzn" /> </g>
The barcode is generated in various widths, lengths. How can I set the width permanently ?
Based on this example, I am asking for a hint. Thank you for your help in advance.
SVG g element does not have width and height attributes. Therefore, you can not set height and width on it.
You should use a foreignObject with a svg inside of it to do so.
<svg width="640" height="480" xmlns="http://www.w3.org/2000/svg">
<foreignObject id="G" width="300" height="200">
<svg>
<!-- Barcode here -->
<rect fill="black" stroke-width="2" height="112" width="84" y="55" x="55" stroke="#000000"/>
<circle fill="#FF0000" stroke="#000000" stroke-width="5" cx="155" cy="65" id="svg_7" r="50"/>
</svg>
</foreignObject>
</svg>

Cut off half of the SVG's text element

How can I make this
to look like this
So I want to halve the text element. I don't want to hide half of the text outside of SVG. Hiding it outside of g would be ok, but haven't found solution.
<svg width="500" height="500">
<g transform="translate(50,50)">
<rect width="80" height="50" style="fill:rgb(0,0,255);"/>
<text font-size="40" x="0" y="15" fill="black">SVG</text>
</g>
</svg>
JSFIDDLE:
http://jsfiddle.net/64nkLcdy/
Use the clip-path property :
<svg width="500" height="500">
<defs>
<clipPath id="myClip">
<rect width="80" height="50" />
</clipPath>
</defs>
<g transform="translate(50,50)">
<rect width="80" height="50" style="fill:rgb(0,0,255);" />
<text font-size="40" x="0" y="15" fill="black" clip-path="url(#myClip)">SVG</text>
</g>
</svg>
Use an <svg> element rather than a <g> as the svg element will clip its contents by default. The overflow property controls clipping i.e overflow="visible" doesn't clip but overflow="hidden" does.
<svg width="500" height="500">
<svg transform="translate(50,50)" width="80" height="50" overflow="hidden">
<rect width="80" height="50" style="fill:rgb(0,0,255);"/>
<text font-size="40" x="0" y="15" fill="black">SVG</text>
</svg>
</svg>

SVG clipPath on multiple Use elements at different locations

Until recently the following SVG code rendered correctly in Firefox (v25), how ever it doesn't any more (v33). All the other browsers I have tested (Chrome 33, Safari 6, IE 10).
http://jsfiddle.net/9btoveeL/
<svg xmlns:xlink="http://www.w3.org/1999/xlink" xmlns="http://www.w3.org/2000/svg" xml:space="preserve" height="1000px" width="1000px" y="0px" x="0px" version="1.1">
<defs>
<clipPath id="clip1">
<rect height="100" width="10" y="0" x="0"/>
</clipPath>
<clipPath id="clip2">
<rect height="100" width="10" y="0" x="10"/>
</clipPath>
<clipPath id="clip3">
<rect height="100" width="10" y="0" x="20"/>
</clipPath>
<symbol id="fill_texture">
<g>
<rect height="10" width="10" x="0" y="0" fill="#ff0000"/>
<rect height="10" width="10" x="3" y="5" fill="#0ff000"/>
<rect height="10" width="10" x="6" y="10" fill="#0000ff"/>
<rect height="10" width="10" x="9" y="15" fill="#ffff00"/>
<rect height="10" width="10" x="12" y="20" fill="#ff00ff"/>
<rect height="10" width="10" x="15" y="25" fill="#00ffff"/>
</g>
</symbol>
</defs>
<g id="columns">
<use id="unclipped" xlink:href="#fill_texture" width="100" height="100" x="0" y="0"/>
<use id="slot1" xlink:href="#fill_texture" clip-path="url(#clip1)" x="50" y="0"/>
<use id="slot2" xlink:href="#fill_texture" clip-path="url(#clip2)" x="100" y="0"/>
<use id="slot3" xlink:href="#fill_texture" clip-path="url(#clip3)" x="150" y="0"/>
</g>
</svg>
What I'm attempting to do is slice a prepared symbol into three parts and then use those three parts wherever I like. In Firefox 33 it seems it is applying the clip at it's original location (0,0 or 0,10 in my example) while the other browsers and previous versions of Firefox apply the clip starting from the top left corner of the use element it is applied to.
If each clip path must be moved to match the location of the use instead of being treated as relative to it, I can't see how a clip could ever be reused in multiple elements.
The position of a clipping path is by default calculated in the user coordinate system of the object to which it is applied (clipPathUnits="userSpaceOnUse").
(You could set it to clipPathUnits="objectBoundingBox", but then you would have to redefine all the lengths of shapes within the clipping paths to be relative to the height and width of the shape being clipped.)
There is a simple solution to always get the effect you want: transform the coordinate system for the use elements. Instead of positioning them using x and y attributes, position them with a transform="translate(x,y)" attribute. That way, the coordinate system used to position the clipping path will move with them.
Here's your fiddle updated to show the change. It's repeated below as a stack snippet, and should work as expected in all browsers.
<svg xmlns:xlink="http://www.w3.org/1999/xlink" xmlns="http://www.w3.org/2000/svg"
xml:space="preserve" version="1.1"
height="100px" width="400px" y="0px" x="0px" >
<defs>
<clipPath id="clip1" >
<rect height="100" width="10" y="0" x="0"/>
</clipPath>
<clipPath id="clip2">
<rect height="100" width="10" y="0" x="10"/>
</clipPath>
<clipPath id="clip3">
<rect height="100" width="10" y="0" x="20"/>
</clipPath>
<symbol id="fill_texture">
<g>
<rect height="10" width="10" x="0" y="0" fill="#ff0000"/>
<rect height="10" width="10" x="3" y="5" fill="#0ff000"/>
<rect height="10" width="10" x="6" y="10" fill="#0000ff"/>
<rect height="10" width="10" x="9" y="15" fill="#ffff00"/>
<rect height="10" width="10" x="12" y="20" fill="#ff00ff"/>
<rect height="10" width="10" x="15" y="25" fill="#00ffff"/>
</g>
</symbol>
</defs>
<g id="columns">
<use id="unclipped" xlink:href="#fill_texture"
width="100" height="100" x="0" y="0"/>
<use id="slot1" xlink:href="#fill_texture" clip-path="url(#clip1)"
transform="translate(50,0)"/>
<use id="slot2" xlink:href="#fill_texture" clip-path="url(#clip2)"
transform="translate(100,0)"/>
<use id="slot3" xlink:href="#fill_texture" clip-path="url(#clip3)"
transform="translate(150,0)"/>
</g>
</svg>
So what should happen when you use x and y instead of transform? It is hard to say, because there is an inconsistency in the specifications.
The Firefox (v33) implementation makes sense from a logical application of the clipping path rules.
When you <use> a <symbol>, you create a new coordinate space for the content within the symbol, but the use element is still in the parent coordinate space. And since it is the use element that is being clipped, that is the coordinate that matters for matching with the clipping path. A use element with an x coordinate of 50 or more will always be outside the clipping paths you give, which don't extend past x=30.
But then, why do the other browsers position the clipping path origin at the <use> element's (x,y) point instead of at the origin of its coordinate system? It's because of the way the specifications define how x and y should be implemented: as an additional transformation added to a grouping element that also has all the other attributes and styles (including clip-path) that you specify on <use>.
According to the specs, the following code:
<use xlink:href="#content" clip-path="url(#clip)"
x="50" y="100" width="50" height="100" />
is supposed to be rendered (assuming "content" is a <symbol>) the same as:
<g clip-path="url(#clip)" transform="translate(50,100)">
<svg width="50" height="100" <!--viewBox and other attributes from the symbol--> >
<!-- graphics from symbol#content go here -->
</svg>
</g>
and under that representation, the clipping path should be translated to match the transform attribute.
However, that is completely different than what would happen if you used an <image> or <rect> instead of <use>, but with the exact same x, y, width, height and clip-path attributes. For these other elements, x and y only define positions within the parent coordinate system, not transformations of the coordinate system, so they do change the origin of the clipping path. Which is why I would call this an unfortunate inconsistency in the specifications.

SVG Text and Rect position

I have this code:
<g pointer-events="default">
<g>
<g x="0" y="0" width="145" height="47" regroup="false">
<text x="0" y="0">
<tspan x="0" y="0">some text</tspan>
</text>
</g>
<g x="0" y="0" width="80" height="60" regroup="false">
<rect x="0" y="0" width="80" height="60" fill="white" />
</g>
</g>
</g>
There is one css rule box-sizing: border-box;which is apply to all elements.
It produce that:
The grid is coming from a sliding element from the first <g>.
I don't understand why the text element and the rect are not displayed at the same y.
Does anyone have any idea?

Resources