0

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

henry
  • 4,244
  • 2
  • 26
  • 37
  • The values depend on where you want the text to go. You've not explained where that is. As to where the translation came from, it's your markup. – Robert Longson Jun 16 '16 at 14:37
  • @RobertLongson I want the text to fit neatly inside the viewBox without clipping – henry Jun 16 '16 at 14:41
  • get the text bounds(getBBox) and calculate the viewBox from those. – Robert Longson Jun 16 '16 at 14:43
  • I have the viewBox dimensions. I've added a codepen link - I think that'll clear up what I'm talking about – henry Jun 16 '16 at 14:45
  • 1
    To clear up what I'm saying, it would be easier to adjust the viewBox to match the text than adjust the text to match the viewBox. – Robert Longson Jun 16 '16 at 14:50
  • Please include a [mcve] in the question itself. You can use the Stack Snippets feature (the icon looks like a page with angle brackets on it) if you want it to be runnable. – Heretic Monkey Jun 16 '16 at 14:52
  • @MikeMcCaughan if you've had the page open for a while you may have missed the codepen link :) – henry Jun 16 '16 at 14:56
  • And if you didn't read my comment in full, you might have missed the words "in the question itself" :). CodePen is not guaranteed to be in business later than, say, tomorrow. If CodePen goes down, this question will no longer be relevant for future readers. – Heretic Monkey Jun 16 '16 at 15:00
  • @RobertLongson thanks for giving this time. So you're saying that the viewBox calculation was off? My intuition is that can't be the problem because text doesn't have to be scaled in order to fit (i.e. the viewBox's aspect ratio is the same as the text's) but if you can make that work I'd love to see it – henry Jun 16 '16 at 15:02
  • @MikeMcCaughan shrug - I did include an example, and a pen as a bonus. And http://meta.stackoverflow.com/questions/289731/are-we-actually-forbidden-from-linking-to-external-tools-such-as-codepen-jsfiddl – henry Jun 16 '16 at 15:04
  • Hahaha, and from the answer on that meta question: *However, you must include the [mcve] **in the question itself** [emphasis mine]*. I didn't say you *had* to use a Stack Snippet. I provided that as an option, if you want to just use code blocks that's fine. The important part is that the code necessary to answer the question is in the question, not an external site. – Heretic Monkey Jun 16 '16 at 15:08
  • What calculation? there's a viewBox and some positioned text. Nowhere are you calculating anything unless you're doing it by hand, if so you could do it in javascript automatically instead. – Robert Longson Jun 16 '16 at 15:09
  • @MikeMcCaughan I'm not trying to be argumentative, but I really don't know what you seeing ;P To make the pen I just copy-and-pasted the code block included in the initial version of the question – henry Jun 16 '16 at 15:17
  • @RobertLongson the calculation for how that text is positioned, i.e. `x="-2" y="25"` came from somewhere – henry Jun 16 '16 at 15:19
  • How did you determine those? Are you looking to do so in javascript, dynamically? – Robert Longson Jun 16 '16 at 15:30
  • The first question is what _I'm_ wondering :) I didn't determine them - they were part of an export (from Sketch). At the moment I don't have a specific need to dynamically determine them, but I'm still interested in how to do it. I've guess-and-checked various percentages of the viewBox, the font-size, etc etc. (Will add that to the question) – henry Jun 16 '16 at 16:03
  • (Revised the question to account for all the clarifications asked so far) – henry Jun 16 '16 at 16:15
  • (Added the stack snippet, just for kicks, and changed the font to Helvetica to make sure people could see it (this changed the y value to 26)) – henry Jun 16 '16 at 22:42
  • @RobertLongson in light of Paul LeBeau's final answer I see that you were probably on track for the solution, I just didn't recognize it – henry Jun 23 '16 at 18:13

1 Answers1

2

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:

  1. Its bottom right corner will be at (241,33)
  2. The text baseline start position will be at (-2,25)

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>
Paul LeBeau
  • 97,474
  • 9
  • 154
  • 181
  • 1] Good point re: Proxima Nova, I've changed to Helvetica to make sure people can see it. (Note for future readers: that changed the `y` value to 26 - that's why until now we were quoting "25".) 2] The viewBox values are not surprising: they're the size of the text (in that font and at that font size) as it would be seen just printed regularly. 3] Looks like you missed the part where I said "it's cleaned up from a Sketch export" and "The thing to notice is that these specific x and y translation values are needed to position the text correctly in the viewBox. How were they calculated?" – henry Jun 16 '16 at 22:36
  • I've added demo styles to the snippet, and added a comment that attempts to word the question another way – henry Jun 16 '16 at 22:58
  • Are you asking why Y is so big (26) compared with X (-2)? It is because the X,Y coordinates you supply to `` elements define the baseline of the text, not it's top. – Paul LeBeau Jun 17 '16 at 03:33
  • Thanks for the help! That fiddle is a beautiful clear illustration. But no, I'm wondering how to calculate the x and y translations that will get the `` to lie exactly in the `viewBox`-sized `` – in this case, the calculation that returns x=-2, y=26. – henry Jun 17 '16 at 13:39
  • I suspect that either (a) it was the other way round - ie. the viewBox was determined by the text bounds, or (b) the designer manually scaled the text in their editor until it fit the viewBox. – Paul LeBeau Jun 17 '16 at 13:54
  • As to the question "how would you calculate them yourself?" Well if you were doing it in-browser, you could add the text at some initial position and font size. Then call `getBBox()` on the text element. Then based on the bounding box returned, calculate a final position and size. Does that answer your question? – Paul LeBeau Jun 17 '16 at 13:59
  • a) I'm sure that's true: the viewBox is the correct size for the text. b) Again, the translation (and the viewBox) were calculated by the exporting app (Sketch). getBBox returns x:0, y: -(viewBox y)… this works even when the svg doesn't have a viewBox attribute, so getBBox could be used to determine the best-fit viewBox y, but sadly this doesn't shine any more light on the translation. See my updated snippet and jsfiddle – henry Jun 17 '16 at 15:22
  • Well then I really don't understand at all what you are trying to ask. Sketch didn't calculate anything. The text is at that position because that's where the author of the file put it. That's the only explanation I can give you. – Paul LeBeau Jun 18 '16 at 08:42
  • I appreciate your time :) but it does seem like we should wait on someone else. Sketch did calculate the translation, but that's beside the point - I'm wondering to calculate it. This isn't about some design decision placement, it's about where svg is placed in relation to the viewBox, as shown in the snippet. The example is generalizable: given an `` with a viewBox of the correct aspect ratio to fit a child ``, the `` *must* be translated if we want it to lie within the viewBox (i.e. so that if `svg {overflow: hidden}` the entirety of the `` will be visible). – henry Jun 21 '16 at 17:33
  • I gave you a solution, which you agreed works. But then said it doesn't help you. I don't know what else you need. https://jsfiddle.net/dxyL5w2x/1/ – Paul LeBeau Jun 22 '16 at 04:49
  • Ha we're just misunderstanding each other. I didn't see that you'd given a solution, and certainly didn't mean to say it worked haha. BUT that fiddle is definitely the solution. Looking back, I see I didn't get what you meant by "Then based on the bounding box returned, calculate a final position and size" (thought you meant "of the " not "of the "). Setting the viewBox based on the bbox is the key I was missing. Thanks, this is great! If you update your answer I'll mark it accepted – henry Jun 22 '16 at 21:17
  • Accepted! n.b. for future readers: in this example, there's no real need to specify a viewBox initially. (You may or may not be able to leave out the xmlns attribute - see http://stackoverflow.com/questions/18467982/are-svg-parameters-such-as-xmlns-and-version-needed) – henry Jun 23 '16 at 06:26