svgdom icon indicating copy to clipboard operation
svgdom copied to clipboard

rbox for text element doesn't take letter-spacing into account

Open kimmobrunfeldt opened this issue 7 years ago • 4 comments

Hi again!

I'm trying to get a tight bounding box of a text, but I'm seeing a bit different results between Chrome and svgdom.

This first result is from Chrome, using svg.querySelector('#small-header').getBBox(). screenshot 2017-06-18 13 30 41

This second result is with [email protected], [email protected] + svgdom (latest master) combo, using .rbox() method for the tspan element (not the text element as with Chrome). I used tspan because rbox for the text element gave even larger bounding box which I suppose vertically starts at y:3727 and ends at y:3727+417 (y attribute of the tspan).

screenshot 2017-06-18 13 33 33

My SVG is this:

<svg height="4700px" version="1.1" viewBox="0 0 3500 4700" width="3500px" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
    <!-- ... -->
    <g id="labels" transform="translate(1773.000000, 3727.000000)">
        <g id="center" text-anchor="middle">
            <text fill="#000000" font-family="JosefinSans, Josefin Sans" font-size="90" font-weight="normal" id="small-header" letter-spacing="18.363636">
                <tspan x="0" y="417">TEST STRING</tspan>
            </text>
        </g>
    </g>
    <!-- ... -->
</svg>

JosefinSans font loads correctly, using the font mappings. I'm pretty sure this happens because of the letter-spacing attribute. Bounding box was correct when there's no letter-spacing.

I would be interested to fix this behavior in svgdom, but do you have some pointers how to implement it? I quickly checked fontkit but didn't see any mentions of letter spacing.

kimmobrunfeldt avatar Jun 18 '17 10:06 kimmobrunfeldt

The text handling of svgdom is not perfect at all. It just grabs the font family and font size. Nothing else is taken into account. Of course that can be improved. I am not sure how letter spacing is applied to the single glyphs but I am certain that this could be implemented. The right place to look at it is the bbox utils where the text bbox gets calculated. I think fontkit can give you glyphs width with and without spacing applied. Atm I use width with spacing applied. Maybe we need to change that so we can calculate the space ourselves

Fuzzyma avatar Jun 18 '17 16:06 Fuzzyma

I tried to replace the fontkit implementation with opentype.js with the following code (taken out of context):

var fontPath = font.getPath(text, x, y, details.fontSize, {
    tracking: details.letterSpacing,
});
var bbox = fontPath.getBoundingBox();

However it didn't result to the correct width either. I'm rendering the actual SVG image with sharp which uses GNOME librsvg under the hood, so there might be inconsistency between how they calculate letter spacing.

kimmobrunfeldt avatar Jun 18 '17 17:06 kimmobrunfeldt

I used opentype first. I didn't like that it has that much dependencies. As I already said: fontkit gives you multiple width with every glyph. Take a look at the documentation. I'm pretty sure there is a width which does not include letter spacing No they dont have :/

Fuzzyma avatar Jun 18 '17 17:06 Fuzzyma

To add an update here, I didn't have time to dig this more and ended up solving this with a minor hack. When I get the rbox result, I manually add letter spacings to the width of the bounding box and reconstruct the bounding box. In code: width: rbox.width + (letterSpacing * (text.length - 1)),.

I don't know how generic this is but it worked well enough in practice in my use case.

kimmobrunfeldt avatar Jun 24 '17 12:06 kimmobrunfeldt