python-docx-template icon indicating copy to clipboard operation
python-docx-template copied to clipboard

Table width becomes narrower after render

Open nikitagashkov opened this issue 6 years ago • 2 comments

The DocxTemplate#fix_tables does not fix width of a table.

Details

The problem is that DocxTemplate#fix_tables correctly updates <w:tblGrid> but does not change <w:tcW> of an individual cell. MS Word prioritizes <w:tcW> over <w:tblGrid> (see this link) and renders cells with same widths.

How to reproduce

Render the template such as the one below and open the output file in MS Word. tpl

Expected behavior

Total width of a table does not change after template rendering and cells become wider. The result should look somewhat like this: post-fix

Actual behavior

Actual widths of cells stay the same and, therefore, total width of a table reduces after rendering. The result looks like this: pre-fix

Possible solution

Currently, my quick-and-dirty workaround is to remove individual width of a cell (<w:tcW>) for the entire document:

for tc_w in tree.xpath('.//w:tcW', namespaces=tree.nsmap):
    tc_w.getparent().remove(tc_w)

Warning! This approach works only for tables that should autofit window (check out this link for details).

I think, this won't work for everyone, so I haven't made a pull request. Probably, the right way is to walk through all cells of a table and update its' individual widths with corresponding <w:gridCol> value.

Source files

  1. template.docx;
  2. pre-change.docx;
  3. post-change.docx.

nikitagashkov avatar Jan 25 '19 12:01 nikitagashkov

sorry, but is this the solution to keep rendered table has the same column widths in template docs file? and how can I use this code?

retsyo avatar Jul 30 '20 14:07 retsyo

Hi, @retsyo!

this the solution to keep rendered table has the same column widths in template docs file?

All dynamically generated columns via {%tc for %} loop will have the same width. Already present columns will stay as is.

and how can I use this code?

If I remember correctly, this is the path that I've taken to add this workaround:

from docxtpl import DocxTemplate


class DocxTemplateWithWorkaround(DocxTemplate):
    # Add the workaround after the original `fix_tables` here:
    def fix_tables(self, xml):
        tree = super().fix_tables(xml)

        for tc_w in tree.xpath('.//w:tcW', namespaces=tree.nsmap):
            tc_w.getparent().remove(tc_w)

        return tree

# After that, you can use `DocxTemplateWithWorkaround` the same way as you did before:
doc = DocxTemplateWithWorkaround("my_word_template.docx")
context = { 'company_name' : "World company" }
doc.render(context)
doc.save("generated_doc.docx")

nikitagashkov avatar Aug 01 '20 09:08 nikitagashkov