使用PIL使文本居中对齐/居中对齐?


Answers:


154

使用Draw.textsize方法来计算文字大小并相应地重新计算位置。

这是一个例子:

from PIL import Image, ImageDraw

W, H = (300,200)
msg = "hello"

im = Image.new("RGBA",(W,H),"yellow")
draw = ImageDraw.Draw(im)
w, h = draw.textsize(msg)
draw.text(((W-w)/2,(H-h)/2), msg, fill="black")

im.save("hello.png", "PNG")

结果:

居中文字图片

如果您的字体大小不同,请包括以下字体:

myFont = ImageFont.truetype("my-font.ttf", 16)
draw.textsize(msg, font=myFont)

6
draw.textsize:(个体,文本,字体=无)
WeizhongTu

27
如果您使用的字体大小不同,那么请务必包含如下所示的字体:draw.textsize(msg, font=myFont),否则它将无法正确居中
Coco

1
如何确保文本不会从Image中溢出?
Dheeraj M Pai

如何居中对齐多行文字?
Nagabhushan SN

2
上面的代码有误。它应该是(Ww / 2,Hh / 2)而不是((Ww)/ 2,(Hh)/ 2)。尝试了以上方法并找到了它。
Ijas

77

这是一些示例代码,该代码使用textwrap将长行分割为多段,然后使用该textsize方法计算位置。

from PIL import Image, ImageDraw, ImageFont
import textwrap

astr = '''The rain in Spain falls mainly on the plains.'''
para = textwrap.wrap(astr, width=15)

MAX_W, MAX_H = 200, 200
im = Image.new('RGB', (MAX_W, MAX_H), (0, 0, 0, 0))
draw = ImageDraw.Draw(im)
font = ImageFont.truetype(
    '/usr/share/fonts/truetype/msttcorefonts/Arial.ttf', 18)

current_h, pad = 50, 10
for line in para:
    w, h = draw.textsize(line, font=font)
    draw.text(((MAX_W - w) / 2, current_h), line, font=font)
    current_h += h + pad

im.save('test.png')

在此处输入图片说明


2
Textwrap很不错,但是请记住,您设置的整数断点width是在计算字符数的同时,PIL图像以像素为单位。我使用了上面的版本,但是在for循环之前添加了while循环来编写行。首先,我设置一个任意高的字符数开始,然后使用textwrap断开行,然后使用.textsize来测量textwrap结果列表中第一个输出的像素宽度。如果适合,请继续,否则减少我的字符数并再次测量,直到线条适合图像为止。
大通

26

应当指出,该Draw.textsize方法是不准确的。我正在处理低像素图像,经过一些测试,结果发现textsize每个字符的宽度均为6像素,而I最大字符数为2。2像素,W耗时最少。8像素(以我为例)。因此,根据我的文字,它居中还是根本不居中。虽然,我猜“ 6”是一个平均值,所以如果您要处理长文本和大图像,应该还是可以的。

但是,现在,如果您希望获得真正的准确性,则最好使用getsize将要使用的字体对象的方法:

arial = ImageFont.truetype("arial.ttf", 9)
w,h = arial.getsize(msg)
draw.text(((W-w)/2,(H-h)/2), msg, font=arial, fill="black")

与Edilio的链接中使用的一样。


1
不是OP的问题的答案,而是很好的迫切需要的功能。1+
ddlab

重要说明:此功能getsize接受€或德国Umlauts等非拉丁字符。textsize别。ThumbsUp :-)
ddlab

6

ImageDraw.textPIL文档是一个不错的起点,但是请不要回答您的问题。

下面是一个示例,该示例说明了如何将文本置于任意边框(而不是图像的中心)的中心。边界框定义为:(x1, y1)=左上角和(x2, y2)=右下角。

from PIL import Image, ImageDraw, ImageFont

# Create blank rectangle to write on
image = Image.new('RGB', (300, 300), (63, 63, 63, 0))
draw = ImageDraw.Draw(image)

message = 'Stuck in\nthe middle\nwith you'

bounding_box = [20, 30, 110, 160]
x1, y1, x2, y2 = bounding_box  # For easy reading

font = ImageFont.truetype('Consolas.ttf', size=12)

# Calculate the width and height of the text to be drawn, given font size
w, h = draw.textsize(message, font=font)

# Calculate the mid points and offset by the upper left corner of the bounding box
x = (x2 - x1 - w)/2 + x1
y = (y2 - y1 - h)/2 + y1

# Write the text to the image, where (x,y) is the top left corner of the text
draw.text((x, y), message, align='center', font=font)

# Draw the bounding box to show that this works
draw.rectangle([x1, y1, x2, y2])

image.show()
image.save('text_center_multiline.png')

输出结果显示文本在边框中垂直和水平居中

无论您是单行还是多行消息,都不再重要,因为PIL合并了该align='center'参数。但是,它仅适用于多行文字。如果消息是一行,则需要将其手动居中。如果消息是多行,align='center'则可以在后续行中为您完成工作,但是您仍然必须手动将文本块居中。在上面的代码中,这两种情况都可以立即解决。


2

在实际绘制文本对象之前,textsize请使用方法(请参阅docs)确定文本对象的尺寸。然后从适当的坐标开始绘制。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.