Skip to main content

oxml

Create and edit Word DOCX files from Python, including text, tables, comments and tracked changes. oxml is a Rust document library with thin Python bindings and types derived from Microsoft's Open XML SDK. It requires neither .NET nor Office.

Edit a document

from oxml import Document

doc = Document.open('draft.docx')
doc.story.find('fourteen days').replace('twenty-one days')
doc.save('edited.docx')

Search works across text runs, so a phrase need not have uniform formatting. Replacement preserves surrounding formatting and uses the first affected run's format for the new text. Find the text again after each edit: ranges refer to a particular version of the XML.

doc.story is the main document text. doc.stories() also gives access to headers, footers, notes and comments. Story(element, view='original') reads the text before tracked changes without accepting or rejecting them.

Replacement can split and join adjacent paragraphs using \n, but cannot cross section or table-cell boundaries. Bookmarks and comment anchors survive text edits. Fields, content controls and existing revision payloads are protected from ordinary text replacement.

Build and edit XML

Use Document.new() to start a document, or Tree(xml_bytes) to work with standalone XML.

from oxml import Document, e, w

doc = Document.new()
body = next(doc.main.xml.elements(w.Body))
paragraph = body(e.p(e.r(e.t('New paragraph'))))
paragraph(e.pPr(e.jc(val='center')))
doc.save('new.docx')

e.p(...) builds a detached XML expression. Calling a live parent, such as body(...), attaches it and returns the new live element. Placement follows the schema: paragraph properties go before runs, and paragraphs go before final section properties. Existing content is never rearranged. Use parent(expression, index=n) when you need an exact XML child-node position or the schema order is unknown.

The e factory supplies the w namespace for elements and attributes. For example, e.tcW(type='dxa', w=2400) creates a table-cell width. Other attribute prefixes use double underscores, such as r__id and xml__space. Configure another namespace with E('a'), or custom bindings with E(ns=bindings).

You can also work directly with typed elements. For example, next(doc.main.xml.elements(w.Text)).value = 'Replacement' changes one text node. Typed attributes check values against SDK rules and refuse constraints they cannot fully check. Raw XML editing remains available for those cases.

The XML editor supports elements, attributes, text, comments and processing instructions, with namespace-aware copying and movement. It reads UTF-8 and UTF-16. Typed views and raw XML edits share the same live tree.

See Editing and preservation contracts for copying, namespaces and raw XML operations.

Comments and tracked changes

Add a comment to a text range:

doc = Document.open('draft.docx')
comment = doc.comments.add(doc.story.find('fourteen days'),
    'Please extend this period.', 'Reviewer')
comment.reply('Agreed.', 'Drafter')
comment.resolve()
doc.save('commented.docx')

Comments support plain-text bodies, replies, resolution and deletion of individual reply subtrees or whole threads. Comment anchors are currently supported in the main document. Modern reply and resolution metadata is maintained alongside the comment text.

Record a replacement as a tracked change:

doc = Document.open('draft.docx')
doc.revisions.replace(doc.story.find('fourteen days'),
    'twenty-one days', author='Drafter')
doc.save('redlined.docx')

Tracked changes cover text insertions and deletions, paragraph splits and joins, and direct run/paragraph formatting. Iterate over doc.revisions to accept or reject individual changes. accept_all() and reject_all() handle a whole story. Use doc.revisions.format(...) to track formatting changes.

Editing inside an existing revision requires accepting or rejecting it first. Table, move and nested revision histories are not supported. Bulk operations refuse unsupported revision types rather than silently skipping them. See Text/review scope for the detailed rules.

Styles, lists, tables and links

  • doc.styles finds, creates and applies paragraph, character and table styles without replacing direct formatting.
  • doc.numbering creates multilevel lists and controls continuation or restart.
  • Table.add(...) creates rectangular tables. Table(element) provides row and column edits. Structural edits do not support merged, offset or revised grids.
  • doc.bookmarks creates, finds and removes bookmarks and builds REF fields.
  • doc.hyperlinks adds and removes internal or external links while retaining the text's formatting. Linked text is protected from range edits until the link is removed.

These helpers edit the document's XML. They do not calculate layout, inherited formatting, displayed list numbers or field results. Usage details are in Document helpers.

doc.set_custom_xml(item_id, xml_bytes, schema_uri=...) creates or replaces a custom XML datastore by GUID and returns its Part. It manages the property part and relationships while preserving unrelated stores. Content controls can refer to the GUID through w:storeItemID.

Compare and import documents

Compare two documents to produce a new document with tracked text and direct-formatting changes:

from oxml import Document, compare

redline = compare(Document.open('original.docx'),
    Document.open('revised.docx'), author='Reviewer')
redline.save('comparison.docx')

The originals stay unchanged, and equal tables and other opaque blocks are retained. Comparison supports body-text and direct-formatting changes. It refuses changes to tables, sections or dependencies, and documents with unresolved revisions. Changes to paragraph counts require matching direct paragraph properties and a paragraph-only body apart from final section properties.

import_content(...) copies selected paragraphs and tables between documents, including their style, numbering, image and hyperlink dependencies. It preserves complete bookmark ranges and remaps conflicting IDs without overwriting destination definitions. Destination themes and document defaults still apply. Content with reviews, fields, sections or unsupported package dependencies is refused. See Import and compare for the detailed rules.

Preservation and limits

Saving an unchanged document returns the original bytes. Edited saves retain untouched package payloads. XML edits preserve namespace meaning and unknown content. Edited XML is serialized as UTF-8 without retaining its original formatting. doc.package gives access to parts, content types and relationships.

doc.validate() checks XML structure, attribute values, supported semantic rules and package relationships, including headers, footers and other reachable parts. Errors and unchecked regions are reported separately. Validation is incomplete: a report without errors does not establish that a document is valid.

Current document support focuses on DOCX. XLSX editing and conversion of Strict XML namespaces to the typed vocabulary are not implemented. Signed documents can pass through unchanged but cannot be edited. Encrypted, macro-enabled and template documents, ZIP64 and multidisk archives are refused. See DEV.md for resource limits, validation gaps and development commands.

License and acknowledgements

oxml's own code is Apache-2.0 licensed. Imported material retains its upstream notices and terms.

  • Open XML SDK — Microsoft, the .NET Foundation and contributors. Its schema metadata, validator behavior, implementation ideas and tests are the foundation for our typed model and validation. The SDK copyright and MIT notice ships with the Python package.
  • Open XML PowerTools — Microsoft, Eric White and contributors, for revision-processing and document-comparison reference behavior, tests and fixtures.
  • Pandoc — John MacFarlane, Jesse Rosenthal and contributors, for DOCX fixtures and independent reader/review expectations.
  • LibreOffice contributors and The Document Foundation, for regression documents and tests covering modern comments and cross-part content.
  • python-docx — Steve Canny and contributors, for the section fixture and API/test examples; Apache POI — the Apache Software Foundation and contributors, for the header-image fixture and relationship tests.

The fixture source table maps borrowed files to their upstream locations and retained licenses. Adapted tests identify their upstream cases in source comments. Thanks also to the developers of our runtime dependencies, especially PyO3 and quick-xml.

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

oxml-0.1.3.tar.gz (827.3 kB view details)

Uploaded Source

Built Distributions

If you're not sure about the file name format, learn more about wheel file names.

oxml-0.1.3-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (2.3 MB view details)

Uploaded CPython 3.13manylinux: glibc 2.17+ x86-64

oxml-0.1.3-cp313-cp313-macosx_11_0_arm64.whl (2.2 MB view details)

Uploaded CPython 3.13macOS 11.0+ ARM64

oxml-0.1.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (2.3 MB view details)

Uploaded CPython 3.12manylinux: glibc 2.17+ x86-64

oxml-0.1.3-cp312-cp312-macosx_11_0_arm64.whl (2.2 MB view details)

Uploaded CPython 3.12macOS 11.0+ ARM64

oxml-0.1.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (2.3 MB view details)

Uploaded CPython 3.11manylinux: glibc 2.17+ x86-64

oxml-0.1.3-cp311-cp311-macosx_11_0_arm64.whl (2.2 MB view details)

Uploaded CPython 3.11macOS 11.0+ ARM64

oxml-0.1.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (2.3 MB view details)

Uploaded CPython 3.10manylinux: glibc 2.17+ x86-64

oxml-0.1.3-cp310-cp310-macosx_11_0_arm64.whl (2.2 MB view details)

Uploaded CPython 3.10macOS 11.0+ ARM64

File details

Details for the file oxml-0.1.3.tar.gz.

File metadata

  • Download URL: oxml-0.1.3.tar.gz
  • Upload date:
  • Size: 827.3 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for oxml-0.1.3.tar.gz
Algorithm Hash digest
SHA256 6547be5f4b0f08a85141580c0d0997582d73aed29bbdb95a2068ca9d50f4394e
MD5 204f8d915ff39936ec9552ad28bebf3e
BLAKE2b-256 5e84d1f7204c62f8754278e35f7f9908228063ecaa4e39ae673027178585ae69

See more details on using hashes here.

Provenance

The following attestation bundles were made for oxml-0.1.3.tar.gz:

Publisher: ci.yml on AnswerDotAI/oxml

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file oxml-0.1.3-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.

File metadata

File hashes

Hashes for oxml-0.1.3-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Algorithm Hash digest
SHA256 1466c9767b1a7736a127adb48f13eab8e5db6a7fe5fd1f52ab04610481c0930f
MD5 ac6b6ddcc4c3f2900e29bdbb26cee5c2
BLAKE2b-256 048b4e81c87690e5ea98376cda57bb8fa0504b5783e76de59724acd0e5b67387

See more details on using hashes here.

Provenance

The following attestation bundles were made for oxml-0.1.3-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl:

Publisher: ci.yml on AnswerDotAI/oxml

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file oxml-0.1.3-cp313-cp313-macosx_11_0_arm64.whl.

File metadata

File hashes

Hashes for oxml-0.1.3-cp313-cp313-macosx_11_0_arm64.whl
Algorithm Hash digest
SHA256 534729332889f3de9a2096d00e78f80c5f63261980b6ff30bb381cbec2f5a223
MD5 e93ad5bf1c6b20a6b57001a1d6d0a132
BLAKE2b-256 d0fc0b03cfc72acef8dec7dab82317d58ee0e0e394561f725a17adc4f8351ba3

See more details on using hashes here.

Provenance

The following attestation bundles were made for oxml-0.1.3-cp313-cp313-macosx_11_0_arm64.whl:

Publisher: ci.yml on AnswerDotAI/oxml

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file oxml-0.1.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.

File metadata

File hashes

Hashes for oxml-0.1.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Algorithm Hash digest
SHA256 81ac5f6067c35e99292033d6d455897aec433e2b8c2c5a28676e76201aff627b
MD5 dafdb302457bc7ed27274dca4a8def6f
BLAKE2b-256 a260506c65a8a35f70b897b67861f67f9e8ece929f770002e9af4cc8c58df59a

See more details on using hashes here.

Provenance

The following attestation bundles were made for oxml-0.1.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl:

Publisher: ci.yml on AnswerDotAI/oxml

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file oxml-0.1.3-cp312-cp312-macosx_11_0_arm64.whl.

File metadata

File hashes

Hashes for oxml-0.1.3-cp312-cp312-macosx_11_0_arm64.whl
Algorithm Hash digest
SHA256 9e167695ca97e9cd43d962aec108d1d7ff3239d67307ca38a1aa7404df4f0de0
MD5 4e224f504ce31ac1088fb15c61b2b728
BLAKE2b-256 8e27cc731a4cb3176c62130d4a7152a948a52636c3fa5024301de1ad4046b26b

See more details on using hashes here.

Provenance

The following attestation bundles were made for oxml-0.1.3-cp312-cp312-macosx_11_0_arm64.whl:

Publisher: ci.yml on AnswerDotAI/oxml

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file oxml-0.1.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.

File metadata

File hashes

Hashes for oxml-0.1.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Algorithm Hash digest
SHA256 f7b2fa59a71ef7fdf29a0b5996b8e652d3950005842cf709daa2519abf9bff4e
MD5 74b58bca7710482fb3a00c0823a24c61
BLAKE2b-256 6ba3f3a4b1ca3de3755725e6b22facf4b680ea8fc83427a9473db583b6c88381

See more details on using hashes here.

Provenance

The following attestation bundles were made for oxml-0.1.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl:

Publisher: ci.yml on AnswerDotAI/oxml

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file oxml-0.1.3-cp311-cp311-macosx_11_0_arm64.whl.

File metadata

File hashes

Hashes for oxml-0.1.3-cp311-cp311-macosx_11_0_arm64.whl
Algorithm Hash digest
SHA256 b466295367e1c7a9ac04325cbf94987a2f9b28c6f8b6f91ed5810776a5d8f79c
MD5 b6f752006e92699a5916500ebb82e628
BLAKE2b-256 ef53c7baffc9244b25de98098dd643818e37125d8e6ab20c77fd4225bebfcd9a

See more details on using hashes here.

Provenance

The following attestation bundles were made for oxml-0.1.3-cp311-cp311-macosx_11_0_arm64.whl:

Publisher: ci.yml on AnswerDotAI/oxml

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file oxml-0.1.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.

File metadata

File hashes

Hashes for oxml-0.1.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Algorithm Hash digest
SHA256 3a74adeef0b60ce8e75bd3e2900df2d8117d6ed010e81a1d9df6860123e63759
MD5 02cecad503e9ed2144d8c59dfc914493
BLAKE2b-256 32cdb25bf6349fa4ae1d9fe7aa5afbe0d7ac9fcb9d3c19f1820645d01536a764

See more details on using hashes here.

Provenance

The following attestation bundles were made for oxml-0.1.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl:

Publisher: ci.yml on AnswerDotAI/oxml

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file oxml-0.1.3-cp310-cp310-macosx_11_0_arm64.whl.

File metadata

File hashes

Hashes for oxml-0.1.3-cp310-cp310-macosx_11_0_arm64.whl
Algorithm Hash digest
SHA256 557f36440a14de3d7f0ab6e9f48e21d2f827d8afdf9d313254bd8593d925deaf
MD5 bf83c857a83c912a714af3e7b8af415f
BLAKE2b-256 0ccfa99b76be8433bd64cd82bf7ff416b19138a802396bdfd7e54a3bb116f505

See more details on using hashes here.

Provenance

The following attestation bundles were made for oxml-0.1.3-cp310-cp310-macosx_11_0_arm64.whl:

Publisher: ci.yml on AnswerDotAI/oxml

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Release history Release notifications | RSS feed

This release

0.1.3 This release

9 files

0.1.2

9 files

0.1.1

9 files

0.1.0

9 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