Related
I try to generate SVGs that are composed of filters and I would like to have exactly the same rendering when the same image is resized (with ratio kept).
I use two filters: feTurbulence and feDisplacementMap
My problem is that I can't get the same rendering when the same image is resized.
I was hoping that it would be enough to change the values of the attributes of the effects proportionally but it doesn't work.
In this case, if I change the "scale" and "baseFrequency" values proportionally it doesn't work.
How can I calculate the values of "scale" and "baseFrequency" to get exactly the same rendering whatever the size of the image?
IMAGE 1 - 300x132px
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="300" height="132">
<defs>
<filter id="filter1" x="0%" y="0%" width="100%" height="100%" filterUnits="userSpaceOnUse">
<feTurbulence baseFrequency="0.05" result="NOISE" type="turbulence" />
<feDisplacementMap in="SourceGraphic" in2="NOISE" result="RESULT" scale="10" xChannelSelector="R" yChannelSelector="R"></feDisplacementMap>
</filter>
</defs>
<image x="0" y="0" width="300" height="132" href="https://i.stack.imgur.com/osUI3.png" filter="url(#filter1)"></image>
</svg>
RESULT 1:
IMAGE 2 - 600x264px (x2)
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="600" height="264">
<defs>
<filter id="filter2" x="0%" y="0%" width="100%" height="100%" filterUnits="userSpaceOnUse">
<feTurbulence baseFrequency="0.05" result="NOISE" type="turbulence" />
<feDisplacementMap in="SourceGraphic" in2="NOISE" result="RESULT" scale="10" xChannelSelector="R" yChannelSelector="R"></feDisplacementMap>
</filter>
</defs>
<image x="0" y="0" width="600" height="264" href="https://i.stack.imgur.com/MNepH.png" filter="url(#filter2)"></image>
</svg>
RESULT 2:
We can see that the effect is different on RESULT 1 and RESULT 2.
The example is on this page: https://jsfiddle.net/qbvp8x1r/
If you use the same viewBox for both SVGs you get the same result. The viewBox defines the "inner coordinate system" for the image.
In this example I moved the filter to a separate SVG to show that it can be reused. To make the image fit in the SVG I set the width to 100%. So, everything is relative except the viewBox.
<svg width="0" height="0" xmlns="http://www.w3.org/2000/svg">
<defs>
<filter id="filter1" x="0%" y="0%" width="100%" height="100%" filterUnits="userSpaceOnUse">
<feTurbulence baseFrequency="0.05" result="NOISE" type="turbulence" />
<feDisplacementMap in="SourceGraphic" in2="NOISE" result="RESULT" scale="10" xChannelSelector="R" yChannelSelector="R"></feDisplacementMap>
</filter>
</defs>
</svg>
<svg xmlns="http://www.w3.org/2000/svg" width="300"
viewBox="0 0 300 132">
<image width="100%" href="https://i.stack.imgur.com/osUI3.png"
filter="url(#filter1)" />
</svg>
<svg xmlns="http://www.w3.org/2000/svg" width="600"
viewBox="0 0 300 132">
<image width="100%" href="https://i.stack.imgur.com/SS7N6.png"
filter="url(#filter1)" />
</svg>
I want to color the background of svg text similar to background-color in css
I was only able to find documentation on fill, which colors the text itself
Is it even possible?
You could use a filter to generate the background.
<svg width="100%" height="100%">
<defs>
<filter x="0" y="0" width="1" height="1" id="solid">
<feFlood flood-color="yellow" result="bg" />
<feMerge>
<feMergeNode in="bg"/>
<feMergeNode in="SourceGraphic"/>
</feMerge>
</filter>
</defs>
<text filter="url(#solid)" x="20" y="50" font-size="50">solid background</text>
</svg>
No this is not possible, SVG elements do not have background-... presentation attributes.
To simulate this effect you could draw a rectangle behind the text attribute with fill="green" or something similar (filters). Using JavaScript you could do the following:
var ctx = document.getElementById("the-svg"),
textElm = ctx.getElementById("the-text"),
SVGRect = textElm.getBBox();
var rect = document.createElementNS("http://www.w3.org/2000/svg", "rect");
rect.setAttribute("x", SVGRect.x);
rect.setAttribute("y", SVGRect.y);
rect.setAttribute("width", SVGRect.width);
rect.setAttribute("height", SVGRect.height);
rect.setAttribute("fill", "yellow");
ctx.insertBefore(rect, textElm);
The solution I have used is:
<svg>
<line x1="100" y1="100" x2="500" y2="100" style="stroke:black; stroke-width: 2"/>
<text x="150" y="105" style="stroke:white; stroke-width:0.6em">Hello World!</text>
<text x="150" y="105" style="fill:black">Hello World!</text>
</svg>
A duplicate text item is being placed, with stroke and stroke-width attributes. The stroke should match the background colour, and the stroke-width should be just big enough to create a "splodge" on which to write the actual text.
A bit of a hack and there are potential issues, but works for me!
Instead of using a <text> tag, the <foreignObject> tag can be used, which allows for XHTML content with CSS.
No, you can not add background color to SVG elements. You can do it programmatically with d3.
var text = d3.select("text");
var bbox = text.node().getBBox();
var padding = 2;
var rect = self.svg.insert("rect", "text")
.attr("x", bbox.x - padding)
.attr("y", bbox.y - padding)
.attr("width", bbox.width + (padding*2))
.attr("height", bbox.height + (padding*2))
.style("fill", "red");
Answer by Robert Longson (#RobertLongson) with modifications:
<svg width="100%" height="100%">
<defs>
<filter x="0" y="0" width="1" height="1" id="solid">
<feFlood flood-color="yellow"/>
<feComposite in="SourceGraphic" operator="xor"/>
</filter>
</defs>
<text filter="url(#solid)" x="20" y="50" font-size="50"> solid background </text>
<text x="20" y="50" font-size="50">solid background</text>
</svg>
and we have no bluring and no heavy "getBBox" :)
Padding is provided by white spaces in text-element with filter.
It's worked for me
Going further with #dbarton_uk answer, to avoid duplicating text you can use paint-order=stroke style:
<svg>
<line x1="100" y1="100" x2="350" y2="100" style="stroke:grey; stroke-width: 100"/>
<text x="150" y="105" style="stroke:white; stroke-width:0.5em; fill:black; paint-order:stroke; stroke-linejoin:round">Hello World!</text>
</svg>
Note the stroke-linejoin:round which is needed to avoid seeing spikes for the W sharp angle.
You can combine filter with the text.
<!DOCTYPE html>
<html>
<head>
<meta charset=utf-8 />
<title>SVG colored patterns via mask</title>
</head>
<body>
<svg viewBox="0 0 300 300" xmlns="http://www.w3.org/2000/svg">
<defs>
<filter x="0" y="0" width="1" height="1" id="bg-text">
<feFlood flood-color="white"/>
<feComposite in="SourceGraphic" operator="xor" />
</filter>
</defs>
<!-- something has already existed -->
<rect fill="red" x="150" y="20" width="100" height="50" />
<circle cx="50" cy="50" r="50" fill="blue"/>
<!-- Text render here -->
<text filter="url(#bg-text)" fill="black" x="20" y="50" font-size="30">text with color</text>
<text fill="black" x="20" y="50" font-size="30">text with color</text>
</svg>
</body>
</html>
this is my favorite hack (not sure it should work). It refer an element that is not yet displayed, and it works pretty well
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 620 40" preserveAspectRatio="xMidYMid meet">
<defs>
<filter x="-0.02" y="0" width="1.04" height="1.1" id="removebackground">
<feFlood flood-color="#00ffff"/>
</filter>
</defs>
<!--Draw the text-->
<use xlink:href="#mygroup" filter="url(#removebackground)" />
<g id="mygroup">
<text id="text1" x="9" y="20" style="text-anchor:start;font-size:14px;">custom text with background</text>
<line x1="200" y1="18" x2="200" y2="36" stroke="#000" stroke-width="5"/>
<line x1="120" y1="27" x2="203" y2="27" stroke="#000" stroke-width="5"/>
</g>
</svg>
For those wondering how to apply padding to a text element when it has a background like in the Robert's answer, do the following:
<svg>
<defs>
<filter x="-0.1" y="-0.1" width="1.2" height="1.2" id="solid">
<feFlood flood-color="#171717"/>
<feComposite in="SourceGraphic" operator="xor" />
</filter>
</defs>
<text filter="url(#solid)" x="20" y="50" font-size="50">Hello</text>
</svg>
In the example above, filter's x and y positions can be used as transform: translate(-10%, -10%) would, and width and height values can be read as 120% and 120%. So we made background 20% bigger, and offsetted it -10%, so background is now 10% bigger on each side of the text.
The previous answers relied on doubling up text and lacked sufficient whitespace.
By using atop and I was able to get the results I wanted.
This example also includes arrows, a common use case for SVG text labels:
<svg viewBox="-105 -40 210 234">
<title>Size Guide</title>
<defs>
<filter x="0" y="0" width="1" height="1" id="solid">
<feFlood flood-color="white"></feFlood>
<feComposite in="SourceGraphic" operator="atop"></feComposite>
</filter>
<marker id="arrow" viewBox="0 0 10 10" refX="5" refY="5" markerWidth="6" markerHeight="6" orient="auto-start-reverse">
<path d="M 0 0 L 10 5 L 0 10 z"></path>
</marker>
</defs>
<g id="garment">
<path id="right-body" fill="none" stroke="black" stroke-width="1" stroke-linejoin="round" d="M0 0 l30 0 l0 154 l-30 0"></path>
<path id="right-sleeve" d="M30 0 l35 0 l0 120 l-35 0" fill="none" stroke-linejoin="round" stroke="black" stroke-width="1"></path>
<use id="left-body" href="#right-body" transform="scale(-1,1)"></use>
<use id="left-sleeve" href="#right-sleeve" transform="scale(-1,1)"></use>
<path id="collar-right-top" fill="none" stroke="black" stroke-width="1" stroke-linejoin="round" d="M0 -6.5 l11.75 0 l6.5 6.5"></path>
<use id="collar-left-top" href="#collar-right-top" transform="scale(-1,1)"></use>
<path id="collar-left" fill="white" stroke="black" stroke-width="1" stroke-linejoin="round" d="M-11.75 -6.5 l-6.5 6.5 l30 77 l6.5 -6.5 Z"></path>
<path id="front-right" fill="white" stroke="black" stroke-width="1" d="M18.25 0 L30 0 l0 154 l-41.75 0 l0 -77 Z"></path>
<line x1="0" y1="0" x2="0" y2="154" stroke="black" stroke-width="1" stroke-dasharray="1 3"></line>
<use id="collar-right" href="#collar-left" transform="scale(-1,1)"></use>
</g>
<g id="dimension-labels">
<g id="dimension-sleeve-length">
<line marker-start="url(#arrow)" marker-end="url(#arrow)" x1="85" y1="0" x2="85" y2="120" stroke="black" stroke-width="1"></line>
<text font-size="10" filter="url(#solid)" fill="black" x="85" y="60" class="dimension" text-anchor="middle" dominant-baseline="middle"> 120 cm</text>
</g>
<g id="dimension-length">
<line marker-start="url(#arrow)" marker-end="url(#arrow)" x1="-85" y1="0" x2="-85" y2="154" stroke="black" stroke-width="1"></line>
<text font-size="10" filter="url(#solid)" fill="black" x="-85" y="77" text-anchor="middle" dominant-baseline="middle" class="dimension"> 154 cm</text>
</g>
<g id="dimension-sleeve-to-sleeve">
<line marker-start="url(#arrow)" marker-end="url(#arrow)" x1="-65" y1="-20" x2="65" y2="-20" stroke="black" stroke-width="1"></line>
<text font-size="10" filter="url(#solid)" fill="black" x="0" y="-20" text-anchor="middle" dominant-baseline="middle" class="dimension"> 130 cm </text>
</g>
<g title="Back Width" id="dimension-back-width">
<line marker-start="url(#arrow)" marker-end="url(#arrow)" x1="-30" y1="174" x2="30" y2="174" stroke="black" stroke-width="1"></line>
<text font-size="10" filter="url(#solid)" fill="black" x="0" y="174" text-anchor="middle" dominant-baseline="middle" class="dimension"> 60 cm </text>
</g>
</g>
</svg>
An obvious workaround to the problem of the blur produced by the filter effect is to render the <text> two times: once for the background (with transparent characters) and once for the characters (without a background filter).
For me, this was the only way to make the text readable in Safari.
<svg width="100%" height="100%">
<filter x="0" y="0" width="1" height="1" id="solid">
<feFlood flood-color="yellow" />
</filter>
<g transform="translate(20, 50)" font-size="50">
<text aria-hidden="true" fill="none" filter="url(#solid)">solid background</text>
<text fill="blue">solid background</text>
</g>
</svg>
The aria-hidden="true" attribute is there to prevent screen readers from speaking the text twice, if the user uses a screen reader.
You can add style to your text:
style="-webkit-tap-highlight-color: rgba(0, 0, 0, 0);
text-shadow: rgb(255, 255, 255) -2px -2px 0px, rgb(255, 255, 255) -2px 2px 0px,
rgb(255, 255, 255) 2px -2px 0px, rgb(255, 255, 255) 2px 2px 0px;"
White, in this example.
Does not work in IE :)
This is an example SVG image, to which I need to change background colour, from the current white to transparent (or black).
So far I could only change the foreground colour.
<svg xmlns:sketch="http://www.bohemiancoding.com/sketch/ns" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:cc="http://creativecommons.org/ns#" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:svg="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" viewBox="5 5 90 90" version="1.1" x="0px" y="0px" id="svg14" sodipodi:docname="icon_link.svg" inkscape:version="0.92.1 r15371">
<metadata
id="metadata20">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
</cc:Work>
</rdf:RDF>
</metadata>
<defs
id="defs18" />
<sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="787"
inkscape:window-height="620"
id="namedview16"
showgrid="false"
inkscape:zoom="1.888"
inkscape:cx="50"
inkscape:cy="62.5"
inkscape:window-x="262"
inkscape:window-y="18"
inkscape:window-maximized="0"
inkscape:current-layer="svg14" />
<title
id="title2">1</title>
<desc
id="desc4">Created with Sketch.</desc>
<g
stroke="none"
stroke-width="1"
fill="none"
fill-rule="evenodd"
sketch:type="MSPage"
id="g8"
style="fill:#ff0000">
<path
d="M72.6749171,46.3009857 L27.3261728,46.3009857 L27.3261728,53.9297668 L72.6749171,53.9297668 L72.6749171,46.3009857 Z M84.6466542,29 L63.3361992,29 C57.6266014,29 52.9828534,33.6448379 52.9828534,39.3533458 L52.9828534,41.9416822 L60.6116345,41.9416822 L60.6116345,39.3533458 C60.6116345,37.8504759 61.8333293,36.6287811 63.3361992,36.6287811 L84.6466542,36.6287811 C86.1495241,36.6287811 87.3712189,37.8504759 87.3712189,39.3533458 L87.3712189,60.8774067 C87.3712189,62.3802766 86.1495241,63.6019714 84.6466542,63.6019714 L63.3361992,63.6019714 C61.8333293,63.6019714 60.6116345,62.3802766 60.6116345,60.8774067 L60.6116345,58.2890703 L52.9828534,58.2890703 L52.9828534,60.8774067 C52.9828534,66.5859146 57.6266014,71.2307525 63.3361992,71.2307525 L84.6466542,71.2307525 C90.3551621,71.2307525 95,66.5859146 95,60.8774067 L95,39.3533458 C95,33.6448379 90.3551621,29 84.6466542,29 L84.6466542,29 Z M39.3894553,58.2890703 L47.0182364,58.2890703 L47.0182364,60.8774067 C47.0182364,66.5859146 42.3733986,71.2307525 36.6648907,71.2307525 L15.3533458,71.2307525 C9.64483786,71.2307525 5,66.5859146 5,60.8774067 L5,39.3533458 C5,33.6448379 9.64483786,29 15.3533458,29 L36.6648907,29 C42.3733986,29 47.0182364,33.6448379 47.0182364,39.3533458 L47.0182364,41.9416822 L39.3894553,41.9416822 L39.3894553,39.3533458 C39.3894553,37.8504759 38.1677605,36.6287811 36.6648907,36.6287811 L15.3533458,36.6287811 C13.8504759,36.6287811 12.6287811,37.8504759 12.6287811,39.3533458 L12.6287811,60.8774067 C12.6287811,62.3802766 13.8504759,63.6019714 15.3533458,63.6019714 L36.6648907,63.6019714 C38.1677605,63.6019714 39.3894553,62.3802766 39.3894553,60.8774067 L39.3894553,58.2890703 Z"
fill="none"
sketch:type="MSShapeGroup"
id="path6"
style="fill:#ffff00"
/>
</g>
</svg>
tried to modify pagecolor without success.
How can I do it, possibly without using CSS?
Inkscape tends to generate grossly bloated files. Here's a cleaned-up version for you:
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 90 90">
<defs>
<mask id="chain">
<rect x="4" y="28" width="34" height="34" fill="black" stroke="white" stroke-width="7" rx="7" />
<rect x="52" y="28" width="34" height="34" fill="black" stroke="white" stroke-width="7" rx="7" />
<rect x="21" y="39" width="48" height="12" fill="white" stroke="black" stroke-width="4" />
</mask>
</defs>
<!-- Uncomment this for manual background -->
<!--rect fill="#000" width="90" height="90" /-->
<rect fill="#ff0" width="90" height="90" mask="url(#chain)" />
</svg>
You'll notice that not only is this significantly smaller than your file, it's also easier to see what's going on: I'm using two rounded-corner squares, and a rectangle for the link, using a mask to clip out the shape. Then, flood-fill with your colour using the shape as a mask to get the result.
This has no background colour defined. If used as an image file, it will by transparent to reveal any background behind it. Or you can uncomment the indicated line to enforce a background on the icon.
Try this
add style="background-color: #000;" inline to <svg>
Check demo here
How can i create the following widget in SVG?
I'm fine with the shapes itself but i'm struggling with the inset shadow on the back circle.
I've tried a radial gradient, which 'works' but it doesn't look that great and I have to fiddle with the stop values on the order of thousandths of a percent to get it exactly right and it just feels totally hacky.
Is there a better way?
Code to produce the SVG:
<svg width="180" height="180" version="1.1" xmlns="http://www.w3.org/2000/svg">
<circle cx="90" cy="90" r="72" fill="none" stroke="#ddd" stroke-width="18"></circle>
<path class="main-arc" d="M 90 18 A 72 72 0 1 1 85.47908259388944 18.142075553164446" fill="transparent" stroke-width="18" stroke="black" stroke-linecap="round" style="stroke-dasharray: 452.389; stroke-dashoffset: 366.435;">
</path>
</svg>
Well you can do it the easy way with an inset shadow:
<svg width="180" height="180">
<defs>
<filter id="inset-shadow">
<feFlood flood-color="black"/>
<feComposite operator="out" in2="SourceGraphic"/>
<feGaussianBlur stdDeviation="2"/>
<feComposite operator="atop" in2="SourceGraphic"/>
</filter>
</defs>
<circle filter="url(#inset-shadow)" cx="90" cy="90" r="72" fill="none" stroke="#ddd" stroke-width="18"></circle>
<path class="main-arc" d="M 90 18 A 72 72 0 1 1 85.47908259388944 18.142075553164446" fill="transparent" stroke-width="18" stroke="black" stroke-linecap="round" style="stroke-dasharray: 452.389; stroke-dashoffset: 366.435;">
</path>
</svg>
But if you really want a 3D effect, then you'll need a lighting effect:
<svg width="180" height="180" version="1.1" xmlns="http://www.w3.org/2000/svg">
<defs>
<filter id="inset-shadow">
<feFlood flood-color="black"/>
<feComposite operator="xor" in2="SourceGraphic"/>
<feGaussianBlur stdDeviation="1"/>
<feComposite operator="in" in2="SourceGraphic" result="map"/>
<feDiffuseLighting lighting-color="white" surfaceScale="2" diffuseConstant="1">
<feSpotLight x="-30" y="-30" z="230"/>
</feDiffuseLighting>
<feBlend mode="multiply" in="SourceGraphic" />
<feComposite operator="in" in2="SourceGraphic"/>
</filter>
</defs>
<circle filter="url(#inset-shadow)" cx="90" cy="90" r="72" fill="none" stroke="#ddd" stroke-width="18"></circle>
<path class="main-arc" d="M 90 18 A 72 72 0 1 1 85.47908259388944 18.142075553164446" fill="transparent" stroke-width="18" stroke="black" stroke-linecap="round" style="stroke-dasharray: 452.389; stroke-dashoffset: 366.435;">
</path>
</svg>
Draw a pale grey stroked circle on a darker grey background, apply a gaussian blur filter, and clip the results with a clipPath:
<svg width="360" height="360" viewBox="0 0 180 180">
<defs>
<!-- Gaussian blur filter used to soften the shadow edges -->
<filter id="blur" filterUnits="userSpaceOnUse" x="-90" y="-90"
width="180" height="180">
<feGaussianBlur in="SourceGraphic" stdDeviation="1" />
</filter>
<!-- Annular clip path for the progress meter background -->
<clipPath id="ring" clip-rule="evenodd">
<path d="M0-81A81 81 0 0 1 0 81A81 81 0 0 1 0-81z
M0-63A63 63 0 0 1 0 63A63 63 0 0 1 0-63z" />
</clipPath>
</defs>
<!-- Set orgin to centre of drawing -->
<g transform="translate(90,90)">
<!-- Start with pale yellow background -->
<rect x="-90" y="-90" width="180" height="180" fill="#e8e0ce"
stroke="none" />
<!-- Draw the progress ring on top, and clip using the above clip path -->
<g clip-path="url(#ring)">
<!-- Dark grey background -->
<rect x="-85" y="-85" width="170" height="170" fill="#433"
stroke="none" />
<!-- Lighter grey circle with blur filter applied -->
<circle cx="0" cy="2.5" r="72" stroke="#655" stroke-width="18"
stroke="#655" fill="none" filter="url(#blur)"/>
</g>
<!-- Progress bar and text -->
<path class="main-arc" d="M 0 -72 A 72 72 0 1 1 -4.52 -71.86"
style="stroke-dasharray: 452.389; stroke-dashoffset: 366.435;"
fill="transparent" stroke-width="18" stroke="#b65"
stroke-linecap="round" />
<text x="0" y="0" font-size="40" font-family="'Trebuchet MS', sans-serif"
fill="#655" text-anchor="middle" dominant-baseline="central">
20%
</text>
</g>
</svg>
I want to color the background of svg text similar to background-color in css
I was only able to find documentation on fill, which colors the text itself
Is it even possible?
You could use a filter to generate the background.
<svg width="100%" height="100%">
<defs>
<filter x="0" y="0" width="1" height="1" id="solid">
<feFlood flood-color="yellow" result="bg" />
<feMerge>
<feMergeNode in="bg"/>
<feMergeNode in="SourceGraphic"/>
</feMerge>
</filter>
</defs>
<text filter="url(#solid)" x="20" y="50" font-size="50">solid background</text>
</svg>
No this is not possible, SVG elements do not have background-... presentation attributes.
To simulate this effect you could draw a rectangle behind the text attribute with fill="green" or something similar (filters). Using JavaScript you could do the following:
var ctx = document.getElementById("the-svg"),
textElm = ctx.getElementById("the-text"),
SVGRect = textElm.getBBox();
var rect = document.createElementNS("http://www.w3.org/2000/svg", "rect");
rect.setAttribute("x", SVGRect.x);
rect.setAttribute("y", SVGRect.y);
rect.setAttribute("width", SVGRect.width);
rect.setAttribute("height", SVGRect.height);
rect.setAttribute("fill", "yellow");
ctx.insertBefore(rect, textElm);
The solution I have used is:
<svg>
<line x1="100" y1="100" x2="500" y2="100" style="stroke:black; stroke-width: 2"/>
<text x="150" y="105" style="stroke:white; stroke-width:0.6em">Hello World!</text>
<text x="150" y="105" style="fill:black">Hello World!</text>
</svg>
A duplicate text item is being placed, with stroke and stroke-width attributes. The stroke should match the background colour, and the stroke-width should be just big enough to create a "splodge" on which to write the actual text.
A bit of a hack and there are potential issues, but works for me!
Instead of using a <text> tag, the <foreignObject> tag can be used, which allows for XHTML content with CSS.
No, you can not add background color to SVG elements. You can do it programmatically with d3.
var text = d3.select("text");
var bbox = text.node().getBBox();
var padding = 2;
var rect = self.svg.insert("rect", "text")
.attr("x", bbox.x - padding)
.attr("y", bbox.y - padding)
.attr("width", bbox.width + (padding*2))
.attr("height", bbox.height + (padding*2))
.style("fill", "red");
Answer by Robert Longson (#RobertLongson) with modifications:
<svg width="100%" height="100%">
<defs>
<filter x="0" y="0" width="1" height="1" id="solid">
<feFlood flood-color="yellow"/>
<feComposite in="SourceGraphic" operator="xor"/>
</filter>
</defs>
<text filter="url(#solid)" x="20" y="50" font-size="50"> solid background </text>
<text x="20" y="50" font-size="50">solid background</text>
</svg>
and we have no bluring and no heavy "getBBox" :)
Padding is provided by white spaces in text-element with filter.
It's worked for me
Going further with #dbarton_uk answer, to avoid duplicating text you can use paint-order=stroke style:
<svg>
<line x1="100" y1="100" x2="350" y2="100" style="stroke:grey; stroke-width: 100"/>
<text x="150" y="105" style="stroke:white; stroke-width:0.5em; fill:black; paint-order:stroke; stroke-linejoin:round">Hello World!</text>
</svg>
Note the stroke-linejoin:round which is needed to avoid seeing spikes for the W sharp angle.
You can combine filter with the text.
<!DOCTYPE html>
<html>
<head>
<meta charset=utf-8 />
<title>SVG colored patterns via mask</title>
</head>
<body>
<svg viewBox="0 0 300 300" xmlns="http://www.w3.org/2000/svg">
<defs>
<filter x="0" y="0" width="1" height="1" id="bg-text">
<feFlood flood-color="white"/>
<feComposite in="SourceGraphic" operator="xor" />
</filter>
</defs>
<!-- something has already existed -->
<rect fill="red" x="150" y="20" width="100" height="50" />
<circle cx="50" cy="50" r="50" fill="blue"/>
<!-- Text render here -->
<text filter="url(#bg-text)" fill="black" x="20" y="50" font-size="30">text with color</text>
<text fill="black" x="20" y="50" font-size="30">text with color</text>
</svg>
</body>
</html>
this is my favorite hack (not sure it should work). It refer an element that is not yet displayed, and it works pretty well
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 620 40" preserveAspectRatio="xMidYMid meet">
<defs>
<filter x="-0.02" y="0" width="1.04" height="1.1" id="removebackground">
<feFlood flood-color="#00ffff"/>
</filter>
</defs>
<!--Draw the text-->
<use xlink:href="#mygroup" filter="url(#removebackground)" />
<g id="mygroup">
<text id="text1" x="9" y="20" style="text-anchor:start;font-size:14px;">custom text with background</text>
<line x1="200" y1="18" x2="200" y2="36" stroke="#000" stroke-width="5"/>
<line x1="120" y1="27" x2="203" y2="27" stroke="#000" stroke-width="5"/>
</g>
</svg>
For those wondering how to apply padding to a text element when it has a background like in the Robert's answer, do the following:
<svg>
<defs>
<filter x="-0.1" y="-0.1" width="1.2" height="1.2" id="solid">
<feFlood flood-color="#171717"/>
<feComposite in="SourceGraphic" operator="xor" />
</filter>
</defs>
<text filter="url(#solid)" x="20" y="50" font-size="50">Hello</text>
</svg>
In the example above, filter's x and y positions can be used as transform: translate(-10%, -10%) would, and width and height values can be read as 120% and 120%. So we made background 20% bigger, and offsetted it -10%, so background is now 10% bigger on each side of the text.
The previous answers relied on doubling up text and lacked sufficient whitespace.
By using atop and I was able to get the results I wanted.
This example also includes arrows, a common use case for SVG text labels:
<svg viewBox="-105 -40 210 234">
<title>Size Guide</title>
<defs>
<filter x="0" y="0" width="1" height="1" id="solid">
<feFlood flood-color="white"></feFlood>
<feComposite in="SourceGraphic" operator="atop"></feComposite>
</filter>
<marker id="arrow" viewBox="0 0 10 10" refX="5" refY="5" markerWidth="6" markerHeight="6" orient="auto-start-reverse">
<path d="M 0 0 L 10 5 L 0 10 z"></path>
</marker>
</defs>
<g id="garment">
<path id="right-body" fill="none" stroke="black" stroke-width="1" stroke-linejoin="round" d="M0 0 l30 0 l0 154 l-30 0"></path>
<path id="right-sleeve" d="M30 0 l35 0 l0 120 l-35 0" fill="none" stroke-linejoin="round" stroke="black" stroke-width="1"></path>
<use id="left-body" href="#right-body" transform="scale(-1,1)"></use>
<use id="left-sleeve" href="#right-sleeve" transform="scale(-1,1)"></use>
<path id="collar-right-top" fill="none" stroke="black" stroke-width="1" stroke-linejoin="round" d="M0 -6.5 l11.75 0 l6.5 6.5"></path>
<use id="collar-left-top" href="#collar-right-top" transform="scale(-1,1)"></use>
<path id="collar-left" fill="white" stroke="black" stroke-width="1" stroke-linejoin="round" d="M-11.75 -6.5 l-6.5 6.5 l30 77 l6.5 -6.5 Z"></path>
<path id="front-right" fill="white" stroke="black" stroke-width="1" d="M18.25 0 L30 0 l0 154 l-41.75 0 l0 -77 Z"></path>
<line x1="0" y1="0" x2="0" y2="154" stroke="black" stroke-width="1" stroke-dasharray="1 3"></line>
<use id="collar-right" href="#collar-left" transform="scale(-1,1)"></use>
</g>
<g id="dimension-labels">
<g id="dimension-sleeve-length">
<line marker-start="url(#arrow)" marker-end="url(#arrow)" x1="85" y1="0" x2="85" y2="120" stroke="black" stroke-width="1"></line>
<text font-size="10" filter="url(#solid)" fill="black" x="85" y="60" class="dimension" text-anchor="middle" dominant-baseline="middle"> 120 cm</text>
</g>
<g id="dimension-length">
<line marker-start="url(#arrow)" marker-end="url(#arrow)" x1="-85" y1="0" x2="-85" y2="154" stroke="black" stroke-width="1"></line>
<text font-size="10" filter="url(#solid)" fill="black" x="-85" y="77" text-anchor="middle" dominant-baseline="middle" class="dimension"> 154 cm</text>
</g>
<g id="dimension-sleeve-to-sleeve">
<line marker-start="url(#arrow)" marker-end="url(#arrow)" x1="-65" y1="-20" x2="65" y2="-20" stroke="black" stroke-width="1"></line>
<text font-size="10" filter="url(#solid)" fill="black" x="0" y="-20" text-anchor="middle" dominant-baseline="middle" class="dimension"> 130 cm </text>
</g>
<g title="Back Width" id="dimension-back-width">
<line marker-start="url(#arrow)" marker-end="url(#arrow)" x1="-30" y1="174" x2="30" y2="174" stroke="black" stroke-width="1"></line>
<text font-size="10" filter="url(#solid)" fill="black" x="0" y="174" text-anchor="middle" dominant-baseline="middle" class="dimension"> 60 cm </text>
</g>
</g>
</svg>
An obvious workaround to the problem of the blur produced by the filter effect is to render the <text> two times: once for the background (with transparent characters) and once for the characters (without a background filter).
For me, this was the only way to make the text readable in Safari.
<svg width="100%" height="100%">
<filter x="0" y="0" width="1" height="1" id="solid">
<feFlood flood-color="yellow" />
</filter>
<g transform="translate(20, 50)" font-size="50">
<text aria-hidden="true" fill="none" filter="url(#solid)">solid background</text>
<text fill="blue">solid background</text>
</g>
</svg>
The aria-hidden="true" attribute is there to prevent screen readers from speaking the text twice, if the user uses a screen reader.
You can add style to your text:
style="-webkit-tap-highlight-color: rgba(0, 0, 0, 0);
text-shadow: rgb(255, 255, 255) -2px -2px 0px, rgb(255, 255, 255) -2px 2px 0px,
rgb(255, 255, 255) 2px -2px 0px, rgb(255, 255, 255) 2px 2px 0px;"
White, in this example.
Does not work in IE :)