[ACCEPTED]-How to get width of a truetype font character in 1200ths of an inch with Python?-truetype
Raw text widths are usually calculated in 38 typographer's points, but since the point for the purpose of 37 font definitions is defined as 1/72 of an 36 inch, you can easily convert it into any 35 other unit.
To get the design width of a character (expressed 34 in em units), you need access to the low-level 33 data of the font. The easiest way is to 32
pip install fonttools, which has everything to work at the lowest 31 possible level of font definitions.
With 30 fontTools installed, you can:
load the font 29 data – this requires the path to the actual 28 font file;
character widths are stored as 27 glyph widths, meaning you must retrieve a 'character-to-glyph' mapping; this 26 is in the
cmaptable of a font:
a. load the
cmapfor 25 your font. The most useful is the Unicode 24 map – a font may contain others. b. load 23 the glyph set for your font. This is a list of names for 22 the glyphs in that font.
Then, for each Unicode 21 character, first look up its name and then 20 use the name to retrieve its width in design 19 units.
Don't forget that the 'design units' is 18 based on the overall 'design width' of a 17 font. This can be a standard value of
1000(typical 16 for Type 1 fonts),
2048(typical for TrueType 15 fonts), or any other value.
That leads to 14 this function:
from fontTools.ttLib import TTFont from fontTools.ttLib.tables._c_m_a_p import CmapSubtable font = TTFont('/Library/Fonts/Arial.ttf') cmap = font['cmap'] t = cmap.getcmap(3,1).cmap s = font.getGlyphSet() units_per_em = font['head'].unitsPerEm def getTextWidth(text,pointSize): total = 0 for c in text: if ord(c) in t and t[ord(c)] in s: total += s[t[ord(c)]].width else: total += s['.notdef'].width total = total*float(pointSize)/units_per_em; return total text = 'This is a test' width = getTextWidth(text,12) print ('Text: "%s"' % text) print ('Width in points: %f' % width) print ('Width in inches: %f' % (width/72)) print ('Width in cm: %f' % (width*2.54/72)) print ('Width in WP Units: %f' % (width*1200/72))
The result is:
Text: "This is a test" Width in points: 67.353516 Width in inches: 0.935465 Width in cm: 2.376082 Width in WP Units: 1122.558594
and is correct 13 when comparing to what Adobe InDesign reports. (Note 12 that per-character kerning is not applied here! That 11 would require a lot more code.)
Characters 10 that are not defined in the font are silently 9 ignored and, as usually is done, the width 8 for the
.notdef glyph gets used. If you want this 7 reported as an error, remove the
if test in 6 the function.
The cast to
float in the function 5
getTextWidth is so this works under both Python 2.7 4 and 3.5, but note that if you use Python 3 2.7 and larger value Unicode characters 2 (not plain ASCII), you need to rewrite the 1 function to correctly use UTF8 characters.
This worked better for me:
def pixel_width(unicode_text): width=len(unicode_text)*50 height=100 back_ground_color=(0,0,0) font_size=64 font_color=(255,255,255) im = Image.new ( "RGB", (width,height), back_ground_color ) draw = ImageDraw.Draw (im) unicode_font = ImageFont.truetype("./usr/share/fonts/truetype/dejavu/DejaVuSansMono.ttf", font_size) draw.text ( (0,0), unicode_text, font=unicode_font, fill=font_color ) im.save("/dev/shm/text.png") box = Image.open("/dev/shm/text.png").getbbox() return box - box
More Related questions