Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Measuring width of text (Python/PIL)

Tags:

I'm using the following two methods to calculate a sample string's rendered width for a set font-type and size:

font = ImageFont.truetype("/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf", 14) sample = "Lorem ipsum dolor sit amet, partem periculis an duo, eum lorem paulo an, mazim feugiat lobortis sea ut. In est error eirmod vituperata, prima iudicabit rationibus mel et. Paulo accumsan ad sit, et modus assueverit eum. Quod homero adversarium vel ne, mel noster dolorum te, qui ea senserit argumentum complectitur. Duo at laudem explicari deterruisset, eu quo hinc mnesarchum. Vel autem insolens atomorum at, dolorum suavitate voluptatum duo ex." #METHOD 1 draw_txt = ImageDraw.Draw(img) width, height = draw_txt.textsize(sample, font=font) print width #METHOD 2 width = 0 for c in sample:     width += font.getsize(c)[0] print width 

METHOD 1 yields a width of 3236, whereas METHOD 2 yields 3270. Why the discrepancy? Moreover, I've also noticed that shorter the sample text, smaller the discrepancy between these two methods.

What's going on under the hood? And which width can be thought of as the true width of the rendered sentence? Lastly, is there a tweak I can do to have both methods report approximately the same widths?

Note: the sample text is 445 characters long

like image 247
Hassan Baig Avatar asked May 07 '17 07:05

Hassan Baig


People also ask

How do you find the width of a string in Python?

If you want the size of the string in bytes, you can use the getsizeof() method from the sys module.

How do you calculate text size?

The calculation of font size as prescribed in DIN 1450 is based on x-heights. Font size (here x-height) is generally set depending on the viewer's distance from it. The shorter a viewer's distance from a text is, the smaller its font can be and vice-versa.

How can I tell the size of text in an image?

Once the text in the word processor looks like the text in the image, simply look at the font size that you're using in the word processor and that will be the font size that was used to create the image.


1 Answers

Kerning

You're doing two different things here:

  • Find the width of a long text.
  • Find the width of all the characters, and blindly adding them together

If you were using a monospace font, things might be different, but fonts generally use something called kerning to make the text smoother and a bit tighter.

Wikipedia says:

In typography, kerning is the process of adjusting the spacing between characters in a proportional font, usually to achieve a visually pleasing result. Kerning adjusts the space between individual letter forms, while tracking (letter-spacing) adjusts spacing uniformly over a range of characters. In a well-kerned font, the two-dimensional blank spaces between each pair of characters all have a visually similar area.

Kerning brings A and V closer with their serifs over each other

Here's some kerning of the DejaVuSans font:

font kerning showing different char widths

Under the hood

Under the hood, Pillow isn't doing much different for your two methods. It's just you're calling them in different ways.

If you add a third method to get the width of the whole sentence using the same function as in method two, you'll also get the same width as getting the whole sentence as in method one:

# METHOD 3 width = font.getsize(sample)[0] print width 

Here's Pillow's ImageDraw.textsize (from methods one and three):

def textsize(self, text, font=None, *args, **kwargs):     """Get the size of a given string, in pixels."""     if self._multiline_check(text):         return self.multiline_textsize(text, font, *args, **kwargs)      if font is None:         font = self.getfont()     return font.getsize(text) 

For single-line text, this is just returning the font.getsize, the same as method two. (And for multiline text, it just splits it into lines and returns the sum of several font.getsize calls.)

like image 57
Hugo Avatar answered Sep 30 '22 04:09

Hugo