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.

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.2.tar.gz (809.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.2-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.2-cp313-cp313-macosx_11_0_arm64.whl (1.8 MB view details)

Uploaded CPython 3.13macOS 11.0+ ARM64

oxml-0.1.2-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.2-cp312-cp312-macosx_11_0_arm64.whl (1.8 MB view details)

Uploaded CPython 3.12macOS 11.0+ ARM64

oxml-0.1.2-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.2-cp311-cp311-macosx_11_0_arm64.whl (1.8 MB view details)

Uploaded CPython 3.11macOS 11.0+ ARM64

oxml-0.1.2-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.2-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.2.tar.gz.

File metadata

  • Download URL: oxml-0.1.2.tar.gz
  • Upload date:
  • Size: 809.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.2.tar.gz
Algorithm Hash digest
SHA256 bc261cd46d2ffb2104c6820f7edea0a4863ef7d857160688126f0d8cce85b24b
MD5 6fef951b5a10ca4d8a8add8ee877b9ec
BLAKE2b-256 4bc8db58628132356a641b16f0a4029801d301dcf64c21d3427d9e5d47444263

See more details on using hashes here.

Provenance

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

File metadata

File hashes

Hashes for oxml-0.1.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Algorithm Hash digest
SHA256 8fc4674a54121d011a906f31e6a953effa8b75a4591d3c2bbdce2965ed9b3021
MD5 706359867c9a3bc9262f1c8e9dcf4cca
BLAKE2b-256 5b4f17902738b2b3f8654224535ef1813eb8bc8c31bdc70e6c22b4796f1f5a0a

See more details on using hashes here.

Provenance

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

File metadata

File hashes

Hashes for oxml-0.1.2-cp313-cp313-macosx_11_0_arm64.whl
Algorithm Hash digest
SHA256 d7bcf6a461e10595402dab8670b2bbd7b2134bd1d2602bec1e021802b74210f3
MD5 32446530150e82b4719b89001030c052
BLAKE2b-256 1637b5cc3bcf8a0ca9f7605ad4a5546e1a972876232ea00a8478d806f880cc94

See more details on using hashes here.

Provenance

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

File metadata

File hashes

Hashes for oxml-0.1.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Algorithm Hash digest
SHA256 c403f9721c3b781aec0ae853bbed280018755493080d58faddb1ac2e4201207d
MD5 6f844f8beb57c1061c738d99ad00bcb8
BLAKE2b-256 47b8dde16fab1a9fb2538f7495fb07139562b6a268b5b23ca87c807a56347d68

See more details on using hashes here.

Provenance

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

File metadata

File hashes

Hashes for oxml-0.1.2-cp312-cp312-macosx_11_0_arm64.whl
Algorithm Hash digest
SHA256 0c0a572c95222a23661c69801557ac4cd27f40a557033bfe46a9bc6e15f8801a
MD5 33db34d076a946caede6ae37146c6b31
BLAKE2b-256 6dba8039365ae54c2d230c6d46ff86555f1ff95dbbd8930f7fbd6aecf37cd559

See more details on using hashes here.

Provenance

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

File metadata

File hashes

Hashes for oxml-0.1.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Algorithm Hash digest
SHA256 da109eedecd5360ad0dbf2c2308a0c4def86fdf67625f4387043040b92667ab1
MD5 b1aa2a0a5602ef97f6fa87f0a118e01a
BLAKE2b-256 91f3832f4ba77c9338e808c12ffa0430cd8277b9c5cd72432365de03aafddba3

See more details on using hashes here.

Provenance

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

File metadata

File hashes

Hashes for oxml-0.1.2-cp311-cp311-macosx_11_0_arm64.whl
Algorithm Hash digest
SHA256 9de04406224a51ad334770987cfb6b375aab056e3cf6e51797a3815478ef6d6c
MD5 dd2478ca6b91a4b89c07aa028e0a2e18
BLAKE2b-256 7895812ce29f2555ec954d7984b5937cbf392bdc9006195ad02b7eca67309c93

See more details on using hashes here.

Provenance

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

File metadata

File hashes

Hashes for oxml-0.1.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Algorithm Hash digest
SHA256 f6a252e066c4ab99de257fe4213be2c07fe7cb81ddcc137feb559cbb42973361
MD5 1a493a21686881c62b9c6a03aaef2946
BLAKE2b-256 cfcbd3f85a291f6d42fbab4dafefecc984f949af5f7bf10549ebb8118e7a98b9

See more details on using hashes here.

Provenance

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

File metadata

File hashes

Hashes for oxml-0.1.2-cp310-cp310-macosx_11_0_arm64.whl
Algorithm Hash digest
SHA256 b4ec2c40108b5ed3d8999feb52b83c98aa5893be8edf8d58aa29016f0359fa87
MD5 aa1335f8a4a36075fc44e6a84074d51e
BLAKE2b-256 c1fd5a67754071906199628802da4ae14ee3a6a4e37bf73c7642f33a6fde5476

See more details on using hashes here.

Provenance

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

This release

0.1.2 This release

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