Programmatically Fix text into viewBox - svg

I have this svg:
<svg xmlns="http://www.w3.org/2000/svg" version="1.1"
viewBox="0 0 ' + size + ' ' + size +'" width="'+ boxW +'" height="'+ boxH +'">
<text>Sample Text</text>
</svg>
size: A parameter that is needed to the viewBox in order to create the wrapper.
width & height: the width and height of the container of the text.
I have a function that generate this svg. The problem is that the text is not fitting into the box; the result is like this:
(Is blue due to the Chrome inspector, you can see up in the top-left corner the text being small instead of full size.
The SVG resulted is this:
<svg xmlns="http://www.w3.org/2000/svg" version="1.1"
viewBox="0 0 580 532">
<text x="0" y="15" style="font-family:Arial;fill:%230000ff;fill-opacity:1;font-weight:normal;font-style:normal;"
>test</text>
</svg>
The whole img is this:
<img src="data:image/svg+xml;charset=UTF-8,<svg xmlns="http://www.w3.org/2000/svg" version="1.1" viewBox="0 0 580 532"><text x="0" y="15" style="font-family:Arial;fill:%230000ff;fill-opacity:1;font-weight:normal;font-style:normal;">test</text></svg>" class="leaflet-marker-icon leaflet-zoom-animated leaflet-interactive" alt="" tabindex="0" style="margin-left: -298px; margin-top: -291px; width: 309px; height: 295px; transform: translate3d(683px, 317px, 0px); z-index: 317; outline: none;">
So my quesiton is: How to fit the text into the main wrapper?

You either have to:
fit the viewBox to the text, or
fit the text to the viewBox.
You are not doing either. You are not even setting a font-size.
Option 1 is not really available to you. You can measure the text if you have access to the SVG DOM, but you can't do that if you aren't in a rendering environment, like a browser.
Perhaps you could use a font loading library to get metadata about the glyphs in the font. Then calculate the size of a piece of text that way. You don't mention which language you are using to produce these SVGs, so I can't advise further on that.
So you are left with option 2. The only option that SVG has to let you fit text to a particular size, is the textLength and lengthAdjust attributes on the <text> element.
textLength
Sets a length to which you want the text to be fitted
lengthAdjust
Sets the method to be used to adjust the length. You can either stretch just the spacing between the letters, or you can stretch the letter glyphs
See the <text> section in the spec for more information
There are no options for adjusting the text height.
svg {
width: 400px;
background-color: linen;
}
<svg viewBox="0 0 200 40">
<line x1="10" y1="30" x2="190" y2="30" stroke="black" opacity="0.2"/>
<text x="10" y="30"
textLength="180"
lengthAdjust="spacing">Sample Text</text>
</svg>
<br/>
<svg viewBox="0 0 200 40">
<line x1="10" y1="30" x2="190" y2="30" stroke="black" opacity="0.2"/>
<text x="10" y="30"
textLength="180"
lengthAdjust="spacingAndGlyphs">Sample Text</text>
</svg>
If you want the font size to be a better match, then you are going to have to work out a method of calculating an approximate font size. Eg.
var numChars = text.length()
var fontSize = (desiredTextWidth / numChars) * someScalingFactor
The scaling factor will depend on your font.

This is my solution: I'm putting the text inside a <symbol>. I get the size of the text with getBBox() and use it to set the viewBox for the <symbol>. Please note that the <use> element has a width of 100%.
let bb = text.getBBox();
test.setAttributeNS(null, "viewBox", `${bb.x} ${bb.y} ${bb.width} ${bb.height}`);
*{font-size:16px;}
svg{border:1px solid;width:90vh}
body{font-family:Arial;fill:#0000ff;fill-opacity:1;font-weight:normal;font-style:normal;}
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" viewBox="0 0 580 532">
<symbol id="test">
<text id="text" dominant-baseline="central" text-anchor="middle" >test</text>
</symbol>
<use id="_use" xlink:href="#test" width="100%" />
</svg>

Related

Something trimming hastag from svg href

I have a sprite with icons. Everything is good when I'm working on localhost. But when I'm uploading files on hosting, something trimming my #plus after .svg.
Only this icon, other icons showing.
<span #click="increment">
<svg class="icon icon-plus">
<use href="../assets/icons/feather-sprite.svg#plus" />
</svg>
</span>
Your problem will be because of the way that icon (and many others in that set) are designed. It looks like this:
<symbol id="plus" viewBox="0 0 24 24">
<line x1="12" y1="5" x2="12" y2="19"></line>
<line x1="5" y1="12" x2="19" y2="12"></line>
</symbol>
The problem is that the icon is designed using simple straight lines. This is quite unusual for icon libraries. Normally they use filled shapes.
By default SVG shapes have a black "fill" and no "stroke". The fill is the area inside a shape, and the stroke is a boundary of a shape. Straight lines have no inside, so they have no fill to draw. And if a colour and size are not specified for the stroke, it won't be rendered either. That's why the icon is not showing up.
To fix that you need to specify a stroke and stroke-width for the icons that are like this.
.icon {
width: 100;
}
.icon-plus {
stroke: blue;
stroke-width: 4;
}
<svg class="icon icon-plus">
<symbol id="plus" viewBox="0 0 24 24">
<line x1="12" y1="5" x2="12" y2="19"></line>
<line x1="5" y1="12" x2="19" y2="12"></line>
</symbol>
<use href="#plus" />
</svg>

How to use a transformed path in textPath?

Consider following code:
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg width="2000" height="2000" viewBox="-1000 -1000 2000 2000"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink">
<defs>
<style>text { font-size: 60px; }</style>
<style>path { fill: none; stroke: black; }</style>
<style>textPath { text-anchor: middle; }</style>
<path d="M -258.8190, -965.9258 a 1000, 1000 0 0,1 517.6381, 0" id="ttO"/>
<use xlink:href="#ttO" transform="scale(.85)" id="ttI"/>
</defs>
<use xlink:href="#ttO"/>
<text dy="55"><textPath xlink:href="#ttO" startOffset="50%">Hello</textPath></text>
<use xlink:href="#ttI"/>
<text dy="25"><textPath xlink:href="#ttI" startOffset="50%">World!</textPath></text>
</svg>
... and the resulting image:
.
The explicitly defined path (ttO) and the textPath (Hello) using that path works fine. To define the smaller concentric arc (ttI), rather than calculating coordinates, I like to use transform. This works fine. However, I cannot use this second arc 'ttI' in the second textPath, at least Chrome doesn't render the second textPath. Is this because 'use' cannot be used to define new ids? What is the best way to do this?
If it was just this example, probably I can define one path halfway between two arcs and use that to draw textPaths with +/- offsets and draw the two arcs as transforms of the defied path. My goal is to extend the pattern I attempted to create more paths/textPaths at other angles / distances from the center with different languages to form a circular graphic. Without defining transformed paths, I have to explicitly specify at least one arc for each language.
This is how I would do it: I would create a text element using the same text path and I would scale the text - in this case transform="scale(.7,.7)". Also since I want the text to be the same size as the unscaled one I would use a different font size: 1em for the scaled text and .7em for the unscaled. Of coarse this is just an example. You can pick your font size making sure the font size for the scaled text is bigger than the font-size of the unscaled one by the same amount as the transformation.
Please observe that for the path I'm using vector-effect="non-scaling-stroke"so that that stroke appears the same in both the scaled and unscaled one.
svg{border:solid}
path{fill:none;stroke:black;}
<svg viewBox="-110 -110 220 220" >
<g>
<path id="path0" d="M90,0A90,90 0 0 1 -90,0A90,90 0 0 1 90,0" vector-effect="non-scaling-stroke"/>
<text font-size=".7em" >
<textPath xlink:href="#path0" startOffset="75%" text-anchor="middle">Hello World!</textPath>
</text>
</g>
<g transform="scale(.7,.7)">
<text font-size="1em" >
<textPath xlink:href="#path0" startOffset="75%" text-anchor="middle">Hello World!</textPath>
</text>
<use xlink:href="#path0"/>
</g>
</svg>

How to trace one edge of an open path in SVG

I am trying to make a web page that allows the user to draw lines in an SVG image. The drawing part is fine, but each line needs to carry a label that fills the width of the line (the lines are 15px wide).
I have tried to use a <textpath> referencing the line they drew, but the baseline of the label ends up running down the middle of the line. Here is a screenshot to show what I mean.
I have tried various ways to nudge the text over slightly using CSS and properties, but the only success I have had is to use a transform, which will often result in the text 'spilling out' if the direction of the line takes a sudden turn.
The other solution I have tried is to generate a second path that runs down one edge of the user-drawn path and using that for the <textpath>, but I'm struggling to find a way to translate the user-drawn path points into points that correspond to the rendered edge of the line.
Does anybody know a way to make either one of these methods work?
I understand that the lines need to carry a label that fills the width of the line (the lines are 15px wide).
In order to move the text I use dy="4"
text{fill:white;stroke:none;font-family:consolas;}
path{stroke-width:15px;fill:none;}
<svg viewBox="50 150 350 150">
<defs>
<path id="path" d="M70,180Q100,330 195,225Q290,120 380,250"></path>
</defs>
<use xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="#path" stroke="#000000"></use>
<text stroke="#000000" font-size="12" dy="4">
<textPath id="tp" xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="#path" startOffset="30%">
just some words I wrote
</textPath>
</text>
</svg>
An other option is using dominant-baseline="middle"
text{fill:white;stroke:none;font-family:consolas;}
path{stroke-width:15px;fill:none;}
<svg viewBox="50 150 350 150">
<defs>
<path id="path" d="M70,180Q100,330 195,225Q290,120 380,250" ></path>
</defs>
<use xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="#path" stroke="#000000"></use>
<text stroke="#000000" font-size="12" dominant-baseline="middle">
<textPath id="tp" xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="#path" startOffset="30%">
just some words I wrote
</textPath>
</text>
</svg>
I hope this is what you were asking.
You can use the dy attribute to move glyphs in a string - either individually or together - in a vertical direction relative to their orientation.
The spec chapter on <tspan> elements has a lot of practical examples on how to use the various positioning attributes (dx, dy, rotate); I'd recomend to read it.
path {
fill:none;
stroke: red;
stroke-width: 15px;
}
text {
font-family: sans-serif;
font-size: 20px;
}
<svg>
<path id="p1" d="M 25,60 60,30 H 80 V 120" />
<text dy="-7.5px">
<textPath href="#p1">abcdefghijklmn</textPath>
</text>
</svg>

SVG or HTML text that scales to fully fit a container so it stretches, bot vertically and horizontally, disregarding aspect ratio

I need to make text automatically stretch in both dimensions, to fill a container. It will distort.
This shows the the container space in red
This shows what a long name would normally resize to put in that space and maintaining aspect ratio
.
This shows what my client wants to happen
.
I would prefer to use SVG but I will work with what works.
I have searched for a solution to the best of my abilities but all seem to either refer to maintaining aspect ratio or stretching text when the page or viewbox changes dimensions.
That's quite a broad question, but yes you can do it with svg, I'll let you implement it though since you didn't provided anything to chew on.
The key point is to set your svg's preserveAspectRatio to "none":
svg{
height: 100vh;
width: 50vw;
}
body{
margin:0;
}
<div>
<svg xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 300 40" preserveAspectRatio="none">
<text x="0" y="35" font-family="Verdana" font-size="35">
Hello, out there
</text>
</svg>
</div>
If your text is already part of an SVG (as it appears in your example), you will probably need to use a nested <svg> element.
<svg width="400" height="400">
<rect width="400" height="400" fill="rebeccapurple"/>
<!-- rect representing area that our text has to squeeze into -->
<rect x="20" y="50" width="200" height="50" fill="white"/>
<!-- x y width height match above rect -->
<!-- viewBox values need to match text bounds -->
<svg x="20" y="50" width="200" height="50"
viewBox="0 8 244 28" preserveAspectRatio="none">
<text x="0" y="35" font-family="Verdana" font-size="35">
HELLO THERE
</text>
</svg>
</svg>
The hardest part is workoing out the correct values for viewBox. It needs to match the bounds of the (normal unsqueezed) text.

Scale a svg text to fit an exact width or the parent container width [duplicate]

This is likely a very simple question, but how do I get text in SVG to stretch to fit its container?
I don't care if it looks ugly from being stretched too long or high, but it needs to fits its container and be as big as possible.
Thanks
If you really don't care that the text gets ugly, here's how to fit unknown length text into a known width.
<svg width="436" height="180"
style="border:solid 6px"
xmlns="http://www.w3.org/2000/svg">
<g>
<text y="50%" textLength="436" lengthAdjust="spacingAndGlyphs">UGLY TEXT</text>
</g>
</svg>
Here is what I have come up with... Its similar to what other people have posted, but I think it resizes and scales nicely. This code will add spacing to any text roughly between 10-25 characters to make it fill the entire width of its parent. If you need longer or shorter text, just adjust the viewBox width and textLength attributes.
<svg xmlns="http://www.w3.org/2000/svg" width="100%" height="100%" viewBox='0 0 300 24'>
<text textLength='290' lengthAdjust="spacing" x='5' y="14" >
Some Unknown Text that is resizing
</text>
</svg>
Maybe this could work for you. You'd have to tweak the values, but it does resize when the parent div resizes. Here's my dabblet example. It works similarly to fittext.js
I used the ‘viewBox’ & ‘preserveAspectRatio’ attributes.
<svg><text x="50%" y="50%" dy=".3em">Look, I’m centered!</text></svg>
<svg viewBox="-50 -50 100 100" preserveAspectRatio="xMidYMid meet"><text font-size="16" dy=".3em" >I’m also resizeable!</text></svg>
other resources I looked at:
Making Sense of SVG viewBox's Madness
How to Style Scalable Vector Graphics Using CSS
You can use the textPath tag in conjunction with the text tag. If you then set the lengthAdjust attribute of the textPath tag to "spacingAndGlyphs" (you may additionally use the method attribute and set it to "stretch" to adjust the rendering method).
Example:
<div style="width: 100%">
<svg xmlns="http://www.w3.org/2000/svg" preserveAspectRatio="xMinYMin meet" viewBox="0 0 200 100"
style="border:solid 6px"
xmlns="http://www.w3.org/2000/svg">
<g>
<path id="svg-text" d="M 10 50 H 180" fill="transparent" stroke="lightgray" />
<text>
<textPath
xlink:href="#svg-text"
method="stretch"
lengthAdjust="spacingAndGlyphs"
>Beautifully resized!</textPath>
</text>
</g>
</svg>
<div>

Resources