texdiff
AST-driven semantic diff for LaTeX documents.
texdiff compares two revisions of a LaTeX document and produces a single
marked-up diff.tex that compiles with pdflatex — added text in blue,
deleted text struck out — while preserving the original document's styling,
cross-references and custom classes.
Why
latexdiff is the de-facto standard for
review-marked LaTeX, but it diffs token soup: it guesses word boundaries with
regexes and re-injects markup into the merged text, hoping braces still
balance. On real-world documents the result is markup that crosses brace /
environment boundaries, glues old and new table cells together, and corrupts
listings — which is why every serious latexdiff user eventually ends up
maintaining a pile of fragile pre/post-processing scripts around it.
texdiff takes the other road: diff the tree, never the text.
- Parse old and new documents into a LaTeX syntax tree (pylatexenc — a faithful concrete syntax tree carrying exact source spans, no macro expansion).
- Align the trees level by level (document → environments → groups → text runs) with difflib-style sequence matching. A change can never leak across a node boundary.
- Walk the merged tree and emit
\DIFadd{...}/\DIFdel{...}markup — latexdiff-compatible rendering, valid by construction.
Restructured tables (re-ordered rows, changed column layouts), math, listings and verbatim environments are handled as atomic or row-aligned blocks instead of being shredded by a word diff.
Multi-file sources are flattened automatically (\input/\include
resolved relative to each file, like latexdiff --flatten), and the
preamble of the new revision is used verbatim so custom classes and
macros keep working.
Usage
texdiff old/main.tex new/main.tex > diff.tex # \input expansion built in
pdflatex diff.tex # done - no pre/postprocessing
texdiff old.tex new.tex -o diff.tex --check # + fail loudly if it won't compile
texdiff old.tex new.tex --stats # "611 unchanged, 29 added, ..."
pip install texdiff # or from a checkout:
pip install -e ".[dev]" && pytest
Status: v0.2.0 — core pipeline plus flattening, row-granular table
alignment, preamble policy, --check and a compile-guaranteed corpus in
CI; validated on a 65-page generated longtable-heavy document. See the
roadmap for what is next.
License
MIT
Metadata
Release files for texdiff 0.2.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| texdiff-0.2.0.tar.gz | 73.2 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| texdiff-0.2.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 129.3 kB
Release files / texdiff-0.2.0.tar.gz
| Download URL | texdiff-0.2.0.tar.gz |
|---|---|
| Size | 73.2 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
8b140851bfb003c04ecc9664d090d68ef00fd9db954c3040cb9c84f2fc3e5f37
|
|
BLAKE2b-256 checksum How to use checksums |
178559ee43eb74d2d8eb99b058880fe2d15aa4395fb436a32ebf017397365daf
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.12.14
|
Release files / texdiff-0.2.0-py3-none-any.whl
| Download URL | texdiff-0.2.0-py3-none-any.whl |
|---|---|
| Size | 56.1 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
704adc28f4d26aea8422c1976b0ec311f644be78f58555e10e7c27439873dd16
|
|
BLAKE2b-256 checksum How to use checksums |
4e5f8effb7aaf321ce92ad182ac3724cb1ec6e0bfc8b8eda4682e705198286ac
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.12.14
|