Word Documents
Purpose
Produce and manipulate .docx files programmatically, with real styles and structure rather than a wall of manually formatted paragraphs.
When to Use
- Generating a report, memo, letter, or contract as a Word document.
- Extracting text, tables, or comments from an existing
.docx.
- Performing find-and-replace across a document while preserving formatting.
- Filling a template with data.
- Working with tracked changes or comments.
Capabilities
- Document generation with headings, tables, images, headers, and footers.
- Style application and modification.
- Content extraction, including tables and embedded objects.
- Find-and-replace preserving runs and formatting.
- Template filling.
- Tracked changes and comments.
Inputs
- The content and its structure.
- A template or style reference, if the output must match a house style.
- The source document, when reading or editing.
Outputs
- A valid
.docx with a proper style hierarchy.
- Extracted content in a structured form.
- An edited document that preserves the formatting it should preserve.
Workflow
- Use styles, never direct formatting — A heading is
Heading 1, not 18pt bold. Styles produce a navigable document, a working table of contents, and a document that can be restyled in one operation.
- Start from a template when the house style matters — Load the organization's
.dotx or a reference .docx and write into its styles rather than recreating them.
- Structure before content — Headings, then the body. The outline is what makes a long document usable.
- Preserve formatting when editing — A naive find-and-replace on the XML destroys runs. Replace within runs, and handle text split across runs.
- Verify the output — Open it. A
.docx that a library writes without error can still be malformed in ways Word will complain about.
Best Practices
- Direct formatting (bold, a font size, a colour applied by hand) makes a document unmaintainable. Every visual property should come from a style.
- Text in Word is stored in runs, and a single sentence is frequently split across several. A replace that only handles single-run text will silently miss matches where formatting changes mid-word.
- Tables need a table style, or they render with no borders and are unreadable when printed.
- Images need explicit dimensions. Without them, Word inserts at native resolution, which is usually enormous.
- A table of contents field must be marked for update, and Word will prompt the user on open. This is expected behavior, not a bug.
- For a document that will be edited by humans, keep the structure simple. Deeply nested tables and text boxes are fragile.
Examples
Generating a structured document:
from docx import Document
from docx.shared import Pt, Inches
from docx.enum.text import WD_ALIGN_PARAGRAPH
# Start from the house template so styles already exist and match.
doc = Document("templates/report-template.docx")
doc.add_heading("Quarterly Operations Review", level=0) # Title style
p = doc.add_paragraph()
p.alignment = WD_ALIGN_PARAGRAPH.CENTER
run = p.add_run("Q2 2026")
run.italic = True
doc.add_heading("Summary", level=1)
doc.add_paragraph(
"Availability met the 99.9% target in each month of the quarter. "
"Two incidents exceeded the 30-minute recovery objective; both are covered below."
)
doc.add_heading("Incidents", level=1)
table = doc.add_table(rows=1, cols=4)
table.style = "Light Grid Accent 1" # a real table style, not hand-drawn borders
for cell, heading in zip(table.rows[0].cells, ["Date", "Duration", "Impact", "Cause"]):
cell.paragraphs[0].add_run(heading).bold = True
for incident in incidents:
row = table.add_row().cells
row[0].text = incident.date.isoformat()
row[1].text = f"{incident.minutes} min"
row[2].text = incident.impact
row[3].text = incident.cause
doc.add_page_break()
doc.save("output/q2-review.docx")
Find-and-replace that survives split runs:
def replace_text(paragraph, old: str, new: str) -> None:
"""Word splits text across runs at formatting boundaries. A naive
run-by-run replace misses any match that spans a boundary — which is
most of them, in a real document."""
full_text = "".join(run.text for run in paragraph.runs)
if old not in full_text:
return
replaced = full_text.replace(old, new)
# Write the result into the first run and clear the rest, preserving the
# first run's formatting for the whole paragraph.
paragraph.runs[0].text = replaced
for run in paragraph.runs[1:]:
run.text = ""
Notes
- The split-run problem is the most common source of "my find-and-replace only worked sometimes". A placeholder like
{{name}} is frequently stored as {{, nam, e}} across three runs because of an autocorrect or a spellcheck boundary.
.dotx templates preserve styles, headers, footers, and page setup. Loading one is nearly always better than building a document's styling from scratch.
- If the output will be converted to PDF, verify the conversion — fonts that are not embedded will be substituted, and the layout will shift.
1---2name: word-documents3description: Use when creating, reading, or editing Word documents (.docx). Covers document generation with styles and structure, extracting content, find-and-replace, tracked changes, and templates.4---56# Word Documents78## Purpose910Produce and manipulate `.docx` files programmatically, with real styles and structure rather than a wall of manually formatted paragraphs.1112## When to Use1314- Generating a report, memo, letter, or contract as a Word document.15- Extracting text, tables, or comments from an existing `.docx`.16- Performing find-and-replace across a document while preserving formatting.17- Filling a template with data.18- Working with tracked changes or comments.1920## Capabilities2122- Document generation with headings, tables, images, headers, and footers.23- Style application and modification.24- Content extraction, including tables and embedded objects.25- Find-and-replace preserving runs and formatting.26- Template filling.27- Tracked changes and comments.2829## Inputs3031- The content and its structure.32- A template or style reference, if the output must match a house style.33- The source document, when reading or editing.3435## Outputs3637- A valid `.docx` with a proper style hierarchy.38- Extracted content in a structured form.39- An edited document that preserves the formatting it should preserve.4041## Workflow42431. **Use styles, never direct formatting** — A heading is `Heading 1`, not 18pt bold. Styles produce a navigable document, a working table of contents, and a document that can be restyled in one operation.442. **Start from a template when the house style matters** — Load the organization's `.dotx` or a reference `.docx` and write into its styles rather than recreating them.453. **Structure before content** — Headings, then the body. The outline is what makes a long document usable.464. **Preserve formatting when editing** — A naive find-and-replace on the XML destroys runs. Replace within runs, and handle text split across runs.475. **Verify the output** — Open it. A `.docx` that a library writes without error can still be malformed in ways Word will complain about.4849## Best Practices5051- Direct formatting (bold, a font size, a colour applied by hand) makes a document unmaintainable. Every visual property should come from a style.52- Text in Word is stored in runs, and a single sentence is frequently split across several. A replace that only handles single-run text will silently miss matches where formatting changes mid-word.53- Tables need a table style, or they render with no borders and are unreadable when printed.54- Images need explicit dimensions. Without them, Word inserts at native resolution, which is usually enormous.55- A table of contents field must be marked for update, and Word will prompt the user on open. This is expected behavior, not a bug.56- For a document that will be edited by humans, keep the structure simple. Deeply nested tables and text boxes are fragile.5758## Examples5960**Generating a structured document:**6162```python63from docx import Document64from docx.shared import Pt, Inches65from docx.enum.text import WD_ALIGN_PARAGRAPH6667# Start from the house template so styles already exist and match.68doc = Document("templates/report-template.docx")6970doc.add_heading("Quarterly Operations Review", level=0) # Title style7172p = doc.add_paragraph()73p.alignment = WD_ALIGN_PARAGRAPH.CENTER74run = p.add_run("Q2 2026")75run.italic = True7677doc.add_heading("Summary", level=1)78doc.add_paragraph(79 "Availability met the 99.9% target in each month of the quarter. "80 "Two incidents exceeded the 30-minute recovery objective; both are covered below."81)8283doc.add_heading("Incidents", level=1)8485table = doc.add_table(rows=1, cols=4)86table.style = "Light Grid Accent 1" # a real table style, not hand-drawn borders8788for cell, heading in zip(table.rows[0].cells, ["Date", "Duration", "Impact", "Cause"]):89 cell.paragraphs[0].add_run(heading).bold = True9091for incident in incidents:92 row = table.add_row().cells93 row[0].text = incident.date.isoformat()94 row[1].text = f"{incident.minutes} min"95 row[2].text = incident.impact96 row[3].text = incident.cause9798doc.add_page_break()99doc.save("output/q2-review.docx")100```101102**Find-and-replace that survives split runs:**103104```python105def replace_text(paragraph, old: str, new: str) -> None:106 """Word splits text across runs at formatting boundaries. A naive107 run-by-run replace misses any match that spans a boundary — which is108 most of them, in a real document."""109 full_text = "".join(run.text for run in paragraph.runs)110 if old not in full_text:111 return112113 replaced = full_text.replace(old, new)114115 # Write the result into the first run and clear the rest, preserving the116 # first run's formatting for the whole paragraph.117 paragraph.runs[0].text = replaced118 for run in paragraph.runs[1:]:119 run.text = ""120```121122## Notes123124- The split-run problem is the most common source of "my find-and-replace only worked sometimes". A placeholder like `{{name}}` is frequently stored as `{{`, `nam`, `e}}` across three runs because of an autocorrect or a spellcheck boundary.125- `.dotx` templates preserve styles, headers, footers, and page setup. Loading one is nearly always better than building a document's styling from scratch.126- If the output will be converted to PDF, verify the conversion — fonts that are not embedded will be substituted, and the layout will shift.