Emoji character outline in SVG - html

I'm trying to create an outline of an emoji character (meaning I want to draw just the outer shape of the emoji) in SVG. The best I could come up with so far is to use two masked rectangles, scaled and super-imposed on each other.
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="100" height="100">
<defs>
<filter id="filter">
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 0 0 0 1 0" />
</filter>
<mask id="mask" x="0" y="0" width="100" height="100">
<text filter="url(#filter)" x="0" y="80" font-family="Helvetica" font-weight="bold" font-size="7em" fill="#000000">🦄</text>
</mask>
</defs>
<path fill="#fff" d="M0 0 L0 100 L100 100 L100 0z"/>
<path fill="#000000" d="M0 0 L0 100 L100 100 L100 0z" mask="url(#mask)"/>
<g transform="translate(5, 5) scale(0.9)">
<path d="M0 0 L100 0 L100 100 L0 100z" mask="url(#mask)" fill="#fff"/>
</g>
</svg>
Depending on the particular emoji the result isn't very satisfactory though because not all emojis fill width and height equally.
Is there a better way to achieve an outline effect for emoji characters?

You need a viewBox for the svg element and I am using viewBox="0 -30 157 140" slightly bigger than the bounding box of the text element.
instead of a mask I'm using clipPath to clip a white rectangle
The filter I'm using is feMorphology operator="dilate" and I'm applying the filter to a group wrapping the clipped rectangle.
svg{border:solid}
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="100" viewBox="0 -30 157 140">
<defs>
<filter id="outline-indigo">
<feMorphology in="SourceAlpha" result="expanded"
operator="dilate" radius="3"/>
<feFlood flood-color="indigo" result="indi" />
<feComposite in ="indi" in2="expanded" operator="in" />
<feComposite in="SourceGraphic"/>
</filter>
<clipPath id='emojiClipPath'>
<text filter="url(#filter)" x="0" y="80" font-family="Helvetica" font-weight="bold" font-size="7em" fill="#000000">🦄</text>
</clipPath>
</defs>
<g filter="url(#outline-indigo)">
<rect y="-30" width="157" height="140" fill="white" clip-path='url(#emojiClipPath)'/>
</g>
</svg>

Related

How to create svg gradient with 3 points set out in a triangle that blend together

<svg id="color-gradient" width="400" height="400" version="1.1" xmlns="http://www.w3.org/2000/svg">
<defs>
<linearGradient id="gradient" x1="0" x2="0" y1="0" y2="1">
<stop offset="0%" stop-color="red"/>
<stop offset="50%" stop-color="blue" />
<stop offset="100%" stop-color="yellow"/>
</linearGradient>
</defs>
<circle cx="200" cy="200" r="100" fill="url(#gradient)"/>
</svg>
I want to create a svg gradient in a circle that has 3 points of color, set out in a triangle like this.
<svg id="color-gradient" width="400" height="400" version="1.1" xmlns="http://www.w3.org/2000/svg">
<defs>
<linearGradient id="gradient" x1="0" x2="0" y1="0" y2="1">
<stop offset="0%" stop-color="red"/>
<stop offset="50%" stop-color="blue" />
<stop offset="100%" stop-color="yellow"/>
</linearGradient>
</defs>
<circle cx="200" cy="200" r="100" fill="url(#gradient)"/>
</svg>
I have tried creating a linear Gradient with three stops, but I am not sure how to position the stops where I need them (top left right).
This is about as close as you can get.
svg {
width: 400px;
}
<svg viewBox="0 0 100 100">
<defs>
<filter id="blur" color-interpolation-filters="linear" x="-50%" y="-50%" width="200%" height="200%">
<feGaussianBlur in="SourceGraphic" stdDeviation="9"/>
</filter>
<mask id="circle">
<circle cx="50" cy="50" r="50" fill="white"/>
</mask>
</defs>
<g mask="url(#circle)" filter="url(#blur)">
<rect x="-10" width="110" height="110" fill="blue"/>
<rect x="50" width="60" height="110" fill="yellow"/>
<polygon points="50,50, 60,110, 40,110" fill="#0f8"/>
<polygon points="0,0, 100,0, 100,20, 50,50, 0,20" fill="red"/>
<polygon points="0,10, 50,50, 0,30" fill="#f0f"/>
<polygon points="100,10, 100,30, 50,50" fill="#f80"/>
</g>
</svg>
Since the blending you get in CSS/SVG works purely by combining the red, green, and blue channels of RGB colours separately, it doesn't know that we expect to see green when we blend blue and yellow. Instead you just get a murky grey.
So in the example above, I "cheated" by adding slivers of the "correct" colours in between our three main colours. For example I put a sliver of green between the blue and yellow sectors.
If I don't do that, the above example would look like this:
svg {
width: 400px;
}
<svg viewBox="0 0 100 100">
<defs>
<filter id="blur" color-interpolation-filters="linear" x="-50%" y="-50%" width="200%" height="200%">
<feGaussianBlur in="SourceGraphic" stdDeviation="7"/>
</filter>
<mask id="circle">
<circle cx="50" cy="50" r="50" fill="white"/>
</mask>
</defs>
<g mask="url(#circle)" filter="url(#blur)">
<rect x="-10" width="110" height="110" fill="blue"/>
<rect x="50" width="60" height="110" fill="yellow"/>
<polygon points="0,0, 100,0, 100,20, 50,50, 0,20" fill="red"/>
</g>
</svg>
This topic is inspired by the answer #Paul LeBeau
The author of the question did not ask a question on animation. But I think that the options will be useful to someone.
Gradient rotation
An animation command is added for a group of elements:
circle cx="50" cy="50" r="5" fill="white" stroke="silver">
<animateTransform attributeName="transform" type="rotate" xlink:href="#gr1" dur="2s" values="0 50 50;360 50 50" repeatcount="indefinite"/>
</circle>
<style>
svg {
width: 400px;
}
</style>
<svg viewBox="0 0 100 100">
<defs>
<filter id="blur" color-interpolation-filters="linear" x="-50%" y="-50%" width="200%" height="200%">
<feGaussianBlur in="SourceGraphic" stdDeviation="10"/>
</filter>
<mask id="circle">
<circle cx="50" cy="50" r="50" fill="white">
</circle>
</mask>
</defs>
<g id="gr1" mask="url(#circle)" filter="url(#blur)">
<rect x="-10" width="110" height="110" fill="blue"/>
<rect x="50" width="60" height="110" fill="yellow"/>
<polygon points="50,50, 60,110, 40,110" fill="#0f8"/>
<polygon points="0,0, 100,0, 100,20, 50,50, 0,20" fill="red"/>
<polygon points="0,10, 50,50, 0,30" fill="#f0f"/>
<polygon points="100,10, 100,30, 50,50" fill="#f80"/>
</g>
<circle cx="50" cy="50" r="5" fill="white" stroke="silver">
<animateTransform attributeName="transform" type="rotate" xlink:href="#gr1" dur="2s" values="0 50 50;360 50 50" repeatcount="indefinite"/>
</circle>
</svg>
Animation tracks
The command of animation of the radius of circles is added.
<circle cx="50" cy="50" r="5" fill="none" stroke-width="0.25" stroke="gray" >
<animate id="an1" attributeName="r" values="5;50" dur="2s" begin="0s" repeatcount="indefinite" />
</circle>
<style>
svg {
width: 400px;
}
</style>
<svg viewBox="0 0 100 100">
<defs>
<filter id="blur" color-interpolation-filters="linear" x="-50%" y="-50%" width="200%" height="200%">
<feGaussianBlur in="SourceGraphic" stdDeviation="10"/>
</filter>
<mask id="circle">
<circle cx="50" cy="50" r="50" fill="white">
</circle>
</mask>
</defs>
<g id="gr1" mask="url(#circle)" filter="url(#blur)">
<rect x="-10" width="110" height="110" fill="blue"/>
<rect x="50" width="60" height="110" fill="yellow"/>
<polygon points="50,50, 60,110, 40,110" fill="#0f8"/>
<polygon points="0,0, 100,0, 100,20, 50,50, 0,20" fill="red"/>
<polygon points="0,10, 50,50, 0,30" fill="#f0f"/>
<polygon points="100,10, 100,30, 50,50" fill="#f80"/>
</g>
<circle cx="50" cy="50" r="5" fill="white" stroke="silver">
<animateTransform attributeName="transform" type="rotate" xlink:href="#gr1" dur="2s" values="0 50 50;360 50 50" repeatcount="indefinite"/>
</circle>
<circle cx="50" cy="50" r="5" fill="none" stroke-width="0.25" stroke="gray" >
<animate id="an1" attributeName="r" values="5;50" dur="2s" begin="0s" repeatcount="indefinite" />
</circle>
<circle cx="50" cy="50" r="5" fill="none" stroke-width="0.25" stroke="gray" >
<animate id="an2" attributeName="r" values="5;50" dur="2s" begin="0.5s" repeatcount="indefinite" />
</circle>
<circle cx="50" cy="50" r="5" fill="none" stroke-width="0.25" stroke="gray" >
<animate id="an3" attributeName="r" values="5;50" dur="2s" begin="1s" repeatcount="indefinite" />
</circle>
<circle cx="50" cy="50" r="5" fill="none" stroke-width="0.25" stroke="gray" >
<animate id="an3" attributeName="r" values="5;50" dur="2s" begin="1.5s" repeatcount="indefinite" />
</circle>
<circle cx="50" cy="50" r="5" fill="none" stroke-width="0.25" stroke="gray" >
<animate id="an3" attributeName="r" values="5;50" dur="2s" begin="2s" repeatcount="indefinite" />
</circle>
</svg>
i wanted to give it a try, too.
the result is very different depending on the browser, firefox for example doesn't produce a very good result. (although that is also true for the other solutions, I think)
but it doesn't need the manually inserted composite colors and produces a very homogeneous effect on Safari and Chrome…
<svg viewBox="0 0 100 100">
<defs>
<filter id="colorblend">
<feColorMatrix in="SourceGraphic" result="red" type="matrix" values="
1 0 0 0 0
0 0 0 0 0
0 0 0 0 0
1 0 0 0 0" />
<feColorMatrix in="SourceGraphic" result="green" type="matrix" values="
0 0 0 0 0
0 1 0 0 0
0 0 0 0 0
0 1 0 0 0" />
<feColorMatrix in="SourceGraphic" result="blue" type="matrix" values="
0 0 0 0 0
0 0 0 0 0
0 0 1 0 0
0 0 1 0 0" />
<feGaussianBlur in="red" stdDeviation="20" result="red" />
<feGaussianBlur in="green" stdDeviation="20" result="green" />
<feGaussianBlur in="blue" stdDeviation="20" result="blue" />
<feBlend mode="screen" in="red" in2="green" result="redplusgreen" />
<feBlend mode="screen" in="redplusgreen" in2="blue" result="rainbow" />
<!--fix alpha -->
<feColorMatrix in="rainbow" result="rainbow" type="matrix" values="
1 0 0 0 0
0 1 0 0 0
0 0 1 0 0
0 0 0 20 0" />
<!-- increase brightness -->
<feBlend in="rainbow" in2="rainbow" mode="screen" result="rainbow" />
<!-- remove artefacts -->
<feGaussianBlur in="rainbow" stdDeviation="1" />
</filter>
<mask id="mask">
<circle cx="50" cy="50" r="50" fill="white"></circle>
</mask>
</defs>
<g mask="url(#mask)" filter="url(#colorblend)">
<rect x="-10" width="110" height="110" fill="red" />
<rect x="50" width="60" height="110" fill="lime" />
<polygon points="0,0, 100,0, 100,30, 50,50, 0,30" fill="blue" />
</g>
</svg>

Image not centering in SVG arc (HTML)

The background picture is not centering behind my SVG circle arc. You can see the image:
I want something like this:
obviously without the red cut part.
My HTML is as follows:
<svg width="200" height="200">
<defs>
<pattern id="image" x="0" y="0" width="1" height="1">
<image x="0%" y="0%" width="100%" height="100%" xlink:href="Assets/Images/RoundSlider.png"></image>
</pattern>
</defs>
<g transform="translate(100,100)" stroke="black" stroke-width="2">
<path d="M 0 0 -70 70 A 99 99 0 1 0 -70 -70 Z" fill="url(#image)" />
</g>
</svg>
This happens because your path's bounding box actually starts more on the right than if it were a full circle:
const bbox = document.querySelector('path').getBBox();
const rect = document.querySelector('rect');
rect.setAttribute('x', bbox.x);
rect.setAttribute('y', bbox.y);
rect.setAttribute('width', bbox.width);
rect.setAttribute('height', bbox.height);
<h3>The red rectangle represents your <path> BBox.</h3>
<svg width="200" height="200">
<g transform="translate(100,100)" stroke="black" stroke-width="2" fill="none">
<path d="M 0 0 -70 70 A 99 99 0 1 0 -70 -70 Z"/>
<rect stroke="red"/>
<circle stroke="green" r="100" cx="0" cy="0"/>
</g>
</svg>
So you can simply adjust your image's x attribute so it takes this offset into account (assuming you got a square image):
<svg width="200" height="200">
<defs>
<pattern id="image" width="1" height="1">
<!--
#mypath.getBBox().x = -70
Add 100 from translate(100, 100)
and we got our 30 units offset
-->
<image x="-30" y="0" width="200" height="200" xlink:href="https://upload.wikimedia.org/wikipedia/commons/9/95/Clock-green.png"/>
</pattern>
</defs>
<g transform="translate(100,100)" stroke="red" stroke-width="2">
<path id="mypath" d="M 0 0 -70 70 A 99 99 0 1 0 -70 -70 Z" fill="url(#image)"/>
</g>
</svg>
Or you could also just display this <image> and clip it (might be easier with more complex pathes):
<svg width="200" height="200">
<defs>
<clipPath id="myclip">
<use xlink:href="#mypath"/>
</clipPath>
</defs>
<image x="0" y="0" width="200" height="200" xlink:href="https://upload.wikimedia.org/wikipedia/commons/9/95/Clock-green.png" clip-path="url(#myclip)"/>
<path id="mypath" d="M 0 0 -70 70 A 99 99 0 1 0 -70 -70 Z" fill="transparent" transform="translate(100,100)" stroke="red" stroke-width="2"/>
</svg>

How to fix: text displaying in svg across-browsers?

My svg is displayed correct in IE, Edge and Chrome. Firefox doesn't work. The problem is, that the text doesn't appear correct.
Chrome
chrome
FF
Firefox Version
Here the code of my svg. I really don't know what the problem is.
<?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 version="1.1" id="Ebene_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
width="780px" height="540px" viewBox="0 0 780 540" enable-background="new 0 0 780 540" xml:space="preserve">
<a href="https://www.google.ch" target="_blank">
<g>
<image
overflow="visible"
opacity="0.35"
width="56"
height="52"
xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADkAAAA1CAYAAAD72kP+AAAACXBIWXMAAAsSAAALEgHS3X78AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAu9JREFUeNrsmmtv0zAUhp1LU5goDDGuAgk+8oH//0s2beIqBmUbW9e10DVN13AOeU0PrtMLrUuqOtKreU2jniev7ZP4OFRbcMQzzgcbxJIvAhkY2iRIqVJIhorweYx2uAGwI2goNLJBasA66Q6pQdoh1SrsqnbshpSSfpA6pGtSpkFNyBiAT0nPSXsAjSvu4oB0RTohHZPOAf6769ogGwB8Q3pF2hVuVtFJhuyRTkmHpD4c7QP0j0OBgGTnHpJekl6jnVR4XPL4+0m6S+qSPiNePZfkZjcM4dpt0n0APsI4rTJkF+41EGsk4y1LITFgE1xUxw2oYneNMCZriHsiG8RTHgBkrqxqGpGxhWVZIFRbcHhID+khPaSH9JAe0kN6SA/pIT2kh/SQHtJDbiFkbvzdlKM07jInR0J5hYF1bGa8UyH5C7y0zsWSFMrUuK5QNUBdyRqIeIcmbGzcEV0d4hXpFulCFQUgvhm31HhlOqgIIMNxieASsbZVURfJZA+MxIV6gbYGoET9vSodWtwP/hMcQ3BBh8t0Z6RPpCPSW1XUQlowa2RCyqB1l81s9lsAgzV2TV2HZOe+kt6R9kkHaJ/g/NDmpDkm+7C+h3a/ZHy6hLXBsUvfSB/h3j7EgE24m0pjopLZaij6u67edtC+XgNsGVzTAneEz84wl6RmbNEcs1YPF7ehK/zvAnYeuAPAccH1A861xIQzMbSiGT8ou6521AXsonDvMR7P8d1UjMGJVBfN+eM3cHXVsMvAdRFP2cQ4N+QqYU1I53DLTBA6n0bIpVx653r9A9ITVWyqeAE9U0VJfhcPFQmuy41cd4Gpn0GOoSYmk7boktm/PGouMwsuCruH84ka79i4dAm3yrw2L6wG3UFXlvtuvriAc5G8p8E+VsUGKA05BOQpEvt3F3AuH8dssLz15B6g63CyB9COK7h1PHNK2BrgzIlnADmBW+dbRCDeYkJLWnL+Yr7OVyW5Pyi3LFv4w6/WzTh+CTAA7UuPJ4vLMvAAAAAASUVORK5CYII="
transform="matrix(1 0 0 1 490.2483 147.248)"
></image>
<g>
<g>
<polygon
fill="red"
points="540.724,151.425 540.724,180.043 518.262,191.72 495.801,180.043 495.801,151.425"
/>
<polygon
fill="none"
stroke="#D9D9D9"
stroke-width="0.75"
stroke-linejoin="round"
stroke-miterlimit="10"
points="540.724,151.425 540.724,180.043 518.262,191.72 495.801,180.043 495.801,151.425"
/>
</g>
</g>
</g>
</a>
<text transform="matrix(1 0 0 1 509.5081 163.1104)">
<tspan x="0" y="0" fill="#949495" font-family="'Calibri'" font-size="11">
KVP
</tspan>
<tspan
x="-0.516"
y="12"
fill="#949495"
font-family="'Calibri'"
font-size="11"
>
[VP]
</tspan>
</text>
<path
fill="none"
stroke="#A3D2C4"
stroke-width="0.75"
stroke-linejoin="round"
stroke-miterlimit="10"
d="M664.349,162.086"
/>
</svg>
This is not an answer, it's just a comment: As you can see the next example works the same way in Chrome and FF. There must be something else in your code that is causing the problem.
svg{border:1px solid;}
<svg viewBox="240 120 90 50">
<rect x="260" y="135" width="50" height="20" fill="none" stroke="black" />
<text id="txt"
transform="matrix(1 0 0 1 262.6731 147.9746)"
fill="#1D1D1B"
font-family='Calibri'
font-size="11"
>
Betrieb
</text>
</svg>
UPDATE
The OP added more code.
I've replaced the <tspan> with <text> elements. Instead of having the <a> inside the text, now the <a> is wrapping the text.
Also I've replaced your image with a shadow generated with an svg filter.
svg{border:1px solid}
text{fill:#949495;font-family:Calibri;font-size:11;text-anchor:middle}
<svg viewBox="480 140 78 60">
<defs>
<filter id="f">
<feGaussianBlur in="SourceAlpha" stdDeviation="1.3" result="desenfoque"></feGaussianBlur>
<feOffset in="desenfoque" dx="0" dy="0" result="sombra"></feOffset>
<feMerge>
<feMergeNode in="sombra"></feMergeNode>
<feMergeNode in="SourceGraphic"></feMergeNode>
</feMerge>
</filter>
</defs>
<a href="https://www.google.ch" target="_blank">
<g>
<g>
<g filter="url(#f)">
<polygon
fill="red"
points="540.724,151.425 540.724,180.043 518.262,191.72 495.801,180.043 495.801,151.425"
/>
<polygon
fill="none"
stroke="#D9D9D9"
stroke-width="0.75"
stroke-linejoin="round"
stroke-miterlimit="10"
points="540.724,151.425 540.724,180.043 518.262,191.72 495.801,180.043 495.801,151.425"
/>
</g>
</g>
</g>
</a>
<a href="https://google.ch">
<text x="518.5" y="167" >KVP</text>
</a>
<a href="https://google.ch">
<text x="518.5" y="180" >[VP]</text>
</a>
<!--This path has a width and a height == 0
you can delete it -->
<path
fill="none"
stroke="#A3D2C4"
stroke-width="0.75"
stroke-linejoin="round"
stroke-miterlimit="10"
d="M664.349,162.086"
/>
</svg>

SVG rect as background to text [duplicate]

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 :)

SVG <use> behaves strange in Firefox and IE

I have noticed that some SVGs behave strangely when I try to include them with the <svg><use xlink:href="id-of-symbol" /></svg>. But if SVG is injected directly or referenced through <img> they look correctly.
I have created an example on CodePen: http://codepen.io/andeersg/full/qRWMQv/. In Chrome all three versions look the same, while in FireFox and IE11 the middle one is broken (the use version).
Have I done something wrong with SVG or is this a known bug?
mask, clipPath and filter elements (basically anything non-rendered that needs to be accessed by url() reference) don't work if they are within symbol elements in Firefox. This is a known bug.
There's an easy workaround though which is to move those elements outside the symbol element.
<svg width="0" height="0" style="position:absolute">
<mask id="ata" fill="#fff"><path d="M0 4.11c0 2.27 1.838 4.11 4.103 4.11a4.107 4.107 0 0 0 4.104-4.11C8.207 1.84 6.37 0 4.103 0A4.107 4.107 0 0 0 0 4.11z"></path></mask>
<mask id="atb" fill="#fff"><path d="M0 5.17c0 2.348 1.9 4.25 4.242 4.25a4.245 4.245 0 0 0 4.243-4.25c0-2.346-1.9-4.248-4.243-4.248A4.246 4.246 0 0 0 0 5.17z"></path></mask>
<symbol viewBox="0 0 65 72" id="helseogfamilie"><title>Page 1</title><g transform="translate(1)" fill="none" fill-rule="evenodd"><g transform="translate(1.06 4.242)" stroke="#4A5A28" stroke-width="3"><path d="M37.736.928V28.19c0 10.34-8.447 18.802-18.772 18.802S.19 38.531.19 28.19V.928"></path><path d="M55.824 26.98v19.743c0 10.34-8.447 18.801-18.772 18.801s-18.773-8.46-18.773-18.801"></path></g><g transform="translate(0 1.06)"><path fill="#4A5A28" mask="url(#ata)" d="M-5.303 13.523H13.51V-5.303H-5.303z"></path></g><g transform="translate(31.818)"><path fill="#4A5A28" mask="url(#atb)" d="M-5.303 14.722h19.09V-4.38h-19.09z"></path></g><path d="M62.465 25.635a5.584 5.584 0 0 1-5.579 5.588 5.584 5.584 0 0 1-5.58-5.588 5.584 5.584 0 0 1 5.58-5.589 5.584 5.584 0 0 1 5.579 5.589z" stroke="#4A5A28" stroke-width="2"></path></g></symbol>
</svg>
<svg width="65px" height="72px" viewBox="0 0 65 72" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<!-- Generator: Sketch 40.2 (33826) - http://www.bohemiancoding.com/sketch -->
<title>Page 1</title>
<desc>Created with Sketch.</desc>
<defs>
<path d="M0,4.10984848 C0,6.37954545 1.8380303,8.21969697 4.10348485,8.21969697 C6.37,8.21969697 8.2069697,6.37954545 8.2069697,4.10984848 C8.2069697,1.84015152 6.37,0 4.10348485,0 C1.8380303,0 0,1.84015152 0,4.10984848 L0,4.10984848 Z" id="helseogfamilie-path-1"></path>
<path d="M0,5.17045455 C0,7.51757576 1.89954545,9.41924242 4.24242424,9.41924242 C6.58530303,9.41924242 8.48484848,7.51757576 8.48484848,5.17045455 C8.48484848,2.82439394 6.58530303,0.921666667 4.24242424,0.921666667 C1.89954545,0.921666667 0,2.82439394 0,5.17045455 L0,5.17045455 Z" id="helseogfamilie-path-3"></path>
</defs>
<g id="Forside" stroke="none" fill="none" fill-rule="evenodd">
<g transform="translate(-988.000000, -623.000000)" id="Page-1">
<g transform="translate(989.000000, 623.000000)">
<g id="Group-5" transform="translate(1.060606, 4.242424)" stroke="#4A5A28" stroke-width="3">
<path d="M37.7363636,0.928136364 L37.7363636,28.1899545 C37.7363636,38.5308636 29.2886364,46.9923788 18.9636364,46.9923788 C8.63863636,46.9923788 0.190909091,38.5308636 0.190909091,28.1899545 L0.190909091,0.928136364" id="helseogfamilie-Stroke-1"></path>
<path d="M55.8244697,26.9805455 L55.8244697,46.7226667 C55.8244697,57.0635758 47.3767424,65.5240303 37.0517424,65.5240303 C26.7267424,65.5240303 18.2790152,57.0635758 18.2790152,46.7226667" id="helseogfamilie-Stroke-3"></path>
</g>
<g id="Group-8" transform="translate(0.000000, 1.060606)">
<mask id="helseogfamilie-mask-2" fill="white">
<use xlink:href="#helseogfamilie-path-1"></use>
</mask>
<g id="Clip-7"></g>
<polygon id="Fill-6" fill="#4A5A28" mask="url(#helseogfamilie-mask-2)" points="-5.3030303 13.5227273 13.51 13.5227273 13.51 -5.3030303 -5.3030303 -5.3030303"></polygon>
</g>
<g id="Group-11" transform="translate(31.818182, 0.000000)">
<mask id="mask-4" fill="white">
<use xlink:href="#helseogfamilie-path-3"></use>
</mask>
<g id="Clip-10"></g>
<polygon id="Fill-9" fill="#4A5A28" mask="url(#mask-4)" points="-5.3030303 14.7222727 13.7878788 14.7222727 13.7878788 -4.38136364 -5.3030303 -4.38136364"></polygon>
</g>
<path d="M62.4649242,25.6347424 C62.4649242,28.7211061 59.967197,31.2230758 56.8861364,31.2230758 C53.8029545,31.2230758 51.3052273,28.7211061 51.3052273,25.6347424 C51.3052273,22.5483788 53.8029545,20.0464091 56.8861364,20.0464091 C59.967197,20.0464091 62.4649242,22.5483788 62.4649242,25.6347424 L62.4649242,25.6347424 Z" id="helseogfamilie-Stroke-12" stroke="#4A5A28" stroke-width="2"></path>
</g>
</g>
</g>
</svg>
<svg width="65" height="72"><use xlink:href="#helseogfamilie" /></svg>
<img src="https://beta.mgk.no/themes/custom/mgk/dev/svg/helseogfamilie.svg">