You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
TrueType fonts rendered from top pixel instead of top of font #285
As of this commit in Pillow 2.1.0, TrueType fonts are drawn from the top pixel down, instead of from the max height of the font. This means that lines containing only half-height lowercase characters, for example, are rendered at a different height than full-height characters. Because of this, it's basically impossible to evenly space text over multiple lines.
from PIL import Image, ImageDraw, ImageFont
im = Image.new(mode='RGB', size=(150,100))
font = ImageFont.truetype("Arial.ttf", 16, encoding='unic')
draw = ImageDraw.Draw(im)
line_spacing = draw.textsize('A', font=font)[1] + 5
lines = ['hey you', 'you are awesome', 'this looks awkward']
y = 0
for line in lines:
draw.text((0, y), line, font=font)
y += line_spacing
im.save('test.png', 'PNG')
IIRC, we've already applied and reversed one patch for this or very similar behavior. I think that perhaps it's time to define exactly what behavior we want from draw.text, then test the hell out of it.
I'd propose that there should be a consistent distance from the starting point to the baseline of the text, based on the font/size. Specifically, I'd think that it should draw based on the Cap Height, and that ascender height should potentially be over that. (see http://en.wikipedia.org/wiki/Typeface#Font_metrics) There are some fonts where there's significant difference between the ascender height and the capital height (especially in script fonts, like Zapfino).
Alternately, we could add something in that would draw fonts based on the baseline position, which would be the ideal reference for getting rid of all of this, as it's the most stable point of any.
I was going to raise an issue on this as I am wanting to switch pi3d to Pillow and its behaviour is different (and wrong!) It's especially noticeable for us as we draw each character individually to a texture then uv map them onto 3D surfaces.
There is an 'anchor=None' keyword argument to the text() method maybe this could be used to vary which font metric to use to anchor the x,y location. Default behaviour should be to draw the text base line ascent (value from hhea table in truetype font) distance below the x,y location.
I don't know if the current master branch will just become 2.2.0 or otherwise how to get a revised version to test it out. Do I have to make my own branch then submit a pull request? If someone is already set up and working on this project then they could try it for me (and put in the pull request too). What I would try is to change
_imagingft.c line 338
from
ascender = 0;
to
ascender = PIXEL(self->face->size->metrics.ascender);
hopefully that would allow the user to line up separate text strings arranged horizontally, which is currently impossible! Ideally I would take out the block of code that also checks the maximum size of characters in the current string. This should be done externally, in my opinion, but at least for normal fonts the max size should fall within the ascender so give a consistent base line.
OK, not so simple as my suggestion. Obviously more changes impact on this subsequent to efd70fb. I will carry on looking at it when I have time but for the moment I have added a hack to (our project) pi3d code to prepend a '|' onto the start of each string then not display it (yes it's horrible!).
Also I noticed with this new code that supplying color for text to draw onto an rgba image seems to require an rgba tuple. This means that existing code that worked with PIL specifying text color like "#ffff00" or "cornsilk" will crash. Maybe ImageColor.py line 102+ should be
if mode == "RGB":
return color
if mode == "RGBA":
if len(color) == 3:
color = (color + (255,))
r, g, b, a = color
return r, g, b, a
I've fixed these two issues to my satisfaction: https://github.com/paddywwoof/Pillow
rgba as above
the y offset I have put into the font_getsize function, previously it always returned 0. I have also added a method FreeTypeFont.getoffset() to access the output of font_getsize as otherwise it's not possible to work out where the bottom of the string is. As I understand it it's like this picture:
I can put in a pull request if people are happy this works.
EDIT 'before' means as present, 'now' means my mod!
Activity
aclark4life commentedon Jul 12, 2013
Can you send a PR to fix this issue please? I remember some confusion at the time about it… but I thought we had it figured out. Thanks
wiredfool commentedon Jul 12, 2013
IIRC, we've already applied and reversed one patch for this or very similar behavior. I think that perhaps it's time to define exactly what behavior we want from draw.text, then test the hell out of it.
I'd propose that there should be a consistent distance from the starting point to the baseline of the text, based on the font/size. Specifically, I'd think that it should draw based on the Cap Height, and that ascender height should potentially be over that. (see http://en.wikipedia.org/wiki/Typeface#Font_metrics) There are some fonts where there's significant difference between the ascender height and the capital height (especially in script fonts, like Zapfino).
Alternately, we could add something in that would draw fonts based on the baseline position, which would be the ideal reference for getting rid of all of this, as it's the most stable point of any.
paddywwoof commentedon Jul 17, 2013
I was going to raise an issue on this as I am wanting to switch pi3d to Pillow and its behaviour is different (and wrong!) It's especially noticeable for us as we draw each character individually to a texture then uv map them onto 3D surfaces.
There is an 'anchor=None' keyword argument to the text() method maybe this could be used to vary which font metric to use to anchor the x,y location. Default behaviour should be to draw the text base line ascent (value from hhea table in truetype font) distance below the x,y location.
aclark4life commentedon Jul 17, 2013
@paddywwoof Thanks for the feedback! Hopefully a "fix" (something everyone agrees is the right fix) for this will go into 2.2.0
paddywwoof commentedon Jul 17, 2013
@aclark4life Sounds good. Any idea when 2.2.0 is scheduled for?
I've not done more than look at source on github but my vote would be for
default behaviour to replace L338 in_imagingft.c with something like
ascender = PIXEL(self->face->ascender);
aclark4life commentedon Jul 17, 2013
Three months: https://github.com/python-imaging/Pillow/issues/milestones
paddywwoof commentedon Sep 18, 2013
I don't know if the current master branch will just become 2.2.0 or otherwise how to get a revised version to test it out. Do I have to make my own branch then submit a pull request? If someone is already set up and working on this project then they could try it for me (and put in the pull request too). What I would try is to change
_imagingft.c line 338
from
ascender = 0;
to
ascender = PIXEL(self->face->size->metrics.ascender);
hopefully that would allow the user to line up separate text strings arranged horizontally, which is currently impossible! Ideally I would take out the block of code that also checks the maximum size of characters in the current string. This should be done externally, in my opinion, but at least for normal fonts the max size should fall within the ascender so give a consistent base line.
aclark4life commentedon Sep 18, 2013
Master is 2.2.0
paddywwoof commentedon Sep 19, 2013
OK, not so simple as my suggestion. Obviously more changes impact on this subsequent to efd70fb. I will carry on looking at it when I have time but for the moment I have added a hack to (our project) pi3d code to prepend a '|' onto the start of each string then not display it (yes it's horrible!).
Also I noticed with this new code that supplying color for text to draw onto an rgba image seems to require an rgba tuple. This means that existing code that worked with PIL specifying text color like "#ffff00" or "cornsilk" will crash. Maybe ImageColor.py line 102+ should be
this lets me use my existing code
paddywwoof commentedon Sep 20, 2013
I've fixed these two issues to my satisfaction: https://github.com/paddywwoof/Pillow

rgba as above
the y offset I have put into the font_getsize function, previously it always returned 0. I have also added a method FreeTypeFont.getoffset() to access the output of font_getsize as otherwise it's not possible to work out where the bottom of the string is. As I understand it it's like this picture:
I can put in a pull request if people are happy this works.
EDIT 'before' means as present, 'now' means my mod!
Merge pull request python-pillow#285 from radarhere/fribidi