python PIL chops top of my draw.text - python

I am using python and PIL to draw some text. I stumbled upon certain tutorial 'cause I needed some guides on how to center text within certain bounds:
the code works but it has some strange behavior. It eats the top of string. Here's how it should like http://img855.imageshack.us/img855/9292/qttempya4744.png
and here's how it looks http://img546.imageshack.us/img546/8541/outn.jpg
here's my code :
import os
from PIL import ImageDraw, ImageFont, Image
def draw_text(text, size, fill=None):
font = ImageFont.truetype('C:\exl.ttf', 30)
size = font.getsize(text)# Returns the width and height of the given text, as a 2-tuple.
size = (size[0],size[1]+15)
im = Image.new('RGBA', size, (0, 0, 0, 0)) # Create a blank image with the given size
draw = ImageDraw.Draw(im)
draw.text((0, 25), text, font=font, fill=fill) #Draw text
return im
img = draw_text('zod', 30, (82, 124, 178))
img.save('C:\out.jpg',"JPEG")
print 'Complete!'
os.startfile('C:\out.jpg')
I have this bug with other fonts too (tried Arial and Verdana).
help plz :)

I have managed to fix this. It appears that drawtext has some issues with certain font sizes.
I've done a bit of experimenting, and it can clearly be seen here that certain font sizes get their top chopped off http://img838.imageshack.us/img838/7677/pilfontsize.jpg
Code for testing above mentioned :
from PIL import ImageDraw, ImageFont, Image
im = Image.new('RGBA', (700, 1600), (0, 0, 0, 0))
fSize = 1
yVal = 1
while fSize <= 50:
font = ImageFont.truetype('arial.ttf', fSize)
fString = "This line is in Arial font size " + str(fSize)
size = font.getsize(fString)
draw = ImageDraw.Draw(im)
draw.text((5, yVal), fString, font=font, fill=None)
fSize += 1
yVal += fSize + 5
Anyhow, I made it, and I'm proud :D
This rendering issue should get worked into by developers of PIL.

Related

Kerning text in Python PIL

I have some code to produce an image with text on it, using PIL:
from PIL import Image, ImageDraw, ImageFont
width = 2480
height = 3071
message = "Hello"
font = ImageFont.truetype("Arial.ttf", size=900)
img = Image.new('RGB', (width, height), color='black')
imgDraw = ImageDraw.Draw(img)
textWidth, textHeight = imgDraw.textsize(message, font=font)
xText = (width - textWidth) / 2
yText = (height - textHeight) / 2
imgDraw.text((152, 2100), message, font=font, fill=(255, 255, 255))
img.save('result.png')
I get a result like this: Example
But I need the text to have less space between letters. I intend to use this code for a batch of 40+ words located in a CSV file. How can I automatically adjust the kerning? I've seen other articles pointing to a related problem but they haven't helped.

Outline text on image in Python

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.
Link to the font
Link to original image
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')
I don't know since what version, but about a year ago Pillow added text stroking. You probably need to update it if you haven't do so lately. Example usage with stroke_width of 2:
from PIL import Image, ImageDraw, ImageFont
caption = 'I need to update my Pillow'
img = Image.open('./example-img.jpg')
d = ImageDraw.Draw(img)
font = ImageFont.truetype('impact.ttf', size=50)
d.text((10, 400), caption, fill='white', font=font,
stroke_width=2, stroke_fill='black')
img.save('example-output.jpg')
You can use mathlibplot text Stroke effect which uses PIL.
Example:
import matplotlib.pyplot as plt
import matplotlib.patheffects as path_effects
import matplotlib.image as mpimg
fig = plt.figure(figsize=(7, 5))
fig.figimage(mpimg.imread('seal.jpg'))
text = fig.text(0.5, 0.1, 'This text stands out because of\n'
'its black border.', color='white',
ha='center', va='center', size=30)
text.set_path_effects([path_effects.Stroke(linewidth=3, foreground='black'),
path_effects.Normal()])
plt.savefig('meme.png')
Result:
As #Abang pointed out, use stroke_width and stroke_fill.
Link for more details
Code:
from PIL import Image, ImageDraw, ImageFont
caption = 'Ans: stroke_width & stroke_fill'
img = Image.open('./example-img.jpg')
d = ImageDraw.Draw(img)
font = ImageFont.truetype('impact.ttf', size=50)
d.text((60, 400), caption, fill='white', font=font, spacing = 4, align = 'center',
stroke_width=4, stroke_fill='black')
img.save('example-output.jpg')

PIL: draw transparent text on top of an image

In a project I have, I am trying to create an outline over some text. The overall idea is to offset a black text slightly transparent from the original white text.
For some reasons, I get the black text but nether in transparent. Here is the MCVE:
image = Image.open("spongebob.gif").convert("RGBA")
draw = ImageDraw.Draw(image, "RGBA")
font = ImageFont.truetype("impact.ttf", 25)
draw.text((0, 0), "This text should be 5% alpha", fill=(0, 0, 0, 15), font=font)
image.save("foo.gif")
The result:
What did I miss?
This should work for you.
from PIL import Image, ImageDraw, ImageFont
image = Image.open("spongebob.gif").convert("RGBA")
txt = Image.new('RGBA', image.size, (255,255,255,0))
font = ImageFont.truetype("impact.ttf", 25)
d = ImageDraw.Draw(txt)
d.text((0, 0), "This text should be 5% alpha", fill=(0, 0, 0, 15), font=font)
combined = Image.alpha_composite(image, txt)
combined.save("foo.gif")
edit: The reference example from the Pillow documentation
https://pillow.readthedocs.io/en/stable/reference/ImageDraw.html#example-draw-partial-opacity-text

Correctly centring text (PIL/Pillow)

I'm drawing some text on a black strip, and then pasting the result on top of a base image, using PIL. One criticality is having the text position perfectly in the center of the black strip.
I cater for that via the following code:
from PIL import Image, ImageFont, ImageDraw
background = Image.new('RGB', (strip_width, strip_height)) #creating the black strip
draw = ImageDraw.Draw(background)
font = ImageFont.truetype("/usr/share/fonts/truetype/freefont/FreeSansBold.ttf", 16)
text_width, text_height = draw.textsize("Foooo Barrrr!")
position = ((strip_width-text_width)/2,(strip_height-text_height)/2)
draw.text(position,"Foooo Barrrr!",(255,255,255),font=font)
offset = (0,base_image_height/2)
base_image.paste(background,offset)
Notice how I'm setting position.
Now with all said and done, the result looks like so:
The text isn't precisely middled. It's slightly to the right and down. How do I improve my algorithm?
Remember to pass your font to draw.textsize as a second parameter (and also make sure you are really using the same text and font arguments to draw.textsize and draw.text).
Here's what worked for me:
from PIL import Image, ImageFont, ImageDraw
def center_text(img, font, text, color=(255, 255, 255)):
draw = ImageDraw.Draw(img)
text_width, text_height = draw.textsize(text, font)
position = ((strip_width-text_width)/2,(strip_height-text_height)/2)
draw.text(position, text, color, font=font)
return img
Usage:
strip_width, strip_height = 300, 50
text = "Foooo Barrrr!!"
background = Image.new('RGB', (strip_width, strip_height)) #creating the black strip
font = ImageFont.truetype("times", 24)
center_text(background, font, "Foooo Barrrr!")
Result:

python pil draw text offset

I tried to draw a character on an image with Python PIL. With the function ImageDraw.Draw.text(), the xy parameter points to the left-top corner of text. However I set xy to (0,0), the character haven't been draw the the left-top of images.
from PIL import ImageFont, ImageDraw, Image
imageSize=(40,40)
mage = Image.new("RGB", imageSize, (0,0,0))
draw = ImageDraw.Draw(image)
txt = "J"
font = ImageFont.truetype("ANTQUAB.ttf",35)
draw.text((0,0), txt, font=font)
why?
The xy parameter of draw.text() is the top left corner of the text (http://pillow.readthedocs.io/en/3.1.x/reference/ImageDraw.html), however the font might have some padding around the text, especially vertically. What I did was set the y part of the tuple to a negative number (maybe somewhere around -5?) and it worked for me.
This code:
from PIL import ImageFont, ImageDraw, Image
imageSize=(100,100)
image = Image.new("RGB", imageSize, (0,0,0))
draw = ImageDraw.Draw(image)
txt = "J"
font = ImageFont.truetype("ARIAL.ttf",35)
draw.text((0,0), txt, font=font)
image.show()
generates this:
Is this not what you were expecting?
Looks like there are font-specific offsets; you can get the vertical offset from the "top" value returned from FreeTypeFont.getbbox(). Subtracting this offset from your y-coordinate on the draw.text call will align top of text with top of image.
from PIL import ImageFont, ImageDraw, Image
text = 'J'
font = "arial.ttf"
fontsize = 12
img_w = 100
img_h = 100
canvas = Image.new('RGBA', size=(img_w,img_h), color='white')
draw = ImageDraw.Draw(canvas)
y_text = 0
font_obj = ImageFont.truetype(font, fontsize)
(left, top, right, bottom) = font_obj.getbbox(text)
x_pos = 0
y_pos = 0
# offset the y coordinate in the draw call by the "top" parameter fromgetbbox; this is the font-specific text padding
draw.text(xy=(x_pos, y_pos-top),
text=text,
font=font_obj,
align='left',
fill='black')
canvas.show()

Categories

Resources