Python 将文本居中/居中与PIL对齐?

Python 将文本居中/居中与PIL对齐?,python,python-imaging-library,Python,Python Imaging Library,使用PIL时,如何将文本居中对齐(和中间垂直对齐)?在实际绘制文本对象之前,使用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.te

使用PIL时,如何将文本居中对齐(和中间垂直对齐)?

在实际绘制文本对象之前,使用
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)

下面是一些示例代码,它使用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')

应注意
Draw.textsize
方法不准确。我正在处理低像素图像,经过一些测试,结果表明
textsize
认为每个字符都是6像素宽,而
I
最多需要2个像素,
W
最少需要8个像素(在我的例子中)。所以,取决于我的文本,它是否居中。虽然,我猜“6”是一个平均值,所以如果你在处理长文本和大图像,它应该还是可以的

但是现在,如果您想要一些真正的准确性,最好使用将要使用的font对象的
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的链接中使用的那样。

是一个很好的开始,但不要回答您的问题

下面是一个示例,说明如何将文本置于任意边界框的中心,而不是图像的中心。边界框定义为:
(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'
会在后续行中为您完成这项工作,但您仍然需要手动将文本块居中。这两种情况都可以在上面的代码中一次解决。

如果您使用的是或更高版本,这是一个简单的解决方案:

mm
表示在水平和垂直方向上使用文本的中间部分作为锚定


有关其他类型的锚定,请参见锚定页面。例如,如果您只想水平居中,您可能需要使用
ma

draw.textsize:(self,text,font=None)如果您的字体大小不同,请务必包括以下字体:
draw.textsize(msg,font=myFont)
,否则它将无法正确居中如何确保文本不会溢出图像?如何居中对齐多行文本?上述代码中存在错误。它应该是(W-W/2,H-H/2)而不是(W-W)/2,(H-H)/2)。尝试了上述方法并找到了它。这不是OP问题的答案,而是一个非常需要的功能。1+重要提示:此函数
getsize
接受非拉丁字符,如欧元或德语Umlauts<代码>文本大小不要。ThumbsUp:-)Textwrap很好,但请记住,为
宽度设置的整数断点是计数字符,而PIL图像以像素为单位。我使用了上面的一个版本,但在写入行的for循环之前添加了一个while循环。首先,我设置一个任意高的字符数作为开始,然后使用textwrap中断行,然后使用.textsize测量textwrap结果列表中第一个输出的像素宽度。如果合适,请继续,否则请减少我的字符数并再次测量,直到线条适合图像。对于最新版本的枕头,这是一个更好、更灵活且易于使用的答案
width, height = # image width and height
draw = ImageDraw.draw(my_image)

draw.text((width/2, height/2), "my text", font=my_font, anchor="mm")