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

2025-04-16 08:55:00
admin
原创
25
摘要:问题描述:使用 PIL 时,如何使文本居中对齐(和中间垂直对齐)?解决方案 1:弃用警告:textsize 已弃用,将于 Pillow 10 (2023-07-01) 中移除。请使用 textbbox 或 textlength 代替。代码使用textbbox而不是textsize。from PIL impor...

问题描述:

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


解决方案 1:

弃用警告:textsize 已弃用,将于 Pillow 10 (2023-07-01) 中移除。请使用 textbbox 或 textlength 代替。

代码使用textbbox而不是textsize

from PIL import Image, ImageDraw, ImageFont


def create_image(size, bgColor, message, font, fontColor):
    W, H = size
    image = Image.new('RGB', size, bgColor)
    draw = ImageDraw.Draw(image)
    _, _, w, h = draw.textbbox((0, 0), message, font=font)
    draw.text(((W-w)/2, (H-h)/2), message, font=font, fill=fontColor)
    return image
myFont = ImageFont.truetype('Roboto-Regular.ttf', 16)
myMessage = 'Hello World'
myImage = create_image((300, 200), 'yellow', myMessage, myFont, 'black')
myImage.save('hello_world.png', "PNG")

结果

结果


使用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)

解决方案 2:

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

在此处输入图片描述

解决方案 3:

如果您使用PIL 8.0.0或更高版本,则有一个简单的解决方案:文本锚点

width, height = # image width and height
draw = ImageDraw.draw(my_image)

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

mm表示使用文本的中间作为锚点,水平和垂直方向均如此。

请参阅锚点页面,了解其他类型的锚点。例如,如果您只想水平居中,则可能需要使用ma

解决方案 4:

需要注意的是,这种Draw.textsize方法并不准确。我处理的是低像素图像,经过一些测试后发现,textsize会将每个字符的宽度视为 6 像素,而I最大宽度为 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 的链接中所使用的那样。

解决方案 5:

ImageDraw.text 的 PIL 文档是一个很好的起点,但不能回答您的问题。

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

解决方案 6:

结合使用anchor="mm"align="center"效果奇佳。示例

draw.text(
        xy=(width / 2, height / 2),
        text="centered",
        fill="#000000",
        font=font,
        anchor="mm",
        align="center"
    )

注意:测试了类对象构造如下的font位置:ImageFont

ImageFont.truetype('path/to/font.ttf', 32)

解决方案 7:

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

解决方案 8:

所有其他答案均未考虑文本上升部分。

这是 的反向移植ImageDraw.text(..., anchor="mm")。我不确定它是否与 完全兼容anchor="mm",因为我还没有测试过类似kwargsspacingstroke_width但我保证这个偏移修复对我来说是有效的。

from PIL import ImageDraw
from PIL import __version__ as pil_ver

PILLOW_VERSION = tuple([int(_) for _ in pil_ver.split(".")[:3]])


def draw_anchor_mm_text(
    im,
    xy,
    # args shared by ImageDraw.textsize() and .text()
    text,
    font=None,
    spacing=4,
    direction=None,
    features=None,
    language=None,
    stroke_width=0,
    # ImageDraw.text() exclusive args
    **kwargs,
):
    """
    Draw center middle-aligned text. Basically a backport of 
    ImageDraw.text(..., anchor="mm"). 

    :param PIL.Image.Image im:
    :param tuple xy: center of text
    :param unicode text:
    ...
    """
    draw = ImageDraw.Draw(im)
    # Text anchor is firstly implemented in Pillow 8.0.0.
    if PILLOW_VERSION >= (8, 0, 0):
        kwargs.update(anchor="mm")
    else:
        kwargs.pop("anchor", None)  # let it defaults to "la"
        if font is None:
            font = draw.getfont()
        # anchor="mm" middle-middle coord xy -> "left-ascender" coord x'y'
        # offset_y = ascender - top, https://stackoverflow.com/a/46220683/5101148
        # WARN: ImageDraw.textsize() return text size with offset considered.
        w, h = draw.textsize(
            text,
            font=font,
            spacing=spacing,
            direction=direction,
            features=features,
            language=language,
            stroke_width=stroke_width,
        )
        offset = font.getoffset(text)
        w, h = w - offset[0], h - offset[1]
        xy = (xy[0] - w / 2 - offset[0], xy[1] - h / 2 - offset[1])
    draw.text(
        xy,
        text,
        font=font,
        spacing=spacing,
        direction=direction,
        features=features,
        language=language,
        stroke_width=stroke_width,
        **kwargs,
    )

参考文献

解决方案 9:

您可以使用以下算法:

  1. 假设主图像具有白色背景。

  2. 创建一个空图像(textImg)并在图像的左上角(或您希望的任何位置)绘制文本。

  3. 修剪文本图像中的所有空格。

  4. 最后,使用渲染文本的尺寸将textImg粘贴到主图像上,该尺寸等于textImg的宽度和高度。

from PIL import Image, ImageFont, ImageDraw

text = "© Lorem Ipsum"

# this is main image we want to draw centered text
mainImg = Image.new(mode='RGB', size=(600, 600), color='white')

# this is image text  that will hold trimmed text, create image with any size and draw text in it
textImg = Image.new(mode='RGB', size=(200, 200), color='white')
draw = ImageDraw.Draw(textImg)
font = ImageFont.load_default() # ImageFont.truetype("your_font.ttf", 12)
draw.text((1, 1), text, fill='black', font=font)

# now trim white space from text image 
pixels = textImg.load()
xmin, ymin, xmax, ymax = textImg.width, textImg.height, 0, 0
for x in range(textImg.width):
    for y in range(textImg.height):
        if pixels[x, y] != (255, 255, 255):
            xmin, ymin = min(x, xmin), min(y, ymin)
            xmax, ymax = max(x, xmax), max(y, ymax)
textImg = textImg.crop((xmin, ymin, xmax+1, ymax+1))

# paste trimmed text image into main image and save
x, y = mainImg.width//2 - textImg.width//2, mainImg.height//2 - textImg.height//2
mainImg.paste(textImg, (x, y, x + textImg.width, y + textImg.height))
mainImg.save('mainImg.png')

解决方案 10:

如果您使用默认字体,那么您可以使用这个简单的计算

draw.text((newimage.width/2-len(text)*3, 5), text,fill="black", align ="center",anchor="mm") 

最重要的是你必须将图像宽度除以 2,然后得到你想要的字符串的长度,并将其乘以 3,然后从除法结果中减去它

newimage.width/2-len(text)*3 #this is X position

**此答案是对默认字体大小的估计,如果您使用自定义字体,则必须相应地更改乘数。默认情况下,它是 3

解决方案 11:

这是一个在图像中心添加文本的简单示例

from PIL import Image, ImageDraw, ImageFilter

msg = "hello"

img = Image.open('image.jpg')
W, H = img.size
box_image = img.filter(ImageFilter.BoxBlur(4))
draw = ImageDraw.Draw(box_image)
w, h = draw.textsize(msg)
draw.text(((W - w) / 2, (H - h) / 2), msg, fill="black")

box_image.show()
相关推荐
  政府信创国产化的10大政策解读一、信创国产化的背景与意义信创国产化,即信息技术应用创新国产化,是当前中国信息技术领域的一个重要发展方向。其核心在于通过自主研发和创新,实现信息技术应用的自主可控,减少对外部技术的依赖,并规避潜在的技术制裁和风险。随着全球信息技术竞争的加剧,以及某些国家对中国在科技领域的打压,信创国产化显...
工程项目管理   2482  
  为什么项目管理通常仍然耗时且低效?您是否还在反复更新电子表格、淹没在便利贴中并参加每周更新会议?这确实是耗费时间和精力。借助软件工具的帮助,您可以一目了然地全面了解您的项目。如今,国内外有足够多优秀的项目管理软件可以帮助您掌控每个项目。什么是项目管理软件?项目管理软件是广泛行业用于项目规划、资源分配和调度的软件。它使项...
项目管理软件   1533  
  PLM(产品生命周期管理)项目对于企业优化产品研发流程、提升产品质量以及增强市场竞争力具有至关重要的意义。然而,在项目推进过程中,范围蔓延是一个常见且棘手的问题,它可能导致项目进度延迟、成本超支以及质量下降等一系列不良后果。因此,有效避免PLM项目范围蔓延成为项目成功的关键因素之一。以下将详细阐述三大管控策略,助力企业...
plm系统   0  
  PLM(产品生命周期管理)项目管理在企业产品研发与管理过程中扮演着至关重要的角色。随着市场竞争的加剧和产品复杂度的提升,PLM项目面临着诸多风险。准确量化风险优先级并采取有效措施应对,是确保项目成功的关键。五维评估矩阵作为一种有效的风险评估工具,能帮助项目管理者全面、系统地评估风险,为决策提供有力支持。五维评估矩阵概述...
免费plm软件   0  
  引言PLM(产品生命周期管理)开发流程对于企业产品的全生命周期管控至关重要。它涵盖了从产品概念设计到退役的各个阶段,直接影响着产品质量、开发周期以及企业的市场竞争力。在当今快速发展的科技环境下,客户对产品质量的要求日益提高,市场竞争也愈发激烈,这就使得优化PLM开发流程成为企业的必然选择。缺陷管理工具和六西格玛方法作为...
plm产品全生命周期管理   0  
热门文章
项目管理软件有哪些?
曾咪二维码

扫码咨询,免费领取项目管理大礼包!

云禅道AD
禅道项目管理软件

云端的项目管理软件

尊享禅道项目软件收费版功能

无需维护,随时随地协同办公

内置subversion和git源码管理

每天备份,随时转为私有部署

免费试用