pythonms-worddocxpython-docx

Make the contents of a table in the header and/or footer RTL using Python docx library


I've got a word document that has an empty 1 by 3 table (or a table with any dimension for that matter) in the header. I want to be able to manipulate the cells of the table using python-docx library. The language of the document is Farsi and naturally all the text in Farsi must start from right to left. Here is how the code looks like:

from docx import Document

document = Document("test.docx")

c = document.sections[0].header.tables[0].rows[0].cells[0]
c.text = "سلام"
document.save("output.docx")

The problem is no matter what kind of trick I use to change the text direction, I can't get it to work. I've tried using the WD_TABLE_DIRECTION.RTL flag to change the direction of the table or changing the direction of the run using run.font.rtl = True or even using special unicode characters such as u'\u202B' and u'\u202C' to change the direction, but to no success.

I even tried altering the xml related to the header but honestly that's beyond me and all my attempts were unfortunately unsuccessful.

I'm writing this question late at night and I cannot express the amount of hatred I have for my native language right now. So any help would be appreciated.


Solution

  • If you set _Cell.text using python.docx then that first removes all cell content and then set into the cell a new paragraph containing one new text-run containing the text. But the bidirectional setting to make the paragraph's text direction right to left is part of the paragraph properties which get removed while removing the cell content. And the new created paragraph will be a default paragraph without bidirectional setting.

    So instead of c.text = "سلام" you should try to find the first paragraph in cell - there always should be one - and clear this paragraph. Then get paragraph properties of that found paragraph and check whether it has w:bidi set. If not, set it. Then add a new text-run to the paragraph, copy possibly existing run properties from paragraph properties or create new run properties and set w:rtl to that run properties. Finally set run.text = "سلام".

    Complete example:

    from docx import Document
    from copy import copy
    from docx.oxml import OxmlElement
    
    document = Document('test_in.docx')
    
    cell = document.sections[0].header.tables[0].rows[0].cells[0]
    #c.text = "سلام"
    
    paragraph = cell.paragraphs[0]
    
    paragraph.clear()
    
    ct_ppr = paragraph.paragraph_format._element.pPr
    
    ct_ppr_has_bidi = len(ct_ppr.xpath("./w:bidi")) > 0
    if not ct_ppr_has_bidi:
        ct_bidi = OxmlElement("w:bidi")
        ct_ppr.append(ct_bidi)
    
    run = paragraph.add_run()
    ct_ppr_has_rPr = len(ct_ppr.xpath("./w:rPr")) > 0
    if ct_ppr_has_rPr:
        ct_rpr = copy(ct_ppr.xpath("./w:rPr")[0])
    else:
        ct_rpr = OxmlElement("w:rPr")
    ct_rtl = OxmlElement("w:rtl")
    ct_rpr.append(ct_rtl)
    run._element.append(ct_rpr)
            
    run.text = "سلام"
                
    document.save('output.docx')