Rectangle Border Around Svg Text

How can I draw a box around text with SVG?

Sorry the answer took me so long, but I was learning how to use ECMAScript with an XML DOM.

Alright. So suppose you have your document structure like so:

<svg
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
version="1.1"

width="800"
height="600"
id="example_text">

<g id="layer1">

<text
x="123.4"
y="567.8"
id="text_holder">

<tspan id="tspan1">Text</tspan>
<tspan id="tspan2">More text</tspan>

</text>
</g>
<script type="text/ecmascript">

function create_from_rect (client_rect, offset_px) {
if (! offset_px) {offset_px=0;}
var box = document.createElementNS(
document.rootElement.namespaceURI,
'rect'
);

if (client_rect) {
box.setAttribute('x', client_rect.left - offset_px);
box.setAttribute('y', client_rect.top - offset_px);
box.setAttribute('width', client_rect.width + offset_px * 2);
box.setAttribute('height', client_rect.height + offset_px * 2);
}

return box;
}

function add_bounding_box (text_id, padding) {
var text_elem = document.getElementById(text_id);
if (text_elem) {
var f = text_elem.getClientRects();
if (f) {
var bbox = create_from_rect(f[0], padding);
bbox.setAttribute(
'style',
'fill: none;'+
'stroke: black;'+
'stroke-width: 0.5px;'
);
text_elem.parentNode.appendChild(bbox);
}
}
}

add_bounding_box('text_holder', 5);

</script>
</svg>

Adding the <script> tag at the bottom of the root <svg> element causes it to execute after it's created the DOM structure above it, just like JavaScript on a web page.

Loop SVG text animation around rectangle path

If you extend the path and add textLength settings to make sure that the text wraps perfectly - and tweak a few other things you can get this to look better. Still has a tiny seam jitter, but it's not that noticeable.

html, body {
background: black;
margin: 0 auto;
}

.container {
widht: 100%;
background: red;
}

.svgwave {
margin-left: calc(50% - 150px);
margin-top: 100px;
}
            <svg class="svgwave" xmlns="http://www.w3.org/2000/svg" width="301" height="301" viewBox="0 0 301 301" style="width:auto; height: auto; overflow: visible;">
<path id="wavepath" d="M145.5 301.5H13C6.09645 301.5 0.5 295.904 0.5 289V13C0.5 6.09645 6.09644 0.5 13 0.5H289C295.904 0.5 301.5 6.09644 301.5 13V289C301.5 295.904 295.904 301.5 289 301.5H156.5 H13C6.09645 301.5 0.5 295.904 0.5 289V13C0.5 6.09645 6.09644 0.5 13 0.5H289C295.904 0.5 301.5 6.09644 301.5 13V289C301.5 295.904 295.904 301.5 289 301.5H156.5 H13C6.09645 301.5 0.5 295.904 0.5 289V13C0.5 6.09645 6.09644 0.5 13 0.5H289C295.904 0.5 301.5 6.09644 301.5 13V289C301.5 295.904 295.904 301.5 289 301.5H156.5 H13C6.09645 301.5 0.5 295.904 0.5 289V13C0.5 6.09645 6.09644 0.5 13 0.5H289C295.904 0.5 301.5 6.09644 301.5 13V289C301.5 295.904 295.904 301.5 289 301.5H156.5" style="fill: transparent; stroke: transparent; stroke-width: 1px;" ></path>

<foreignObject x='6' y='6' width='300px' height='300px'>
<div
style="width: 282px; height: 282px;
border-radius: 8px;
background-size: contain;
border: 4px solid white;
display:inline-block; "
></div>
</foreignObject>
<text text-anchor="left" style="text-transform: uppercase; font-family: Arial; font-size: 20px; fill: white;">
<textPath style=" fill-opacity: 1" href="#wavepath" side="left" startOffset="0%" textLength="1175">
<animate attributeName="startOffset" from="20%" to="42%" begin="0s" dur="12s" repeatCount="indefinite"></animate>
First <tspan style="fill: #DED279;">Second</tspan>
First <tspan style="fill: #DED279;">Second</tspan>
First <tspan style="fill: #DED279;">Second</tspan>
First <tspan style="fill: #DED279;">Second</tspan>
First <tspan style="fill: #DED279;">Second</tspan>
First <tspan style="fill: #DED279;">Second</tspan>
First <tspan style="fill: #DED279;">Second</tspan>
First <tspan style="fill: #DED279;">Second</tspan>
</textPath>
</text>
</svg>

SVG - Text with background color and rounded borders

You can do this with a filter in two alternative ways:

  1. Do a flood, blur it, then clip the low opacities, then dial up the remaining opacity to full
  2. Smuggle in a rounded corner rect via feImage and use relative sizing to stretch it

In both cases padding is relative so if your text is too long, the rounded corners will overflow the filter area. Unlike CSS, you can't combine relative and absolute sizings in SVG (well SVG 1.1 at least). So this is as good as you get.

Since you're really looking for HTML text behavior but you want it in SVG, you might want to consider using a foreignObject and embedding HTML text that way.

<svg width="800px" height="600px"><defs>  <filter id="rounded-corners" x="-5%" width="110%" y="0%" height="100%"><feFlood flood-color="#FFAA55"/><feGaussianBlur stdDeviation="2"/><feComponentTransfer>  <feFuncA type="table"tableValues="0 0 0 1"/></feComponentTransfer>
<feComponentTransfer> <feFuncA type="table"tableValues="0 1 1 1 1 1 1 1"/></feComponentTransfer> <feComposite operator="over" in="SourceGraphic"/> </filter> <filter id="rounded-corners-2" primitiveUnits="objectBoundingBox"><feImage preserveAspectRatio="none" width="110%" height="110%" x="-5%" y="0%" xlink:href="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' x='0px' y='0px' viewBox='0 0 400 40' height='40' width='400'%3E%3Crect fill='red' x='0' y='0' rx='10' ry='10' width='400' height='40'/%3E%3C/svg%3E"/> <feComposite operator="over" in="SourceGraphic"/> </filter> </defs>
<text filter="url(#rounded-corners)" x="20" y="40" style="font-size:30">Blur & opacity filter</text> <text filter="url(#rounded-corners)" x="20" y="80" style="font-size:30"> But the x padding is relative and overflows with long text</text> <text filter="url(#rounded-corners-2)" x="20" y="140" style="font-size:30">feImage and a rect filter</text> <text filter="url(#rounded-corners-2)" x="20" y="180" style="font-size:30">But you still can't get away from relative padding</text>
</svg>

SVG: text inside rect

This is not possible. If you want to display text inside a rect element you should put them both in a group with the text element coming after the rect element ( so it appears on top ).

<svg xmlns="http://www.w3.org/2000/svg">  <g>    <rect x="0" y="0" width="100" height="100" fill="red"></rect>    <text x="0" y="50" font-family="Verdana" font-size="35" fill="blue">Hello</text>  </g></svg>

Rounding the corners of an SVG

The easiest solution is probably to use the border-radius CSS property on the svg wrapper element. It will allow you to clip round corners on your svg element and expose the background color behind the svg.
Here is an example :

body {
background: darkorange;
}

#wrapper {
display: block;
width: 100%;
border-radius: 50px;
overflow: hidden;
border:10px solid pink;
}
<svg id="wrapper" viewBox="0 0 100 100">
<svg>
<rect fill="teal" x="0" y="0" width="100" height="100"/>
</svg>
</svg>

SVG: scale text with enclosing rectangle or SVG element?

You can create the base svg and scale that entire svg with a scale value, then the text also will be scaled.
Here is a demo in which I scaled the svg to .5, the entire svg with the text is scaled to half.

svg {   transform: scale(.5);  transform-origin: 0% 0%;}
<svg width="200" height="300"><g><rect x="0" y="0" width="100%" height="100%" fill="red"></rect><text x="50%" y="50%" font-family="Verdana" font-size="82" fill="blue" dominant-baseline="middle" text-anchor="middle">Hello</text></g></svg>

SVG text background color with border radius and padding that matches the text width

if you can use script, you can use this little function. It handles some of the CSS values. You could however implement whatever you need...

function makeBG(elem) {  var svgns = "http://www.w3.org/2000/svg"  var bounds = elem.getBBox()  var bg = document.createElementNS(svgns, "rect")  var style = getComputedStyle(elem)  var padding_top = parseInt(style["padding-top"])  var padding_left = parseInt(style["padding-left"])  var padding_right = parseInt(style["padding-right"])  var padding_bottom = parseInt(style["padding-bottom"])  bg.setAttribute("x", bounds.x - parseInt(style["padding-left"]))  bg.setAttribute("y", bounds.y - parseInt(style["padding-top"]))  bg.setAttribute("width", bounds.width + padding_left + padding_right)  bg.setAttribute("height", bounds.height + padding_top + padding_bottom)  bg.setAttribute("fill", style["background-color"])  bg.setAttribute("rx", style["border-radius"])  bg.setAttribute("stroke-width", style["border-top-width"])  bg.setAttribute("stroke", style["border-top-color"])  if (elem.hasAttribute("transform")) {    bg.setAttribute("transform", elem.getAttribute("transform"))  }  elem.parentNode.insertBefore(bg, elem)}

var texts = document.querySelectorAll("text")for (var i = 0; i < texts.length; i++) { makeBG(texts[i])}
text {  background: red;  border-radius: 5px;  border: 2px solid blue;  padding: 5px}
text:nth-of-type(2) { background: orange; border-color: red}
g text { border-width: 4px}
<svg width="400px" height="300px">  <text x="20" y="40">test text</text>  <text x="20" y="80" transform="rotate(10,20,55)">test with transform</text>    <g transform="translate(0,100) rotate(-10,20,60) ">    <text x="20" y="60">test with nested transform</text>  </g></svg>

D3 - Create Dynamic Border Rectangle around SVG group

The simplest, cross-browser compatible way is to implement a border is to use a rect exactly as I did, but place it outside of the group, as mentioned by @Duopixel in his comment. As it is still positioned by the bounding box, it will have the correct width, height, x, and y.

<rect></rect>
<g></g>


Related Topics



Leave a reply



Submit