6

此图像是使用 PIL 创建的。看看这张图片中的 g 和 y 是如何被截断的?我怎样才能防止这种情况?

http://img109.imageshack.us/img109/8874/screenshotep.png

创建此图像的代码非常简单(缩写):

import Image, ImageDraw, ImageFont

im = Image.new("RGBA", (200, 200), 'white')
draw = ImageDraw.Draw(im)

font = ImageFont.truetype("VeraSe.ttf", 12)

draw.text(
           (1, 1),
           " %s: " % "ggjyfFwe__",
           font=font,
           fill='black'
)

draw.text(
           (1, 30),
           " %s" % 15,
           font=font,
           fill='black'
)

im.show()

我尝试了几种不同的字体,但它总是被剪裁。令人惊讶的是,谷歌搜索“PIL 字体剪辑”返回的有用点击很少......我在 Ubuntu 9.10 上使用 python 2.6.4 和 PIL 1.1.6

4

5 回答 5

3

这是这个较旧问题的较晚答案。

问题似乎是 PIL 和 Pillow 将剪辑呈现文本的边缘。这最常显示在尾随的宽字符和后缀(如 'y's)上。这也可能出现在某些字体的顶部。这个问题至少有十年了。text()无论调用的图像大小如何,都会发生这种情况。冲突似乎是将边界矩形选择为“font.size * number_chars”而不是“我实际需要渲染的任何内容”,这发生在堆栈深处(_imagingft.c)。解决此问题会导致其他问题,例如逐字排列呈现的文本。

一些解决方案包括:

  • 在字符串的末尾附加一个空格。 im.text(xy, my_text + ' ', ...)
  • 对于高度问题,获取文本的宽度 ( font.getsize()),第二次渲染文本加上良好的升序和降序,将渲染的文本切割为第一个报告的宽度和第二个实际高度。
  • 使用不同的库,例如AggDrawpyvips

这在使用 PIL 裁剪字体PIL 截断字母顶部、Python 中使用给定字体正确渲染文本并准确检测其边界的各种问题中都引用了这一点。这些问题涉及相同的潜在问题,但不是重复的

于 2018-12-19T21:58:47.980 回答
1

使用到目前为止提到的方法,我无法解决某些字体的这个问题,所以我最终使用aggdraw作为 PIL 文本绘图方法的透明替代品。

您重写为 aggdraw 的代码如下所示:

import Image
import aggdraw

im = Image.new("RGBA", (200, 200), 'white')
draw = aggdraw.Draw(im)

# note that the color is specified in the font constructor in aggdraw
font = aggdraw.Font((0,0,0), "VeraSe.ttf", size=12, opacity=255)

draw.text((1, 1), " %s: " % "ggjyfFwe__", font) # no color here
draw.text((1, 30), " %s" % 15, font)

draw.flush() # don't forget this to update the underlying PIL image!

im.show()
于 2012-01-31T13:17:02.860 回答
0

“错误”在 2012 年仍然存在,Ubuntu 11.10。字体大小 11、12、13 和 15 完全剪裁下划线。

#!/usr/bin/env python
""" demonstrates clipping of descenders for certain font sizes """
import Image, ImageDraw, ImageFont
fontPath = "/usr/share/fonts/truetype/ttf-dejavu/DejaVuSans-Bold.ttf"
im = Image.new('L', (256, 256))
ys=15
for i in range(10,21):
    fh = ImageFont.truetype(fontPath, i)
    sometext="%dgt_}" % (i)
    ImageDraw.Draw(im).text((10, ys ),sometext , 254, fh)
    ys+=i+5
im.show()
于 2012-03-20T17:19:38.753 回答
0

这是一个对我很有效的kludge。这是 gnud 答案的变体。(差异足以值得一个单独的答案与我希望的评论。)我已经测试了很多单词放置,并且表现一致。

如果在绘制文本时未完全达到字体的完整高度,则可能会发生剪切。正如 gnud 所指出的,通过使用诸如“Aj”(我使用“Fj”)之类的字符可以避免这个错误。

每当放置一个单词时:

1) 用你想要的词做一个 draw.textsize(text, font=font) 。存储高度/宽度。

2) 在词尾添加'Fj'(spaceFJ),重做textsize并存储第三个高/宽。

4)您将使用第 2 项中的单词(末尾带有“Fj”)进行实际的文本绘制。拥有此附录将防止字体被剪裁。

4) 在进行实际的文本绘制之前,裁剪“Fj”将降落的图像(需要crop.load() 以避免延迟复制)。然后绘制文本,将裁剪后的图像移回“Fj”上方。

这个过程避免了剪辑,看起来相当高效,并产生了完整的、未剪辑的文本。下面是我为此使用的一段 Python 代码的复制/粘贴。部分示例,但希望它能增加一些见解。

    # note: xpos & ypos were previous set = coordinates for text draw 
    #       the hard-coded addition of 4 to c_x likely will vary by font
    #       (I only use one font in this process, so kludged it.)
    width, height = draw.textsize(word, font=font)
    word2 = word + ' Fj'
    width2, height2 = draw.textsize(word2, font=font)
    # crop to overwrite ' Fj' with previous image bits
    c_w = width2 - width
    c_h = height2
    c_x = xpos + width + 4
    c_y = ypos
    box = (c_x, c_y, c_x + c_w, c_y + c_h)
    region = img.crop(box)
    region.load()
    draw.text((xpos, ypos), word2, (0,0,0), font=font)
    img.paste(region, box)
于 2015-03-21T01:15:22.727 回答
0

我的建议是,在创建图像对象之前,获取文本所需的大小。

这是使用font.getsize("text")文档)完成的。

在我制作的一个图像生成脚本中,我首先通过调用等效项找到了一行文本的最大高度font.getsize("Åj")(如果您只需要US-ASCII,您可以找到高度"Aj")。然后我计算了所需的图像高度和行偏移,包括边距和行距。

于 2009-12-20T10:48:59.640 回答