[ 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)
my_svg.add(text1)
my_svg.save()
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)
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