SVG border appears trimmed - svg

I think I am missing an apparent offset issue in my very first svg here, The top and left borders are tirmmed (pointed by red arrow), Also if something like nested <g> or <symbol> is possible pleas let me know, Thanks. (screenshot in FF27).
The simplified code and a fiddle
<svg>
<defs>
<symbol id="ringCenters" style="fill:black;">
<circle cx="50" cy="50" r="2" />
/*...more circles*/
</symbol>
<symbol id="ring1" class="rings">
<path d="M99.9746,51.5943
A50 50 0 1 0
62.5254,98.4057"
stroke="green" />
<path d="M99.9746,51.5943
A50 50 0 0 1
62.5254,98.4057"
stroke="red" />
</symbol>
/*...more rings*/
</defs>
<use xlink:href="#ringCenters" x="10" y="10" />
/*...more rings*/
</svg>
.rings{fill:none;}
svg {
width:600px;
height:300px;
}

The stroke around a shape is always centered on the exact geometric border of the shape, and so extends beyond the shape by half the stroke width.
If for any reason you don't want to use overflow:visible, another option is therefore to just adjust the positions of your shape so that you have a bit of padding on the edges, equal to half the stroke width. (Of course, that assumes that the stroke-width will be the same every time you use the symbol.)
Note that you have to adjust the position of the <path> within the <symbol>, not just the position of the <symbol> within your SVG. That's because each reference to a symbol element is drawn as if it was a nested <svg> element within it's own viewport, with a fixed height, width, and "viewBox" coordinate system. You're not setting those attributes on the symbol, so they end up as the defaults for nested SVGs: width and height equal to 100% of the parent, and a default coordinate system with (0,0) at the top left corner of the viewport.
That "nested SVG" then gets positioned so that it's top left corner is at the (x,y) position specified in the <use> element, but as far as the drawing content within the symbol is concerned, the rectangular edges of that viewport are the edges of the drawing surface. Unless, of course, you specifically allow overflow, as #helderdarocha suggested.
(By the way, the symbols-are-drawn-as-nested-SVGs idea is probably why you needed to specify svg{ overflow:visible;} for Firefox, although it really should work by setting the property on the symbols directly; I'd call that a Firefox bug.)
On your other question: <g> elements can be nested multiple times without problem. For <symbol> elements, it's not so clear. The specs say that symbols are much like <g> elements except (a) they have viewports and (b) the symbol is not drawn directly, it is only available for reference by a <use> element.
Now, that suggests that <symbol>s can be nested like <g> elements. And on Chrome it works. On Firefox, not so much. I assume what is happening is that when Firefox copies the internal content of the outer <symbol>, it treats the internal <symbol> elements as if they were just symbol definitions, not as if they were renderings of those symbols.
I.e. code like this
<svg>
<defs>
<symbol id="complicated">
<symbol id="firstPart">
<path d="...."/>
</symbol>
<symbol id="secondPart">
<path d="...."/>
</symbol>
</symbol>
</defs>
<use xlink:href="#complicated"/>
</svg>
Gets rendered as if it was
<svg>
<defs>
<symbol id="complicated">
<symbol id="firstPart">
<path d="...."/>
</symbol>
<symbol id="secondPart">
<path d="...."/>
</symbol>
</symbol>
</defs>
<g> <!-- The <use> element is drawn as if it was a <g> element -->
<svg> <!-- The <symbol> element is drawn as if it was a nested <svg> -->
<symbol id="firstPart"> <!-- the internal symbol is copied as-is -->
<path d="...."/>
</symbol>
<symbol id="secondPart"> <!-- but symbols aren't drawn directly! -->
<path d="...."/>
</symbol>
<svg>
</g>
</svg>
...and that shouldn't really be rendered as anything at all. This, I wouldn't call a Firefox "bug", just a literal interpretation of the specs, which don't give any special treatment to nested symbol elements.
There is a way to get symbols to nest, however, and that's to use <use> elements within the symbol definition:
<svg>
<defs>
<symbol id="firstPart">
<path d="...."/>
</symbol>
<symbol id="secondPart">
<path d="...."/>
</symbol>
<symbol id="complicated">
<use xlink:href="#firstPart"/>
<use xlink:href="#secondPart"/>
</symbol>
</defs>
<use xlink:href="#complicated"/>
</svg>
Here's your fiddle updated with that structure: http://jsfiddle.net/vGNMu/6/

Half of your stroke is outside the viewBox. You can avoid clipping by using:
svg, symbol {
overflow: visible;
}
Or adding an overflow="visible" attribute to each symbol.
Your updated JSFiddle

Related

SVG clipPath - clipped area offset and size problem

Please see the yellow rectangle, this is exactly the same as the rectangle clipping the image.
However image is clipped smaller and moved right.
The image is just one element, but imagine few more elements which all need to be clipped to a shape of a yellow rectangle.
I know I can fix this by wrapping the <image> element (and any more elements) inside a <g> element and applying the clipPath to this <g> element.
Is it possible to fix this issue by modifying just the clipPath part, without touching the rest of the svg structure?
<defs>
<clipPath id="clipPath">
<path d="M150-750 L150,750 L-150,750 L-150,-750Z" transform="matrix(1,0,0,1,152.5,770.5)"></path>
</clipPath>
</defs>
<path fill="#ffff00" d="M150-750 L150,750 L-150,750 L-150,-750Z" transform="matrix(1,0,0,1,152.5,770.5)"></path>
<image x="-1632" y="-1224" width="3264" height="2448" preserveAspectRatio="none"
xlink:href="https://cdn.pixabay.com/photo/2017/07/25/01/22/cat-2536662_960_720.jpg"
transform="matrix(0.3529,0,0,0.3529,246.2554,998.5607)"
style="clip-path: url(#clipPath);"></image>
Please see the jsfiddle here.
Everything happens because you transform the path and the image with a different value. I've removed the transforms and changed the viewBox value so that the clipping path falls inside the svg canvas.
Also I've removed the height of the svg element because I wanted to keep the same aspect ratio as the viewBox.
Please take a look. Let me know if this is what you need.
<svg width="305" style="overflow: hidden; position: relative;"
viewBox="-200 -800 750 3964" preserveAspectRatio="xMinYMin">
<defs>
<clipPath id="clipPath">
<path id="test" d="M150-750 L150,750 L-150,750 L-150,-750Z" ></path>
</clipPath>
</defs>
<use xlink:href="#test" fill="#ffff00" ></use>
<image x="-1632" y="-1224" width="3264" height="2448" preserveAspectRatio="none"
xlink:href="https://cdn.pixabay.com/photo/2017/07/25/01/22/cat-2536662_960_720.jpg"
style="clip-path: url(#clipPath);"></image>
</svg>

SVG | Erase part of another path

I'm working on an SVG image and I can't figure out how to erase a certain part of a path.
This is the current situation: https://gyazo.com/db59fcaf9f122e7e2c0bba5833db9ec5
There are two green letters which overlap and a red bar which does basically represent the area I want to erase so the letters don't stick directly on each other. It works fine when I have a set background colour since I can then easily overwrite lower paths, but with transparent background, this method no longer works, since it appears to make the path transparent, not the entire pixel itself.
TL;DR: How do I make a path actually render the pixel transparent, not just the path element?
You can mask the J with a white rect and a black N with an extra stroke. Next you use again the N. Please play with the stroke width of the mask <use>
svg{border:1px solid; width:90vh}
text{font-family:arial;dominant-baseline:middle}
<svg viewBox="0 0 24 24">
<defs>
<text id="n" x="7" y="14" >N</text>
<mask id="mascara">
<rect width="24" height="24" fill="white" />
<use xlink:href="#n" fill="black" stroke="black" />
</mask>
</defs>
<text x="5" y="10" style="mask: url(#mascara)">J</text>
<use xlink:href="#n" fill="black" />
</svg>

How to automatically create the minimal size of the viewbox which fits for the complete content?

I have a simple or complex SVG graphic. For example a rotated rectangle.
Without calculating you cannot know the minimal size of the viewbox, where the graphic fits into completely.
<svg viewBox="0 0 30 30">
<rect x="20" y="0" width="100" height="20" transform="rotate(45)" fill="black" />
</svg>
The result is, that the graphic does not fit into the viewbox.
Is there any method, how to get an the minimal size of the viewbox, where the graphic is shown completely?
Ideally I do not want to declare a size/ratio of a viewbox. I just want that the minimal size is a result of the content of the SVG graphics.
Is there any disadvantage, when I do not declare the viewBox attribute at all?
Thanks for your help.
One way to do it is wrapping the transformed rectangle in a <g> element and then get the value of the bounding box for theG. Next you use the values of the bounding box (BB) to reset the viewBox of theSVG. I hope it helps.
// the bounding box for the wrapping g
let BB = theG.getBBox();
theSVG.setAttributeNS(null, "viewBox", `${BB.x} ${BB.y} ${BB.width} ${BB.height}`)
svg{border:1px solid}
<svg id="theSVG" viewBox="0 0 30 30" width="300">
<g id="theG">
<rect x="20" y="0" width="100" height="20" transform="rotate(45)" fill="black" />
</g>
</svg>

Coordinate Origin for SVG Symbol

I would like to move a particular composite shape to a <Symbol> definition, and then re-use it. This helps make the SVG code neater, and provides some global control over the actual structure of that shape. The shape is symmetrical about a given mid-point; however, the x/y coordinates of subsequent <Use> statements need to reference the top-left corner rather than its natural original, and this means that all usage of the shape must be aware of its total size. Is there a way to position a Symbol by some origin other than its top-left corner?
Contrived example, purely to explain this better. The following concentric-circle Symbol has a natural origin. However, the subsequent Use statement has to offset its x/y coordinates by half the Symbol size in order to position it correctly (at 20,20 in this example). Ideally, the usage of the symbol should not have to know this information.
<defs>
<symbol id="ex">
<circle fill="green" cx="8" cy="8" r="8"/>
<circle fill="white" cx="8" cy="8" r="6"/>
<circle fill="green" cx="8" cy="8" r="4"/>
</symbol>
</defs>
<use xlink:href="#ex" x="12" y="12">
The downside to a symbol is that it cuts of rendering at the border of a viewport. (which is also an upside, since you can define a viewBox.) But you can avoid using it at all. Everything in a <defs> element is not rendered directly, so you can exchange the <symbol> for a <g> and center everything on the origin:
<defs>
<g id="ex">
<circle fill="green" cx="0" cy="0" r="8"/>
<circle fill="white" cx="0" cy="0" r="6"/>
<circle fill="green" cx="0" cy="0" r="4"/>
</g>
</defs>
<use xlink:href="#ex" x="12" y="12">
http://jsfiddle.net/nuzwn07n/
If I read your question correctly, you want to be able to say
<use xlink:href="#ex" x="20" y="20">
..and then the circle symbol is positioned with its center at [20,20]. The solution I propose requires you to know the size of the symbol, but only once (in the <symbol> declaration), and not on every <use> element.
1: In the <symbol>, put everything in a group which you translate so the center of the graphics lies on the top-left corner.
<symbol id="ex">
<g transform="translate(-8,-8)">
<circle ...
</g>
</symbol>
2: If you now <use> that symbol, you'll only see the quarter circle that's still within the symbol's "viewport". To display the whole circle, simply apply overflow="visible" to the <symbol>.
<symbol id="ex" overflow="visible">
<g transform="translate(-8,-8)">
<circle ...
</g>
</symbol>
http://jsfiddle.net/0ghucsrp/

Improve SVG so pin is centered inside circle, without multiple viewboxes

I have a pin that needs to be shown inside a circle in Svg.
My current code is the following:
<svg viewBox="0 0 20 20" preserveAspectRatio="xMinYMin meet">
<circle cx="50%" cy="1.5" r="1.5" style="fill: green;"></circle>
<svg x="47.5%" y="5%" viewBox="0 0 10000 10000" fill="#fff" preserveAspectRatio="none">
<g>
<path d="M250,124.3c-35,0-63.4,28.8-63.4,64.1c0,35.3,28.5,64,63.4,64s63.4-28.8,63.4-64.1C313.4,153,285,124.3,250,124.3z
M250,222c-18.3,0-33.2-15.1-33.2-33.7s14.9-33.7,33.2-33.7s33.2,15.1,33.2,33.7S268.3,222,250,222z">
</path>
<path d="M250,50.9c-74.9,0-135.8,61.6-135.8,137.4c0,31.3,22.5,84.4,66.9,157.7c32.9,54.4,66.2,100.3,66.6,100.7l2.4,3.3l2.4-3.3
c0.3-0.5,33.7-46.3,66.6-100.7c44.4-73.3,66.9-126.4,66.9-157.7C385.8,112.5,324.9,50.9,250,50.9z M250,397.6
c-16.5-24.3-45.5-68.4-69.9-114c-23.5-44-35.9-77-35.9-95.4c0-59,47.4-107,105.8-107s105.8,48,105.8,107
c0,18.4-12.4,51.4-35.9,95.4C295.4,329.3,266.5,373.4,250,397.6z">
</path>
</g>
</svg>
</svg>
which works somewhat but seems inelegant, and perhaps also buggy. What I would like is a better way to center the group 'inside' the circle without using JavaScript
It would be nice if I could get rid of the extra SVG element in the middle with its really big viewBox that I'm using to place the pin. So if you can show me how to do it with just a g and make a scaling function that would be good.
If you want to use coordinates that contain percentage values, you need an element that has x and y attributes. <use> is such an element, <g> is not.
Your live will be easier if you draw your pin centered on the origin of the coordinate system: translate(-250 -230).
After that, you can easily scale it to the size you need: scale(0.0025) (remember: multiple transform commands are processed right-to-left.)
Finally, you use the pin template with the same x and y coordinates as your circle.
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"
viewBox="0 0 20 20" preserveAspectRatio="xMinYMin meet">
<defs>
<!--center the pin around the origin and scale it to final size-->
<g id="pin" transform="scale(0.0025) translate(-250 -230)">
<path d="M250,124.3c-35,0-63.4,28.8-63.4,64.1c0,35.3,28.5,64,63.4,64s63.4-28.8,63.4-64.1C313.4,153,285,124.3,250,124.3z
M250,222c-18.3,0-33.2-15.1-33.2-33.7s14.9-33.7,33.2-33.7s33.2,15.1,33.2,33.7S268.3,222,250,222z" />
<path d="M250,50.9c-74.9,0-135.8,61.6-135.8,137.4c0,31.3,22.5,84.4,66.9,157.7c32.9,54.4,66.2,100.3,66.6,100.7l2.4,3.3l2.4-3.3
c0.3-0.5,33.7-46.3,66.6-100.7c44.4-73.3,66.9-126.4,66.9-157.7C385.8,112.5,324.9,50.9,250,50.9z M250,397.6
c-16.5-24.3-45.5-68.4-69.9-114c-23.5-44-35.9-77-35.9-95.4c0-59,47.4-107,105.8-107s105.8,48,105.8,107
c0,18.4-12.4,51.4-35.9,95.4C295.4,329.3,266.5,373.4,250,397.6z" />
</g>
</defs>
<!--use the same coordinates for the center of the circle and the pin-->
<circle cx="50%" cy="1.5" r="1.5" fill="green" />
<use xlink:href="#pin" x="50%" y="1.5" fill="white" />
</svg>

Resources