-
-
Notifications
You must be signed in to change notification settings - Fork 2.2k
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
Character bounding boxes and negative offsetx #4789
Comments
Ah, I think you've found an issue I missed when writing some of my previous comments. It seems that the C code is adding (actually subtracting) the This means that I'll have a look at what effects this finding has for #4724 regarding The reason you need to add the x-offset a second time is that You can access the offset with Why is the top edge slightly off? I would guess this #1668. Here is the code I have come up with that should work for you: (click to expand)
def test_bbox(font, txt):
img = Image.new('RGB', (300, 100), 'white')
draw = ImageDraw.Draw(img)
xref, yref = 50, 5
draw.line((0, yref, 300, yref), 'gray')
draw.line((xref, 0, xref, 100), 'gray')
draw.text((xref, yref), txt, 'black', font)
for i, c in enumerate(txt):
text_wd, text_ht = font.getsize(txt[:i+1])
text_ox, text_oy = font.getoffset(txt[:i+1])
text_ht -= text_oy # text_ht is unused
text_x1, text_y1 = xref + text_ox, yref
wd, ht = font.getsize(c)
ox, oy = font.getoffset(c)
ht -= oy
x1, y1 = text_x1 + text_wd - wd, text_y1 + oy
x2, y2 = text_x1 + text_wd, text_y1 + oy + ht
draw.rectangle((x1, y1, x2, y2), outline='red')
return img
italic_font = ImageFont.truetype('Baskervville-Italic.ttf', 60, layout_engine=ImageFont.LAYOUT_BASIC)
test_bbox(italic_font, 'fa').show()
test_bbox(italic_font, 'af').show() Perhaps this (or a variation) should be added to the docs? It seems character bounding boxes are a common question. In #4724 I proposed new functions to make this much simpler to understand. An (untested) example for how this should work using the proposed functions (click to expand)
def test_bbox(font, txt):
img = Image.new(mode='RGB', size=(300,100), color='white')
draw = ImageDraw.Draw(img)
xref, yref = 50, 5
draw.text((xref, yref), txt, font=font, fill='black')
x0, y0 = xref, yref
for i, c in enumerate(txt):
x1, y1, x2, y2 = font.getbbox(c)
draw.rectangle((x0 + x1, y0 + y1, x0 + x2, y0 + y2), fill=None, outline='green', width=1)
x0 += font.getlength(c)
return img
italic_font = ImageFont.truetype('fonts/google/fonts-master/ofl/baskervville/Baskervville-Italic.ttf', 60, layout_engine=ImageFont.LAYOUT_BASIC)
# show_grid just uses matplotlib ax.imshow to display the image
show_grid(test_bbox(italic_font, 'fa'))
show_grid(test_bbox(italic_font, 'af')) |
Regarding @indigoviolet's point 3:
I don't think @nulano's explanation is correct:
#1668 actually makes the bottom and right edges of the red bounding boxes to be off by one. To work around that, this line in @nulano's first example code:
Would have to be changed to this:
That of course does not address why the top edge is off. This seems to have something to do with the subpixel font rendering and rounding. If I change the text from |
I did not originally notice that the From #4910: I was only noting that due to #1668, it is expected that the top edge will be slightly obscured by the bounding box, while the bottom edge won't. I was hinting at the opposite idea of decrementing |
Pillow 8.0.0 has now been released supporting the new API:
def test_bbox(font, txt):
img = Image.new(mode='RGB', size=(300,100), color='white')
draw = ImageDraw.Draw(img)
xref, yref = 50, 5
draw.text((xref, yref), txt, font=font, fill='black')
x0, y0 = xref, yref
for i, c in enumerate(txt):
draw.rectangle(draw.textbbox((x0, y0), c, font=font), fill=None, outline='green', width=1)
x0 += draw.textlength(c, font=font)
return img
italic_font = ImageFont.truetype('Baskervville-Italic.ttf', 60, layout_engine=ImageFont.LAYOUT_BASIC)
test_bbox(italic_font, 'fa').show()
test_bbox(italic_font, 'af').show() This example does not adjust for kerning. Kerning currently has no effect in |
It sounds like this issue is resolved then? |
For anyone looking at this in the future, I have adapted my comment above using the new functions into a Stack Overflow answer here: https://stackoverflow.com/a/70636273/1648883 This version also has a guide on how to adjust for kerning. |
What did you do?
I'm trying to get character level bounding boxes for some text I'm generating, following #3921, and @nulano 's comment, this stackoverflow answer etc. Also tagging #4724 in the hope that this issue will be addressed by it.
What did you expect to happen?
I expected the red bounding box to encompass the
a
in the first image (fa
).What actually happened?
It was shifted to the right.
Also, while flailing around, I realized that the green bounding box, which is created by an additional
offsetx
shift, does appear to encompass thea
. I don't understand why this would be, but it might just be coincidence.Neither of these completely bound the
a
on the top edge. Why would that be?What are your OS, Python and Pillow versions?
The text was updated successfully, but these errors were encountered: