Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

TrueType fonts rendered from top pixel instead of top of font #285

Closed
neruson opened this issue Jul 12, 2013 · 10 comments · Fixed by #333
Closed

TrueType fonts rendered from top pixel instead of top of font #285

neruson opened this issue Jul 12, 2013 · 10 comments · Fixed by #333

Comments

@neruson
Copy link

neruson commented Jul 12, 2013

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')

test

@aclark4life
Copy link
Member

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
Copy link
Member

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
Copy link
Contributor

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
Copy link
Member

@paddywwoof Thanks for the feedback! Hopefully a "fix" (something everyone agrees is the right fix) for this will go into 2.2.0

@paddywwoof
Copy link
Contributor

@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
Copy link
Member

@paddywwoof
Copy link
Contributor

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
Copy link
Member

Master is 2.2.0

@paddywwoof
Copy link
Contributor

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

this lets me use my existing code

@paddywwoof
Copy link
Contributor

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:
temp
I can put in a pull request if people are happy this works.
EDIT 'before' means as present, 'now' means my mod!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

4 participants