docxaudit
Find what your converter silently dropped from a .docx. Zero dependencies.
The figure above is generated by docs/silent-losses_figure.py — pip install 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 docxaudit
Standard library only — a fresh virtualenv gains exactly one package and nothing else. Nothing to break, nothing to audit.
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
.docxholds OMML. Extracted text differs for identical equations, so only ASCII prose is compared. - Justified text hyphenates.
advan-+tagein the PDF andadvantagein the.docxwould 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 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.
Who maintains this
Guo Cheng, University of Chinese Academy of Sciences — medical imaging and machine learning methods. This tool came out of a supplement that was 11 pages as PDF and arrived as 17 pages as .docx, which the editor counted.
Corrections, bug reports and feature requests all go to Issues. Please open one rather than emailing: a public answer helps whoever hits the same thing next, and it is searchable.
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
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file docxaudit-0.1.1.tar.gz.
File metadata
- Download URL: docxaudit-0.1.1.tar.gz
- Upload date:
- Size: 24.4 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via:
twine/7.0.0 CPython/3.13.9
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
5067084ae403c8adaca62b7faf627cbbe09ece9d17f986a94223bfb0f1400e10
|
|
| MD5 |
aaf428e216d0fbbec598ab75893aa1ed
|
|
| BLAKE2b-256 |
12295f5ed4ae1091a0b5028d2561ba4a5da2288e80ba40b7da53f2aab31c3d56
|
File details
Details for the file docxaudit-0.1.1-py3-none-any.whl.
File metadata
- Download URL: docxaudit-0.1.1-py3-none-any.whl
- Upload date:
- Size: 21.6 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via:
twine/7.0.0 CPython/3.13.9
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
cfca1ad8e052741b4d9ac412ef9360988ee4abde285f7645ef44035b2ebf621c
|
|
| MD5 |
77b6f6cf633b31b94a17322e356bb542
|
|
| BLAKE2b-256 |
bc742c2af855338f123ad7d304848323d9ef3419b2cf58f773eeff87d0b0beb2
|