TransWikia.com

How do I draw text at an angle using python's PIL?

Stack Overflow Asked by carrier on November 7, 2021

Using Python I want to be able to draw text at different angles using PIL.

For example, imagine you were drawing the number around the face of a clock. The number 3 would appear as expected whereas 12 would we drawn rotated counter-clockwise 90 degrees.

Therefore, I need to be able to draw many different strings at many different angles.

7 Answers

The previous answers draw into a new image, rotate it, and draw it back into the source image. This leaves text artifacts. We don't want that.

Here is a version that instead crops the area of the source image that will be drawn onto, rotates it, draws into that, and rotates it back. This means that we draw onto the final surface immediately, without having to resort to masks.

def draw_text_90_into (text: str, into, at):
    # Measure the text area
    font = ImageFont.truetype (r'C:WindowsFontsArial.ttf', 16)
    wi, hi = font.getsize (text)

    # Copy the relevant area from the source image
    img = into.crop ((at[0], at[1], at[0] + hi, at[1] + wi))

    # Rotate it backwards
    img = img.rotate (270, expand = 1)

    # Print into the rotated area
    d = ImageDraw.Draw (img)
    d.text ((0, 0), text, font = font, fill = (0, 0, 0))

    # Rotate it forward again
    img = img.rotate (90, expand = 1)

    # Insert it back into the source image
    # Note that we don't need a mask
    into.paste (img, at)

Supporting other angles, colors etc is trivial to add.

Answered by mafu on November 7, 2021

Here's a fuller example of watermarking diagonally. Handles arbitrary image ratios, sizes and text lengths by calculating the angle of the diagonal and font size.

from PIL import Image, ImageFont, ImageDraw
import math

# sample dimensions
pdf_width = 1000
pdf_height = 1500

#text_to_be_rotated = 'Harry Moreno'
text_to_be_rotated = 'Harry Moreno ([email protected])'
message_length = len(text_to_be_rotated)

# load font (tweak ratio based on your particular font)
FONT_RATIO = 1.5
DIAGONAL_PERCENTAGE = .5
diagonal_length = int(math.sqrt((pdf_width**2) + (pdf_height**2)))
diagonal_to_use = diagonal_length * DIAGONAL_PERCENTAGE
font_size = int(diagonal_to_use / (message_length / FONT_RATIO))
font = ImageFont.truetype(r'./venv/lib/python3.7/site-packages/reportlab/fonts/Vera.ttf', font_size)
#font = ImageFont.load_default() # fallback

# target
image = Image.new('RGBA', (pdf_width, pdf_height), (0, 128, 0, 92))

# watermark
opacity = int(256 * .5)
mark_width, mark_height = font.getsize(text_to_be_rotated)
watermark = Image.new('RGBA', (mark_width, mark_height), (0, 0, 0, 0))
draw = ImageDraw.Draw(watermark)
draw.text((0, 0), text=text_to_be_rotated, font=font, fill=(0, 0, 0, opacity))
angle = math.degrees(math.atan(pdf_height/pdf_width))
watermark = watermark.rotate(angle, expand=1)

# merge
wx, wy = watermark.size
px = int((pdf_width - wx)/2)
py = int((pdf_height - wy)/2)
image.paste(watermark, (px, py, px + wx, py + wy), watermark)

image.show()

Here it is in a colab https://colab.research.google.com/drive/1ERl7PiX6xKy5H9EEMulBKPgglF6euCNA?usp=sharing you should provide an example image to the colab. 500 by 300300 by 500

Answered by Harry Moreno on November 7, 2021

for Linux, I was able to get fonts to work on debian using:

fnt = ImageFont.truetype('LinLibertine_RB.otf', 40)

or any font library you see under

ls -lR /usr/share/fonts

the names and examples of fonts are also contained in Gimp if you have that installed.

Answered by peawormsworth on November 7, 2021

Here is a working version, inspired by the answer, but it works without opening or saving images.

The two images have colored background and alpha channel different from zero to show what's going on. Changing the two alpha channels from 92 to 0 will make them completely transparent.

from PIL import Image, ImageFont, ImageDraw

text = 'TEST'
font = ImageFont.truetype(r'C:WindowsFontsArial.ttf', 50)
width, height = font.getsize(text)

image1 = Image.new('RGBA', (200, 150), (0, 128, 0, 92))
draw1 = ImageDraw.Draw(image1)
draw1.text((0, 0), text=text, font=font, fill=(255, 128, 0))

image2 = Image.new('RGBA', (width, height), (0, 0, 128, 92))
draw2 = ImageDraw.Draw(image2)
draw2.text((0, 0), text=text, font=font, fill=(0, 255, 128))

image2 = image2.rotate(30, expand=1)

px, py = 10, 10
sx, sy = image2.size
image1.paste(image2, (px, py, px + sx, py + sy), image2)

image1.show()

Answered by stenci on November 7, 2021

It's also usefull to know our text's size in pixels before we create an Image object. I used such code when drawing graphs. Then I got no problems e.g. with alignment of data labels (the image is exactly as big as the text).

(...)
img_main = Image.new("RGB", (200, 200))
font = ImageFont.load_default()

# Text to be rotated...
rotate_text = u'This text should be rotated.'

# Image for text to be rotated
img_txt = Image.new('L', font.getsize(rotate_text))
draw_txt = ImageDraw.Draw(img_txt)
draw_txt.text((0,0), rotate_text, font=font, fill=255)
t = img_value_axis.rotate(90, expand=1)

The rest of joining the two images together is already described on this page. When you rotate by an "unregular" angle, you have to improve this code a little bit. It actually works for 90, 180, 270...

Answered by on November 7, 2021

Draw text into a temporary blank image, rotate that, then paste that onto the original image. You could wrap up the steps in a function. Good luck figuring out the exact coordinates to use - my cold-fogged brain isn't up to it right now.

This demo writes yellow text on a slant over an image:

# Demo to add rotated text to an image using PIL

import Image
import ImageFont, ImageDraw, ImageOps

im=Image.open("stormy100.jpg")

f = ImageFont.load_default()
txt=Image.new('L', (500,50))
d = ImageDraw.Draw(txt)
d.text( (0, 0), "Someplace Near Boulder",  font=f, fill=255)
w=txt.rotate(17.5,  expand=1)

im.paste( ImageOps.colorize(w, (0,0,0), (255,255,84)), (242,60),  w)

Answered by DarenW on November 7, 2021

I'm not saying this is going to be easy, or that this solution will necessarily be perfect for you, but look at the documentation here:

http://effbot.org/imagingbook/pil-index.htm

and especially pay attention to the Image, ImageDraw, and ImageFont modules.

Here's an example to help you out:


import Image
im = Image.new("RGB", (100, 100))
import ImageDraw
draw = ImageDraw.Draw(im)
draw.text((50, 50), "hey")
im.rotate(45).show()

To do what you really want you may need to make a bunch of separate correctly rotated text images and then compose them all together with some more fancy manipulation. And after all that it still may not look great. I'm not sure how antialiasing and such is handled for instance, but it might not be good. Good luck, and if anyone has an easier way, I'd be interested to know as well.

Answered by user12861 on November 7, 2021

Add your own answers!

Ask a Question

Get help from others!

© 2024 TransWikia.com. All rights reserved. Sites we Love: PCI Database, UKBizDB, Menu Kuliner, Sharing RPP