Reputation: 4385
I'm trying to understand <text>
positioning in an svg.
Say you have <svg viewBox="..."><text>...</text></svg>
, where the viewBox is correctly sized to fit the text given its (known) font, font weight, and font size. Without giving the <text>
an x="..." y="..."
translation, it won't be positioned "correctly" in the viewBox: it will be too high, and a little to the right.
Can I calculate those x
and y
translation values? That could be without analyzing the drawn <text>
if it the translation isn't dependent on the font's characteristics, or analyzing the drawn <text>
if it does depend on the font's characteristics.
Here's an example (also a jsfiddle: https://jsfiddle.net/enry/Lnzxx0jx/). This was exported from Sketch — that's why I don't have access to the calculation that went into the x and y translations. The <svg>
is the thing with the red outline. The thing to notice is that these specific x and y translation values are needed to position the text in the viewBox in such a way that if the <svg>
's overflow is hidden the entirety of the <text>
is visible.
text {
fill: #000;
fill-rule: evenodd;
font-weight: 700;
font-family: Helvetica-Bold, Helvetica;
font-size: 36px
}
/* just for demo purposes */
html {
padding: 20px;
background: #ccc
}
svg {
outline: 1px solid red;
background: #fff;
overflow: hidden /* in case you want to play around with the translation */
}
translated
<svg viewBox="0 0 258 34" xmlns="http://www.w3.org/2000/svg">
<text x="-2" y="26"><!-- << the question:
if these values weren't provided,
how would you calculate them? -->
my sample text
</text>
</svg>
not translated
<svg viewBox="0 0 258 34" xmlns="http://www.w3.org/2000/svg">
<text><!-- fwiw, the getBBox of the text without translation is
somewhere around -33 - -34, depending on the svg's actual size-->
my sample text
</text>
</svg>
So why are the translation values x="-2" and y="26"? I've tried various percentages of the viewBox dimensions; various percentages of font-size; and fractions relating the viewBox to the font-size… but can't figure it out.
They could be dependent on the kerning and leading — the space around the letters. In that case I suppose we can't know the necessary translation until after draw, at which point maybe the space around the characters could be measured with js/jquery? If so, how would this be done (I don't have any experience using a script to analyze printed characters). Fwiw (this was brought up in the discussion), the untranslated text's getBBox is x:0, y:-[something very nearly 34, depending on the svg's actual size] — so, y is very nearly -(viewBox y). Because getBBox doesn't need the svg to have a viewBox, this could be used to determine the viewBox y dynamically… but it doesn't seem to help with the translation calculation.
Or is it some calculation based on the viewBox? In my experiments, the translation values are dependent on font and font-size… but then so is the viewBox, so maybe the viewBox dimensions are all we need in order to calculate the translations.
My guess: The kerning makes sense for the x translation - I can imagine svg getting tripped up by leading whitespace, and that would explain why it has to be pulled to the left a little. Part of the y translation is moving the baseline down to the bottom of the viewBox, but given the fact that x has to be translated I expect y is baseline plus some adjustment… and that calls for calculating both the adjustment (maybe leading?) and the text height (cap height + potentially an ascender height + potentially a descender height - ["typeface anatomy" illustration])
Upvotes: 0
Views: 867
Reputation: 101820
Your question is confusing because we would expect that you would know better (than random strangers on the internet) what the answer is..
My real question is where x="-2" and y="25" came from.
That's like asking why a book is blue, or why is it at the left end of a shelf. It's there because that's where you - or whoever designed the SVG - put it.
In case it is not obvious to you already, SVGs are not HTML. Things are not automatically positioned like they are in HTML. You have to specify the position and size of elements in an SVG document.
If you want to know why the text is positioned where it is, go ask the designer.
My guess
..is that if you take the given text and make its size 36px, set its font to "Proxima Nova", and you position it so it is in the top-left of the page, then:
Thereby explaining your resulting text x,y coords and your viewBox values.
But I don't have that font, so I can't be sure.
Update
If you need to re-position the text in-browser, you can add the text at some initial position and font size. Then call getBBox() on the text element. That will give you the bounds of the text relative to your initial text position. Based on that, you can calculate a final position and size. You can also modify the viewBox so the text is correctly scaled if you need to.
// Find the text element
var mytext = document.getElementById("mytext");
// Get it's bounding box
var bbox = mytext.getBBox();
// Reposition text using the values from its bounding box
mytext.setAttribute("x", -bbox.x);
mytext.setAttribute("y", -bbox.y);
// Top left of text should now be at top-left of SVG (0,0)
// Now update the SVG viewBox so that it is fitted to the text
document.getElementById("mysvg").setAttribute("viewBox", "0 0 "+bbox.width+" "+bbox.height);
// Note that the bbox of the text is not tightly fitted to the text. It includes the em boxes
text {
fill: #000;
fill-rule: evenodd;
font-weight: 700;
font-family: Helvetica-Bold, Helvetica;
font-size: 36px
}
/* just for demo purposes */
html {
padding: 20px;
background: #ccc
}
svg {
outline: 1px solid red;
background: #fff;
overflow: hidden /* in case you want to play around with the translation */
}
<svg id="mysvg" viewBox="0 0 258 34" xmlns="http://www.w3.org/2000/svg">
<text id="mytext" x="0" y="0">my sample text</text>
</svg>
In the above example, we are moving the text position. But we don't really need to. Setting just the viewBox will have the same result.
// Find the text element
var mytext = document.getElementById("mytext");
// Get it's bounding box
var bbox = mytext.getBBox();
// Now update the SVG viewBox so that it is fitted to the text
document.getElementById("mysvg").setAttribute("viewBox", [bbox.x, bbox.y, bbox.width, bbox.height].join(' '));
text {
fill: #000;
fill-rule: evenodd;
font-weight: 700;
font-family: Helvetica-Bold, Helvetica;
font-size: 36px
}
/* just for demo purposes */
html {
padding: 20px;
background: #ccc
}
svg {
outline: 1px solid red;
background: #fff;
overflow: hidden /* in case you want to play around with the translation */
}
<svg id="mysvg" viewBox="0 0 258 34" xmlns="http://www.w3.org/2000/svg">
<text id="mytext" x="0" y="0">my sample text</text>
</svg>
Upvotes: 2