Skip to main content

docxaudit

test python license

Find what your converter silently dropped from a .docx. Zero dependencies.

The figure above is generated by docs/silent-losses_figure.pypip install git+https://github.com/GuoCheng24/sciglyph and run it to reproduce docs/silent-losses.png byte for byte.

Pandoc — or any LaTeX/Markdown → Word pipeline — reports success and still loses things. A table disappears. Figures render in desktop Word but not in Word for the web. A supplement that is 11 pages as PDF arrives as 17 pages as .docx, and the editor counts the .docx.

None of that shows up as an error. You find out from a reviewer.

$ docxaudit examples/sample.docx
sample.docx
  26 paragraphs · 1 tables · 3 images (3 referenced) · 4 equations (1 display) · 0 page breaks

  ERROR   [TBL_NO_GRID] table 1 has no <w:tblGrid>
          columns collapse; the reader has to AutoFit by hand
          fix: Inject an equal-width tblGrid plus a per-cell <w:tcW>, and set
               <w:tblLayout w:type="autofit"/>.

  WARN    [NO_PAGEBREAKS] 3 figures but no page breaks at all
          if the PDF puts one figure per page, the .docx will not match, and
          reviewers count the .docx

  WARN    [FONT_MISMATCH] heading font 'Calibri' differs from body font 'Consolas'

  WARN    [FONT_EA_EMPTY] theme East Asian font is empty while the body sets one
          CJK headings will fall back and stop matching the body text

That output is real: examples/sample.docx is in this repository, built from examples/sample.md with plain pandoc. Three of those problems come from nothing more exotic than converting a short paper.

Why the usual checks miss these

python-docx cannot see them. It reads paragraph text, so equations (OMML) and anything the converter dropped are simply absent from what you inspect. "The text looks fine" is not evidence.

A LibreOffice preview misleads in both directions. It invents ugly font substitutions that real Word does not have — so you waste time fixing cosmetics that were never broken — and it is forgiving enough to render documents that stricter viewers refuse. The namespace bug above renders perfectly in LibreOffice and in desktop Word, and shows nothing in Word for the web.

An AI assistant reading the file cannot check this either. These are structural properties of the OOXML, not something visible in extracted text.

docxaudit reads the raw XML and checks the specific things that ship unnoticed.

Install

pip install git+https://github.com/GuoCheng24/docxaudit

Or clone and pip install -e . to read it first.

Standard library only. Nothing to break, nothing to audit.

Not on PyPI yet, so the git URL above is the install line that works today. When it lands, pip install docxaudit will too.

What it checks

Every check corresponds to a failure that actually reached a submission.

code what goes wrong
NS_PREFIX / NS_GENERATED Post-processing re-serialised the XML and rebound namespaces to ns2:/ns3:. Word resolves by URI so it looks fine; strict viewers show neither figures nor equations.
TBL_NO_GRID Table has no <w:tblGrid> — columns collapse to a sliver in Word. Common when tables come from LaTeX booktabs.
TBL_ZERO_WIDTH Table declares width 0.
IMG_ORPHAN Images embedded in the archive with no <w:drawing> referencing them — the anchors were dropped.
NO_PAGEBREAKS \clearpage does not survive conversion, so one-figure-per-page silently becomes a different page count.
MATH_INLINE Inline maths converts least reliably; complex expressions arrive mangled.
FONT_MISMATCH Theme heading font disagrees with the body default — sans-serif headings over serif text.
FONT_EA_EMPTY Theme East Asian font is empty, so CJK headings fall back and stop matching the body.
HEADING_COLOUR Headings still carry the default template's blue, while the PDF renders them black.
DUP_PREFIX Figure Figure 1 — the source wrote a prefix and the converter added its own.
EMPTY_HEADING A heading whose text did not survive.

Compare two outputs

Give it two files and it reports structural drift — useful for the PDF-side and Word-side versions of one manuscript, or for before/after a post-processing step:

$ docxaudit before.docx after.docx
comparison
  WARN    [DRIFT_PARAGRAPHS] paragraphs: 42 in before.docx vs 36 in after.docx
  ERROR   [DRIFT_TEXT] text length differs by 11%

Check a PDF against the .docx

Structural checks catch content that vanished. They cannot catch layout drift — the same content laid out over a different number of pages. Give it both files and it renders the .docx with LibreOffice and measures the result:

$ docxaudit paper.pdf paper.docx
paper.pdf
  17 pages · 0 images · 43885 chars
paper.docx  (rendered)
  19 pages · 7 images · 35876 chars

layout comparison
  ERROR   [PAGE_COUNT] page count differs: 17 (pdf) vs 19 (docx)
          2 page(s) apart; journals count the .docx

  INFO    [VECTOR_FIGURES] the pdf draws its figures as vectors on 4 page(s);
          the docx embeds 7 raster image(s)
          expected for a LaTeX PDF - image counts are not comparable

  INFO    [TEXT_TAIL_ONLY] 313 word(s) appear only in the pdf, 81% of them in
          the final quarter
          e.g. abdalah, ajani, albu, andrearczyk
          fix: Concentrated at the end: normally the reference list in a
               different citation style, not lost content.

Requires pip install docxaudit[pdf] and LibreOffice on PATH (or set DOCXAUDIT_SOFFICE). Both are optional — the structural checks above need neither.

Comparing two renderings honestly is harder than it sounds, and most of the work here went into not crying wolf:

  • A LaTeX PDF draws figures as vector operators, not embedded images, so counting images reports zero figures for a paper that plainly has them. Figure-bearing pages are compared instead.
  • Maths re-encodes. The PDF holds Unicode mathematical alphanumerics, the .docx holds OMML. Extracted text differs for identical equations, so only ASCII prose is compared.
  • Justified text hyphenates. advan- + tage in the PDF and advantage in the .docx would read as two words missing from each side; they are rejoined first.
  • Reference lists legitimately differ. So the tool reports where the missing words are: bunched in the final quarter it says so and calls it formatting; spread through the body it calls it lost content.

In CI

Exit code is 1 when there are errors, or with --strict when there are warnings too:

- run: pip install git+https://github.com/GuoCheng24/docxaudit
- run: docxaudit build/paper.docx --strict

As a library

from docxaudit import audit

report = audit("paper.docx")
print(report.stats)                      # counts you can assert on
for f in report.errors:
    print(f.code, f.message, f.fix)

--json gives the same structure on the command line.

Scope

It checks structure, not typography. It will not tell you a figure is ugly or a caption reads badly — for that, render the document and look at it. What it does is catch the class of problem that renders fine everywhere you happen to look, and breaks somewhere you do not.

License

MIT © Guo Cheng

关于那行 star 提示

跑命令时,docxaudit 会在第 5 次和第 25 次往 stderr 写一行,提一句这个仓库在哪。一辈子只有这两次,此外再不出声。

它不会出现在:管道或重定向里(stderr 不是终端就直接返回,连计数文件都不建)、CI 环境里(CI / GITHUB_ACTIONS)。它写的是 stderr 而非 stdout,所以不会污染你的数据输出;它包在 try/finally 里且吞掉自身所有异常,不会改变退出码,也不会影响结果

永久关掉:

export DOCXAUDIT_NO_NUDGE=1

计数存在 $XDG_STATE_HOME/docxaudit/usage.json(默认 ~/.local/state/docxaudit/usage.json),删掉即重置。

Download files

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

Source Distribution

docxaudit-0.1.0.tar.gz (23.6 kB view details)

Uploaded Source

Built Distribution

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

docxaudit-0.1.0-py3-none-any.whl (20.9 kB view details)

Uploaded Python 3

File details

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

File metadata

  • Download URL: docxaudit-0.1.0.tar.gz
  • Upload date:
  • Size: 23.6 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/7.0.0 CPython/3.13.9

File hashes

Hashes for docxaudit-0.1.0.tar.gz
Algorithm Hash digest
SHA256 aa39ff1bfcb7c34e612a47afae94c02a10ecd956d8efa97a39483c2baf5c91f2
MD5 c15e65f577d640ea45ae0d7cb8c5eba2
BLAKE2b-256 2d06086da1f59a114e468b2c786528e0de83d639012d58463d8668fb10dfa391

See more details on using hashes here.

File details

Details for the file docxaudit-0.1.0-py3-none-any.whl.

File metadata

  • Download URL: docxaudit-0.1.0-py3-none-any.whl
  • Upload date:
  • Size: 20.9 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/7.0.0 CPython/3.13.9

File hashes

Hashes for docxaudit-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 b0a02bd5f42531a253bbf9cc9ff2eb558202a212342a9585f2edf552d28a34ee
MD5 c0d055b712737a1a39dd6b8e2318b920
BLAKE2b-256 4223d47dd370d1e2e2be0989aab3651f650d09bdc26d137441e1bedaba670c48

See more details on using hashes here.

Release history Release notifications | RSS feed

0.1.5

2 files

0.1.4

2 files

0.1.3

2 files

0.1.2

2 files

0.1.1

2 files

This release

0.1.0 This release

2 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