How can I draw text with different stroke and fill colors on images with python?

后端 未结 6 578
忘了有多久
忘了有多久 2020-12-09 10:33

How can I draw text with different stroke and fill colors on images with python?

Here is some text with red stroke and gray fill.

相关标签:
6条回答
  • 2020-12-09 10:52

    Complementing @Aaron Digulla's answer, this is how you fake the outline color in 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()
    

    But be warned that this method will produce unsatisfactory text if bd_w (border width) is set a little higher. See the following image for effect of bd_w:

    border width = 1

    border width = 4

    border width = 7

    It seems that for large text keeping border width below 4 is acceptable.

    0 讨论(0)
  • 2020-12-09 10:55

    PIL doesn't support this but you can fake it: Render the text four or eight times with the outline color using one pixel offsets:

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

    (four times version)

    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
    

    (eight times version)

    and then once at x,y with the fill color.

    0 讨论(0)
  • 2020-12-09 11:09

    Using 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

    0 讨论(0)
  • 2020-12-09 11:12

    You can use Inkscape:

    import subprocess
    subprocess.call("inkscape in.svg --export-text-to-path --export-plain-svg out.svg", shell = True)
    

    note: you have to download Inkscape to use this, so not practical for permanent use

    0 讨论(0)
  • 2020-12-09 11:14

    Using cairo (with much code taken from here):

    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")
    

    enter image description here

    0 讨论(0)
  • 2020-12-09 11:16

    They added a native solution within Pillow since 6.2.0: https://pillow.readthedocs.io/en/stable/reference/ImageDraw.html#PIL.ImageDraw.PIL.ImageDraw.ImageDraw.text

    You now specify the stroke_fill which is the color of the outline and stroke_width which is the thickness of the outline.

    0 讨论(0)
提交回复
热议问题