Skip to main content

DOCX creation, editing, and analysis

Overview

A .docx file is a ZIP archive containing XML files.

Quick Reference

Converting .doc to .docx

Legacy .doc files must be converted before editing:
Chinese content note: .doc files created on Chinese Windows are often GBK-encoded internally. scripts/office/soffice.py automatically sets LANG=zh_CN.UTF-8 / LC_ALL=zh_CN.UTF-8 when the environment lacks a UTF-8 locale, so LibreOffice can decode CJK characters correctly during conversion. If Chinese text still appears garbled after conversion, verify that the zh_CN.UTF-8 locale is installed on the system:
macOS always has full Unicode locale support, so no additional setup is needed there.

Reading Content

Converting to Images

Accepting Tracked Changes

To produce a clean document with all tracked changes accepted (requires LibreOffice):

Creating New Documents

Generate .docx files with JavaScript, then validate. Install: npm install -g docx

Setup

Validation

After creating the file, validate it. If validation fails, unpack, fix the XML, and repack.

Page Size

Common page sizes (DXA units, 1440 DXA = 1 inch): Landscape orientation: docx-js swaps width/height internally, so pass portrait dimensions and let it handle the swap:

Styles (Override Built-in Headings)

Use Arial as the default font (universally supported). Keep titles black for readability.

Lists (NEVER use unicode bullets)

Tables

CRITICAL: Tables need dual widths - set both columnWidths on the table AND width on each cell. Without both, tables render incorrectly on some platforms.
Table width calculation: Always use WidthType.DXAWidthType.PERCENTAGE breaks in Google Docs.
Width rules:
  • Always use WidthType.DXA — never WidthType.PERCENTAGE (incompatible with Google Docs)
  • Table width must equal the sum of columnWidths
  • Cell width must match corresponding columnWidth
  • Cell margins are internal padding - they reduce content area, not add to cell width
  • For full-width tables: use content width (page width minus left and right margins)

Images

Page Breaks

Table of Contents

Headers/Footers

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 as height, and set orientation: PageOrientation.LANDSCAPE
  • Never use \n - use separate Paragraph elements
  • Never use unicode bullets - use LevelFormat.BULLET with numbering config
  • PageBreak must be in Paragraph - standalone creates invalid XML
  • ImageRun requires type - always specify png/jpg/etc
  • Always set table width with DXA - never use WidthType.PERCENTAGE (breaks in Google Docs)
  • Tables need dual widths - columnWidths array AND cell width, 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
  • 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

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 “Claude” 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:
Adding comments: Use comment.py to handle boilerplate across multiple XML files (text must be pre-escaped XML):
Then add markers to document.xml (see Comments in XML Reference).

Step 3: Pack

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

Schema Compliance

  • Element order in <w:pPr>: <w:pStyle>, <w:numPr>, <w:spacing>, <w:ind>, <w:jc>, <w:rPr> last
  • Whitespace: Add xml:space="preserve" to <w:t> with leading/trailing spaces
  • RSIDs: Must be 8-digit hex (e.g., 00AB1234)

Tracked Changes

Insertion:
Deletion:
Inside <w:del>: Use <w:delText> instead of <w:t>, and <w:delInstrText> instead of <w:instrText>. Minimal edits - only mark what changes:
Deleting entire paragraphs/list items - when removing ALL content from a paragraph, also mark the paragraph mark as deleted so it merges with the next paragraph. Add <w:del/> inside <w:pPr><w:rPr>:
Without the <w:del/> in <w:pPr><w:rPr>, accepting changes leaves an empty paragraph/list item. Rejecting another author’s insertion - nest deletion inside their insertion:
Restoring another author’s deletion - add insertion after (don’t modify their deletion):

Comments

After running comment.py (see Step 2), add markers to document.xml. For replies, use --parent flag and nest markers inside the parent’s. CRITICAL: <w:commentRangeStart> and <w:commentRangeEnd> are siblings of <w:r>, never inside <w:r>.

Images

  1. Add image file to word/media/
  2. Add relationship to word/_rels/document.xml.rels:
  1. Add content type to [Content_Types].xml:
  1. Reference in document.xml:

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: pdftoppm for images