用PIL剪切字体

时间:2009-12-19 18:38:53

标签: python fonts python-imaging-library text-rendering

此图片是使用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()

我尝试了几种不同的字体,它总是会被剪裁。令人惊讶; y,googleing“PIL字体剪辑”返回非常少的有用命中...我在Ubuntu 9.10上使用python 2.6.4和PIL 1.1.6

5 个答案:

答案 0 :(得分: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()

答案 1 :(得分:1)

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

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

一些解决方案包括:

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

在各种问题fonts clipping with PILPIL cuts off top of lettersProperly render text with a given font in Python and accurately detect its boundaries中都引用了此内容。这些问题引用了相同的潜在问题,但并非重复

答案 2 :(得分:0)

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

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

在我制作的图像生成脚本中,我首先通过调用font.getsize("Åj")的等值来找到一行文本的最大高度(如果你只需要US-ASCII,你可以找到{{的高度) 1}}而不是)。然后我计算了所需的图像高度和线偏移,包括边距和行间距。

答案 3 :(得分:0)

2012年,使用Ubuntu 11.10仍然存在“bug”。字体大小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()

答案 4 :(得分:0)

这是一款适合我的kludge。它是gnud的答案的变种。 (差异足以得到一个单独的答案与评论我希望。)我已经测试了很多单词的位置,这已经表现得很好。

如果在未完全达到字体的整个高度的情况下绘制文本,则可能会发生剪切。正如gnud所指出的那样,通过使用像#" Aj" (我使用" Fj")你可以避免这个错误。

每当放置一个单词时:

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

2)添加' FJ' (spaceFJ)到单词的结尾,重做文本大小并存储第三个高度/宽度。

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)