Skip to main content

oxml

Create and edit Word DOCX files from Python, including text, tables, comments and tracked changes. oxml uses a Rust XML editor 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.

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.0.tar.gz (808.0 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.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (1.9 MB view details)

Uploaded CPython 3.13manylinux: glibc 2.17+ x86-64

oxml-0.1.0-cp313-cp313-macosx_11_0_arm64.whl (1.8 MB view details)

Uploaded CPython 3.13macOS 11.0+ ARM64

oxml-0.1.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (1.9 MB view details)

Uploaded CPython 3.12manylinux: glibc 2.17+ x86-64

oxml-0.1.0-cp312-cp312-macosx_11_0_arm64.whl (1.8 MB view details)

Uploaded CPython 3.12macOS 11.0+ ARM64

oxml-0.1.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (1.9 MB view details)

Uploaded CPython 3.11manylinux: glibc 2.17+ x86-64

oxml-0.1.0-cp311-cp311-macosx_11_0_arm64.whl (1.8 MB view details)

Uploaded CPython 3.11macOS 11.0+ ARM64

oxml-0.1.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (1.9 MB view details)

Uploaded CPython 3.10manylinux: glibc 2.17+ x86-64

oxml-0.1.0-cp310-cp310-macosx_11_0_arm64.whl (1.8 MB view details)

Uploaded CPython 3.10macOS 11.0+ ARM64

File details

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

File metadata

  • Download URL: oxml-0.1.0.tar.gz
  • Upload date:
  • Size: 808.0 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.0.tar.gz
Algorithm Hash digest
SHA256 3c5d6b75ff9d61fe0c16c60729cc85d58f5f1908774d970202e5f00b0ab9ddf2
MD5 265bd36fbcae7c2e6ac7f2e9bb8e631c
BLAKE2b-256 9ae20ac157e2f9e9a58d2fdd19694cea7ac79411bb673424250eb262f2f54f8b

See more details on using hashes here.

Provenance

The following attestation bundles were made for oxml-0.1.0.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.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.

File metadata

File hashes

Hashes for oxml-0.1.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Algorithm Hash digest
SHA256 44c624a3de227d72d568d24423fb97c42bb5c544cd2bf4390c7c0d4a4b626386
MD5 2dc3b015f08fb475eefd2c934bcc078e
BLAKE2b-256 e1e9ccb5008ce6790a104445a4ecabc4f14dd014c38b31ef3d545fc845f4c87d

See more details on using hashes here.

Provenance

The following attestation bundles were made for oxml-0.1.0-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.0-cp313-cp313-macosx_11_0_arm64.whl.

File metadata

File hashes

Hashes for oxml-0.1.0-cp313-cp313-macosx_11_0_arm64.whl
Algorithm Hash digest
SHA256 cbb3619504784f7f1de45455de9320a3dda6c2bbc6b71de2696024456423fd34
MD5 c92acb69918fe0a6a8a4e54b5ce171e1
BLAKE2b-256 1b15eff07daf3c3d6a15fc263f1d029b6c6afe7e9ca1c4b12cdfcff3326efcc0

See more details on using hashes here.

Provenance

The following attestation bundles were made for oxml-0.1.0-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.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.

File metadata

File hashes

Hashes for oxml-0.1.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Algorithm Hash digest
SHA256 5af2fab19d24110f235042e9300ffc477203a73fb8331e8758b2ef027a748b25
MD5 984671cbe15e9c732975663845dc9f2c
BLAKE2b-256 142d88fcb14414ce18c1b9bf5540a71ecb0d0775d268c81f2c926d725ec6c3b6

See more details on using hashes here.

Provenance

The following attestation bundles were made for oxml-0.1.0-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.0-cp312-cp312-macosx_11_0_arm64.whl.

File metadata

File hashes

Hashes for oxml-0.1.0-cp312-cp312-macosx_11_0_arm64.whl
Algorithm Hash digest
SHA256 7ebe23d29e510d5a289d9995abe8a2bb0e7fa3cd75c448914d65523aa101f85d
MD5 04724cd84b8cb667a99816360417916f
BLAKE2b-256 a4325fc9b175226c6e002e9158618b3113d5fee83e8dd614504460819359a81a

See more details on using hashes here.

Provenance

The following attestation bundles were made for oxml-0.1.0-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.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.

File metadata

File hashes

Hashes for oxml-0.1.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Algorithm Hash digest
SHA256 3c5090d1bbcbf88c6f6b5003e384cb1eca7f6f5e46984cb4ba26360ddd192421
MD5 1e1d00da347ab19f7d75cfc791342c31
BLAKE2b-256 a3aedceeaf1d1d6b2ed847801d023b289175894cf1deae4c866dc870a1c7e2fb

See more details on using hashes here.

Provenance

The following attestation bundles were made for oxml-0.1.0-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.0-cp311-cp311-macosx_11_0_arm64.whl.

File metadata

File hashes

Hashes for oxml-0.1.0-cp311-cp311-macosx_11_0_arm64.whl
Algorithm Hash digest
SHA256 7ca4f90daef997bd7b82f930c5a3ac3778e35ef09c66a015f8033572d41673bf
MD5 36db64408cc95da854d6f2d608ba96d9
BLAKE2b-256 9d324611b3f7823f028a02ab537c48640d4ceac0e58aa21c396c6e0d7d6de17e

See more details on using hashes here.

Provenance

The following attestation bundles were made for oxml-0.1.0-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.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.

File metadata

File hashes

Hashes for oxml-0.1.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Algorithm Hash digest
SHA256 972bc04fff717634e79e62a713926f8d23f79fa849e1153cdaf88772e8289d32
MD5 96768a822afd2ac03e6acb115dc51943
BLAKE2b-256 6a46dda9e5d3f8f4ade599552f9aa39fbbe22c6f0d0dd37d7c1802c727140f6c

See more details on using hashes here.

Provenance

The following attestation bundles were made for oxml-0.1.0-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.0-cp310-cp310-macosx_11_0_arm64.whl.

File metadata

File hashes

Hashes for oxml-0.1.0-cp310-cp310-macosx_11_0_arm64.whl
Algorithm Hash digest
SHA256 b0e573f21d77a34e50c78021752b268261f612fd305d1c501597b9e1efcce808
MD5 533bfe43f8dafa301b5606ce2860aa67
BLAKE2b-256 78c7928cbeae23d8fe034ea3ed2e22bd8da644f7418b0834289ada0d0e93b9bd

See more details on using hashes here.

Provenance

The following attestation bundles were made for oxml-0.1.0-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

0.1.3

9 files

0.1.2

9 files

0.1.1

9 files

This release

0.1.0 This release

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