I'm using python-docx (v1.1.2) and Python 3.11.3 to work on a tool to fix a bunch of Word documents automatically. I've been able to update fonts, titles, texts, headers and footers and tables (their borders).
However, some tables, and I haven't found the pattern of why this happens, are not updated. This is the snippet I've developed.
def update_borders(table):
#namespaces used when updating the elements (the changes are not persistent if not used)
NSMAP = {"w": "http://schemas.openxmlformats.org/wordprocessingml/2006/main"}
WORD_NS = NSMAP["w"]
# sample color map
BORDER_COLOR_MAP = { "auto": "F07E26" }
small_table = len(table.rows) == 1 and len(table.columns) == 2
tbl_xml = table._element
tbl_pr = tbl_xml.find(f'{{{WORD_NS}}}tblPr')
if tbl_pr is None:
tbl_pr = etree.SubElement(tbl_xml, f"{{{WORD_NS}}}tblPr")
#Ensure tblBorders exists
tbl_borders = tbl_pr.find(f'{{{WORD_NS}}}tblBorders')
# If the table doesn't have a tblPr object (everything is set to "auto"), we create it
if tbl_borders is None:
tbl_borders = etree.SubElement(tbl_pr, f"{{{WORD_NS}}}tblBorders")
if small_table:
border_sides = ["top", "left", "bottom", "right"]
else:
border_sides = ["top", "left", "bottom", "right", "insideH", "insideV"]
for side in border_sides:
border = etree.SubElement(tbl_borders, f"{{{WORD_NS}}}{side}")
border.set(f"{{{WORD_NS}}}val", "single")
border.set(f"{{{WORD_NS}}}sz", "4")
border.set(f"{{{WORD_NS}}}space", "0")
border.set(f"{{{WORD_NS}}}color", "F07E26")
else:
# If there is a tblPr object, we update the color based in a map of colors
for border in tbl_borders:
border_color = border.attrib[f"{{{WORD_NS}}}color"]
if (small_table and border_color != "auto") or not small_table:
border.set(f"{{{WORD_NS}}}color", BORDER_COLOR_MAP[border.attrib[f"{{{WORD_NS}}}color"]])
While this script works for most of the tables, I've found some tables in which it doesn't. This table for instance.
<w:tbl xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main" xmlns:wpc="http://schemas.microsoft.com/office/word/2010/wordprocessingCanvas" xmlns:cx="http://schemas.microsoft.com/office/drawing/2014/chartex" xmlns:cx1="http://schemas.microsoft.com/office/drawing/2015/9/8/chartex" xmlns:cx2="http://schemas.microsoft.com/office/drawing/2015/10/21/chartex" xmlns:cx3="http://schemas.microsoft.com/office/drawing/2016/5/9/chartex" xmlns:cx4="http://schemas.microsoft.com/office/drawing/2016/5/10/chartex" xmlns:cx5="http://schemas.microsoft.com/office/drawing/2016/5/11/chartex" xmlns:cx6="http://schemas.microsoft.com/office/drawing/2016/5/12/chartex" xmlns:cx7="http://schemas.microsoft.com/office/drawing/2016/5/13/chartex" xmlns:cx8="http://schemas.microsoft.com/office/drawing/2016/5/14/chartex" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:aink="http://schemas.microsoft.com/office/drawing/2016/ink" xmlns:am3d="http://schemas.microsoft.com/office/drawing/2017/model3d" xmlns:o="urn:schemas-microsoft-com:office:office" xmlns:oel="http://schemas.microsoft.com/office/2019/extlst" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships" xmlns:m="http://schemas.openxmlformats.org/officeDocument/2006/math" xmlns:v="urn:schemas-microsoft-com:vml" xmlns:wp14="http://schemas.microsoft.com/office/word/2010/wordprocessingDrawing" xmlns:wp="http://schemas.openxmlformats.org/drawingml/2006/wordprocessingDrawing" xmlns:w10="urn:schemas-microsoft-com:office:word" xmlns:w14="http://schemas.microsoft.com/office/word/2010/wordml" xmlns:w15="http://schemas.microsoft.com/office/word/2012/wordml" xmlns:w16cex="http://schemas.microsoft.com/office/word/2018/wordml/cex" xmlns:w16cid="http://schemas.microsoft.com/office/word/2016/wordml/cid" xmlns:w16="http://schemas.microsoft.com/office/word/2018/wordml" xmlns:w16du="http://schemas.microsoft.com/office/word/2023/wordml/word16du" xmlns:w16sdtdh="http://schemas.microsoft.com/office/word/2020/wordml/sdtdatahash" xmlns:w16sdtfl="http://schemas.microsoft.com/office/word/2024/wordml/sdtformatlock" xmlns:w16se="http://schemas.microsoft.com/office/word/2015/wordml/symex" xmlns:wpg="http://schemas.microsoft.com/office/word/2010/wordprocessingGroup" xmlns:wpi="http://schemas.microsoft.com/office/word/2010/wordprocessingInk" xmlns:wne="http://schemas.microsoft.com/office/word/2006/wordml" xmlns:wps="http://schemas.microsoft.com/office/word/2010/wordprocessingShape">\n
<w:tblPr>\n
<w:tblStyle w:val="Tablaconcuadrcula"/>\n
<w:tblW w:w="9684" w:type="dxa"/>\n
<w:jc w:val="center"/>\n
<w:tblLook w:val="04A0" w:firstRow="1" w:lastRow="0" w:firstColumn="1"
w:lastColumn="0" w:noHBand="0" w:noVBand="1"/>\n
</w:tblPr>\n
....
<w:tc>\n
<w:tcPr>\n
<w:tcW w:w="8062" w:type="dxa"/>\n
<w:tcBorders>\n
<w:top w:val="single" w:sz="4" w:space="0" w:color="auto"/>\n
<w:left w:val="single" w:sz="4" w:space="0" w:color="auto"/>\n
<w:bottom w:val="single" w:sz="4" w:space="0" w:color="auto"/>\n
<w:right w:val="single" w:sz="4" w:space="0" w:color="auto"/>\n
</w:tcBorders>\n
<w:shd w:val="clear" w:color="auto" w:fill="FDE7D4"/>\n
<w:hideMark/>\n
</w:tcPr>\n
I noticed that the table doesn't have a tblBorders inside of tblPr, but that every cell has the same style set individually. However I don't understand that even when I set it manually (with etree.SubElement(tbl_pr, f"{{{WORD_NS}}}tblBorders")), if I save that document, and load it again with python-docx, the properties are not there. I assume one alternative could be to edit all the cell individually with a loop, but I don't see why this doesn't work.