TAGS :Viewed: 2 - Published at: a few seconds ago

[ How to determine text width and height when using svgwrite for python? ]

I want to extract pixel size of the text object (svgwrite.Drawing.Text) as it would appear in file after formatting with given style. My font is fixed-width (Courier New).

The reason why I need it is that I want to print to SVG file a string and then map on the resulting text some information: e.g. I have a string "ABCDEF" and external wise man told me that "BCD" portion should be marked. Then I need to know how many pixels (units?) are covered by symbol "A" and symbols "BCD" in both dimensions, and then draw a colored rectangle, or transparent frame, or whatever strictly under the "BCD" portion.

So I have the following code and I would expect to use something like "w = text1.width" to extract width, but it doesn't work this way. Thank you in advance for trying to answer my question.

import svgwrite
my_svg = svgwrite.Drawing(filename = "dasha.svg", size = ("800px", "600px"))
text_style = "font-size:%ipx; font-family:%s" % (12, "Courier New") 
text1 = my_svg.text("HELLO WORLD", insert=(0, 0), fill="black", style=text_style)

UPD1 [22.06.2014]: Intermediate solution which I use at the moment is to measure height and width of the letter with particular font and size manually in Inkscape. I tried my best, but I am not sure such values are perfect, and now I can't change font size in the program.

Answer 1

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