如何在Python中使用不同的描边和填充颜色在图像上绘制文本?

18

如何使用Python在图像上绘制具有不同描边和填充颜色的文本?

这里是一些具有红色描边和灰色填充的文本。

Example

我尝试使用PIL来实现,但没有设置描边颜色的选项。

6个回答

16

使用 cairo(参考了这里的很多代码):

import cairo

def text_extent(font, font_size, text, *args, **kwargs):
    surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, 0, 0)
    ctx = cairo.Context(surface)
    ctx.select_font_face(font, *args, **kwargs)
    ctx.set_font_size(font_size)
    return ctx.text_extents(text)

text='Example'
font="Sans"
font_size=55.0
font_args=[cairo.FONT_SLANT_NORMAL]
(x_bearing, y_bearing, text_width, text_height,
 x_advance, y_advance) = text_extent(font, font_size, text, *font_args)
surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, int(text_width), int(text_height))
ctx = cairo.Context(surface)
ctx.select_font_face(font, *font_args)
ctx.set_font_size(font_size)
ctx.move_to(-x_bearing, -y_bearing)
ctx.text_path(text)
ctx.set_source_rgb(0.47, 0.47, 0.47)
ctx.fill_preserve()
ctx.set_source_rgb(1, 0, 0)
ctx.set_line_width(1.5)
ctx.stroke()

surface.write_to_png("/tmp/out.png")

在此输入图片描述


我需要知道文本的尺寸才能使用它吗? - Seppo Erviälä
我可以使用Context.text_extents("Example")获取所需的信息,但之后我需要创建一个具有正确宽度和高度的新表面。 - Seppo Erviälä
是的,不幸的是,我不知道更好的方法。 - unutbu
@unutbu,我成功地将你的代码移植到了C++,但是遇到了一个关于透明度的问题。你能在这里检查一下吗?链接 谢谢 - Prakash M

9

PIL不支持这个功能,但你可以通过伪装来实现:使用一个像素的偏移量,使用外部颜色渲染文本四遍或八遍:

x+1,y
x-1,y
x  ,y+1
x  ,y-1 

(四倍版本)
x+1,y+1
x  ,y+1
x-1,y+1

x+1,y
x-1,y

x+1,y-1
x  ,y-1 
x-1,y-1

(八倍版本)

然后在 x,y 处填充颜色。


1
8x版本基本上相当于只在对角线上渲染4次。 - Luke Taylor
@HassanBaig 你不会注意到它。 - Aaron Digulla
1
只有在边框很细的情况下才有效,例如1像素。如果使用更宽的边框,则生成的文本效果不太令人满意。 - jdhao

7

使用 imagemagick

import subprocess

args = {
    'bgColor': 'transparent',
    'fgColor': 'light slate grey',
    'fgOutlineColor': 'red',
    'text': 'Example',
    'size': 72,
    'geometry': '350x100!',
    'output': '/tmp/out.png',
    'font': 'helvetica'
}

cmd = ['convert', 'xc:{bgColor}', '-resize', '{geometry}', '-gravity', 'Center', 
       '-font', '{font}', '-pointsize', '{size}', '-fill', '{fgColor}', 
       '-stroke', '{fgOutlineColor}', '-draw', "text 0,0 '{text}'", '-trim', '{output}']
cmd = [item.format(**args) for item in cmd]

proc = subprocess.Popen(cmd)
proc.communicate()

enter image description here


我需要在创建输出文本之前知道其尺寸吗? - Seppo Erviälä
你可以使用任何你想要的 geometry -- 只要你使用感叹号,imagemagick 就会创建一个完全符合该几何形状的图像 -- 但是如果字体 size 太大,文本的一部分可能会被裁剪掉。 - unutbu
我想获得一张与文本精确大小相同的图片。没有剪切的文字,也没有某些边缘上的额外空间。 - Seppo Erviälä
1
您可以使用“-trim”选项将裁剪到文本大小。 - unutbu
最好将命令及其选项放入列表中,然后在每个元素中替换变量。这将解决参数中空格和/或引号字符的问题。 - Aaron Digulla
@AaronDigulla:感谢您的建议/改进。 - unutbu

6

升级pillow,请执行pip install -U pillow。该命令适用于Pillow 7.2版本。 - jdhao
https://pillow.readthedocs.io/en/stable/reference/ImageDraw.html#PIL.ImageDraw.ImageDraw.text 这是指向特定标题的链接。 - undefined

2
补充@Aaron Digulla的回答,这是如何在PIL中伪造轮廓颜色:
from PIL import Image, ImageDraw, ImageFont

def main():
    text_color = (255, 0, 0)
    outline_color = (0, 0, 255)
    size = (512, 256)
    img = Image.new(mode='RGB', size=size, color=(255, 255, 255))
    font = ImageFont.truetype(font="C:/WINDOWS/Fonts/STKAITI.TTF", size=100)
    drawer = ImageDraw.Draw(img)

    x = 10
    y = 10
    bd_w = 1
    drawer.text((x-bd_w, y), "测试文字", font=font, fill=outline_color)
    drawer.text((x, y-bd_w), "测试文字", font=font, fill=outline_color)
    drawer.text((x+bd_w, y), "测试文字", font=font, fill=outline_color)
    drawer.text((x, y+bd_w), "测试文字", font=font, fill=outline_color)

    drawer.text((x+bd_w, y-bd_w), "测试文字", font=font, fill=outline_color)
    drawer.text((x-bd_w, y-bd_w), "测试文字", font=font, fill=outline_color)
    drawer.text((x-bd_w, y+bd_w), "测试文字", font=font, fill=outline_color)
    drawer.text((x+bd_w, y+bd_w), "测试文字", font=font, fill=outline_color)

    drawer.text((x, y), "测试文字", font=font, fill=text_color)

    img.show()


if __name__ == "__main__":
    main()

但要注意,如果将bd_w(边框宽度)设置得稍高一些,此方法会产生不令人满意的文本。请参见以下图像以了解bd_w的效果:
边框宽度为1

enter image description here

边框宽度 = 4

enter image description here

边框宽度 = 7

enter image description here

似乎对于较大的文本,将边框宽度保持在4以下是可接受的。

2
你可以使用Inkscape:
import subprocess
subprocess.call("inkscape in.svg --export-text-to-path --export-plain-svg out.svg", shell = True)

注意:您需要下载Inkscape才能使用它,因此不适合长期使用。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接