Is there a way to outline text with a dark line in PIL?

只谈情不闲聊 提交于 2020-03-18 03:43:14

问题


I'm using python/PIL to write text on a set of PNG images. I was able to get the font I wanted, but I'd like to now outline the text in black.

This is what I have: as you can see, if the background is white it is difficult to read.

This is the goal:

Is there a way to accomplish this with PIL? If not, I am open to hearing other suggestions, but no promises because I've already begun a large project in python using PIL.

The section of code that deals with drawing on the images:

for i in range(0,max_frame_int + 1):
    writeimg = Image.open("frameinstance" + str(i) + ".png")
    newimg = Image.new("RGB", writeimg.size)
    newimg.paste(writeimg)
    width_image = newimg.size[0]
    height_image = newimg.size[1]
    draw = ImageDraw.Draw(newimg)
    # font = ImageFont.truetype(<font-file>, <font-size>)
    for font_size in range(50, 0, -1):
        font = ImageFont.truetype("impact.ttf", font_size)
        if font.getsize(meme_text)[0] <= width_image:
            break
    else:
        print('no fonts fit!')

    # draw.text((x, y),"Sample Text",(r,g,b))
    draw.text((int(0.05*width_image), int(0.7*height_image)),meme_text,(255,255,255),font=font)
    newimg.save("newimg" + str(i) +".png")

回答1:


You can take a look at this Text Outline Using PIL




回答2:


This is how I handled the problem when I needed to do it for frame counters. Just a heads up if you start to push this too far for the thickness, then you will need more draws to cover your areas you're missing.

from PIL import Image,ImageDraw,ImageFont
import os

#setting varibles
imgFile = "frame_0.jpg"
output = "frame_edit_0.jpg"
font = ImageFont.truetype("arial.ttf", 30)
text = "SEQ_00100_SHOT_0004_FR_0001"
textColor = 'white'
shadowColor = 'black'
outlineAmount = 3

#open image
img = Image.open(imgFile)
draw = ImageDraw.Draw(img)

#get the size of the image
imgWidth,imgHeight = img.size

#get text size
txtWidth, txtHeight = draw.textsize(text, font=font)

#get location to place text
x = imgWidth - txtWidth - 100
y = imgHeight - txtHeight - 100

#create outline text
for adj in range(outlineAmount):
    #move right
    draw.text((x-adj, y), text, font=font, fill=shadowColor)
    #move left
    draw.text((x+adj, y), text, font=font, fill=shadowColor)
    #move up
    draw.text((x, y+adj), text, font=font, fill=shadowColor)
    #move down
    draw.text((x, y-adj), text, font=font, fill=shadowColor)
    #diagnal left up
    draw.text((x-adj, y+adj), text, font=font, fill=shadowColor)
    #diagnal right up
    draw.text((x+adj, y+adj), text, font=font, fill=shadowColor)
    #diagnal left down
    draw.text((x-adj, y-adj), text, font=font, fill=shadowColor)
    #diagnal right down
    draw.text((x+adj, y-adj), text, font=font, fill=shadowColor)

#create normal text on image
draw.text((x,y), text, font=font, fill=textColor)

img.save(output)
print 'Finished'
os.startfile(output)



回答3:


This answer is based on others but uses the .create_text ((x1, y1), ...) function in tkinter. Additionally it focuses on achieving a large outline when needed whilst avoiding shadow effect:

Here is a closeup:

Here is the main function:

def create_text((x1, y1), **kwargs):
#    mycanvas.create_text((X1, Y1 + PanelHgt/Scale), text=str(Mon[MonN]) + \
#                             "  [" + str(Mon[MonW]) + " x " + \
#                             str(Mon[MonH]) +"]", \
#                            fill="white", shawdowfill="black", thick=0, \
#                            anchor="nw", font=(None, MonFont))
    if 'thick' in kwargs:
        thick = kwargs.pop('thick')
        if thick > 5 : thick = 0
    else :
        thick = 0

    if 'shadowfill' in kwargs:
        shadowfill = kwargs.pop('shadowfill')
    else:
        mycanvas.create_text((x1, y1), **kwargs)
        return

    if 'fill' in kwargs:
        fill = kwargs.pop('fill')
    else:
        fill = "black"

    x = x1 + 1 + thick
    y = y1 + 1 + thick
    if thick>1: thickless=thick-1 
    else: thickless=1
    while thickless > 0:
        mycanvas.create_text((x-thickless, y+thick), fill=shadowfill, **kwargs)
        mycanvas.create_text((x+thickless, y+thick), fill=shadowfill, **kwargs)
        mycanvas.create_text((x-thick, y-thickless), fill=shadowfill, **kwargs)
        mycanvas.create_text((x+thick, y+thickless), fill=shadowfill, **kwargs)
        thickless-=1
        thick-=1
    mycanvas.create_text((x, y), fill=fill, **kwargs)

Here's sample code to call the function:

    InvertColor="white"
    ShadowColor="black"
    String=str(Win[WinN][0:30]) + "\n " \
           + str(Win[WinW]) + " x " + str(Win[WinH])
    # if top appliciton indicadtor pannel special formtting
    if ( Win[WinH] == PanelHgt ) : 
        color="light grey"
        InvertColor="black"
        String=Win[WinN]
        mycanvas.create_rectangle(X1, Y1, X2, Y2, fill=color)
        mycanvas.create_text((CentX, CentY), text=String, fill=InvertColor, \
                              font=(None, 11))
    else :
        color=Colors[j]
        j+=1
        if j==ColorCnt : j=0
        create_rectangle(X1, Y1, X2, Y2, fill=color, alpha=.6)
        create_text((CentX, CentY), text=String, fill=InvertColor, \
                     shadowfill=ShadowColor, thick=4, \
                     font=(None, 11))

The key line is:

shadowfill=ShadowColor, thick=4
  • If you omit thick it defaults to 0 which is a thin shadow and probably best in most circumstances.
  • The maximum shadow thickness is set to 5 for sanity checking buy you can simply change the line: if thick > 5 : thick = 0
  • If you omit shadowfill then no shadow is drawn at all.

If code isn't understandable don't hesitate to ask questions in comments.

OTOH this is my first python project (still a WIP) so constructive criticism is welcome!



来源:https://stackoverflow.com/questions/41556771/is-there-a-way-to-outline-text-with-a-dark-line-in-pil

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!