Search code examples
ubuntufontspython-imaging-libraryligature

Using Pillow to draw cursive text


I'm to draw text over an image in a Django application hosted on an Ubuntu 14.04 LTS machine. Pillow 4.2.1 is my lib of choice.

I've successfully accomplished this task via ImageDraw imported from PIL (The actual code is at the end of this question)

My code works perfectly for languages such as English, French or Spanish.

But not for naturally cursive languages like Arabic, Persian or Urdu. In such cases, it draws each letter separately. E.g. فارسی(Persian) is drawn as:

enter image description here

Note that I installed sudo apt-get install ttf-mscorefonts-installer and tried /fonts/truetype/msttcorefonts/Arial.ttf for this.

Someone advised me to make sure the font I'm using has ligatures. My understanding is that Arial does support ligatures, yet the problem persists.

My question is: what should I do to fix this issue? My code must support naturally cursive languages like Arabic, Persian or Urdu.


The code:

from PIL import ImageDraw
draw = ImageDraw.Draw(img)
base_width, base_height = img.size
y = 2
for line in lines:
    width, height = font.getsize(line)
    x = (base_width - width) / 2
    text_with_stroke(draw,x,y,line,font,fillcolor,shadowcolor)
    y += height

where text_with_stroke is simply:

def text_with_stroke(draw,width,height,line,font,fillcolor,shadowcolor):
    draw.text((width-1, height), line, font=font, fill=shadowcolor)
    draw.text((width+1, height), line, font=font, fill=shadowcolor)
    draw.text((width, height-1), line, font=font, fill=shadowcolor)
    draw.text((width, height+1), line, font=font, fill=shadowcolor)
    draw.text((width, height), line, font=font, fill=fillcolor)

In a nutshell, this code breaks any given text into separate lines, taking into account the font size and underlying image size. It then iteratively draws each line of text on the image.


Solution

  • The best answer for this problem is already tackled on this rather excellent SO post: https://stackoverflow.com/a/25727238/4936905.

    Essentially, two Python libraries are needed here: BiDi and Arabic Reshaper.

    That's pip install python-bidi and pip install git+https://github.com/mpcabd/python-arabic-reshaper. The exact implementation entails transforming text as follows before drawing it via PIL:

    reshaped_text = arabic_reshaper.reshape(line)
    final_text = get_display(reshaped_text)
    
    draw.text((width-1, height), final_text, font=font, fill=shadowcolor)
    draw.text((width+1, height), final_text, font=font, fill=shadowcolor)
    draw.text((width, height-1), final_text, font=font, fill=shadowcolor)
    draw.text((width, height+1), final_text, font=font, fill=shadowcolor)
    draw.text((width, height), final_text, font=font, fill=fillcolor)