Skip to main content

ooxml-integrity

CI PyPI Python License: MIT

ooxml-integrity checks .docx and .pptx files after automated editing. It finds broken comment anchors, lost tracked changes, orphaned footnotes, and presentation text that is likely to overflow its box.

The checker reads the document package and local font files. It runs locally, without rendering, model calls or network access, so documents can stay on your machine.

The browser demo runs without installation. Files stay in your tab; Python downloads once at startup. The demo pins published 0.4.3 and reports its installed version in the footer. Real browser checks gate changes before Pages deployment. Version 0.4.3 is available on PyPI. The upgrade notes describe changed findings and compatibility.

pip install ooxml-integrity
ooxml-integrity check edited.docx --against original.docx
edited.docx: 2 error(s), 0 warning(s), 0 info
  [ERROR] CMT005  comment id=1 is orphaned - present in comments.xml but
                  anchored to nothing - the reviewer's note is invisible in Word
  [ERROR] FID001  comment anchors: 2 -> 1 (1 lost)

In this example, the edited file opens in Word without a warning and keeps the same word and page counts. The reviewer's note, "Confirm this figure against the source table before circulation", is still inside the package, but its anchor is gone and Word no longer shows it beside the figure.

Why this exists

I started with a small experiment: ask agents to edit a contract that already contains reviewer comments and tracked changes. In two runs, the task was to update three figures in the same table. The run labelled careful preserved the comment and recorded the edits under a separate author. The fast run used python-docx and lost the comment anchor:

fast agent careful agent
Reviewer comment in margin absent present, anchored to the figure
Table edits untracked (0 w:ins, 0 w:del) tracked (9 w:ins, 2 w:del)
Word warning on open none none
Word count 118 118

The cause is a paragraph replacement. Assigning paragraph.text in python-docx replaces the paragraph's contents with a new run. That also removes comment anchors, footnote references, run formatting and tracked-change markup stored there. The file can still open normally after those losses.

In a separate test with six deliberately introduced defect cases, the checker found all six. XML parsing, a namespace/root-element check and successful LibreOffice PDF conversion did not distinguish those cases from the controls. The experiment did not run a full XSD validator or a systematic visual review. Across the eight real agent runs, the checker reported no false positives.

The research notes describe the experiments, saved outputs, renderer measurements and limitations.

Applied to other projects

The method was run against four third-party tools that edit or validate DOCX. Each finding was filed with a self-contained reproduction; two are fixed upstream, and a third has a proposed fix in an open pull request.

Project Finding Status
adeu comment reference run carried rStyle="CommentReference" with no such style defined fixed in 3.0.3; the reference document from this corpus was adopted as an upstream fixture
python-docx paragraph.text setter detaches comment anchors created through 1.2's comment API and existing footnote references open; PR #1605 restores the comment anchors, footnotes and revisions remain
python-docx add_comment() references the CommentReference style without defining it open
anthropics/skills the docx skill's validate.py passes a file whose comment is present in comments.xml but anchored to nothing — it checks marker → comment, not the reverse open; PR #1734 awaits maintainer review
docx-mcp offer of labelled pairs; question about the policy of rejecting inputs that already carry revisions open

Fixture contributions merged upstream: adeu #138 (comment projection across LibreOffice, Word for Mac and Word for Windows) and adeu #140 (revision projection and accept/reject). Preparing them surfaced a Python/TypeScript namespace-serialization mismatch in adeu's own consistency suite (#139, fixed in 3.0.4).

Two questions

A document can lose all its styles, footnotes and revisions and still be internally consistent. The checker therefore supports two kinds of inspection.

Are the internal references intact? Check comments, footnotes, styles, numbering, relationships and tracked changes within one file.

ooxml-integrity check report.docx

What did the edit lose? Compare construct counts and text with the source. This can find losses even when the edited file has no broken references.

ooxml-integrity check edited.docx --against original.docx

Exit codes are 0 for no findings at or above --fail-on (default error), 1 when such findings exist, and 2 for a usage error. Use --json for machine-readable output and --quiet to print only findings that fail the run.

Decks: does the text fit the box?

ooxml-integrity check deck.pptx
deck.pptx: 6 error(s), 4 warning(s), 1 info
  [ERROR] PPT001  text needs 144pt in a 40pt box - 104pt too tall (260% over), 3 line(s)
            -> slide1/OVER_huge_type_tiny_box
  [ERROR] PPT003  word wrap is off and the longest line is 304pt in a 182pt box
                  - 122pt runs outside the shape
            -> slide2/OVER_nowrap_single_line
  [WARN ] PPT004  shape extends 142pt past the right edge - content will be cut off
            -> slide3/OFFCANVAS_right
  [WARN ] PPT006  overlaps 'OVERLAP_upper_right' over 23% of the smaller shape
            -> slide3/OVERLAP_lower_left

Text fitting requires the effective font and size, which may be inherited through runs, paragraphs, list styles, layout and master placeholders, presentation defaults and themes. The checker resolves these values and reads widths from the font's hmtx/cmap tables through fontTools. The related python-pptx discussion describes the need for text measurement when fitting text.

In the reference-deck check against PowerPoint for Mac, all 21 checkable shapes matched the predicted verdict and line count. Other renderers disagreed on a shape close to its width limit. The checker reports predicted vertical overflow of up to 5% as PPT002 borderline, and missing fonts as PPT007. The validation record gives the method and the limits of that comparison.

Later checks in 0.4.0 cover long basic-Latin words, TTC/OTC font members, presentation slide order and each slide master's Latin theme fonts. Their four dedicated decks add 29 recorded PowerPoint slide observations. The long-token, font-collection, slide-order and theme notes record the cases and their remaining limits.

In CI

- uses: Dmitry-Kov/ooxml-integrity@v0.4.3
  with:
    files: "out/**/*.docx"
    against: templates/master.docx   # optional, enables the fidelity check
    fail-on: error

Run the check after a generation script, agent edit or template merge, while the source and edited files are still available for comparison.

The action writes a summary to the job page and can produce JSON and SARIF reports. SARIF findings can appear as code-scanning annotations in a pull request. The configuration guide covers severity overrides, path-scoped ignores with a required reason, and counted baselines for repositories that already have findings.

From Python

from ooxml_integrity import check, compare

for f in check("edited.docx"):
    print(f.code, f.severity.value, f.message, f.where)

for f in compare("original.docx", "edited.docx"):
    print(f.code, f.message)

Python 3.9+. Two dependencies: lxml, fonttools (plus tomli on 3.10 and older, only to read the config file). If the console script is not on your PATH, python -m ooxml_integrity check report.docx works anywhere.

What it reports, and how much it covered

Every finding has a stable code and severity, plus a part, shape or XPath where one can be identified. Losses that hide content or its audit trail are errors. Losses that affect only appearance are warnings. Undefined paragraph and table styles remain errors because they can carry numbering and structure; undefined character styles are warnings. These tables describe 0.4.3. Version 0.4.0 treated undefined character styles as errors; use --fail-on warn to keep them failing after upgrading.

.docx:

code check
PKG001-008 OPC package integrity, content types, archive budgets, unsafe part names
PKG009 unreleased: Strict Open XML is not supported, so the Word checks were not run; reported as an error
XML001 well-formedness of every XML part
REL001-003 r:id / r:embed / r:link references resolve; targets exist; unreferenced relationships
STY001 undefined paragraph/table styles: error; undefined character styles: warning
STY002 undefined basedOn / next / link style references: warning
NUM001-004 numId → w:num → abstractNumId → w:abstractNum; ilvl defined
FTN001-002 footnote references resolve; orphaned footnotes
CMT001-006 Comment ranges/references ↔ the related comments part; unresolved or unreadable comments parts
REV001-003 revision-ID collisions outside the verified paragraph-mark/content pair; w:del carries w:delText, respecting legal w:ins > w:del nesting
TBL001-002 tblGrid present; cells per row vs grid columns, accounting for gridSpan
SDT001-002 content-control integrity
TXT001 XML edge whitespace in runs without effective xml:space="preserve"
FID000 requested source comparison could not run
FID001-003 losses and additions relative to the source, by construct count; drop in text volume
FID004-006 comment, footnote or endnote text missing from the edited file, allowing for changed ids
FID007-008 header/footer story missing or changed; tracked constructs lost from headers/footers
FID009 0.4.2: missing literal insertion/deletion text with equal wrapper counts in the supported main-document profile; limits
FID010 0.4.2: fewer notes retain insertion/deletion markup in equally populated footnote/endnote text groups; limits

.pptx:

code check
PPT000 text could not be measured; reported as an error
PKG009 unreleased: Strict Open XML is not supported, so the layout checks were not run; reported as an error
PPT001 text taller than its box, beyond the measurement tolerance
PPT002 predicted text height exceeds the box by up to 5% — borderline overflow
PPT003 line runs outside the usable width: wrap off, or a single overwide glyph
PPT004 shape extends past the slide edge, or sits entirely outside it
PPT005 shrink-to-fit requested but no fontScale stored — result depends on the renderer
PPT006 two text-bearing shapes overlap
PPT007 declared font unavailable; measurements for those shapes are estimates

Use --coverage to see the scope of a result. It distinguishes checked, not-present, estimated, skipped and unsupported surfaces per file, and a result with a gap says no findings in checked surfaces, not clean. ooxml-integrity doctor reports which measurements are available on the current machine. See the support matrix and coverage and doctor.

Limitations

  • The DOCX evidence corpus uses 50 synthetic sources and 220 labelled pairs across five producers (Word for Windows, Word for Mac, Word Online, LibreOffice, python-docx): 120 clean controls and 100 seeded-defect pairs. The recorded result has 111 error-level true positives, zero false positives and zero false negatives. The 100% precision and recall apply to that corpus; 14 rules are measured and 30 are unmeasured. Accuracy on customer documents has not been measured.
  • A separate existing-revision tranche has 30 pairs: 15 controls preserving review content through adeu, Word Online and Word for Windows, nine seeded defects (historical checker revision: seven detected, two missed; 0.4.2 FID009/FID010 follow-up: nine detected), and six accept/reject characterizations excluded from preservation metrics. Producer groups are reported separately. The three Windows saves preserve revisions but change table widths; unchanged layout and broader Office review operations remain unmeasured.
  • Eight real agent runs is a small sample, on one document, on one day.
  • PPTX evidence comes from PowerPoint for Mac editing-view checks and native exports of the later regression decks. Windows and Slide Show mode are untested. GPOS kerning and shaping are not applied, so complex scripts and heavily-ligatured faces are estimates.
  • Grouped shapes, tables, SmartArt and charts in decks are not modelled. Rotated-shape geometry is only partially checked.
  • Only the Carlito/Calibri metric-compatible pairing has been measured.

The full list, with the numbers behind each, is in docs/research.md.

Where this is going

The eight agent runs showed that different editing approaches can preserve or lose review information on the same document. I would like to compare more DOCX editing tools and agent setups on a shared corpus, with enough real documents to make the results useful outside this experiment.

If your workflow includes automated DOCX edits followed by human review, try a 30-minute pilot with one local before/after pair. Share feedback about a useful finding, false alarm, missed defect or setup problem. Include the generator and checker versions, finding code and expected/actual behavior. GitHub reports are public; attaching a document is optional.

Contributing and compatibility

See CONTRIBUTING.md for reproducible reports, development setup, evidence requirements and the checks relevant to a PR. Compatibility and upgrades explains rule codes, severity, JSON/coverage versions, baseline migration and changes that can affect a CI gate even in a patch release. For sensitive vulnerability reports, use the private channel in SECURITY.md.

Repository layout

src/ooxml_integrity/   inspector, fidelity, fonts, pptx layout and checks,
                       coverage, doctor, policy, sarif, cli
tests/                 labelled-corpus, story-fidelity and false-positive regressions
research/              corpus builders, mutators, calibration, renderer comparison
docs/                  research notes, configuration, support matrix, validation records
demo/                  browser checker, landing page, bundled fonts and examples
corpus/                reference .docx and .pptx, byte-reproducible
evidence/docx-beta/    50 producer sources and 220 labelled DOCX pairs
evidence/docx-revisions/  30 pairs with existing revisions and explicit known misses
runs/                  eight real agent outputs, used as fixtures
action.yml             the GitHub Action

License

MIT.

Release files for ooxml-integrity 0.4.4

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

Source distribution (sdist)

Source distribution for ooxml-integrity 0.4.4
File Size Uploaded
ooxml_integrity-0.4.4.tar.gz 8.9 MB Details

Built distribution (wheel)

Table of built distributions (wheels) for ooxml-integrity 0.4.4
File Interpreter ABI Platform
ooxml_integrity-0.4.4-py3-none-any.whl Python 3 none any Details

Total release size: 9.0 MB

Release files / ooxml_integrity-0.4.4.tar.gz

Download URL ooxml_integrity-0.4.4.tar.gz
Size 8.9 MB
Tags Source
SHA-256 checksum
How to use checksums
53c33e41c0ba8705946678331db7f26df9f0e1f93e86827bea4cfd6b0e498294
BLAKE2b-256 checksum
How to use checksums
aaa539a226031f268e6e053ba6551a546c81b672d1371fe6a6f36a5122218b47
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

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 Sep 25, 2026.

Transparency log

Release files / ooxml_integrity-0.4.4-py3-none-any.whl

Download URL ooxml_integrity-0.4.4-py3-none-any.whl
Size 86.7 kB
Tags Python 3
SHA-256 checksum
How to use checksums
1babb29e96d5f0bc8f0f31673e831a6da72d9fd50f137d2e0c94774acb50db8e
BLAKE2b-256 checksum
How to use checksums
ea34a31185b0bb3081a7037c017a39e6b2d50cb3b93823a3c2bc0beb7c0f98cb
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

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 Sep 25, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

0.4.4 This release

2 release files

0.4.3

2 release files

0.4.2

2 release files

0.4.1

2 release files

0.4.0

2 release files

0.3.1

2 release files

0.3.0

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