I wanted to add custom tab positions at fixed positions for particular paragraphs or styles and did not find an easy way to do this in docx. A quick scroll through the issues list found some example code which I have adapted. If anyone wants to tidy this up and add this to this fantastic library...
To use:
from docx import Document
from docx.shared import Inches, Cm
document = Document()
# add to style
style = document.document.styles["Header 2"]
add_custom_tabs_to_paragraph_format(style.paragraph_format, [Cm(tt) for tt in range(1, 5)])
# add to paragraph
pp = document.add_paragraph("test", 3)
add_custom_tabs_to_paragraph_format(pp.paragraph_format, [Cm(tt) for tt in range(1, 10)])
document.save("test.docx")
Custom Tab Code:
#__________________________________________________________________________________________________________________
# add_custom_tabs_to_paragraph_format
#__________________________________________________________________________________________________________________
def add_custom_tabs_to_paragraph_format(paragraph_format, tab_stops, tab_style = "left", tab_leader = None):
"""
tab_stops specified as list of Cm or Inches
N.B. Hanging indents create an implicit tab stop
<w:pPr>
<w:tabs><w:tab w:val="left" w:pos="567"/>
<w:tab w:val="left" w:pos="1134"/>
<w:tab w:val="left" w:pos="1701"/>
<w:tab w:val="left" w:pos="2268"/></w:tabs>
...
</w:pPr>
"""
if tab_style not in ["left", "right", "center", "decimal", "bar", "clear", "end", "num", "start", ]:
raise Exception("tab_style = %s is not a valid tab style in add_custom_tabs_to_paragraph_format(...)" % tab_style )
if tab_leader not in [None, "dot", "heavy", "hyphen", "middleDot", "none", "underscore", ]:
raise Exception("tab_leader = %s is not a valid tab leader in add_custom_tabs_to_paragraph_format(...)" % tab_leader )
from docx.oxml import OxmlElement
from docx.oxml.ns import qn
def first_child_found_in(parent, tagnames):
"""
Return the first child of parent with tag in *tagnames*, or None if
not found.
"""
for tagname in tagnames:
child = parent.find(qn(tagname))
if child is not None:
return child
return None
def insert_element_before(parent, elm, successors):
"""
Insert *elm* as child of *parent* before any existing child having
tag name found in *successors*.
"""
successor = first_child_found_in(parent, successors)
if successor is not None:
successor.addprevious(elm)
else:
parent.append(elm)
return elm
elem = paragraph_format.element # elem is the <w:style> XML element
pPr = elem.get_or_add_pPr()
pTabs = OxmlElement('w:tabs')
insert_element_before(pPr, pTabs, successors=(
'w:shd', 'w:tabs', 'w:suppressAutoHyphens', 'w:kinsoku', 'w:wordWrap',
'w:overflowPunct', 'w:topLinePunct', 'w:autoSpaceDE', 'w:autoSpaceDN',
'w:bidi', 'w:adjustRightInd', 'w:snapToGrid', 'w:spacing', 'w:ind',
'w:contextualSpacing', 'w:mirrorIndents', 'w:suppressOverlap', 'w:jc',
'w:textDirection', 'w:textAlignment', 'w:textboxTightWrap',
'w:outlineLvl', 'w:divId', 'w:cnfStyle', 'w:rPr', 'w:sectPr',
'w:pPrChange'
))
for tt in tab_stops:
tab_n = OxmlElement('w:tab')
tab_n.set(qn('w:val'), tab_style)
tab_n.set(qn('w:pos'), '%s' % (tt / 625))
if tab_leader is not None:
tab_n.set(qn('w:leader'), tab_leader)
pTabs.append(tab_n)
I wanted to add custom tab positions at fixed positions for particular paragraphs or styles and did not find an easy way to do this in docx. A quick scroll through the issues list found some example code which I have adapted. If anyone wants to tidy this up and add this to this fantastic library...
To use:
Custom Tab Code: