how to fix the reversed Lines when using arabic-reshaper and python-bidi (in multiple lines situation)?

Question:

When I am using arabic-reshaper and python-bidi I faced a bad result that the lines start presenting from the last one.

Asked By: TheEngineer

||

Answers:

I made a function to round on this problem since there is no way to solve it in another way, bidi is reversing the characters to put the first at the end and so on, that’s because the Arabic language starts from right to left and that way bidi will fake the result to appear in the right shape, but whene the text has to go into more than one line that will be wrong to present the first word at the end! so I have to let it do that then I have to reverse the result as reversed lines this time depending on how many words the line could contain, I am calculating that through passing two arguments, w_w for the width of the widget (or the other place) where the text will appear and (f_w) which means the character width of the used font.

Then after cumulating each line, I reverse the line presentation, that’s it! and here is the function I made:

import arabic_reshaper
import bidi.algorithm
    
def getAR(arWord, w_w=0, f_w=0):
    arWord = arWord.strip()
    if len(arWord) <= 0: return ''
    startList0 = bidi.algorithm.get_display(arabic_reshaper.reshape(arWord))
    if (not w_w) or (not f_w):
        return startList0
    else:
        # return startList0
        startList = startList0.split(' ')[::-1]
        if len(startList) == 0: return ''
        if len(startList) == 1: return str(startList[0])
        n = floor( w_w / f_w )
        for i in startList:
            if len(i) > n: return startList0
        tempS = ''
        resultList = []
        for i in range(0, len(startList)):
            if (tempS != ''): tempS = ' ' + tempS
            if (len(tempS) + (len(startList[i])) > n):
                tempS = tempS + "n"
                resultList.append(tempS)
                tempS = startList[i]
            else:
                tempS = startList[i] + tempS
                if i == (len(startList)-1):
                    resultList.append(tempS)
        return ''.join(resultList)

you will use it like this :

w_w = ... # calculat it yourself, the width of where you will put the text.
f_w = ... # calculat it yourself, the width of the character in the font you are using. 
paragraph = "...  ..."
widget.text = getAr(paragraph, w_w=w_w, f_w=f_w)

enter image description here

Answered By: TheEngineer

Reviving this in case anyone still has this problem. Anyway here’s my case followed by the solution :

Case :

I tried automating adding user info in arabic to a business card template using Pillow, arabic-reshaper and python-bidi :

from PIL import Image, ImageFont, ImageDraw 
import arabic_reshaper
from bidi.algorithm import get_display
import textwrap

# this function ensures the arabic letters are not disjointed :
def arabic_reshaper_function(string_ar) :
    string_ar = arabic_reshaper.reshape(string_ar)
    string_ar = get_display(string_ar , base_dir='R')
    return string_ar

# Name string I want to add to the empty business card :
name_ar = 'أول ثاني ثالث رابع خامس'
name_ar = arabic_reshaper_function(name_ar)

# Job title string, tends to be long so I use textwrap here which returns a list :
job_ar = "أول ثاني ثالث رابع خامس سادس سابع ثامن تاسع عاشر حادي عشر ثاني عشر ثالث عشر"
job_ar = arabic_reshaper_function(job_ar)
wrapped_job_ar = textwrap.wrap(job_ar, width=60)

# Address string, also tends to be long so I use textwrap here which returns a list :
address_ar = "أول ثاني ثالث رابع خامس سادس سابع ثامن تاسع عاشر حادي عشر ثاني عشر ثالث عشر رابع عشر خامس عشر سادس عشر سابع عشر"
address_ar = arabic_reshaper_function(address_ar)
wrapped_address_ar = textwrap.wrap(address_ar, width=90)

# additional PILLOW code goes here to import fonts ...

# Here we open the template business card & use a loop to print the lines from the lists wrapped_job_ar and wrapped_address_ar one by one :

with Image.open('template_ar.png') as template_ar:
    
    # Converting the image into an editable format :
    template_ar_editable = ImageDraw.Draw(template_ar)
    
    # Rendering the name using the top-right corner as anchor because it's arabic :
    template_ar_editable.text((2523,480), name_ar, (0, 0, 0), name_ar_font, anchor='rs') 
    
    # Rendering the job title. Here we iterate line by line & offset the y axis by some pixels :
    job_ar_y = 571
    for line in wrapped_job_ar:
        template_ar_editable.text((2523, job_ar_y), line, (0, 0, 0), job_ar_font, anchor='ra')
        job_ar_y += 139
            
    # Rendering the address the same way :    
    address_ar_y = 1067
    for line in wrapped_address_ar:
        template_ar_editable.text((2523, address_ar_y), line, (0, 0, 0), rest_ar_font, anchor='ra')
        address_ar_y += 100
    
    # Rendering the phone :
    template_ar_editable.text((2247,1331), phone, (0, 0, 0), rest_font, anchor='ra') 
    
    # Rendering the fax :
    template_ar_editable.text((1023,1325), fax, (0, 0, 0), rest_font, anchor='ra') 
    
    # Rendering the email :
    template_ar_editable.text((2058,1490), email, (0, 0, 0), rest_font, anchor='ra') 
    
    # Saving the result as a png :
    template_ar.save(name + ' -AR' + '.png')

Here’s the result :

enter image description here

All strings should start (from right to left) with the word ‘أول’ which means ‘first’. The name is correct because it’s short enough not to need a text-wrap. The title and the address are botched however.

Solution :

To solve this I tried to apply the arabic_reshaper_function AFTER I used text-wrapper, and for each line individually. Here’s the bit of code that changed :

job_ar = "أول ثاني ثالث رابع خامس سادس سابع ثامن تاسع عاشر حادي عشر ثاني عشر ثالث عشر"
wrapped_job_ar = [arabic_reshaper_function(x) for x in textwrap.wrap(job_ar, width=65)]

address_ar = "أول ثاني ثالث رابع خامس سادس سابع ثامن تاسع عاشر حادي عشر ثاني عشر ثالث عشر رابع عشر خامس عشر سادس عشر سابع عشر"
wrapped_address_ar = [arabic_reshaper_function(x) for x in textwrap.wrap(address_ar, width=90)]

Result :

enter image description here

Answered By: kelmouloudi
Categories: questions Tags: , ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.