Unable to make text bold in SVG - html

I have something like this:
.speed-description {
font-size: calculate-rem(9px);
}
<svg #speedAnimation class="{{ speedAnimationClass }}" viewBox="-10 50 140 80" preserveAspectRatio="xMinYMax meet">
<path class="loader" d="m 0 120 a 1 1 0 0 1 120 0" fill="none" stroke-linecap="round" stroke-width="11" />
<text text-anchor="middle" x="60" y="110" font-weight="900" ></text>
<text text-anchor="middle" x="60" y="120" font-weight="bold" class="speed-description">
{{ speedDescription }}
</text>
</svg>
Whatever I try I cannot make the 2 texts in bold.
What am I doing wrong?
Currently it's something like this

As I've commented: in SVG you can add a stroke to the text. This will make the text look like bold. You can use a stroke-width to set it as bold as you need.
text{stroke:black; stroke-width:.5}
<svg #speedAnimation class="speedAnimationClass" viewBox="-10 50 140 80" preserveAspectRatio="xMinYMax meet">
<text text-anchor="middle" x="60" y="100" >some text</text>
<text text-anchor="middle" x="60" y="120" class="speed-description">speed Description</text>
</svg>
Alternatively you can use <feMorphology> for this. In this case you will need to play with the value of the radius attribute of the <feMorphology>
<svg #speedAnimation class="speedAnimationClass" viewBox="-10 50 140 80" preserveAspectRatio="xMinYMax meet">
<filter id="dilate">
<feMorphology operator="dilate" radius=".5"/>
</filter>
<text text-anchor="middle" x="60" y="100" filter="url(#dilate)" >some text</text>
<text text-anchor="middle" x="60" y="120" class="speed-description" filter="url(#dilate)">speed Description</text>
</svg>

Related

How to vertically center a character within a svg path?

I have an svg path with a text and a character like this:
<svg id="svg1" style="overflow:visible">
<path id="path1" stroke="black" d="M 0 50 L 100 50" stroke-width="10" />
<text text-anchor="middle" dy="-30" >
<textPath href="#path1" startOffset="50%" fill="red">Shape</textPath>
<textPath href="#path1" startOffset="50%" fill="red" dominant-baseline="central" >
<tspan font-size="30" dy="-5">⬤</tspan>
</textPath>
</text>
</svg>
And I want the circle (or any character in general) to align perfectly at the center of the line regardless of font-size or any other attribute
I tried changing dy but it's really isn't a universal solution.
Here's what it looks like for different browsers:
for font-size="50" in firefox
for font-size="50" in Brave
for font-size="10" in firefox & Brave
So how do I universally align the character to the vertical center for any given style and/or attribute?
Edit
as mentioned in the comments, I tried this solution but it really doesn't solve the problem for varying font-size nor does it perfectly align to the center cross browser
<svg id="svg1" style="overflow:visible">
<path id="path1" stroke="black" d="M 0 50 L 100 50" stroke-width="10" />
<text text-anchor="middle" dy="-30" >
<textPath href="#path1" startOffset="50%" fill="red">Shape</textPath>
<textPath href="#path1" startOffset="50%" fill="red" dominant-baseline="central"
alignment-baseline="central">
<tspan font-size="30" dy="-5">⬤</tspan>
</textPath>
</text>
</svg>
Unfortunately, there is no reliable way to center text vertically.
Firefox and chrome will interpret dominant-baseline quite differently.
For a consistent rendering, you could use some javaScript.
This approach also requires to measure some character/glyph proportions to calculate an ideal baseline shift.
So we need to get a ratio between font size and character height.
E.g write a capital in Arial at 100 points in inkscape, Illustrator etc. and convert it to paths/outlines and check it's height: 71.582 pt
So the capital to font-size ratio is: 100/71.582 = 0.71582
Example 1: emulate dominant-baseline:central; font-size 10 and 20
<style>
svg{
height:90vmin;
border:1px solid #ccc
}
</style>
<svg viewBox="0 0 50 20">
<line x1="0" x2="100%" y1="50%" y2="50%" stroke="#000" stroke-width="10" /></line>
<text font-size="10" style="font-family:Arial" x="0" y="50%" fill="red" dy="3.5791">I⬤</text>
<text font-size="20" style="font-family:Arial" x="20" y="50%" fill="red" dy="7.1582">I⬤</text>
</svg>
The dy values are calculated like so:
10 (1. font-size) * 0.71582 (capheight ratio) / 2 = 3.5791
20 (2. font-size) * 0.71582 (capheight ratio) / 2 = 7.1582
The bad news: you need to get different ratios for each type of character e.g lowercase letters.
Albeit, it might be enough to check the x-height for lowercase characters.
Example 2: save ratios to data-attribute; calculate dy via js
let texts = document.querySelectorAll('text');
texts.forEach(function(text){
let offsetRatio = parseFloat(text.getAttribute('data-offsetratio'));
let style = window.getComputedStyle(text);
let fontSize = parseFloat(style.fontSize);
let dy = fontSize * offsetRatio /2;
text.setAttribute('dy', dy );
})
svg{
width:100%;
border:1px solid #ccc
}
<svg id="svg1" style="overflow:visible" viewBox="0 30 100 40">
<path id="path1" stroke="black" d="M 0 50 L 100 50" stroke-width="10" />
<path id="path2" stroke="#fff" d="M 0 50 L 100 50" stroke-width="0.25" />
<g font-size="10" style="font-family:Arial,Georgia,'Segoe UI'">
<text data-offsetratio="0.71582" x="0" y="50" fill="red">I⬤</text>
<text data-offsetratio="0.51855" x="15" y="50" fill="red">x</text>
</g>
<g font-size="25" style="font-family:Arial,Georgia,'Segoe UI'">
<text data-offsetratio="0.71582" x="30" y="50" fill="red">I⬤</text>
<text data-offsetratio="0.56409" x="60" y="50" fill="red">●</text>
<text data-offsetratio="0.70081" x="75" y="50" fill="red">•</text>
</g>
</svg>
Vertically aligning a text is done using dominant-baseline, but you can see from this example that it is not easy to do. Here I align ⬤Gga using the values middle and central.
I added a viewBox to <svg> so that it is easier to control the position. But I guess it all depends on the font used.
<svg overflow="hidden" viewBox="0 0 100 65" width="250">
<rect width="100" height="65" fill="#eee" />
<path id="path1" stroke="black" d="M 0 50 L 100 50" stroke-width="10" />
<text text-anchor="middle" dy="-30" fill="red">
<textPath href="#path1" startOffset="50%">Shape</textPath>
<textPath href="#path1" startOffset="50%" dominant-baseline="middle" font-size="30">⬤Gga</textPath>
</text>
</svg>
<svg overflow="hidden" viewBox="0 0 100 65" width="250">
<rect width="100" height="65" fill="#eee" />
<path id="path1" stroke="black" d="M 0 50 L 100 50" stroke-width="10" />
<text text-anchor="middle" dy="-30" fill="red">
<textPath href="#path1" startOffset="50%">Shape</textPath>
<textPath href="#path1" startOffset="50%" dominant-baseline="central" font-size="30">⬤Gga</textPath>
</text>
</svg>

SVG text not horizontally center aligning

I generated a SVG using Adobe XD. They use transform for positioning things but the text in my mini computer screen is not always the same width (it is dynamically generated). I have tried anchored, anything I could find but it still didn't work. This is how it looks with the current code:
Here is the code:
<svg xmlns="http://www.w3.org/2000/svg" width="903.5" height="860.5" viewBox="0 0 1200 1041">
<g transform="translate(-397)">
<g
transform="translate(507 975)"
fill="#fff"
stroke="#707070"
strokeWidth="1"
>
<rect width="907" height="66" rx="33" stroke="none" />
<rect x="0.5" y="0.5" width="906" height="65" rx="32.5" fill="none" />
</g>
<rect width="119" height="395" transform="translate(901 613)" fill="#fff" />
<g
transform="translate(397)"
fill="#232323"
stroke="#fff"
stroke-width="30"
>
<rect width="1127" height="627" rx="103" stroke="none" />
<rect x="15" y="15" width="1097" height="597" rx="88" fill="none" />
</g>
<text
fill="white"
fontSize="96"
fontFamily="Fredoka"
>
{screenText}
</text>
</g>
</svg>
You can use transform/translate, text-anchor and dominant-baseline to place a text in the middle of something.
body {
background: gray;
}
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1200 1041">
<g
transform="translate(600 975)"
fill="#fff"
stroke="#707070"
stroke-width="1">
<rect x="-453.5" width="907" height="66" rx="33" stroke="none" />
<rect x="-454" y="0.5" width="906" height="65" rx="32.5" fill="none" />
</g>
<rect width="119" height="395" transform="translate(545.5 613)" fill="#fff" />
<g transform="translate(50)"
fill="#232323"
stroke="#fff"
stroke-width="30">
<rect width="1127" height="627" rx="103" stroke="none" />
<rect x="15" y="15" width="1097" height="597" rx="88" fill="none" />
</g>
<text
fill="#fff"
font-size="96"
font-family="Fredoka"
transform="translate(600 300)"
text-anchor="middle"
dominant-baseline="middle">
{screenText}
</text>
</svg>
Thanks to Buhan Yu's comment I learned that you need to specify x and y to center align it. I set x="50%" and it worked!

how to pass clear html elements(as text format) to backend(express js)

I need to pass an <svg></svg> element to backend as clear text using angular HTTP.
for example, I have an angular-html page with a <svg> element,
<div>
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 540 320">
<defs>
<path id="Triangle" d="M120 20 L 220 193.205 L 20 193.205 Z" stroke="red" fill="url(#radial)"/>
<path id="Maths" d="M250,50C380,50 350,150 480,150" stroke="green" fill="none"/>
<path id="Chemistry" transform="translate(-200,0) scale(1.5)" d="M250,50C380,50 350,150 480,150" stroke="blue" fill="none"/>
<path id="Path" d="M20,240 C40,280 60,280 200,230 A100 100 0 0 1 300,260 L 330,280 Q 400,320 500,200" stroke="orange" fill="none"/>
<linearGradient id="linear" x1="0" x2="1" gradientUnits="objectBoundingBox">
<stop stop-color="green" offset="0"/>
<stop stop-color="red" offset="1"/>
</linearGradient>
<radialGradient id="radial" cx="0.5" cy="0.6667" r="0.6" gradientUnits="objectBoundingBox">
<stop stop-color="orange" offset="0.2"/>
<stop stop-color="yellow" offset="1"/>
</radialGradient>
<pattern id="pattern" width="4" height="4" patternUnits="userSpaceOnUse">
<rect width="4" height="4"/>
<rect width="2" height="2" fill="red"/>
<rect x="2" y="2" width="2" height="2" fill="red"/>
</pattern>
</defs>
<rect x="2%" y="2%" width="96%" height="96%" fill="url(#linear)" fill-opacity="0.3" stroke="orange" stroke-width="2" stroke-dasharray="5 2 2 2 5 5"/>
<use xlink:href="#Triangle"/>
<use xlink:href="#Maths"/>
<use xlink:href="#Chemistry"/>
<use xlink:href="#Path"/>
<text textLength="600px" lengthAdjust="spacingAndGlyphs" fill="url(#pattern)">
<textPath xlink:href="#Triangle" font-size="20" font-family="serif" font-weight="bold" textLength="600px" lengthAdjust="spacingAndGlyphs">Text adjusted around a triangle</textPath>
</text>
<text font-family="sans-serif">
<textPath xlink:href="#Maths" font-size="30" fill="blue" text-anchor="middle" startOffset="50%">Maths x<tspan font-size="20" baseline-shift="super">2</tspan> + y<tspan font-size="20" baseline-shift="super">2</tspan> = z<tspan font-size="20" baseline-shift="super">2</tspan>!</textPath>
<textPath xlink:href="#Chemistry" font-size="30" fill="green" text-anchor="middle" startOffset="50%">Chemistry 2H<tspan font-size="20" baseline-shift="sub">2</tspan> + O<tspan font-size="20" baseline-shift="sub">2</tspan> -> 2H<tspan font-size="20" baseline-shift="sub">2</tspan>O</textPath>
<textPath xlink:href="#Path" font-size="30" fill="purple" dominant-baseline="middle" text-anchor="middle" startOffset="50%">Text centered on a complex path</textPath>
</text>
</svg>
</div>
Is it possible to pass the svg to backend with angular HTTP.
You can use ViewChild for selecting svg element and then by outerHTML access the HTML.
Demo
Set reference to svg in HTML,
<svg #svgElement ...
Get data from reference,
#ViewChild('svgElement', {static: true}) svgElement: ElementRef;
onClick() {
console.log(this.svgElement.nativeElement.outerHTML)
}

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