31

I have a Python script which needs to calculate the exact size of arbitrary strings displayed in arbitrary fonts in order to generate simple diagrams. I can easily do it with Tkinter.

import Tkinter as tk
import tkFont
root = tk.Tk()
canvas = tk.Canvas(root, width=300, height=200)
canvas.pack()
(x,y) = (5,5)
text = "yellow world"
fonts = []
for (family,size) in [("times",12),("times",24)]:
    font = tkFont.Font(family=family, size=size)
    (w,h) = (font.measure(text),font.metrics("linespace"))
    print "%s %s: (%s,%s)" % (family,size,w,h)
    canvas.create_rectangle(x,y,x+w,y+h)
    canvas.create_text(x,y,text=text,font=font,anchor=tk.NW)
    fonts.append(font) # save object from garbage collecting
    y += h+5
tk.mainloop()

The results seem to depend on the version of Python and/or the system:

Python 2.5 Mac 0S X, times 12: (63,12), times 24: (128,24). Python 2.6 Mac OS X, times 12: (64,14), times 24: (127,27). Python 2.6 Windows XP, times 12: (78,19), times 24: (169,36) http://grab.by/grabs/d24a5035cce0d8032ea4e04cb8c85959.png

After Ned Batchelder mentioned it, I discovered that the size of fonts differs from platform to platform. It may not be a deal breaker as long as you stick with Tkinter, which remains consistent with itself. But my complete program does not use Tkinter to perform the actual drawing: it just relies on its font size calculations to generate an output (in SVG or as a Python script to be sent to Nodebox). And it's there that things go really wrong:

Output of mocodo http://grab.by/grabs/f67b951d092dd1f4f490e1469a53bca2.png

(Please look at the image in real size. Note that the main font used for these outputs is not Times, but Trebuchet MS.)

I now suspect that such discrepancies can't be avoided with Tkinter. Which other cross-platform solution would you recommend?

Cœur
  • 37,241
  • 25
  • 195
  • 267
Aristide
  • 3,606
  • 2
  • 30
  • 50
  • 1
    Did you take screenshots of the text in these fonts, and compare them with the values you are getting? – Justin Ethier May 27 '10 at 15:08
  • I don't see what sort of "cross platform" solution you wish to have... different platforms and different hardware use various font resolutions and font rendering algorithms. I hardly think you could get a constant answer for all of them. – Assaf Lavie Jun 01 '10 at 18:31
  • 1
    Please update the images if you can, as they're gone now. – Nae Feb 11 '18 at 05:28
  • @Nae indeed, sorry about that :( TinyGrab seems dead now. Google gives me no image with the filename, and Wayback Machine tells me: "No URL has been captured for this domain" for this SO page. Are there other options? I have no local copy. – Aristide Feb 11 '18 at 09:23
  • @Aristide I'm unaware if any. – Nae Feb 11 '18 at 10:42

4 Answers4

20

You have two problems. Let's tackle them one at a time

1: the difference between python 2.5 and 2.6 on the same platform with the same font

These two versions of python use different versions of tk. On my mac box, 2.5 uses tk version 8.4.19 and 2.6 uses 8.5.7. In version 8.5.2 of tk were some changes to the font measurement features of tk. Assuming that the changes were improvements, I think it's safe to assume that the numbers you get from python 2.6 are more accurate than the ones from 2.5.

2: the difference between python 2.6 on the mac and 2.6 on the PC.

Obviously, from the screenshots you include, the PC is using a larger font and thus you get larger numbers for the measurement. The question is, why? You are specifying the font size in points (1/72 of an inch). In order for Tk (or any rendering system) to render the font, it needs to know how many pixels are in an inch on the actual display. This will vary on different systems, and Tk isn't always given an accurate number by the underlying OS in order to do its calculations.

Historically, Apple and Microsoft have standardized on 72ppi and 96ppi regardless of the actual display, so the numbers are always going to be different. For more information about the differences in how the mac and windows calculate pixel density see the Dots Per Inch article on wikipedia.

You might try solving this by specifying a font in pixels rather than in points. You can do this by using negative numbers for the font size.

Finally, one thing you might add to your little example code is to print out the result of the font.actual() command -- you might see something different between your windows and mac boxes, which would explain the differences there. This tells you exactly which font is being used by Tk.

Bryan Oakley
  • 370,779
  • 53
  • 539
  • 685
  • Thank you so much for your answer! You made me understand and fix the two problems. The first one came from my version of Python 2.6: namely, 2.6.1, which actually suffers of a regression bug concerning the font metrics: updating to Python 2.6.5 was the easy solution. The second problem disappeared too when I used negative numbers to specify the size of the fonts. This behavior was actually explained in the Tk documentation at http://www.tcl.tk/man/tcl8.4/TkCmd/tk.htm, which I didn't know. All rocks now and I'm very grateful! – Aristide Jun 01 '10 at 18:13
  • @Aristide: If my answer or any other answer helps you, please consider marking the answer as 'accepted' -- it's a part of your duty as a question-asker. – Bryan Oakley Jun 01 '10 at 20:28
  • your answer did help me. I've already marked it as useful, but for the rest of my duty, the system keeps saying: "You may accept this answer and award your bounty in 10 hours". I suppose there's a minimum delay of 24h. Please be assured I will gladly retry tomorrow! – Aristide Jun 01 '10 at 20:51
  • But is font size still measured in points? Does it have any relation with screen resolution? – Delrius Euphoria Dec 25 '20 at 19:18
  • 1
    @CoolCloud: yes, fonts are typically defined in points. Different monitors have different pixel densities though. Not all displays are literally 72dpi. – Bryan Oakley Dec 25 '20 at 20:08
  • Oh, I see, so is there anyway to get them to be dynamic? By getting the dpi of the display? or something? – Delrius Euphoria Dec 25 '20 at 20:13
7

After searching for ages I finally found out a way to get the width of some text in any font and size!

from tkinter import *

Window = Tk()
Window.geometry("500x500+80+80")

frame = Frame(Window) # this will hold the label
frame.pack(side = "top")

# CALCULATE:
measure = Label(frame, font = ("Purisa", 10), text = "The width of this in pixels is.....", bg = "yellow")
measure.grid(row = 0, column = 0) # put the label in
measure.update_idletasks() # this is VERY important, it makes python calculate the width
width = measure.winfo_width() # get the width

# PROOF IT WORKS:
canvas = Canvas(frame, width = 400, height = 200, bg = "light green")
canvas.grid(row = 1, column = 0, columnspan = 100) # collumnspan is 100 so that the line lines up with the text
line = canvas.create_line(0, 10, width, 10, width = 4) # make a line the same length as the text
canvas.create_text(10, 20, font = ("Purisa", 10), text = "... "+str(width)+" Pixels", anchor = "nw")

The line I make is proof that this works for any font.

I have tested this for different fonts and sizes, as far as I know it works.

This is a picture of the output: This is a picture of the output

Sam
  • 1,509
  • 3
  • 19
  • 28
Tom Fuller
  • 5,291
  • 7
  • 33
  • 42
  • 1
    this will stop all the users who think its funny to type WWWWWWWWWWW so it doesn't fit on the screen!!!!!!!!!!! – Tom Fuller Dec 21 '15 at 16:46
5

You haven't done anything wrong: the size of fonts does differ from platform to platform.

I'm not sure why the Python version matters, but the differences are only one pixel, so it could be different rounding or different rendering of the same font.

Ned Batchelder
  • 364,293
  • 75
  • 561
  • 662
  • 2
    the python version matters because the tk version matters -- font measurements changed between tk 8.4.x and 8.5.x (the versions used by python 2.5 and 2.6, respectively). – Bryan Oakley Jun 01 '10 at 18:09
0

In tkinter if font size is positive it is in points(1 point (abbreviated pt) is equal to 1/72 of an inch) if font size is given in negetive values it is in pixels.

  • Your answer could be improved with additional supporting information. Please [edit] to add further details, such as citations or documentation, so that others can confirm that your answer is correct. You can find more information on how to write good answers [in the help center](/help/how-to-answer). – Community Sep 25 '21 at 07:22