I am getting different values for text measurements (i.e. pixel width of a line of text) when using tkinter.font.Font
and PIL.ImageFont.FreeTypeFont
objects.
Example Code (python 3)
import tkinter as tk
from tkinter.font import Font
import shlex, subprocess
import PIL
"""
Get the path of the font to use with PIL
"""
def get_font_path(fname):
command_line = str('fc-match -f "%{file}\n" '+fname)
args = shlex.split(command_line)
p=subprocess.check_output(args, stderr=subprocess.STDOUT)
fontpath = p.decode('UTF-8').splitlines()[0]
print(fontpath)
return fontpath
fname = 'Courier'
fsize = 14
sampletxt = "Am I a beagle?"
fontpath = get_font_path(fname)
pilfont = PIL.ImageFont.FreeTypeFont(font=fontpath, size=fsize)
print(f'width, height: {pilfont.getsize(sampletxt)}')
The output from the above PIL measurement.
/usr/share/fonts/type1/texlive-fonts-recommended/pcrr8a.pfb
width, height: (112, 14)
Example Code Continued..
"""
The Tkinter measurements
"""
##root = tk.Tk() #Don't need this apparently
tkfont = Font(family="Courier", size=fsize)
print(f'width, height: {(tkfont.measure(sampletxt), tkfont.metrics("linespace"))}')
tkfont.actual()
The output from the above Tkinter measurement:
width, height: (168, 17)
{'family': 'gothic',
'size': 14,
'weight': 'normal',
'slant': 'roman',
'underline': 0,
'overstrike': 0}
These values are different. Obviously, tkinter
is using a different font, but from where?
Making Tkinter
use the same font that is specified in the PIL
object may fix this, but I cannot determine how tkinter
loads or uses fonts.
I've looked at this related truly custom font in tkinter question, but I am using debian (ubuntu) not Windows.
Here are some more tkinter font properties if it helps you help me.
tkinter.font.families()
## OUTPUT:
('fangsong ti',
'fixed',
'clearlyu alternate glyphs',
'courier 10 pitch',
'open look glyph',
'bitstream charter',
'song ti',
'open look cursor',
'newspaper',
'clearlyu ligature',
'mincho',
'clearlyu devangari extra',
'clearlyu pua',
'clearlyu',
'clean',
'nil',
'clearlyu arabic',
'clearlyu devanagari',
'gothic',
'clearlyu arabic extra')
tkinter.font.names()
##OUTPUT:
('TkCaptionFont',
'TkSmallCaptionFont',
'TkTooltipFont',
'font308',
'TkFixedFont',
'TkHeadingFont',
'font310',
'TkMenuFont',
'TkIconFont',
'TkTextFont',
'TkDefaultFont')
UPDATE:
I have tried using a Tkinter
font and then using the corresponding font file to create a PIL FreeType
font object and the measurements are still different....
I am pretty stumped.
text = "Where are my beards?"
### PIL Measurement ###
# The location of the fontfile corresponding to 'courier 10 pitch'
fontpath = '/usr/share/fonts/X11/Type1/c0419bt_.pfb'
font = ImageFont.FreeTypeFont(font=fontpath, size=80)
im = Image.new("RGBA", (1092, 1456), (0, 0, 0))
draw = ImageDraw.Draw(im)
draw.multiline_text((0,0), text, (255, 255, 255), font=font)
multi_size = draw.multiline_textsize(text, font=font)
print(f"multi size: {multi_size}\n")
w,h = font.getsize_multiline(text)
im.close()
del im
print(f'Width: {w}, Height: {h}\n')
#print font name
print(font.getname())
Output of PIL measurement:
multi size: (960, 69)
Width: 960, Height: 69
('Courier 10 Pitch', 'Regular')
Tkinter Font Measurement
fname = 'courier 10 pitch'
font = tkFont.Font(family=fname, size=80)
(w,h) = (font.measure(text),font.metrics("linespace"))
print(f'Width: {w}, Height: {h}\n')
print(f'{ sorted([k, v] for k,v in font.actual().items()) }')
Ouput of Tkinter Measurement:
Width: 1280, Height: 124
[['family', 'courier 10 pitch'], ['overstrike', 0], ['size', 80], ['slant', 'roman'], ['underline', 0], ['weight', 'normal']]
The Width: 960, Height: 69
of the PIL measurement vs. Width: 1280, Height: 124
of the Tkinter measurement is quite a difference.
I believe the Tkinter measurement is the accurate one, but I cannot rely on using Tkinter display on a headless server (for example).
If I am not mistaken, these two measurements should be identical because I have used the same font file for both.
Any ideas?