How to Work with XML in DOCX Files Using the Unpack/Pack Workflow

The unpack/pack workflow treats DOCX files as ZIP archives containing XML documents, allowing you to extract, edit, and rebuild Word documents programmatically using the unpack.py and pack.py scripts from the anthropics/skills repository.

Working with XML in DOCX files using the unpack/pack workflow provides a deterministic, scriptable approach to manipulating Word documents without proprietary software. The anthropics/skills repository implements this workflow through a pair of Python scripts that automate ZIP extraction, XML normalization, optional cleanup, and archive reconstruction. This approach enables direct manipulation of word/document.xml and other Open XML components using standard text editors or programmatic tools.

Unpacking DOCX Archives with unpack.py

The unpack.py script located at skills/docx/scripts/office/unpack.py serves as the entry point for the workflow. It extracts the DOCX ZIP contents to a specified directory and applies several normalization steps to make the XML human-readable and suitable for editing.

The function signature is:

unpack(input_file: str,
       output_directory: str,
       merge_runs: bool = True,
       simplify_redlines: bool = True) -> tuple[None, str]

XML Normalization and Cleanup

During unpacking, the script performs several operations on the extracted XML files:

  • Pretty-printing: Every *.xml and *.rels file is formatted with indentation to reveal the document structure
  • Smart quote escaping: Converts "smart quotes" to XML entities to prevent parsing errors
  • Run merging: When merge_runs=True, combines adjacent <w:r> elements with identical formatting to reduce noise caused by Word's editor
  • Redline simplification: When simplify_redlines=True, merges adjacent <w:ins> or <w:del> elements from the same author to streamline tracked changes

Helper Modules

The cleanup operations rely on helper modules in skills/docx/scripts/office/helpers/:

  • merge_runs.py: Implements the logic for combining adjacent runs with identical properties
  • simplify_redlines.py: Handles tracked change simplification and provides infer_author functionality for determining change authorship

Editing XML Content Directly

After unpacking, the document's main content resides at word/document.xml within the output directory. Because the XML has been pretty-printed, you can manipulate it using any text editor, XML-specific tools, or programmatic libraries.

Common editing tasks include:

  • Adding custom <w:customXml> elements for content controls
  • Adjusting paragraph properties (<w:pPr>) for styling
  • Removing unwanted markup or namespaces
  • Inserting new runs (<w:r>) or text elements (<w:t>)

Repacking with pack.py

The pack.py script at skills/docx/scripts/office/pack.py reverses the process, converting the edited directory back into a valid DOCX file.

The function signature is:

pack(input_directory: str,
     output_file: str,
     original_file: str | None = None,
     validate: bool = True,
     infer_author_func=None) -> tuple[None, str]

XML Condensing and Validation

Before creating the ZIP archive, pack.py performs several critical steps:

  • Condensing: The _condense_xml function removes whitespace-only text nodes, XML comments, and empty elements to minimize file size
  • Validation: When validate=True, the script runs schema validators located in skills/docx/scripts/office/validators/ to ensure Open XML compliance
  • Auto-repair: Validators can automatically fix common issues before finalizing the archive

Author Inference

If you provide the original_file parameter, the packing process can infer the author of new tracked changes using the infer_author functionality from helpers/simplify_redlines.py. This maintains proper attribution when adding new revisions to an existing document.

Practical Workflow Examples

Basic Command-Line Workflow


# Unpack the DOCX with default cleanup options

python -m skills.docx.scripts.office.unpack \
    my_report.docx unpacked_dir/

# Edit word/document.xml using your preferred editor

# (Example: adding a custom XML element via Python)

python - <<'PY'
import pathlib
import xml.etree.ElementTree as ET

doc_path = pathlib.Path('unpacked_dir/word/document.xml')
tree = ET.parse(doc_path)
root = tree.getroot()

# Define namespace

ns = {'w': 'http://schemas.openxmlformats.org/wordprocessingml/2006/main'}
body = root.find('w:body', ns)

# Add custom XML element

custom = ET.Element('{%s}customXml' % ns['w'])
custom.text = 'Added by script'
body.append(custom)

tree.write(doc_path, encoding='utf-8', xml_declaration=True)
PY

# Repack with validation against original

python -m skills.docx.scripts.office.pack \
    unpacked_dir/ edited_report.docx \
    --original my_report.docx

Programmatic Library Usage

from pathlib import Path
from skills.docx.scripts.office.unpack import unpack
from skills.docx.scripts.office.pack import pack
import defusedxml.minidom as minidom

# Step 1: Unpack with specific options

_, msg = unpack(
    input_file="contract.docx",
    output_directory="temp_unpacked",
    merge_runs=False,          # Preserve original run structure

    simplify_redlines=True,    # Clean up tracked changes

)
print(msg)

# Step 2: Manipulate XML using defusedxml

doc_xml = Path("temp_unpacked/word/document.xml")
dom = minidom.parseString(doc_xml.read_text(encoding="utf-8"))
body = dom.getElementsByTagName("w:body")[0]

# Insert new paragraph at beginning

p = dom.createElement("w:p")
r = dom.createElement("w:r")
t = dom.createElement("w:t")
t.appendChild(dom.createTextNode("Automated insertion"))
r.appendChild(t)
p.appendChild(r)
body.insertBefore(p, body.firstChild)

doc_xml.write_bytes(dom.toxml(encoding="utf-8"))

# Step 3: Pack with validation

_, msg = pack(
    input_directory="temp_unpacked",
    output_file="contract_modified.docx",
    original_file="contract.docx",  # Enables author inference

    validate=True,
)
print(msg)

Condensing Without Validation

If you need to repack quickly without schema validation:

python -m skills.docx.scripts.office.pack \
    unpacked_dir/ output.docx \
    --validate false

Key Files and Components

Path (relative to repo) Role
skills/docx/scripts/office/unpack.py Extracts DOCX archives, pretty-prints XML, merges runs, simplifies redlines, escapes smart quotes
skills/docx/scripts/office/pack.py Re-compresses directories into DOCX, condenses XML, runs optional validation and auto-repair
skills/docx/scripts/office/helpers/merge_runs.py Implements run-merging logic to combine adjacent <w:r> elements with identical formatting
skills/docx/scripts/office/helpers/simplify_redlines.py Handles tracked-change simplification and author inference for <w:ins> and <w:del> elements
skills/docx/scripts/office/validators/ Contains schema validators and repair logic for Open XML compliance checking

Summary

  • The unpack/pack workflow treats DOCX files as ZIP archives containing Open XML files, enabling direct manipulation of word/document.xml and other components.
  • unpack.py extracts and normalizes XML through pretty-printing, run merging, redline simplification, and smart quote escaping.
  • pack.py rebuilds DOCX files by condensing XML (removing whitespace and empty elements) and optionally validating against Open XML schemas.
  • Helper modules in skills/docx/scripts/office/helpers/ provide specialized logic for merging adjacent runs and simplifying tracked changes while maintaining author attribution.
  • Validation and repair occur through the validators in skills/docx/scripts/office/validators/, ensuring output documents remain compatible with Microsoft Word and other processors.

Frequently Asked Questions

What is the unpack/pack workflow for DOCX files?

The unpack/pack workflow is a method for treating DOCX files as ZIP archives containing XML documents. It involves extracting the archive contents to access and edit Open XML files (like word/document.xml) directly, then repackaging the modified files back into a valid DOCX format. This approach enables programmatic manipulation of Word documents without requiring the Microsoft Word application or complex binary parsing libraries.

How does the unpack.py script clean up XML content?

The unpack.py script performs several normalization steps when extracting DOCX files: it pretty-prints all *.xml and *.rels files for readability, merges adjacent <w:r> (run) elements that share identical formatting to reduce editor noise, simplifies tracked changes by combining adjacent <w:ins> or <w:del> elements from the same author, and escapes smart quotes to XML entities to prevent parsing errors.

What validation does the pack.py script perform when rebuilding DOCX files?

The pack.py script optionally validates the rebuilt DOCX file against Open XML schemas using validators located in skills/docx/scripts/office/validators/. Before validation, it condenses the XML by removing whitespace-only text nodes, XML comments, and empty elements to optimize file size. If you provide the --original parameter, the script can also infer the author of new tracked changes by comparing against the source document's revision history.

Can I use the unpack/pack workflow programmatically in Python?

Yes, both unpack.py and pack.py expose Python functions that you can import and call directly in your scripts. Import unpack from skills.docx.scripts.office.unpack and pack from skills.docx.scripts.office.pack, then pass the appropriate file paths and boolean flags (such as merge_runs, simplify_redlines, and validate) to control the extraction and reconstruction process. This allows you to integrate DOCX manipulation into automated pipelines without shelling out to command-line tools.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →