docx_plus¶
OOXML-level extensions for python-docx — the library every python-docx power user ends up writing badly: hardened helpers for OOXML operations that sit just past python-docx's abstraction boundary.
Status: v0.2.0 released — published on PyPI.
Capabilities¶
- Style cascade — read the effective formatting that would apply to any paragraph/run/cell, with per-field provenance; modify styles in the Word-native way rather than scattering direct formatting.
- Content controls — text / dropdown / date / checkbox controls
via
FormBuilder; round-trip read/write of values; form protection. - Fields — PAGE / NUMPAGES / DATE / generic complex fields; mark fields dirty so Word recalculates them on next open.
- Protection — enforce form-fill, read-only, comments-only, or tracked-changes mode at the document level.
- Anchored comments (v0.2) — the body-side range markers python-docx skips, so "show in document" actually works.
- Layout (v0.2) — multi-column sections, mid-document section breaks, distinct even/odd headers.
- Bookmarks + cross-references (v0.2) — paired body markers plus
REF/PAGEREFfields. - Footnotes + endnotes (v0.2) — separate
footnotes.xml/endnotes.xmlparts; insert + edit in-place. - Layout: line numbers + page borders (v0.2) — marginal line numbering and decorative page borders.
- Conditional table-style formatting (v0.2) — the cascade
applies
<w:tblStylePr>branches (first row, banded rows, corners) in ECMA-376 17.7.6.5 precedence order. - Publishing (v0.2) — Table of Contents, figure / table captions
via
SEQ, and a downstream Table of Figures.
Where to start¶
- New to the library? Read the Architecture overview and skim the API index.
- Want the full reference? API Index lists every public symbol; Reference has per-module pages with full signatures and docstrings.
- Driving the library from an LLM coding agent? See the Agent Skill — point Claude Code (or any agent) at it to generate docx_plus automation without hand-feeding the API.
- Auditing the project? See the Test Gaps snapshot.
Quickstart¶
Inspect¶
from docx import Document
from docx_plus.styles import resolve_effective_formatting
doc = Document("report.docx")
p = doc.paragraphs[0]
resolved = resolve_effective_formatting(p, include_provenance=True)
print(resolved.font_size, resolved.bold)
print(resolved.provenance["font_size"]) # which cascade layer set it
Modify¶
from docx import Document
from docx_plus.styles import create_style, apply_style
doc = Document()
create_style(
doc, "BrandHeading",
style_type="paragraph",
based_on="Heading1",
font_name="Inter",
font_size=18.0,
color_rgb="2F5496",
bold=True,
)
apply_style(doc.add_paragraph("Hello"), "BrandHeading")
doc.save("out.docx")
Ensure (latent built-ins)¶
from docx_plus.styles import ensure_style
ensure_style(doc, "Heading1") # one of 107 known built-ins
ensure_style(doc, "TOC2")
ensure_style(doc, "BlockText")
See ARCHITECTURE.md §5 for
the full tiered table.
Forms¶
from docx_plus.controls import FormBuilder
fb = FormBuilder()
p = fb.doc.add_paragraph("Name: ")
fb.add_text_control(p, tag="name", placeholder="Type your name")
p = fb.doc.add_paragraph("Dept: ")
fb.add_dropdown(p, tag="dept", items=["Eng", "Design", "Ops"])
fb.save("form.docx")
Read / update with read_controls(doc) and set_control_value(doc,
tag, value). See ARCHITECTURE.md §6.
Fields & protection¶
from docx_plus.fields import add_page_number_field, mark_fields_dirty
from docx_plus.protection import protect_document
p = doc.add_paragraph("Page ")
add_page_number_field(p)
p.add_run(" of ")
add_page_number_field(p, field="NUMPAGES")
mark_fields_dirty(doc) # Word recalculates on open
protect_document(doc, mode="forms") # only content controls editable
See ARCHITECTURE.md §7.
Comments (v0.2)¶
from docx import Document
from docx_plus.comments import add_comment, read_comments
doc = Document()
p = doc.add_paragraph()
p.add_run("Project Apollo ")
target = p.add_run("ships next quarter")
add_comment(target, "Optimistic — let's see QA.", author="Alice")
for c in read_comments(doc):
print(c.author, c.text, "→", c.anchored_text)
Unlike python-docx's own add_comment (which only writes the part-side
body), docx_plus writes the three body-side anchors — so Word's
"show in document" jumps to the right place. See
ARCHITECTURE.md §7.6.
Layout (v0.2)¶
from docx_plus.layout import insert_section_break, set_columns
split = doc.add_paragraph("Section break here ↓")
new_section = insert_section_break(split, start_type="continuous")
set_columns(new_section, 2, space=720, separator=True)
See ARCHITECTURE.md §7.7.
Bookmarks + cross-references (v0.2)¶
from docx_plus.bookmarks import add_bookmark, add_cross_reference
from docx_plus.fields import mark_fields_dirty
heading = doc.add_heading("Introduction", level=1)
add_bookmark(heading, "intro_section")
p = doc.add_paragraph("See ")
add_cross_reference(p, bookmark="intro_section", kind="text")
mark_fields_dirty(doc) # Word recalculates REF / PAGEREF on open
See ARCHITECTURE.md §7.8.
Footnotes + endnotes (v0.2)¶
from docx_plus.notes import add_footnote, add_endnote, edit_footnote
p = doc.add_paragraph("This claim has a footnote")
ref = add_footnote(p, "Sourced from internal benchmarks, 2026-05-19.")
add_endnote(p, "Re-validated against external dataset Q3 2026.")
# Need to update the footnote body later?
edit_footnote(doc, ref.note_id, "Re-sourced from external benchmarks.")
See ARCHITECTURE.md §7.9.
Layout: line numbers + page borders (v0.2)¶
from docx_plus.layout import Border, set_line_numbering, set_page_borders
set_line_numbering(doc.sections[0], count_by=5, restart="newPage")
rule = Border(style="single", size=8, color="2F5496")
set_page_borders(
doc.sections[0], top=rule, bottom=rule, left=rule, right=rule,
)
See ARCHITECTURE.md §7.7.
Publishing (v0.2)¶
from docx_plus.fields import mark_fields_dirty
from docx_plus.publishing import add_caption, add_table_of_figures, add_toc
doc.add_heading("Contents", level=1)
add_toc(doc.add_paragraph(), levels=(1, 2))
doc.add_heading("Body", level=1)
cap = doc.add_paragraph()
add_caption(cap, "Figure ", caption_type="Figure")
cap.add_run(": System overview.")
doc.add_heading("List of Figures", level=1)
add_table_of_figures(doc.add_paragraph())
mark_fields_dirty(doc) # Word populates TOC / SEQ / ToF on open
Roadmap¶
| Phase | Capability | Status |
|---|---|---|
| 1 | Foundation | ✓ complete |
| 2 | Style inspection | ✓ complete |
| 3 | Style modification | ✓ complete |
| 3.5 | Style remapping | ✓ complete |
| 4 | Content controls | ✓ complete |
| 5 | Fields + protection | ✓ complete |
| 6 | Polish (examples, smoke tests, CI doc build) | ✓ complete |
| v0.2 | Comments, layout, bookmarks/cross-refs, notes, core/parts |
✓ complete |
| v0.2 expansion | Toggle props, in-place edits, line numbering, page borders, conditional table styles, publishing/ |
✓ complete |