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

> Learn the unpack pack workflow to programmatically edit XML within DOCX files. Extract, modify, and rebuild Word documents easily with Python scripts.

- Repository: [Anthropic/skills](https://github.com/anthropics/skills)
- Tags: how-to-guide
- Published: 2026-02-16

---

**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`](https://github.com/anthropics/skills/blob/main/unpack.py) and [`pack.py`](https://github.com/anthropics/skills/blob/main/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`](https://github.com/anthropics/skills/blob/main/word/document.xml) and other Open XML components using standard text editors or programmatic tools.

## Unpacking DOCX Archives with [`unpack.py`](https://github.com/anthropics/skills/blob/main/unpack.py)

The [`unpack.py`](https://github.com/anthropics/skills/blob/main/unpack.py) script located at [`skills/docx/scripts/office/unpack.py`](https://github.com/anthropics/skills/blob/main/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:

```python
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`](https://github.com/anthropics/skills/blob/main/merge_runs.py)**: Implements the logic for combining adjacent runs with identical properties
- **[`simplify_redlines.py`](https://github.com/anthropics/skills/blob/main/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`](https://github.com/anthropics/skills/blob/main/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`](https://github.com/anthropics/skills/blob/main/pack.py)

The [`pack.py`](https://github.com/anthropics/skills/blob/main/pack.py) script at [`skills/docx/scripts/office/pack.py`](https://github.com/anthropics/skills/blob/main/skills/docx/scripts/office/pack.py) reverses the process, converting the edited directory back into a valid DOCX file.

The function signature is:

```python
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`](https://github.com/anthropics/skills/blob/main/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`](https://github.com/anthropics/skills/blob/main/helpers/simplify_redlines.py). This maintains proper attribution when adding new revisions to an existing document.

## Practical Workflow Examples

### Basic Command-Line Workflow

```bash

# 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

```python
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:

```bash
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`](https://github.com/anthropics/skills/blob/main/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`](https://github.com/anthropics/skills/blob/main/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`](https://github.com/anthropics/skills/blob/main/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`](https://github.com/anthropics/skills/blob/main/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`](https://github.com/anthropics/skills/blob/main/word/document.xml) and other components.
- **[`unpack.py`](https://github.com/anthropics/skills/blob/main/unpack.py)** extracts and normalizes XML through pretty-printing, run merging, redline simplification, and smart quote escaping.
- **[`pack.py`](https://github.com/anthropics/skills/blob/main/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`](https://github.com/anthropics/skills/blob/main/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`](https://github.com/anthropics/skills/blob/main/unpack.py) script clean up XML content?

The [`unpack.py`](https://github.com/anthropics/skills/blob/main/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`](https://github.com/anthropics/skills/blob/main/pack.py) script perform when rebuilding DOCX files?

The [`pack.py`](https://github.com/anthropics/skills/blob/main/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`](https://github.com/anthropics/skills/blob/main/unpack.py) and [`pack.py`](https://github.com/anthropics/skills/blob/main/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.