Skip to main content

paper-docx

paper-docx is an agent-first Python library for safely inspecting, editing, reviewing, and composing existing Microsoft Word (.docx) documents. It is a strict-superset hard fork of python-docx and a drop-in replacement. The distribution is renamed; the import name stays docx, so existing code keeps working unchanged.

import docx                       # the import name is unchanged
doc = docx.Document("contract.docx")

Why it exists

python-docx is excellent at creating documents. Its lossless package layer, disciplined XML mapping, and years of absorbed edge cases are why this fork builds on it.

The harder problem is changing a contract or other real-world document without losing formatting, revisions, fields, or content outside the body. Hand-edited XML can produce silent corruption: a file that opens fine and is quietly wrong. An agent cannot eyeball the result, so it needs the document's structure and every edit outcome as typed, machine-readable data. It also needs the library to refuse rather than guess.

Safety contract

Every added operation either does exactly what it claims or refuses atomically. Mutating operations validate fully before they change anything. If an operation cannot proceed safely, it raises a typed PaperRefusal and leaves the document byte-for-byte unchanged in memory and on disk. Callers can catch PaperRefusal separately from programmer errors, which remain plain ValueError or TypeError. Comparison and rewrite paths preserve meaningful whitespace, including trailing spaces inside runs.

A short example

Create a native Word redline from two document versions:

import tempfile, docx
from docx.package import compare

tmp = tempfile.mkdtemp()
a = docx.Document()
a.add_paragraph("Payment is due within thirty calendar days of the invoice date.")
a.save(f"{tmp}/v1.docx")
b = docx.Document()
b.add_paragraph("Payment is due within thirty business days of the invoice date.")
b.save(f"{tmp}/v2.docx")

result = compare(f"{tmp}/v1.docx", f"{tmp}/v2.docx", author="Reviewer")
[(r.revision_type, r.text) for r in result.document.revisions]
# [('deletion', 'calendar'), ('insertion', 'business')]

result.document.revisions.accept_all()
result.document.paragraphs[0].text
# 'Payment is due within thirty business days of the invoice date.'

compare emits markup Word renders as tracked changes. Before returning, it accepts and rejects private copies and verifies both outcomes. If a difference cannot be represented safely as a redline, such as a style or package-part change, it raises a typed refusal instead of returning an incomplete result.

What it adds

Reading and editing one document

  • docx.story traverses the body, headers, footers, footnotes, endnotes, comments, tracked insertions, content controls, and text boxes. Callers can view the document as it stands, before pending revisions, or all at once.
  • docx.search finds normalized text across Word's run fragmentation. A returned Span can replace the matched text while preserving unaffected runs, emit the replacement as a tracked change, or anchor a comment.
  • docx.blocks inserts, deletes, or replaces whole paragraphs relative to a text anchor, plainly or as a tracked change.
  • docx.tableops / docx.numbering provide cell, row, and list edits that refuse on unsafe structures such as merged cells, nested tables, or undefined numbering.
  • docx.controls fills content controls with the correct value type and clears placeholder state so Word treats them as filled.
  • docx.bookmarks / docx.fields create bookmarks over a span and author page numbers, dates, cross-references, and tables of contents as fields with placeholder results.
  • docx.formatting resolves effective formatting through document defaults, styles, and direct formatting, with provenance for each value.

Reviewing and finalizing

  • doc.revisions enumerates and resolves tracked changes across every part: insertions, deletions, run and paragraph format changes, table-row revisions, and moves. Unresolvable markup is listed by name.
  • doc.finalize() / doc.scrub() accepts or rejects all revisions, then removes reviewing residue and reports exactly what was removed.
  • docx.protection respects Restrict-Editing. Mutating operations refuse on a protected document unless the caller explicitly overrides; the setting is preserved in the document.

Working across documents

  • docx.package.compare generates a native tracked-change redline from two documents, with the accept/reject round-trip shown above.
  • docx.package.patch_save / diff_package / text_diff keeps unchanged parts byte-identical and reports changed parts and text. diagnose explains why an unreadable file cannot be opened.
  • docx.composition copies formatted content between documents, reconciles styles, numbering, media, hyperlinks, and bookmarks, and reports every part touched.
  • docx.errors exposes typed refusals, distinct from programmer errors.

Drop-in and name map

Only the distribution and repository are renamed. The importable package stays docx. This is the same distribution/import split as Pillow (pip install pillow, import PIL), and it preserves existing code, snippets, and model priors.

  • GitHub repository / PyPI distribution: paper-docx
  • Python import: docx
  • Fork sentinel: docx.__paper_version__ = "0.1.2"

Installation

Install from PyPI:

python -m pip uninstall -y python-docx paper-docx
python -m pip install paper-docx

The clean uninstall is required when migrating from python-docx. Both distributions use the frozen docx import package, and pip cannot safely overlay or uninstall two distributions that own the same files.

Confirm the install:

paper-docx-doctor

Pip does not treat paper-docx as satisfying another package's declared dependency on python-docx. That dependency will reinstall upstream and overwrite shared docx files. Replace or remove the dependency, or run that package in a separate environment.

In a controlled deployment, a constraint containing python-docx<0 makes pip reject direct or transitive attempts to install upstream. The constraint must be applied to every install in that environment.

Documentation

The Sphinx docs extend the upstream python-docx documentation to cover the fork's additions: start with docs/user/paper-additions.rst and the docs/api/paper-*.rst reference pages. Everything inherited from python-docx works as documented at the python-docx documentation.

How it's tested

  • Upstream's pytest and behave suites run on every commit to check compatibility with existing behavior.
  • A frozen, hash-pinned fixture corpus spans generated and LibreOffice-authored documents.
  • The contract harness checks refusal atomicity and validates the fixture corpus with a headless LibreOffice load smoke.

License

MIT, inherited from python-docx. Original work © Steve Canny and the python-docx contributors; fork additions © Paper Instruments, Inc. This fork preserves the upstream license and attribution. See LICENSE.

Release files for paper-docx 0.1.2

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for paper-docx 0.1.2
File Size Uploaded
paper_docx-0.1.2.tar.gz 7.2 MB Details

Built distribution (wheel)

Table of built distributions (wheels) for paper-docx 0.1.2
File Interpreter ABI Platform
paper_docx-0.1.2-py3-none-any.whl Python 3 none any Details

Total release size: 7.6 MB

Release files / paper_docx-0.1.2.tar.gz

Download URL paper_docx-0.1.2.tar.gz
Size 7.2 MB
Tags Source
SHA-256 checksum
How to use checksums
8791d7e22e2ee847ad20638c85b544ce045e35ad98759f5f8ef3559604eb8d00
BLAKE2b-256 checksum
How to use checksums
a2cf81647f53dd68d80c0bbf455f163eb29853ab2041de1106bcc57aa8b46088
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.13

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Jul 12, 2026.

Transparency log

Release files / paper_docx-0.1.2-py3-none-any.whl

Download URL paper_docx-0.1.2-py3-none-any.whl
Size 416.8 kB
Tags Python 3
SHA-256 checksum
How to use checksums
d5805e491365b1494bc6434a2b636011334f811f034caea99d9ebb2cd479b9a6
BLAKE2b-256 checksum
How to use checksums
71340a6b039b5b9df2a0718a2122ed02f8d656b7e3b4466724fb4f7a08291376
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.13

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Jul 12, 2026.

Transparency log

Release history Release notifications | RSS feed

0.2.0

2 release files

This release

0.1.2 This release

2 release files

0.1.0

2 release files

0.0.1

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page