Comments (10)
I don't understand "backwards compatibility"
Pillow would like to continue working as expected when users upgrade to newer versions. If text changes position, this would break expectations.
Alternatively, we could
- add new methods with the new line heights, deprecate the current methods and then after a period of at least a year remove the current methods
- keep the current behaviour as the default, but add a setting to change the text position
Neither of these are very elegant though.
from pillow.
Shall I close this and let the problem be followed in #1646 then?
from pillow.
(even "A" is not the highest character!! ex."[", "}" ). line spacing can be different line by line
I think the unfortunate reality is that each font can set the tallest character to be whatever it wants. Here's a font where 'A' is taller.
It's also feasible that a font doesn't have the '[' character. 'A' was chosen in #1574
Textbox height is wrong
If I understand correctly, you're not saying that the co-ordinates returned by draw.textbbox()
for multiline text don't match the co-ordinates of multiline text drawn by draw.text()
. Rather, draw.textbbox()
and draw.text()
are both incorrect for multiline text because you're saying the line height is incorrect?
I suggest
def _multiline_spacing(self, font, spacing, stroke_width): return ( self.textbbox((0, 0), "[", font, stroke_width=stroke_width)[3] - self.textbbox((0, 0), "[", font, stroke_width=stroke_width)[1] + spacing )
So your suggestion is to remove the vertical offset that the font chooses to apply to the text. I'm not sold on the idea that we should be ignoring the font author's idea about how much vertical gap should be above characters. If you'd like to do so in your code, that is fine.
from pillow.
So your suggestion is to remove the vertical offset that the font chooses to apply to the text. I'm not sold on the idea that we should be ignoring the font author's idea about how much vertical gap should be above characters. If you'd like to do so in your code, that is fine.
We already ignore the font author's idea about line spacing, see #1646 / #6469 (comment).
My suggestion from #6469 (comment) is:
def _multiline_spacing(self, font, spacing, stroke_width):
return font.font.height
I just don't know how we could make this change without breaking backwards compatibility for a lot of people.
from pillow.
I understand "A" can be taller than "[".
If I understand correctly, you're not saying that the co-ordinates returned by draw.textbbox() for multiline text don't match the co-ordinates of multiline text drawn by draw.text(). Rather, draw.textbbox() and draw.text() are both incorrect for multiline text because you're saying the line height is incorrect?
Sorry for confusing. I think draw.textbbox() and draw.text() are both incorrect for multiline text. because def _multiline_spacing()
add stroke_width 3 times (rather 2). ie.(self.textbbox(2) + stroke_width(1))
def _multiline_spacing(self, font, spacing, stroke_width):
return (
self.textbbox((0, 0), "A", font, stroke_width=stroke_width)[3]
+ stroke_width
+ spacing
)
from pillow.
+ stroke_width
was added #7080 for the sake of backwards compatibility. See #7059 (comment) for further info.
from pillow.
I don't understand "backwards compatibility"
I suggest 2 method. (but only font.font.height
is always tallest character's height)
def _multiline_spacing(self, font, spacing, stroke_width):
return font.font.height + 2*stroke_width + spacing
I think spacing must be out of def _multiline_spacing
, rather added for line in lines
loop
def _multiline_spacing(self, font, stroke_width):
return font.font.height + 2*stroke_width
Why i don't want use self.textbbox((0, 0), "A", font, stroke_width=stroke_width)[3]
, self.textbbox((0, 0), "A", font, stroke_width=stroke_width)[0]
can be minus value width stroke_width...
def _multiline_spacing(self, font, spacing, stroke_width):
return font.font.height + 2*stroke_width
from pillow.
Sorry for confusing. I think draw.textbbox() and draw.text() are both incorrect for multiline text. because
def _multiline_spacing()
add stroke_width 3 times (rather 2). ie.(self.textbbox(2) + stroke_width(1))
No, textbbox
returns font.getbbox
with an offset ((0,0)
in this case):
Lines 753 to 756 in 95a69ec
And
font.getbbox
only adds stroke_width once:Lines 402 to 404 in 95a69ec
Removing all except the last return value, we are left with:
top = offset[1] - stroke_width
height = size[1] + 2 * stroke_width
return ..., top + height
Inline the variables:
return ..., (offset[1] - stroke_width) + (size[1] + 2 * stroke_width)
Rearrange:
return ..., (offset[1] + size[1]) + (2 * stroke_width - stroke_width)
Simplify:
return ..., (offset[1] + size[1] + stroke_width)
but only font.font.height is always tallest character's height
No, font.font.height
is the font designer's intended line spacing, it has nothing to do with the height of the glyphs.
keep the current behaviour as the default, but add a setting to change the text position
Yeah, that is what I've been leaning towards, using the version from #8092 (comment) (but perhaps as a font object member function, since we'll need a different API if we ever want to finish #6926), but I suspect the API might get quite ugly for this.
from pillow.
Appreciate for explaination. I understand "backwards compatibility", "font.font.height is not height of the glyphs", "font.getbbox() add stroke_width once"
But, if stroke_width > offset[1]. top coords of font.getbbox() can be minus value. so, draw.textbbox() left,top can be minus value. and finally def _multiline_spacing() is not what we
def _multiline_spacing(self, font, spacing, stroke_width):
return (
self.textbbox((0, 0), "A", font, stroke_width=stroke_width)[3]
+ stroke_width
+ spacing
)
== offset[1] + size[1] + 2*stroke_width + spacing
It doesn't mean line_spacing to me,
I expected, def _multiline_spacing()
output is
def _multiline_spacing(self, font, stroke_width, spacing):
size, offset = font.font.getsize('A')
return (
max(offset[1], stroke_width) # upside
+ size[1]
+ stroke_width # downside
+ spacing
or consider font.font.height
def _multiline_spacing(self, font, stroke_width, spacing):
size, offset = font.font.getsize('A')
return (
max(font.font.height,
max(offset[1], stroke_width) # upside
+ size[1]
+ stroke_width # downside
+ spacing
)
)
from pillow.
Have a look at this diagram: https://pillow.readthedocs.io/en/stable/handbook/text-anchors.html#quick-reference
We currently calculate the line spacing as the sum of:
- height of the letter 'A' as measured from the ascender line (global font property) to the bottom line (specific to the letter 'A'),
- 2x stroke width
- user-specified spacing value
It is not a good idea to use the max
function as it is not predictable here, instead I think you meant the following:
def _multiline_spacing(self, font, stroke_width, spacing):
size, offset = font.font.getsize('A')
return (
offset[1] + stroke_width # top
+ size[1] + stroke_width # bottom
+ spacing
This would be similar to what we currently use, but instead of measuring the letter 'A' from the ascender line, it would be measuring it from the top line (specific to the letter 'A'), so even less predictable than currently.
The value font.font.height
is equal to the sum of:
- the distance between the ascender line and the descender line (of any text, it doesn't change),
- the line_gap value as specified by the font author - this cannot be obtained in any other way IIRC
I believe this would be better than what we use since:
- it measures between font lines that do not depend on the given text,
- stroke is just adding a background so I find it surprising that it affects the line spacing,
However, as stated above, we can't easily make this change without breaking backwards compatibility.
from pillow.
Related Issues (20)
- Cannot import ImageCms with no LITTLECMS2 feature installed HOT 1
- Passing a numpy.ndarray as the size to Image.resize() works in 10.3.0, but not in 10.4.0 HOT 4
- WebP image saving broken from 10.3.0 to 10.4.0 HOT 8
- Python 3.13 free-threading compatibility HOT 6
- Pillow installed via Conda on Win10 errors with "ImportError: The _imaging extension was built for another version of Pillow or PIL" HOT 7
- Release Pillow 11.0.0 on October 15, 2024
- KHKT HOT 1
- GIF extents size change HOT 4
- Resize Error HOT 2
- GIF optimizer acts wrongly with black pixels when `disposal=2`
- Add CONTRIBUTORS.md HOT 1
- Save 16-bit PGM Image? HOT 2
- Possible to add an IFD to an image's EXIF data? HOT 2
- Having trouble installing libraqm HOT 23
- `Image.fromqimage()` raises `TypeError` HOT 2
- `alpha_composite` works poorly with transparency
- Windows Docker / nanoserver support HOT 8
- Save GIF without loss of quality HOT 22
- scientific-python-nightly-wheels-publish triggered in fork HOT 1
- `ImageFont` claims to load font sizes in pixels but seems to use points? HOT 6
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from pillow.