I have been using PIL Image
I am trying to draw text on an image. I want this text to have a black outline like most memes. I've attempted to do this by drawing a shadow letter of a bigger font behind the letter in front. I've adjusted the x and y postions of the shadow accordingly. The shadow is slightly off though. The letter in front should be exactly in the middle of the shadow letter, but this isn't the case. The question mark certainly isn't centered horizontally, and all the letters are too low vertically. The outline also just doesn't look good.
Below is a minimum reproducible example to produce the image above.
from PIL import Image, ImageDraw, ImageFont
caption = "Why is the text slightly off?"
img = Image.open('./example-img.jpg')
d = ImageDraw.Draw(img)
x, y = 10, 400
font = ImageFont.truetype(font='./impact.ttf', size=50)
shadowFont = ImageFont.truetype(font='./impact.ttf', size=60)
for idx in range(0, len(caption)):
char = caption[idx]
w, h = font.getsize(char)
sw, sh = shadowFont.getsize(char) # shadow width, shadow height
sx = x - ((sw - w) / 2) # Shadow x
sy = y - ((sh - h) / 2) # Shadow y
# print(x,y,sx,sy,w,h,sw,sh)
d.text((sx, sy), char, fill="black", font=shadowFont) # Drawing the text
d.text((x, y), char, fill=(255,255,255), font=font) # Drawing the text
x += w + 5
img.save('example-output.jpg')
Another approach includes drawing the text 4 times in black behind the main text at positions slightly higher, slightly lower, slightly left, and slightly right, but these have also not been optimal as shown below
Code to produce the image above
from PIL import Image, ImageDraw, ImageFont
caption = "Why does the Y and i look weird?"
x, y = 10, 400
font = ImageFont.truetype(font='./impact.ttf', size=60)
img = Image.open('./example-img.jpg')
d = ImageDraw.Draw(img)
shadowColor = (0, 0, 0)
thickness = 4
d.text((x - thickness, y - thickness), caption, font=font, fill=shadowColor, thick=thickness)
d.text((x + thickness, y - thickness), caption, font=font, fill=shadowColor, thick=thickness)
d.text((x - thickness, y + thickness), caption, font=font, fill=shadowColor, thick=thickness)
d.text((x + thickness, y + thickness), caption, font=font, fill=shadowColor, thick=thickness)
d.text((x, y), caption, spacing=4, fill=(255, 255, 255), font=font) # Drawing the text
img.save('example-output.jpg')