python-pillow / Pillow

Python Imaging Library (Fork)
https://python-pillow.org
Other
12.35k stars 2.24k forks source link

`ImageFont` claims to load font sizes in pixels but seems to use points? #8258

Closed xmo-odoo closed 4 months ago

xmo-odoo commented 4 months ago

Per the documentation

size – The requested size, in pixels.

load_default does not clarify but I assume it's similar.

Now I will readily confess I'm not well versed in fonts, but my understanding was that the font-size, or em-size, is the size from the ascent to the descent. That is, the sum of the values FreeTypeFont.getmetrics returns. However when I test this:

>>> ImageFont.load_default(16).getmetrics()
(16, 4)
>>> f = ImageFont.truetype("Ubuntu-R.ttf", size=16)
>>> f.getmetrics()
(15, 4)

This does however align with the font-size being in points, and a point being 4/3 pixels (aka fixed resolutions of 72 dpi and 96 ppi which are correct for just about nothing but are common fixed values notably matching CSS's): 16(4/3) is 21.333..., which doesn't quite match the font sizes of 20 and 19 pixels we get from the metrics but seems reasonably close and could be chalked up to the font itself: just by sampling a bit I get metrics down to (13, 4) (Lato) and up to (18, 5) (Noto, Open).

Not really anything which truly reconciles with size being a pixel size though, Lato is the closest and it's a pixel above still.

nulano commented 4 months ago

This seems to be the opposite of #6079.

Has something changed since, or is one of these questions incorrect?

radarhere commented 4 months ago

Comparing with Gimp, I would still conclude that we're using pixels and not points.

Looking at

>>> from PIL import ImageFont
>>> ImageFont.load_default(120).getmetrics()
(117, 28)

I definitely don't think that anything needs to be adjusted by 4/3.

xmo-odoo commented 4 months ago

Comparing with Gimp, I would still conclude that we're using pixels and not points.

It certainly matches the pixel version indeed.

The point size I'm not sure is really relevant without knowing what conversion ratio was used (the point size, in pixels) e.g. if you create a project for printing at say 600ppi, you'll have 600 pixels / inch and 72 points / inch, or 8.33 pixels per point.

But different backends will work at different pixel densities, and it is my understanding that computer graphics commonly use a fixed 96 pixels per inch because reasons (that's what's hard-coded into CSS for instance), regardless of the actual display.

Looking at

>>> from PIL import ImageFont
>>> ImageFont.load_default(120).getmetrics()
(117, 28)

I definitely don't think that anything needs to be adjusted by 4/3.

From my understanding the font size is defined from ascent to descent, so you load a font requesting a font size of 120 and you get a font size of 145.

Is it just expected that you get some best effort approximation of the request? And possibly that the error gets magnified at low sizes? (here the "error" is 20% rather than the 30 I get at lower sizes)

nulano commented 4 months ago

Has something changed since

Something has changed since, but it is inconsequential for this issue: #7107 inlined our call to FT_Set_Pixel_Sizes, so we now directly call FT_Request_Size instead: https://github.com/python-pillow/Pillow/blob/6dd4b3c826512b6e50f7343951bcb3650087119b/src/_imagingft.c#L211-L217

From the FT_Size_RequestRec documentation:

horiResolution: The horizontal resolution (dpi, i.e., pixels per inch). If set to zero, width is treated as a 26.6 fractional pixel value, which gets internally rounded to an integer. vertResolution: The vertical resolution (dpi, i.e., pixels per inch). If set to zero, height is treated as a 26.6 fractional pixel value, which gets internally rounded to an integer.

We are passing in zero for both of these, so we are in fact requesting the size in pixels, not points.

As for why it might seem incorrect to you, FT_Request_Size documentation may have the answer:

The relation between the requested size and the resulting glyph size is dependent entirely on how the size is defined in the source face. The font designer chooses the final size of each glyph relative to this size. For more information refer to ‘https://www.freetype.org/freetype2/docs/glyphs/glyphs-2.html’.

xmo-odoo commented 4 months ago

Ah that makes sense, I thought the relationship between the requested font size and the observed dimensions would be much stricter, and the observed deviation seemed to match the relationship between pixels and points on digital devices / computer graphics hence my confusion.

Thanks a bunch.

xmo-odoo commented 4 months ago

Also FWIW the link doesn't work because github made the trailing quote part of the link.