DOCX creation, editing, and analysis
Overview
A .docx file is a ZIP archive containing XML files.
Quick Reference
| Task | Approach |
|---|---|
| Read/analyze content | pandoc or unpack for raw XML |
| Create new document | Use docx-js - see Creating New Documents below |
| Edit existing document | Unpack → edit XML → repack - see Editing Existing Documents below |
Converting .doc to .docx
Legacy .doc files must be converted before editing:
python scripts/office/soffice.py --headless --convert-to docx document.doc
Reading Content
# Text extraction with tracked changes
pandoc --track-changes=all document.docx -o output.md
# Raw XML access
python scripts/office/unpack.py document.docx unpacked/
Converting to Images
python scripts/office/soffice.py --headless --convert-to pdf document.docx
pdftoppm -jpeg -r 150 document.pdf page
Accepting Tracked Changes
To produce a clean document with all tracked changes accepted (requires LibreOffice):
python scripts/accept_changes.py input.docx output.docx
Creating New Documents
Generate .docx files with JavaScript, then validate. Install: npm install -g docx
For detailed API examples (page size, styles, lists, tables, images, hyperlinks, footnotes, tab stops, multi-column layouts, TOC, headers/footers), see
references/docxjs-api.md.
Validation
After creating the file, validate it. If validation fails, unpack, fix the XML, and repack.
python scripts/office/validate.py doc.docx
Critical Rules for docx-js
- Set page size explicitly - docx-js defaults to A4; use US Letter (12240 x 15840 DXA) for US documents
- Landscape: pass portrait dimensions - docx-js swaps width/height internally; pass short edge as
width, long edge asheight, and setorientation: PageOrientation.LANDSCAPE - Never use
\n- use separate Paragraph elements - Never use unicode bullets - use
LevelFormat.BULLETwith numbering config - PageBreak must be in Paragraph - standalone creates invalid XML
- ImageRun requires
type- always specify png/jpg/etc - Always set table
widthwith DXA - never useWidthType.PERCENTAGE(breaks in Google Docs) - Tables need dual widths -
columnWidthsarray AND cellwidth, both must match - Table width = sum of columnWidths - for DXA, ensure they add up exactly
- Always add cell margins - use
margins: { top: 80, bottom: 80, left: 120, right: 120 }for readable padding - Use
ShadingType.CLEAR- never SOLID for table shading - Never use tables as dividers/rules - cells have minimum height and render as empty boxes (including in headers/footers); use
border: { bottom: { style: BorderStyle.SINGLE, size: 6, color: "2E75B6", space: 1 } }on a Paragraph instead. For two-column footers, use tab stops (see Tab Stops section), not tables - TOC requires HeadingLevel only - no custom styles on heading paragraphs
- Override built-in styles - use exact IDs: "Heading1", "Heading2", etc.
- Include
outlineLevel- required for TOC (0 for H1, 1 for H2, etc.)
Editing Existing Documents
Follow all 3 steps in order.
Step 1: Unpack
python scripts/office/unpack.py document.docx unpacked/
Extracts XML, pretty-prints, merges adjacent runs, and converts smart quotes to XML entities (“ etc.) so they survive editing. Use --merge-runs false to skip run merging.
Step 2: Edit XML
Edit files in unpacked/word/. See XML Reference below for patterns.
Use "AI Agent" as the author for tracked changes and comments, unless the user explicitly requests use of a different name.
Use the Edit tool directly for string replacement. Do not write Python scripts. Scripts introduce unnecessary complexity. The Edit tool shows exactly what is being replaced.
CRITICAL: Use smart quotes for new content. When adding text with apostrophes or quotes, use XML entities to produce smart quotes:
<!-- Use these entities for professional typography -->
<w:t>Here’s a quote: “Hello”</w:t>
| Entity | Character |
|---|---|
‘ |
‘ (left single) |
’ |
’ (right single / apostrophe) |
“ |
“ (left double) |
” |
” (right double) |
Adding comments: Use comment.py to handle boilerplate across multiple XML files (text must be pre-escaped XML):
python scripts/comment.py unpacked/ 0 "Comment text with & and ’"
python scripts/comment.py unpacked/ 1 "Reply text" --parent 0 # reply to comment 0
python scripts/comment.py unpacked/ 0 "Text" --author "Custom Author" # custom author name
Then add markers to document.xml (see Comments in XML Reference).
Step 3: Pack
python scripts/office/pack.py unpacked/ output.docx --original document.docx
Validates with auto-repair, condenses XML, and creates DOCX. Use --validate false to skip.
Auto-repair will fix:
durableId>= 0x7FFFFFFF (regenerates valid ID)- Missing
xml:space="preserve"on<w:t>with whitespace
Auto-repair won't fix:
- Malformed XML, invalid element nesting, missing relationships, schema violations
Common Pitfalls
- Replace entire
<w:r>elements: When adding tracked changes, replace the whole<w:r>...</w:r>block with<w:del>...<w:ins>...as siblings. Don't inject tracked change tags inside a run. - Preserve
<w:rPr>formatting: Copy the original run's<w:rPr>block into your tracked change runs to maintain bold, font size, etc.
XML Reference
For complete XML editing reference (schema compliance, tracked changes, comments, images), see
references/xml-reference.md.
Dependencies
- pandoc: Text extraction
- docx:
npm install -g docx(new documents) - LibreOffice: PDF conversion (auto-configured for sandboxed environments via
scripts/office/soffice.py) - Poppler:
pdftoppmfor images