Jakub M.
Jakub M.

Reputation: 33827

get svg text size in python

I am generating SVG image in python (pure, no external libs yet). I want to know what will be a text element size, before I place it properly. Any good idea how to make it? I checked pysvg library but I saw nothing like getTextSize()

Upvotes: 5

Views: 2292

Answers (2)

Douglas Kastle
Douglas Kastle

Reputation: 739

I had this exact same problem, but I had a variable width font. I solved it by taking the text element (correct font and content) I wanted, wrote it to a svg file, and I used Inkscape installed on my PC to render the drawing to a temporary png file. I then read back the dimensions of the png file (extracted from the header), removed the temp svg and png files and used the result to place the text where I wanted and elements around it.

I found that rendering to a drawing, using a DPI of 90 seemed to give me the exact numbers I needed, or the native numbers used in svgwrite as a whole. -D is the flag to use so that only the drawable element, i.e. the text, is rendered.

os.cmd(/cygdrive/c/Program\ Files\ \(x86\)/Inkscape/inkscape.exe -f work_temp.svg -e work_temp.png -d 90 -D)

I used these functions to extract the png numbers, found at this link, note mine is corrected slightly for python3 (still working in python2)

def is_png(data):
    return (data[:8] == b'\x89PNG\r\n\x1a\n'and (data[12:16] == b'IHDR'))

def get_image_info(data):
    if is_png(data):
        w, h = struct.unpack('>LL', data[16:24])
        width = int(w)
        height = int(h)
    else:
        raise Exception('not a png image')
    return width, height

if __name__ == '__main__':
    with open('foo.png', 'rb') as f:
        data = f.read()

    print is_png(data)
    print get_image_info(data)

It's clunky, but it worked

Upvotes: 1

Roland Smith
Roland Smith

Reputation: 43495

This can be be pretty complicated. To start with, you'll have to familiarize yourself with chapter on text of the SVG specification. Assuming you want to get the width of plain text elements, and not textpath elements, at a minimum you'd have to:

  • Parse the font selection properties, spacing properties and read the xml:space attibute, as well as the writing-mode property (can also be top-bottom instead of just left-to-right and right-to-left).
  • Based on the above, open the correct font, and read the glyph data and extract the widths and heights of the glyphs in your text string. Alone finding the font can be a big task, seeing the multiple places where font files can hide.
  • (optionally) Look through the string for possible ligatures (depending on the language), and replace them with the correct glyph if it exists in the font.
  • Add the widths for all the characters and spaces, the latter depending on the spacing properties and (optionally) possible kerning pairs.

A possible solution would be to use the pango library. You can find python bindings for it in py-gtk. Unfortunately, except from some examples, the documentation for the python bindings is pretty scarce. But it would take care of the details of font loading and determining the extents of a Layout.

Another way is to study the SVG renderer in your browser. But e.g. the support for SVG text in Firefox is limited.

Also instructive is to study how TeX does it, especially the concept (pdf) of boxes (for letters) and glue (for spacing).

Upvotes: 1

Related Questions