【问题标题】:Center-/middle-align text with PIL?使用 PIL 居中/中间对齐文本?
【发布时间】:2010-12-30 13:25:24
【问题描述】:

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

【问题讨论】:

    标签: python python-imaging-library


    【解决方案1】:

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

    【讨论】:

      【解决方案2】:

      使用Draw.textsize method 计算文本大小并相应地重新计算位置。

      这是一个例子:

      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)
      

      【讨论】:

      • draw.textsize: (self, text, font=None)
      • 如果您的字体大小不同,请务必包含如下字体:draw.textsize(msg, font=myFont),否则将无法正确居中
      • 如何保证文字不溢出Image?
      • 如何居中对齐多行文本?
      • 以上代码有错误。它应该是 (W-w/2,H-h/2) 而不是 ((W-w)/2,(H-h)/2)。试了上面的方法,找到了。
      【解决方案3】:

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

      【讨论】:

      • Textwrap 很好,但请记住,您为 width 设置的整数断点是计数字符,而 PIL 图像以像素为单位。我使用了上面的一个版本,但在写入行的 for 循环之前添加了一个 while 循环。首先,我设置一个任意高的字符数开始,然后使用 textwrap 来换行,然后使用 .textsize 来测量 textwrap 结果列表中第一个输出的像素宽度。如果合适,继续,否则减少我的字符数并再次测量,直到线条适合图像。
      • 太棒了!非常感谢,这不仅解决了对齐问题,还解决了行高问题,我花了两天时间寻找该问题的解决方案,并在这里找到了它。 (垫变量)
      【解决方案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 的链接中使用。

      【讨论】:

      • 不是 OP 问题的答案,而是急需的好功能。 1+
      • 重要提示:此函数getsize 接受非拉丁字符,如€ 或德语变音符号。 textsize不要。竖起大拇指 :-)
      【解决方案5】:

      PIL docs for ImageDraw.text 是一个很好的起点,但不要回答您的问题。

      下面是一个示例,说明如何将文本置于任意边界框中,而不是图像的中心。边界框定义为:(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')
      

      The output shows the text centered vertically and horizontally in the bounding box.

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

      【讨论】:

        【解决方案6】:

        如果您使用PIL 8.0.0 或更高版本,一个简单的解决方案:text anchors

        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

        【讨论】:

        • 这是最新 Pillow 版本的更好、更灵活、更易于使用的答案
        猜你喜欢
        • 1970-01-01
        • 2013-10-12
        • 2016-12-20
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2021-01-14
        相关资源
        最近更新 更多