From 195d6a7090c2a4a5b0730ca33518b422179ae3ac Mon Sep 17 00:00:00 2001 From: Nick Gashkov Date: Mon, 17 Dec 2018 15:27:14 +0300 Subject: [PATCH] Fix tables with gridSpan that still have less cells after the tc forloop --- docxtpl/__init__.py | 47 ++++++++++++++++++ tests/less_cells_after_loop.py | 5 ++ .../templates/less_cells_after_loop_tpl.docx | Bin 0 -> 4828 bytes 3 files changed, 52 insertions(+) create mode 100644 tests/less_cells_after_loop.py create mode 100644 tests/templates/less_cells_after_loop_tpl.docx diff --git a/docxtpl/__init__.py b/docxtpl/__init__.py index 42b8183..f968fdf 100644 --- a/docxtpl/__init__.py +++ b/docxtpl/__init__.py @@ -4,6 +4,7 @@ Created : 2015-03-12 @author: Eric Lapouyade ''' +import functools __version__ = '0.5.11' @@ -340,6 +341,52 @@ class DocxTemplate(object): for i in range(to_add): etree.SubElement(tblGrid, ns+'gridCol', {ns+'w': str(int(new_average))}) + + # Refetch columns after columns addition. + columns = tblGrid.findall(ns + 'gridCol') + columns_len = len(columns) + + cells_len_max = 0 + + def get_cell_len(total, cell): + tc_pr = cell.find(ns + 'tcPr') + grid_span = tc_pr is not None and tc_pr.find(ns + 'gridSpan') + + if grid_span is not None: + return total + int(grid_span.get(ns + 'val')) + + return total + 1 + + # Calculate max of table cells to compare with `gridCol`. + for r in t.iter(ns + 'tr'): + cells = r.findall(ns + 'tc') + cells_len = functools.reduce(get_cell_len, cells, 0) + cells_len_max = max(cells_len_max, cells_len) + + to_remove = columns_len - cells_len_max + + # If after the loop, there're less columns, than + # originally was, remove extra `gridCol` declarations. + if to_remove > 0: + # Have to keep track of the removed width to scale the + # table back to its original width. + removed_width = 0.0 + + for c in columns[-to_remove:]: + removed_width += float(c.get(ns + 'w')) + + tblGrid.remove(c) + + columns_left = tblGrid.findall(ns + 'gridCol') + + # Distribute `removed_width` across all columns that has + # left after extras removal. + extra_space = removed_width / len(columns_left) + extra_space = int(extra_space) + + for c in columns_left: + c.set(ns+'w', str(int(float(c.get(ns+'w')) + extra_space))) + return tree def new_subdoc(self,docpath=None): diff --git a/tests/less_cells_after_loop.py b/tests/less_cells_after_loop.py new file mode 100644 index 0000000..4e0cd5a --- /dev/null +++ b/tests/less_cells_after_loop.py @@ -0,0 +1,5 @@ +from docxtpl import DocxTemplate + +tpl = DocxTemplate('templates/less_cells_after_loop_tpl.docx') +tpl.render({}) +tpl.save('output/less_cells_after_loop.docx') diff --git a/tests/templates/less_cells_after_loop_tpl.docx b/tests/templates/less_cells_after_loop_tpl.docx new file mode 100644 index 0000000000000000000000000000000000000000..43ef566074768ec3f39a06dcddf953abfa5d5970 GIT binary patch literal 4828 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