SVG | Erase part of another path - svg

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>

Related

Reversing the flip and rotation of an image fill in a path that is flipped and rotated

I am needing some help understanding how to "unflip/unrotate" and image fill in SVG for a path. When I fill a path with an image and then rotate and fill the path with an image, the image also flips and rotates. But I'd like to keep the image upright and non-flipped, regardless of the rotation and flipping. The size of the picture is the bounding box of the rotated shape.
So, for example, say I have this path and this picture:
If the path is only rotated (in this case, 315 degrees), it's easy to unrotate the image by just reversing the angle in the pattern that is used for a fill (i.e. 45 degrees).
<svg name="rotate only" x="0" y="0" width="100.08" height="200" overflow="visible" fill="url(#fillImages0sp15)" stroke="#4472C4" stroke-miterlimit="8" stroke-width="2.25">
<defs>
<image id="bgImage" preserveAspectRatio="none" width="159.113" height="159.113" xlink:href="THE IMAGE URL"></image>
<pattern id="fillImages0sp15" x="-38.362" y="11.598" width="159.113" height="159.113" patternTransform="rotate(45,50.04,100)" patternUnits="userSpaceOnUse">
<use xlink:href="#bgImages0sp15"></use>
</pattern>
</defs>
<path d="M0,149.96 25.02,149.96 25.02,0 75.06,0 75.06,149.96 100.08,149.96 50.04,200 Z " transform="rotate(315,50.04,100)"></path>
</svg>
But if there any kind of flip on the path (horizontal, vertical, or both), it doesn't work by just reversing the transformation on the pattern used for the image fill. For example, if the image is rotated 315 degrees and flipped vertical, the path has transform="rotate(45,50.04,100) translate(0,200), scale(1,-1)" for flipping vertically. That works. But the image fill needs to get reset back to be upright and not flipped. So the patternTransform should just be the same transformation. But this isn't working. This is the result I get.
<svg name="flipV" x="0" y="0" width="100.08" height="200" overflow="visible" fill="url(#fillImages0sp14)" stroke="#4472C4" stroke-miterlimit="8" stroke-width="2.25">
<defs>
<image id="bgImages" preserveAspectRatio="none" width="159.113" height="159.113" xlink:href="THE IMAGE URL"></image>
<pattern id="fillImages0sp14" x="-20.671" y="-370.711" width="159.113" height="159.113" patternTransform="rotate(45,50.04,100) translate(0,200) scale(1,-1)" patternUnits="userSpaceOnUse">
<use xlink:href="#bgImages"></use>
</pattern>
</defs>
<path d="M0,149.96 25.02,149.96 25.02,0 75.06,0 75.06,149.96 100.08,149.96 50.04,200 Z " transform="rotate(45,50.04,100) translate(0,200) scale(1,-1)"></path>
</svg>
Notice the path has transform="rotate(45,50.04,100) translate(0,200) scale(1,-1) and the fill pattern with the image has patternTransform="rotate(45,50.04,100) translate(0,200) scale(1,-1). This produces the wrong results.
In fact, here's all of it. This is what I'm hoping to achieve:
Does anyone know how to set the patternTransform so that it can "unflip/unrotate" the filled image? Is it that the translate in the patternTransform needs to be calculated differently?
Rather than filling the arrow, <svg ... fill="url(#fillImages0sp14)", transforming it and then trying to somehow separate it from its fill, untransform that, and then refill it, I'd just display the image, but masked by the transformed arrow.
I don't understand why the orange border still shows up. I've made the black rectangle overly large (which helped), and I've changed the overflow="...", but neither made it disappear.
Edit: Your global stroke attributes were messing up the mask. Moving them to the displayed arrow (the only thing using that stroke) fixed the orange border issue.
P.S. xlink is deprecated. Just use href.
P.P.S. I had to add a final translate to center the transformed arrow over the image. It's easier and more accurate to move the center of the arrow to the center of the image first, and then do your transformations about that center.
<svg name="transformed" x="0" y="0" width="159.113" height="159.113" overflow="visible" xmlns="http://www.w3.org/2000/svg">
<defs>
<path id="Arrow" d="M0,149.96 25.02,149.96 25.02,0 75.06,0 75.06,149.96 100.08,149.96 50.04,200 Z" />
<use id="TransformedArrow" href="#Arrow" transform="translate(38.3625,-29.2893) rotate(45,50.04,100) translate(0,200) scale(1,-1)" />
<mask id="ArrowMask">
<!-- Everything under black will be invisible -->
<rect x="0" y="0" width="100%" height="100%" fill="black" />
<!-- Everything under white will be visible -->
<use href="#TransformedArrow" fill="white" />
</mask>
</defs>
<image width="159.113" height="159.113" mask="url(#ArrowMask)" href="https://i.stack.imgur.com/yDcGi.png" />
<use href="#TransformedArrow" fill="none" stroke="#4472C4" stroke-miterlimit="8" stroke-width="2.25" />
</svg>

Is there a way for SVG strokes to "skip" intersecting each other, the same sort of way that CSS has `text-decoration-skip-ink`?

I could probably manually fake it using a solid-edged drop shadow filter around the strokes, set to the background color, but that's neither resilient nor ideal.
Visually, instead of this:
I want to have this (if the circle is on top):
A posible solution would be creating a mask with a white rectangle and a black stroked <use> element that is using the circle.
Please note that the white rectangle is covering all the svg element and the stroke-width of the <use> element is wider than the stroke of the circle.
This way you create a hole in the rect that is letting you to see whatever you have in the background.
<svg fill="none" stroke="black" stroke-width="3">
<mask id="m">
<rect width="100%" height="100%" fill="white" />
<use xlink:href="#c" stroke-width="10" />
</mask>
<rect x="10" y="5" width="70" height="70" mask="url(#m)" />
<circle id="c" cx="80" cy="75" r="40" />
</svg>

Adding feDropShadow to a vertical line in SVG makes it disappear

I have the following SVG document:
<svg preserveAspectRatio="xMinYMin meet" viewBox="0 0 21 484" xmlns="http://www.w3.org/2000/svg">
<defs>
<filter id="dropShadow">
<feDropShadow dx="4" dy="0" stdDeviation="4"></feDropShadow>
</filter>
</defs>
<g id="Artboard" stroke-width="5" stroke="#FF0000" fill="#000000" stroke-linecap="round">
<path style="filter: url(#dropShadow)" d="M7.5,8.5 L7.5,471.5" id="path-1"></path>
</g>
</svg>
In Firefox, when I open the SVG document, it simply shows a very thin (not 5 wide) vertical line. In Chrome, it doesn't show anything (nor does it in codepen, here: https://codepen.io/jwir3/pen/BJBqEK ).
I'm not quite sure what I'm doing incorrectly here, but it has something to do with the filter, because, if I remove the filter: url(#dropShadow) from the path definition, the line shows up as expected.
You can't use objectBoundingBox units if your shape has no height or width.
Keyword objectBoundingBox should not be used when the geometry of the applicable element has no width or no height, such as the case of a horizontal or vertical line, even when the line has actual thickness when viewed due to having a non-zero stroke width since stroke width is ignored for bounding box calculations. When the geometry of the applicable element has no width or height and objectBoundingBox is specified, then the given effect (e.g., a gradient or a filter) will be ignored.
The default for filterUnits is objectBoundingBox units so you need to change that to userSpaceOnUse i.e.
<svg preserveAspectRatio="xMinYMin meet" viewBox="0 0 21 484" xmlns="http://www.w3.org/2000/svg">
<title>Line Drop Shadow</title>
<description>A red line with 5px width thickness and round caps, having a drop-shadow. This highlights the regression documented in PURP-1017.</description>
<defs>
<filter id="dropShadow" filterUnits="userSpaceOnUse">
<feDropShadow dx="4" dy="0" stdDeviation="4"></feDropShadow>
</filter>
</defs>
<g id="Artboard" stroke-width="5" stroke="#FF0000" fill="#000000" stroke-linecap="round">
<path style="filter: url(#dropShadow)" d="M7.5,8.5 L7.5,471.5" id="path-1"></path>
</g>
</svg>
When processing filters, different browsers process in different stroke.
Chrome considers stroke as a value with a zero pixel, so it does not include it in the filter region.
Therefore, to make the result look the same in different browsers, it is better to replace path with stroke-width ="5", a rectangle with a width of 5px withoutstroke (stroke="none")
In addition, the default values for the filter area are: x =" - 10% "" y = "- 10%" `` width = "120%" `` height = "120%"- large blur sizes are usually truncated .
By default, filterUnits = "objectBoundingBox" and therefore the values are specified in percentages.
To make it easier to calculate the size of the filter region action, specify the value offilterUnits = "userSpaceOnUse" and then you can specify all dimensions for thefilter region` in pixels.
<svg preserveAspectRatio="xMinYMin meet" width="100%" height="100%" viewBox="0 0 21 484" xmlns="http://www.w3.org/2000/svg" >
<defs>
<filter id="dropShadow" filterUnits = "userSpaceOnUse" x="4" y="0" width="12" height="472">
<feDropShadow dx="6" dy="4" stdDeviation="3"></feDropShadow>
</filter>
</defs>
<g id="Artboard" fill="#FF0000" filter="url(#dropShadow)" >
<!-- <path style="filter: url(#dropShadow)" d="M7.5,8.5 L7.5,471.5" id="path-1" stroke-width="5" ></path>-->
<rect x="5" y="5" width="5" stroke="none" height="463" />
</g>
</svg>
Swapping to userSpaceOnUse is the correct answer in most circumstances but has the following limitations:
The filter effects region will apply from -10% to 120% of the canvas, rather than the bounding box of the element (using more memory and processing time)
For large dynamic SVGs (such as created by d3) it can be hard to calculate the required filter x/y/width/height to ensure the filter applies to all elements.
An alternate (less elegant) solution is to apply the filter to a <g> and use a hidden node within this to give the group the correct width or height:
<svg xmlns="http://www.w3.org/2000/svg">
<defs>
<filter id="dropShadow" width="20">
<feDropShadow dx="4" dy="0" stdDeviation="4"></feDropShadow>
</filter>
</defs>
<g id="Artboard" style="filter: url(#dropShadow)">
<circle r="5" cx="0" cy="0" visibility="hidden"></circle>
<path d="M10,10 L10,100" stroke-width="5" stroke="#FF0000" fill="#000000" stroke-linecap="round"></path>
</g>
</svg>

svg draw circle with curved text inside

I need to draw red cirle with two curved string inside like that:
upper string always be 3 chars length
lower string can be from 1 to 20 chars length
UPDATE1:
I try to use textpath and circle tags, but I think I need to change some coordinates:
<svg xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink">
<circle cx="40" cy="40" r="24" style="stroke:#006600; fill:none"/>
<defs>
<path id="myTextPath"
d="M75,20
a1,1 0 0,0 150,0"
/>
</defs>
<text x="5" y="50" style="stroke: #000000;">
<textPath xlink:href="#myTextPath" >
string
</textPath>
</text>
</svg>
Also I didnt clear understand <path> 'd' atrribute , but I found out that I can change starting point to M10,20 but how I can change text curve orientation?
d="M10,20 a1,1 0 0,0 150,0"
To have text that "hangs" from a line nicely, the best way right now is to use a path with a smaller radius. There is an attribute to adjust the text's baseline, but that doesn't work reliably.
So you need two arcs. One for the bottom half of the circle, and one with a smaller radius for the top half. They also need to both start from the left. That means one will go clockwise, and the other will go anti-clockwise. You control that with the arc command's "sweep" flag.
We need to also use startOffset="50%" and text-anchor="middle" to centre the text on the paths.
<svg xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
viewBox="0 0 80 80">
<defs>
<path id="tophalf" d="M26,40 a14,14 0 0,1 28,0" />
<path id="lowerhalf" d="M16,40 a24,24 0 0,0 48,0" />
</defs>
<circle cx="40" cy="40" r="24" style="stroke:#006600; fill:none"/>
<path d="M16,40 a24,24 0 0,0 48,0" style="stroke:#600; fill:none"/>
<text x="5" y="50" style="stroke: #000000;"
text-anchor="middle">
<textPath xlink:href="#tophalf" startOffset="50%">str</textPath>
</text>
<text x="5" y="50" style="stroke: #000000;"
text-anchor="middle">
<textPath xlink:href="#lowerhalf" startOffset="50%">second st</textPath>
</text>
</svg>
This works fine in FF, but unfortunately, it seems there are bugs in Chrome and IE right now that is causing the text to not be centred properly on those browsers.

How I can get text with desired height and width?

I need to get text with desired height and width.
I tried to find something in documentation of svg but found only font-size and also I tried to use scale in such manner:
<text xmlns="http://www.w3.org/2000/svg" id="10996080909940" name="-1"
x="1782.9351809218" y="-751.796133712862" width="1" height="1" style="font:Arial;text-
anchor:start;stroke:#000000" transform="rotate(0) scale(2 2)"> SOME TEXT </text>
But I get too big size of text and in place not where I need.
If you mean you want the text to exactly fill an arbitrary width and height, then there isn't really an easy way to do it in SVG. You can't specifiy a width and height on the <text> element. At least not in the current SVG spec (1.1).
However there are several ways to achieve this effect with a bit of trickery.
One way is by using a transform, as you suggested:
<svg>
<text font-size="10px" font-family="Verdana" transform="translate(99,400) scale(3.5,13.7)">SQUASHED TEXT</text>
<rect x="100" y="300" width="300" height="100" fill="none" stroke="red" />
</svg>
A second way is by using an inner <svg> element and setting the viewBox to match the bounds of the text. You then set preserveAspectRatio="none".
<svg>
<svg x="100" y="100" width="300" height="100" viewBox="0.2 -7.3 86 7.3" preserveAspectRatio="none" overflow="visible">
<text font-size="10px" font-family="Verdana">SQUASHED TEXT</text>
</svg>
<rect x="100" y="100" width="300" height="100" fill="none" stroke="red" />
</svg>
This way is more verbose, but it has the advantage that once you have found the correct viewBox for a piece of text, you can make it fit any sized rectangle very easily. Just set the x,y,width and height of the inner <svg> to the size of the rectangle.
Demo here: http://jsfiddle.net/ZRgEF/3/

Resources