Using python docx to create word documents

Python Docx

If you need to create a word doc on a server I recommend using the python package “docx”.

To install docx run

1
pip install python-docx

You can read through the official documentation here:

http://python-docx.readthedocs.io/en/latest/user/install.html

Styling your template

A useful thing to do is to style your template in microsoft word and then save the template and use it as your starting document.

eg

1
document = Document("document_path.docx", "path_to_template.docx"))

Some tricks I have learnt while using docx

First you create your document:

1
document = Document("path/doc.docx")

Then you add elements to your document using the following

  • add_paragraph
  • add_picture
  • add_page_break
  • add_heading
  • add_table
  • add_row

Examples

1
document.add_paragraph('Hello World'

Adding a table

1
2
3
4
table = document.add_table(rows=1, cols=5)
row = table.add_row().cells
index = 0
row[index].add_paragraph('Hello World')

XML

To view the xml of an element use element.xml

Eg

1
2
p = document.add_paragraph('Hello World')
p.xml

Advanced

I ran into a tricky part where I wanted to add content to the middle of an existing document with existing content.

I added this copy to the document “[BOOKMARK HERE]”

And then I used this code to search for that and then to add elements after that copy.

Add a paragraph after a paragraph, or a heading after a paragraph:

1
2
3
4
5
def addParagraphAfterParagraph(self, document, paragraph, bookmark = '[BOOKMARK HERE]'):
  for para in document.paragraphs:
    if para.text == bookmark:
      p = para._p
      p.addnext(paragraph._p)

To add a table after the copy I had to use something like this:

1
2
3
4
5
def addTableAfterParagraph(self, document, table, bookmark = '[BOOKMARK HERE]'):
  for para in document.paragraphs:
    if para.text == bookmark:
      p = para._p
      p.addnext(table._tbl)

To add an image after that copy I had to use something like this:

1
2
3
4
5
6
7
def addImageAfterParagraph(self, document, image_path, width, bookmark = '[IMAGE HERE]'):
  for para in document.paragraphs:
    if para.text == bookmark:
      p = para._p
      para.text = ''
      run = para.add_run()
      run.add_picture(image_path, width=width)

Also remember you have to run these in reverse because you’re adding it after an element, so it will place before the next element each time and immediately after the copy.