Catalog›Plugins›Files & documents›jubarte-redlinesJRjubarte-redlinesRead, edit as tracked changes, compare, accept/reject and render Word .docx files with one engine; no LibreOffice, pandoc or Poppler. Open site Open in the app JSON README (API)Read, edit as tracked changes, compare, accept/reject and render Word .docx files with one engine; no LibreOffice, pandoc or Poppler.READMELoading documentation…<!-- SPDX-FileCopyrightText: 2026 Jandira Technologies, LLC SPDX-License-Identifier: AGPL-3.0-only --> > **See every page side by side: [jandira-tech.github.io/neurotic_docx_bench](https://jandira-tech.github.io/neurotic_docx_bench/)** > jubarte vs Microsoft Word, docxide-pdf, LibreOffice, PyMuPDF Pro, MiniPdf, rdocx and office2pdf on 808 documents, DOCX to PDF, scored per page. # jubarte **Word-faithful DOCX redlines and rendering, without Word.** Compare two Word documents into native tracked changes, inspect or resolve changes programmatically, apply validated edit plans, and render DOCX to PDF or PNG — from Rust, Python, Node/browser, or the CLI. [](https://www.bestpractices.dev/projects/15267) [](https://github.com/jandira-tech/jubarte-redlines/actions/workflows/ci.yml) [](https://codecov.io/gh/jandira-tech/jubarte-redlines) [](https://crates.io/crates/jubarte-redlines) [](https://docs.rs/jubarte-redlines) [](https://pypi.org/project/jubarte-redlines/) [](https://www.npmjs.com/package/jubarte-wasm) [](./Cargo.toml) [](./LICENSE) [](https://badge.socket.dev/cargo/package/jubarte-redlines/0.13.0) Jubarte is an in-process DOCX engine for applications that need Microsoft Word-style review workflows without automating Microsoft Word or LibreOffice. - **Compare DOCX → tracked DOCX** with native insertions, deletions, moves and formatting changes. - **List, accept or reject changes** globally or selectively by change ID, author and kind. - **Render DOCX → PDF / PNG** with an independent Word-oriented layout engine. - **Inspect and edit documents safely** using paragraph IDs, source hashes and atomic JSON edit plans. - **Use the same core engine everywhere**: Rust, CLI, Python and WebAssembly for Node/browser. - **Keep the original package**: relationships, styles, headers/footers, notes, media and other DOCX parts are preserved by the comparison workflow. > Jubarte is not affiliated with Microsoft. “Word-faithful” describes > engineering targets measured against Microsoft Word behavior; it is not a > claim that every DOCX will render byte-for-byte or page-for-page identically. **Live benchmark, every page against Microsoft Word: [jandira-tech.github.io/neurotic_docx_bench](https://jandira-tech.github.io/neurotic_docx_bench/)** · tables: [neurotic_docx_bench RESULTS.md](https://github.com/jandira-tech/neurotic_docx_bench/blob/main/RESULTS.md) ## Feedback and contributions Report bugs and request enhancements in the public [issue tracker](https://github.com/jandira-tech/jubarte-redlines/issues), which also archives reports and responses. English reports are welcome. See [CONTRIBUTING.md](https://github.com/jandira-tech/jubarte-redlines/blob/HEAD/CONTRIBUTING.md) for pull requests, coding requirements, building and tests, and [SECURITY.md](https://github.com/jandira-tech/jubarte-redlines/blob/HEAD/SECURITY.md) for private vulnerability reports. The [OpenSSF evidence review](https://github.com/jandira-tech/jubarte-redlines/blob/HEAD/docs/OPENSSF.md) tracks the criteria and outstanding maintainer attestations. ## Quick start Install the CLI: ```sh cargo install jubarte-redlines jubarte --version ``` Prebuilt archives are also published on [GitHub Releases](https://github.com/jandira-tech/jubarte-redlines/releases). Check the assets attached to the release you intend to install. ### Compare two Word documents ```sh jubarte original.docx modified.docx \ -o redline.docx \ --author "Reviewer" ``` `redline.docx` contains native Word tracked changes. Without `-o`, jubarte prints the redline as the agent view and writes nothing; `-o redline.pdf` renders it instead. ### Inspect and resolve tracked changes ```sh jubarte changes redline.docx # one change per line (a replacement is one): r12, kind, target, author, paragraph, text # Resolve one specific change and leave the others tracked: jubarte accept redline.docx \ -o partially-accepted.docx \ --id r12 # Or resolve everything: jubarte accept redline.docx -o final.docx jubarte reject redline.docx -o original-state.docx ``` Selection flags can be repeated and combined: ```sh jubarte reject redline.docx \ -o reviewed.docx \ --author "Reviewer A" \ --kind formatting ``` ### Render DOCX to PDF or PNG ```sh # PDF jubarte convert redline.docx \ -o redline.pdf \ --revisions word \ --compress # Page images: redline-page-01.png, redline-page-02.png, ... jubarte convert redline.docx \ -o redline.png \ --dpi 120 ``` No Microsoft Word or LibreOffice process is launched. ## How the workflow fits together ```mermaid flowchart LR A[original.docx] --> C[Compare] B[modified.docx] --> C C --> D[redline.docx] D --> E[List changes] E --> F[Accept / Reject] F --> G[resolved.docx] H[DOCX] --> I[Read] I --> J[JSON edit plan] J --> K[Edit] K --> L[clean.docx] K --> M[redline.docx] D --> N[Render] G --> N L --> N M --> N N --> O[PDF / PNG] ``` ## Documentation Per-surface guides, each ending in a **generated** reference (CLI help from the real runner, API from the shipped module) that CI keeps drift-free — `scripts/gen_docs.sh` regenerates them: - [Rust guide](https://github.com/jandira-tech/jubarte-redlines/blob/HEAD/docs/rust.md) — crate features, API tour, rendering styles, `jubarte` CLI reference - [Python guide](https://github.com/jandira-tech/jubarte-redlines/blob/HEAD/docs/python.md) — wheel and `uvx`, the three library tiers, generated API reference - [Node and browser guide](https://github.com/jandira-tech/jubarte-redlines/blob/HEAD/docs/javascript.md) — `jubarte-wasm` and `npx jubarte-redlines`, entry points, generated API reference Topical documents: [Markdown pipeline](https://github.com/jandira-tech/jubarte-redlines/blob/HEAD/docs/MARKDOWN.md) · [Word differences](https://github.com/jandira-tech/jubarte-redlines/blob/HEAD/docs/WORD_DIFFERENCES.md) · [Word layout rules](https://github.com/jandira-tech/jubarte-redlines/blob/HEAD/docs/WORD_LAYOUT_RULES.md) · [comment balloons](https://github.com/jandira-tech/jubarte-redlines/blob/HEAD/docs/WORD_COMMENT_BALLOONS.md) · [self-update](https://github.com/jandira-tech/jubarte-redlines/blob/HEAD/docs/SELF_UPDATE.md) · [benchmark classes](https://github.com/jandira-tech/jubarte-redlines/blob/HEAD/docs/bench_classes.md) · [per-release API snapshots](https://github.com/jandira-tech/jubarte-redlines/blob/HEAD/docs/api/README.md). ## Install ### CLI ```sh cargo install jubarte-redlines ``` The installed binary is named `jubarte`. From 0.11.0, the Python wheel and the npm package also run the CLI without an install: ```sh uvx jubarte-redlines redline a.docx b.docx -o redline.docx npx jubarte-redlines redline a.docx b.docx -o redline.docx ``` Both runners speak the shared command set — `compare`/`redline`, `diff`, `changes`, `accept`, `reject`, `read`, `edit`, `add`, `convert`, `capabilities` — and share the Rust clap parser for help, defaults, aliases and usage errors. The Python wheel also runs `comments`, `validate` and `visual-diff`; `append`, `scrub`, `debug` and `self-update` remain in the binary. The npm CLI renders no PNG pages ([`jubarte-wasm/cli/README.md`](https://github.com/jandira-tech/jubarte-redlines/blob/HEAD/jubarte-wasm/cli/README.md)). For a GitHub-style text review of two Word documents: ```sh jubarte diff a.docx b.docx --format github jubarte diff a.docx b.docx --format github --context 5 -o changes.patch uvx jubarte-redlines diff a.docx b.docx --format github npx jubarte-redlines diff a.docx b.docx --format github ``` `github` (also `unified` or `text`) prints a standard unified patch with `diff --git`, file headers, hunk ranges and context. It compares the complete `debug -c text` view, including existing revision marks and story text in headers, footers, notes, comments, tables and text boxes. It extracts all content before display clipping and never caps the number of changes, and does not create an implicit DOCX. Header/footer part renumbering is paired by section role. This is a patch of extracted **text**, for review; it cannot patch a binary DOCX. Formatting and media differences belong to `compare`, `debug` or `visual-diff`. CriticMarkup keeps its existing role: representing a document's current content with its tracked marks and comments. `github` gives a separate comparison of those contents; it does not accept, reject or reinterpret the marks unless `--accept-changes` is requested. `--format word` always accepts ALL changes in both inputs first, then produces fresh word-level CriticMarkup. `normal`, `context` and `side-by-side` are also available. Long lines show a 70-character window around the first change; `--full-lines` shows everything. See [text diff details](https://github.com/jandira-tech/jubarte-redlines/blob/HEAD/docs/TEXT_DIFF.md). Markdown sits beside Word: `jubarte diff` prints the changes between any two documents, Word or Markdown, as a git-style patch; `jubarte convert draft.md` writes CommonMark (with CriticMarkup as tracked changes) to DOCX/PDF/PNG; and `jubarte edit` writes the redline's patch as `patch.diff`. See [`docs/MARKDOWN.md`](https://github.com/jandira-tech/jubarte-redlines/blob/HEAD/docs/MARKDOWN.md). For a source checkout, the repository also contains installation scripts that can install supplemental fonts used by the renderer: ```sh scripts/install.sh scripts/install.sh --fonts-only ``` On Windows, see `scripts/install.ps1`. ### Rust library ```sh cargo add jubarte-redlines --no-default-features ``` The package is called `jubarte-redlines`; the Rust library import path is `jubarte`. ```rust use jubarte::{convert, document_comparer}; fn main() -> Result<(), Box<dyn std::error::Error>> { let original = std::fs::read("original.docx")?; let modified = std::fs::read("modified.docx")?; let redline = document_comparer::compare_documents(&original, &modified, "Reviewer")?; std::fs::write("redline.docx", &redline)?; let pdf = convert::docx_to_pdf(&redline)?; std::fs::write("redline.pdf", &pdf)?; Ok(()) } ``` The default crate features include the CLI, fast allocator and self-update support. Library-only consumers can disable defaults and opt into features deliberately. MSRV: **Rust 1.94**. ### Python Python requires CPython 3.10 or later. ```sh pip install jubarte-redlines ``` Low-level byte API: ```python from pathlib import Path from jubarte_redlines import ( compare_documents, get_revisions, docx_to_pdf, ) original = Path("original.docx").read_bytes() modified = Path("modified.docx").read_bytes() redline = compare_documents( original, modified, author="Reviewer", ) Path("redline.docx").write_bytes(redline) for revision in get_revisions(redline): print(revision) Path("redline.pdf").write_bytes( docx_to_pdf(redline, revisions="word") ) ``` The richer `Document` API additionally supports inspection, Markdown projection, selective change resolution, edit plans, PDF/PNG rendering and preview. ### Node and browser Node 18+: ```sh npm install jubarte-wasm ``` ```js const { compareDocuments, docxToPdf, listChanges, acceptChanges, rejectChanges, } = require("jubarte-wasm"); const redline = compareDocuments(originalBytes, modifiedBytes, "Reviewer"); const pdf = docxToPdf(redline, false, "word"); ``` For applications that do not need the PDF renderer, use the slim package exports: ```js const { compareDocuments } = require("jubarte-wasm/slim"); ``` Browser builds are exported from `jubarte-wasm/web` and `jubarte-wasm/web-slim`. ### MCP server `jubarte-mcp` exposes the engine as MCP tools (read, edit as tracked changes, render, compare, accept and reject) to coding agents. Every path stays under `--root`. | Host | Install | | --- | --- | | Claude Code | `claude mcp add --transport stdio jubarte --scope project -- uvx --from 'jubarte-redlines[mcp]' jubarte-mcp --root .` | | Codex | `codex mcp add jubarte -- uvx --from 'jubarte-redlines[mcp]' jubarte-mcp --root .` | | Gemini CLI | `gemini extensions install https://github.com/jandira-tech/jubarte-redlines` | Tools, security model and config files: [docs/adoption/mcp.md](https://github.com/jandira-tech/jubarte-redlines/blob/HEAD/docs/adoption/mcp.md). ## CLI reference Every command, from `jubarte --help` (regenerated by `scripts/gen_docs.sh`). The complete per-command `--help` of every runner (the `jubarte` binary, the Python wheel, the npm CLI) is in the [per-surface guides](#documentation). <!-- gen:cli-summary:start --> | Command | What it does | |---|---| | `jubarte ORIGINAL MODIFIED` | Read, edit, compare and render Word documents | | `jubarte compare` | Compare documents: print the redline as the agent view, or write it with -o [alias: redline] | | `jubarte changes` | List tracked changes with IDs for accept, reject and edit plans | | `jubarte accept` | Accept all tracked changes, or select by ID, author or kind | | `jubarte reject` | Reject all tracked changes, or select by ID, author or kind | | `jubarte convert` | Convert Word or Markdown to DOCX, PDF, PNG or Markdown | | `jubarte diff` | Review differences as GitHub, word, normal, context or side-by-side text | | `jubarte read` | Read the agent view: YAML header, `<!-- pN -->` id lines, changes and comments with ids | | `jubarte edit` | Edit a document: -p WHERE with --anchor, --content, --delete, --resolve, --reply, --accept, --reject, --strike, --restore or --style (several -p per command), or a JSON plan. Writes FILE_modified.docx (--out-dir: the bundle), then prints SUCCESS and a fenced before/after diff of the full agent views (refusal: exit 3) | | `jubarte add` | Add a paragraph, a comment or a reply: -p WHERE --content TEXT (several -p per command). Writes FILE_modified.docx, as edit does, and prints SUCCESS and a fenced before/after diff of the full agent views | | `jubarte capabilities` | What this binary can do, for agents choosing an operation | | `jubarte visual-diff` | Compare rendered pages pixel by pixel (different pages: exit 5) | | `jubarte comments` | List comments, threads and the text they annotate | | `jubarte append` | Join documents in order, preserving images, styles, lists and notes | | `jubarte validate` | Check Word validity, tracked edits, accessibility, style and structure; or repair Word validity | | `jubarte check` | Check a document against a source-bound expectation contract | | `jubarte scrub` | Remove authors, editing IDs, metadata and comments before sharing | | `jubarte self-update` | Check or install a release from GitHub | <!-- gen:cli-summary:end --> ### Compare ```text jubarte ORIGINAL MODIFIED [OPTIONS] ``` Without `-o`, `jubarte A B` (or `jubarte compare A B`) prints the redline as the agent view (the `read` options apply) and writes nothing. With `-o`, the extension picks what is written: | `-o` | Writes | |---|---| | `.docx` (or no or an unknown extension) | the redline | | `.md`, `.markdown`, `.txt` | the agent view of the redline; CriticMarkup when both inputs are Markdown | | `.pdf` | the redline, rendered (`--revisions`, `--pages`, `--changed-pages` apply) | | `.png` | one `STEM-page-NN.png` per page | | `.json` | the snapshot of the redline, as `read --json` prints it | `jubarte FILE` alone is `jubarte read FILE`; a lone name without an extension is taken for a mistyped task, so read such a file with `jubarte read FILE`. Common options: | Option | Purpose | |---|---| | `-b, --original FILE` | Original document | | `-m, --modified FILE` | Modified document | | `-o, --output FILE` | Write the redline; the extension picks the format (above) | | `-a, --author NAME` | Revision author (default: `Redline`) | | `-d, --datetime ISO8601` | Revision timestamp (default: now, UTC) | | `--detail-threshold RATIO` | Comparison-detail tuning | | `--mode word|powertools` | Word-oriented or classic PowerTools comparison behavior | | `--force` | Replace an existing output | | `--reference-doc FILE`, `--critic`, `--resource-path DIR` | Markdown inputs | | `--revisions`, `--dpi`, `--pages`, `--changed-pages` | Rendering, for `-o FILE.pdf` or `.png` | | `-q, --quiet` | No status lines on success; a reading task still prints what it read | The command line dates the revisions now; pin `-d 1970-01-01T00:00:00Z` for byte-identical output from identical inputs. The library calls keep the epoch as their default, so they stay deterministic. ### List tracked changes ```sh jubarte changes FILE.docx jubarte changes FILE.docx --json ``` `changes` is intended for individually addressable review operations and reports stable IDs while the corresponding tracked change remains present. The text listing prints short ids (`r12`, `header1.r1`, `footnotes.r3`); `--json` prints JSON Lines with the same short `id` (and `ref`). `--id` takes the short ids: `r12`, `r1+2` (both marks of one change), `header1.r1`. A change that holds a comment's reference mark ends its line with `holds comment c4's mark` (`holds comment marks c4, c5` for a thread's reply too), and `--json` lists them in `holds_comments`: resolving the change so its content goes (accepting a deletion, rejecting an insertion) removes those threads, as Word does. The `read` view says so on the note: `{>>c4 @AC in r13: …<<}` when the note prints outside r13. With human-readable CLI output, every command that writes a DOCX from a document prints a confirmation after publication (unless `-q`): ````text SUCCESS: ~~~~diff --- before in.docx +++ after out.docx @@ -1,3 +1,3 @@ --- -source: in.docx +source: out.docx sha256: … ~~~~ ```` The diff compares the full input and output agent views, including `source:` and `sha256:`. It uses three context lines; the output view comes from the published bytes. Markdown conversion uses the original Markdown text as its before-view; comparison uses its first input. Identical views print `no differences` inside the fence. The fence grows past any leading tilde run in the diff. Edit's former changed-range view and resolved-lines output are replaced by this block; an existing bundle summary stays before it, with one block per DOCX. The `wrote …` lines and notes remain on stderr. `-q` silences the block, summary and status; warnings and errors still print. Dry runs, saved plans and non-DOCX outputs retain their existing output. Template generation writes a JSON plan; applying it writes the DOCX. Typed `--json`/`--jsonl` delivery retains its machine-readable receipt. ### Accept or reject ```sh jubarte accept FILE.docx -o OUTPUT.docx jubarte reject FILE.docx -o OUTPUT.docx ``` Optional selection filters: ```text --id ID r12, r1+2, header1.r1 --author NAME --kind insertion|deletion|move|formatting ``` Filters are repeatable. Categories combine, so a change must satisfy each category you supplied. `jubarte edit FILE -p r12 --accept` (or `--reject`) resolves one change and writes `FILE_modified.docx`. Add `--keep-nested-comment` to `accept` or `reject` to preserve a comment and its replies when settlement removes their whole anchor. Their markers move together to an empty, untracked anchor where the removed text was; the command reports, for example, `kept c0 and its reply c1 on an empty anchor in p1`. Partial overlaps keep the existing behavior. The default removes wholly removed threads, as Word does. ### Convert to PDF / PNG ```sh jubarte convert FILE.docx [OPTIONS] ``` | Option | Purpose | |---|---| | `-o, --output FILE` | Output path; the extension picks the format: `.docx`, `.md`, `.pdf`, `.png` (pages named `STEM-page-NN.png`) or `.json` (the snapshot) | | `--dpi DPI` | PNG resolution; default 96 | | `--compress` | Deflate PDF content streams | | `--revisions conventional|word|custom` | Tracked-change visualization | | `--keep-nested-comment` | With `--track-changes accept` or `reject`, keep comment threads whose whole anchor is removed, on an empty anchor; off by default | | `--revision-palette SPEC` | Custom change colors/lines | | `--font-report FILE` | Write font-resolution JSON | | `--report FILE` | Write page/render report | | `--force` | Replace existing output | | `--fail-on-substitution` | Exit 4 when a requested font was substituted | | `--pages SPEC` | Only these pages, for example `1-3,7`, in the PDF and the PNGs alike; PNGs keep the document's page numbers | | `--move-comments` | List comments after the last page; the text keeps its tint and a `[JR1]` marker | | `--changed-pages` | Keep only the pages a change touches: a body or note revision, a comment anchor, or the first page a revised header or footer paints on (page numbers stay the document's) | | `-f, --from` / `-t, --to` | `docx`, `md`, `pdf`, `png` or `json`, when `-o` has no extension: Markdown in (CriticMarkup becomes tracked changes) or out ([`docs/MARKDOWN.md`](https://github.com/jandira-tech/jubarte-redlines/blob/HEAD/docs/MARKDOWN.md)) | Without `-o`, a `.docx` becomes `STEM.pdf` beside it and Markdown becomes `STEM.docx`; `-t md` prints to stdout. Markdown to Word: a change or comment that names no author or date (by its tag or the `authors:` table) takes `-a, --author` and `-d, --datetime`, else the front matter's `author:` and `last_modified:` (else `modified:`, `date:`, `created:`), else the `--reference-doc`'s `dc:creator` and `dcterms:created`, else `Redline` and now. Pin `-d 1970-01-01T00:00:00Z` for byte-identical output; the library calls keep `Redline` and that epoch as their default and read no reference document's metadata. `compare`, `edit`, `add` and `diff` never take the files' metadata: their changes carry `--author` and `--datetime` (default `Redline` and now). Examples: ```sh jubarte convert contract.docx jubarte convert redline.docx --revisions word jubarte convert redline.docx -o pages.png --dpi 144 jubarte convert long.docx -o excerpt.pdf --pages 1-3,7 jubarte convert redline.docx -o changed.pdf --changed-pages jubarte convert contract.docx --compress --font-report fonts.json ``` A document with comments gets Word's markup pane: each comment in a balloon beside its range, joined to it by a dotted connector, with the range tinted and bracketed. As Word does, no style writes a PDF comment annotation: a viewer would paint it as a note icon over the text ([`docs/WORD_DIFFERENCES.md`](https://github.com/jandira-tech/jubarte-redlines/blob/HEAD/docs/WORD_DIFFERENCES.md)). A custom palette can be supplied with: ```sh jubarte convert redline.docx \ --revisions custom \ --revision-palette \ "deleted=#AA0000:strike,inserted=#0055FF:double-underline" ``` ### Read a document ```sh jubarte read contract.docx # the agent view jubarte read contract.docx --json # the snapshot: paragraphs, runs, tables, changes, comments jubarte read contract.docx --tables # top-level tN grids, with nested cell paths jubarte read contract.docx --tables -p t0 # t0 and its nested tables: grid, then view jubarte read contract.docx -p p12,p40-p48 -o excerpt.md ``` `read` prints the agent view: a YAML header (`sha256:` of the file, the revision and comment counts, authors and their handles, page setup, styles, headers, footers), then Markdown with an id line before every paragraph (`p12`, the body's paragraph 12) and an id on every tracked change and comment (`r3+4` is the two marks with `w:id` 3 and 4, `c5` is comment 5, `header1.r1` a change in a header). A table cell's paragraph is `t0.row1.cell2`, a footnote's `footnotes.p0`, and `c5.text` the text comment 5 covers. No id needs quoting in bash, zsh or csh. `--json` prints the snapshot instead: document metadata, story and paragraph coordinates, `source_sha256`, runs, structural limitations, tables, content controls, and the changes and comments with their short `ref`. The selection flags (`-p`, `--head`, `--tail`, `--changed`, `--by`) narrow it as they narrow the view. `-o FILE.md` or `-o FILE.json` writes either to a file. The snapshot uses `schema_version: 3`. `changes` lists logical changes: a change Word wrote as several marks is one entry with its `members`, and a deletion followed by its author's insertion is one `replacement`. `summary.tables` and `tables[]` count top-level body tables only; each `index` is the view's `tN`. Every table has a `nested` array (empty for a leaf), containing its cell tables. A nested entry has `parent` (for example `t0.row2.cell0`) and no `index`. A deeper parent includes the cell-local table ordinal, for example `t0.row2.cell0.t0.row0.cell1`. Paragraph ids and edit-plan schemas are unchanged. `--tables` numbers only top-level tables as `table N`, matching `tN` in the view and `-p tN`. Nested tables follow their containing cell row, indented two spaces per nesting level and labelled `nested in CELL_PATH`. For example, `nested in t0.row2.cell0`; a further level uses `t0.row2.cell0.t0.row0.cell1`. `--tables -p t0` includes this whole tree. ```text <!-- p0 center --> # Consulting Agreement <!-- p1 justify --> This Consulting Agreement (the “Agreement”) is made between Harbor Point Analytics LLC ("Consultant") and Juniper Freight Co. ("Client"). <!-- p2 --> ## 1. Services <!-- p3 --> Consultant will deliver the services in each Statement of Work, including {++quarterly++}{r0 @AC} route-cost reports and a {~~weekly~>monthly~~}{r1+2 @AC} review call. ``` Attribution uses braces after the change closer: `{++new++}{r1 @AL}`. `{>>c5 @AC: note<<}` remains a comment. A deletion inside another author's insertion names its parent with `{r3 @BO re r1 @AL}`. Distinct revision ids stay separate; only a deletion directly followed in the rendered stream by a live insertion from the same author forms `{~~old~>new~~}{r4+5 @AL}`. Different inline dates keep the two marks separate. An inserted paragraph mark is `¶{r7 @AL}`. To write literal attribution-shaped text after a closer, escape the opening brace: `{++new++}\{r1 @AL}`; write literal `¶\{` likewise. See [docs/MARKDOWN.md](https://github.com/jandira-tech/jubarte-redlines/blob/HEAD/docs/MARKDOWN.md#agent-view) for the grammar. ### Validate ```sh jubarte validate contract.docx jubarte validate contract.docx --json jubarte validate contract.docx --repair fixed.docx jubarte validate review/redline.docx --original contract.docx --author Claude ``` `validate` lists what makes Word refuse or repair a file, beyond what an XSD sees: one line per finding (`code`, `part#path`, message, a `*` when it is Word-fatal), or one JSON object each with `--json`. `--repair` writes a copy with every repairable finding fixed and lists what remains; `--original` with `--author` audits that every text change against the original is a revision by that author (`UNTRACKED_EDIT`, `FOREIGN_AUTHOR`). The `edits` rules also report permission endpoints lost from any part (`PERMISSION_RANGE_DROPPED`); losing an editable region is not Word-invalid. `--rules a11y,style,structure` adds accessibility, style and structure findings (heading skips, images without alt text, tables without a header row, literal bullets, stale caches), each located by paragraph id; `--rules` also takes single rule codes. `NESTED_SAME_REVISION` is non-fatal: direct same-kind nesting is `info` (Word opens and reads it faithfully); alternating nesting such as `ins > del > ins` is a `warning` because Word opens it but misreads reject-all (#1134, Word probes 2026-10-17). `NESTED_TEXT_BOX` is a Word-fatal Ring-1 error: a DrawingML or VML text box inside another box's readable content prevents Word from opening the file (#1132, Word probes 2026-10-18). The check covers every story part and ignores nesting only in `mc:Fallback`; `--repair` leaves it unchanged. Any `word` or `edits` finding exits 2, and so does any `error`; `--strict` fails on warnings too. A file that cannot be read exits 1. | Option | Purpose | |---|---| | `--json` | JSON Lines: one object per finding, nothing when there is none | | `--rules SETS` | `word` (the default), `edits`, `a11y`, `style`, `structure`, or rule codes, comma-separated | | `--strict` | Fail on warnings too | | `--repair FILE` | Write the repaired package; remaining findings still exit 2 | | `--original FILE` | Audit tracked edits against this original (needs `--author`) | | `--author NAME` | The author every change must carry | | `--force` | Replace an existing `--repair` output | `jubarte audit FILE` still runs, as `validate --rules a11y,style,structure` with a note; it is removed in 0.14. ### Scrub ```sh jubarte scrub contract.docx -o outgoing.docx jubarte scrub contract.docx -o outgoing.docx --rsids --docprops ``` Removes who touched a document before it goes out: author names (as one alias, `--author-alias NAME`), rsids, the people and dates in the document properties, and comments. Text and tracked changes stay. Without a flag all four go, under the alias "Author"; with flags, only those given. ### Append documents ```sh jubarte append a.docx b.docx c.docx -o combined.docx jubarte append a.docx b.docx -o combined.docx --section-break continuous --keep-sections ``` B after A, then C after that, carrying images, links, styles, lists and notes. Comments are dropped with a warning unless `--carry-comments`. `--section-break` is `next-page` (the default), `continuous` or `none`; `--keep-sections` keeps each document's page setup, headers and footers. ### Comments ```sh jubarte comments contract.docx jubarte comments contract.docx --json --author "Reviewer" --latest ``` Every comment with its thread (`parent`, `done`) and the text it is anchored to. Edit plans reply to, resolve, edit and delete comments (see the operation table below). ### Fields ```sh jubarte convert contract.docx -o updated.docx --update-fields --report fields.json ``` `--update-fields` refreshes the cached results of `PAGEREF`, `REF`, `NUMPAGES`, `SEQ` and `TOC` fields from jubarte's layout; TOCs are rebuilt from the headings. Field codes stay, so Word can update them again. Page numbers are jubarte's layout, not Word's ([`docs/WORD_DIFFERENCES.md`](https://github.com/jandira-tech/jubarte-redlines/blob/HEAD/docs/WORD_DIFFERENCES.md)). It runs after `--track-changes accept|reject`; `--report` lists the fields written with their old and new results. ### Compare rendered pages ```sh jubarte visual-diff before.docx after.docx jubarte visual-diff before.docx after.docx --out-dir pages --dpi 150 --json ``` Lays out and rasterizes both documents at one resolution and compares them pixel for pixel. Exit 0 when every page is the same, 5 when any page differs; `--out-dir` writes the changed pages, overlays and `diff.json`. ### Apply an edit plan First read the source: ```sh jubarte read contract.docx -o contract.json ``` Create `plan.json`: ```json { "schema_version": 1, "source_sha256": "<the sha256: line of jubarte read>", "author": "Reviewer", "operations": [ { "id": "term", "kind": "replace", "paragraph": "p12", "find": "two years", "replacement": "three years" } ] } ``` Or record it from flags, one command at a time. Nothing is edited until the plan runs: ```sh jubarte edit contract.docx --plan plan.json -p p12 --anchor "two years" --content "three years" jubarte add contract.docx --plan plan.json -p p14 --comment --content "Why three?" ``` Each recording is checked against `contract.docx` and pins its `source_sha256`; one that does not apply leaves `plan.json` as it was. `--consume` deletes the plan once it has been applied. Preview it: ```sh jubarte edit contract.docx \ --plan plan.json \ --out-dir review \ --dry-run ``` Apply and render: ```sh jubarte edit contract.docx \ --plan plan.json \ --out-dir review \ --pdf \ --png \ --dpi 100 ``` A successful edit can produce: ```text review/ clean.docx redline.docx patch.diff report.jsonl clean.pdf redline.pdf clean-page-01.png redline-page-01.png ... ``` Operation kinds (`jubarte capabilities` lists the ones the installed build supports): | Kind | What it does | |---|---| | `replace` | Replace `find` with `replacement` in one paragraph; only the words that differ are written, so kept words keep their formatting and characters (a curly quote or a non-breaking space stays where the replacement has the plain one); optional `format` for the new text, `"whole": true` for one deletion then one insertion as typed | | `insert` | Insert text `after` or `before` a match, or at `position` `start`/`end` | | `delete` | Delete a match | | `rewrite` | Give a paragraph's whole new text; only the words that differ are edited | | `format_run` | Restyle existing text as a tracked formatting change | | `comment` | Comment on a match, a whole paragraph, or every paragraph up to `through` | | `reply_comment`, `resolve_comment`, `edit_comment`, `delete_comment` | Work on a comment thread by `comment_id`; `edit_comment` on another author's comment needs `"rewrite_others_comments": true` (it keeps their name) | | `strike` | Reject another author's insertion `revision` visibly: your tracked deletion inside it, with an optional `comment` on the struck words | | `restore` | Reject another author's deletion `revision` visibly: your tracked copy of its words right after it, with an optional `comment` | | `insert_paragraph` | A new paragraph after the anchor, with its properties (or those `like` selects) | | `delete_paragraph` | Delete a paragraph, optionally commenting on the deleted text | | `format_paragraph` | Style, alignment, line spacing, space before and after, as a tracked property change | | `merge_paragraphs` | Join the next paragraph onto this one | | `insert_table` | A table before or after a body paragraph, tracked as inserted rows | | `list` | Bulleted, numbered or lettered list, tracked as property changes | | `insert_footnote` | A footnote after an anchor | | `insert_image` | A picture paragraph (PNG, JPEG, GIF, BMP or TIFF) | | `insert_toc` | A table of contents field; with `"update_fields": true` it is filled from the headings | | `page_setup` | Page size, orientation and margins | | `watermark` | Word's diagonal text watermark in every default header (untracked) | | `fill_control` | Fill a content control (text, choice, checkbox or date) by id, tag or alias | | `redact` | Replace text with `█` blocks in both copies, untracked; refused if the text still occurs anywhere. No report, refusal or confirmation repeats the text or the document's copy of it: a plan with a redaction confirms with the published view, not the before/after diff | | `settings` | `trackRevisions`, `updateFields` and document protection in `word/settings.xml` | Plans are atomic: stale sources, ambiguous anchors, overlapping edits or unsupported structures refuse the plan instead of making a guessed edit. See [`skills/jubarte-documents/SKILL.md`](https://github.com/jandira-tech/jubarte-redlines/blob/HEAD/skills/jubarte-documents/SKILL.md) and [`examples/agents/acme-letter`](https://github.com/jandira-tech/jubarte-redlines/blob/HEAD/examples/agents/acme-letter) for the full plan schema and a complete example. ### Capabilities ```sh jubarte capabilities # one line per capability jubarte capabilities --json # the manifest: formats, ids, validate rules, operation kinds ``` Use this when an application or agent needs to discover the exact surface supported by the installed build. ### Self-update ```sh jubarte self-update --check jubarte self-update ``` Optional: ```text -y, --yes --version VERSION ``` See [`docs/SELF_UPDATE.md`](https://github.com/jandira-tech/jubarte-redlines/blob/HEAD/docs/SELF_UPDATE.md). ### Debugging DOCX behavior ```sh jubarte debug FILE.docx --list jubarte debug FILE.docx --check render jubarte debug FILE.docx -c text jubarte debug FILE.docx -c runs jubarte debug FILE.docx -c xml jubarte debug diff A.docx B.docx jubarte debug diff A.docx B.docx C.docx --full ``` `debug` is hidden from `jubarte --help`: it is a diagnostic tool for engine work, not part of the stable surface. Debug catalogs cover package structure, fields, bookmarks, IDs, styles, text boxes, revision changes, numbering, normalized XML/text/runs and render information. Use `jubarte <command> --help` for the authoritative option list for the installed release. ## Rendering and fonts The PDF engine reconstructs Word-oriented layout directly; it does not call Microsoft Word or LibreOffice. The documented rendering surface includes: - tracked changes, and comments as Word's balloons in the markup pane (a resolved comment faded, a reply numbered under its thread); - paragraph/run formatting and advanced line layout; - CJK and RTL/complex-script text; - installed and embedded fonts; - merged, vertically merged, autofit and floating tables; - DrawingML/VML graphics, text boxes, charts and SmartArt; - raster and EMF/WMF images; - headers, footers and floating frames; - sections, columns, backgrounds and page borders; - footnotes and selected fields. Rendering fidelity still depends on fonts and on document features. WebAssembly has no installed fonts: `docxToPdf` paints with the fonts a document embeds and the bundled Carlito and Liberation faces, so Chinese, Japanese or Korean text without an embedded font is not painted (see [`KNOWN_ISSUES.md`](https://github.com/jandira-tech/jubarte-redlines/blob/HEAD/KNOWN_ISSUES.md), item 9). Supplemental font lookup can be configured with: ```sh export JUBARTE_FONT_DIR=/path/to/jubarte/fonts export JUBARTE_FONT_INDEX=/path/to/font-index.tsv ``` Set: ```sh export JUBARTE_FONT_INDEX=off ``` to disable the persistent font index. Default per-user font directories are documented in the installation section of this repository. For known Word-layout differences, see: - [`docs/WORD_LAYOUT_RULES.md`](https://github.com/jandira-tech/jubarte-redlines/blob/HEAD/docs/WORD_LAYOUT_RULES.md) - [`docs/WORD_DIFFERENCES.md`](https://github.com/jandira-tech/jubarte-redlines/blob/HEAD/docs/WORD_DIFFERENCES.md) - [`docs/WORD_COMMENT_BALLOONS.md`](https://github.com/jandira-tech/jubarte-redlines/blob/HEAD/docs/WORD_COMMENT_BALLOONS.md) ## Comparison modes The default comparison mode is: ```sh --mode word ``` It targets Word-like comparison behavior. Classic PowerTools behavior is available with: ```sh --mode powertools ``` Choose explicitly if exact comparison semantics matter to a regression test or downstream workflow. ## Existing revisions in edit plans An edit plan can refuse documents that already contain tracked changes, or resolve them according to an explicit policy. List changes before editing: ```sh jubarte changes source.docx --json ``` Selective resolution can be part of the plan, for example: ```json { "resolve_revisions": { "accept": { "ids": ["r12"] }, "reject": { "authors": ["Previous Reviewer"] } } } ``` Conflicting selections are rejected instead of silently choosing a side. Settling a revision that removes a comment's whole anchor removes the thread and its replies, as Word does. Edit receipts report each removed thread as `removed_comments` (using source ids `c0`, `c1`, etc.), each saying `why`: `anchor removed by accepting r13`, the change that held its mark. Legacy `edit --dry-run` prints the JSONL preview; `edit --out-dir` writes the summary event in `report.jsonl`. Typed `edit apply --json` includes the same records in `data`. To preserve the thread, set `"removed_anchor_comments": "keep"` in a v1 or v2 plan. Its range markers and reference runs move together to an empty, untracked anchor where the removed text was. A reply in the same plan can then use that anchor. The receipt reports `kept_comments` with `anchor: "empty"`. Comments the plan subsequently deletes are not listed; deleted replies leave the kept thread's `replies`, while its surviving root stays listed. This opt-in is a jubarte convention that Word opens with threaded balloons; the default `"remove"` follows Word's removal behavior. Partial overlaps keep the existing behavior. Unknown values are refused as `INVALID_PLAN`. The policy is available in plans; `accept`/`reject` keep their existing warnings. A reply to a comment whose paragraph the same plan deletes goes on the deleted text beside its parent, where Word shows the thread; a v2 plan with `"track": "off"` deletes the anchor outright, and is refused with `COMMENT_ANCHOR_REMOVED`. Comment and bookmark ids stay stable while a plan runs, so later operations still address the source comments. After all operations finish, the preserving path numbers the revision-free working document once; its clean copy, tracked replay and operation receipts share that allocation. Pending source revisions retain their ids. The comparer path retains ids in both outputs: it unions comments from its original and edited inputs, so numbering only the edited input would change comment matching. Settlement receipts retain source comment ids; newly added comments report their written ids. If settlement removes a kept thread's row, cell or whole table, its empty anchor moves to the end of the last surviving paragraph before that container in the same story, or the start of the first paragraph after it. Removed and kept receipts never list the same comment. Removal text names the acceptance or rejection that removed the thread; kept text names its published paragraph. The v1 plan envelope is documented in `schemas/edit-plan-v1.schema.json`, and the typed v2 schema in `schemas/edit-plan-v2.schema.json`. ## Examples ### CLI ```sh jubarte original.docx revised.docx \ -o redline.docx \ --author "Legal" jubarte changes redline.docx --json jubarte convert redline.docx \ -o redline.pdf \ --revisions word ``` ### Python `Document` ```python import jubarte_redlines as jubarte doc = jubarte.read("contract.docx") print(doc.markdown()) snapshot = doc.inspect() print(snapshot) pages = doc.to_png(dpi=120) ``` ### Agent-style document editing See [`examples/agents/acme-letter`](https://github.com/jandira-tech/jubarte-redlines/blob/HEAD/examples/agents/acme-letter), which includes an edit plan, generated report and rendered redline page. ## Safety and validation CI currently includes: - `cargo fmt --check` - Clippy with `-D warnings` on the crate and on the Python, WASM (`wasm32-unknown-unknown`) and in-process bench bindings - all-feature Rust tests on Linux, macOS and Windows - source-based code coverage with a line-coverage floor - MSRV testing on Rust 1.94 (the all-feature test suite, on Linux) - `cargo publish --dry-run` - `cargo-deny` - REUSE/SPDX checks - Python binding tests The project also maintains package/schema validation and real-Microsoft-Word opening probes for release work because an OOXML document can be schema-valid and still trigger Word repair behavior. See: - [`KNOWN_ISSUES.md`](https://github.com/jandira-tech/jubarte-redlines/blob/HEAD/KNOWN_ISSUES.md) - [`docs/VERSIONING.md`](https://github.com/jandira-tech/jubarte-redlines/blob/HEAD/docs/VERSIONING.md) - [`docs/bench_classes.md`](https://github.com/jandira-tech/jubarte-redlines/blob/HEAD/docs/bench_classes.md) ## Benchmarks Jubarte maintains Word-oracle benchmarks for both document comparison and DOCX rendering. The benchmark methodology, corpus provenance, current numbers and historical results belong in: - the [neurotic_docx_bench RESULTS.md](https://github.com/jandira-tech/neurotic_docx_bench/blob/main/RESULTS.md) - the [page-by-page comparison site](https://jandira-tech.github.io/neurotic_docx_bench/) Treat these as reproducible project-maintained measurements rather than as a substitute for testing your own document corpus. For important production workloads, benchmark representative documents from your own environment before choosing an engine. ## Supported environments | Surface | Supported/tested target | |---|---| | Rust / CLI | CI tests Linux, macOS and Windows | | Rust toolchain | Rust 1.94+ | | Python | CPython 3.10+ | | Python release wheels | macOS x86_64/arm64, manylinux_2_28 x86_64/arm64, musllinux_1_2 x86_64/arm64, Windows x86_64 (abi3, CPython 3.10+) | | Node | Node 18+ | | Browser | WebAssembly builds | | CLI release workflow | Linux x86_64/arm64, macOS x86_64/arm64, Windows x86_64 targets | Release assets can vary by tag. Check the [release page](https://github.com/jandira-tech/jubarte-redlines/releases) before scripting a binary download. A Word 97-2003 `.doc` converts to `.docx` with the native binary's `jubarte convert old.doc` (or `jubarte::legacy_doc::doc_to_docx` in Rust): text, headings, lists, bold, italic and tables. Every other command, and the Python and JavaScript packages (the npm command line too), refuse a `.doc` (or an encrypted document of any Word version) with `LEGACY_DOC` and the hint to save it as `.docx` without a password, and RTF with `UNSUPPORTED_PACKAGE`. Convert them to `.docx` first. ## Troubleshooting | Problem | What to check | |---|---| | PDF layout differs from Word | Verify required fonts; inspect `--font-report`; read `docs/WORD_DIFFERENCES.md` | | Text is missing or wraps differently | Check font resolution and embedded/system font availability | | Edit exits with a refusal | Read `report.jsonl` / stdout for `STALE_SOURCE`, `ANCHOR_NOT_FOUND`, `AMBIGUOUS_ANCHOR`, `OVERLAPPING_EDITS`, `UNSUPPORTED_STRUCTURE`, etc. | | `STALE_SOURCE` | Re-run `jubarte read` and regenerate the plan from its new `sha256:` line | | `AMBIGUOUS_ANCHOR` | Use a paragraph ID or a more specific exact anchor | | Document already contains revisions | List them with `jubarte changes`; explicitly accept/reject them or set an edit-plan revision policy | | Comment is present in DOCX but missing from rendered markup | Check the documented comment-balloon limitations and `docs/WORD_COMMENT_BALLOONS.md` | | Output page count is not exactly Word's | The renderer targets Word behavior but is independent; inspect known differences before treating page count as an invariant | | Prebuilt archive is absent for a platform | Install with Cargo or build from source; then check the release assets for a later tag | | Need exact package differences | Use `jubarte debug diff` and the `text`, `runs`, `xml` or `render` debug catalogs | ## Contributing Contributions that improve Word compatibility, package validity, diagnostics, documentation, examples or platform support are welcome. Before opening a PR: ```sh cargo fmt --check cargo clippy --all-targets --all-features -- -D warnings cargo test --all-features --no-fail-fast cargo publish --dry-run ``` If you change any CLI flag, subcommand or public API, also run `scripts/gen_docs.sh` and commit the regenerated guide pages — the CI docs job fails when they drift. For Python binding changes, run the Python test suite from `jubarte-python`. Please include a minimal fixture or regression test for document-behavior changes. For Word-compatibility changes, describe what Microsoft Word does and how the fixture demonstrates it. For security vulnerabilities, use a private GitHub security advisory or the private contact documented by the project rather than a public issue. ## Roadmap Proposed areas of focus: - [ ] Stabilize and document a `1.0` compatibility policy. - [ ] Keep every advertised binary/wheel target release-complete. - [ ] Expand newcomer examples for CLI, Rust, Python, Node and browser. - [ ] Improve editable coverage for currently unsupported DOCX structures. - [ ] Continue closing documented Word-layout differences. - [ ] Add a polished browser demo for compare/review/render workflows. - [ ] Make benchmark environments easier for third parties to reproduce. - [ ] Expand community documentation and contributor onboarding. Roadmap items are goals, not compatibility guarantees. ## Project structure ```text src/ lib.rs public Rust crate document_comparer.rs compare / revisions comparer/ comparison engine changes.rs tracked-change listing / resolution inspect.rs document inspection, edit-plan anchors edit.rs + edit/ validated JSON edit plans markdown/ Markdown in and out: convert, diff, patch convert/ DOCX → PDF/PNG layout engine opc/ DOCX package layer xmllinq/ shared XML model bin/jubarte.rs CLI (compare, edit, convert, diff, debug, …) jubarte-python/ PyO3 / maturin Python package jubarte-wasm/ wasm-bindgen / npm package jubarte-app/ Tauri desktop shell: its own repository (arthrod/jubarte-app), cloned here untracked examples/ examples and profiling tools tests/ integration tests, goldens and corpus docs/ compatibility and engineering documentation tools/ validation / benchmark tooling scripts/ installation, release and Word-probe scripts ``` ## License `jubarte-redlines` is licensed under [GNU Affero General Public License v3.0 only](https://github.com/jandira-tech/jubarte-redlines/blob/HEAD/LICENSE) (`AGPL-3.0-only`). Review the license requirements before embedding or deploying the software in a product or network service. ## Links [Engine comparison site](https://jandira-tech.github.io/neurotic_docx_bench/) · [jandira.tech](https://www.jandira.tech) · [arthur.law](https://arthur.law) · [Cicero](https://www.cicero.im) · [LinkedIn](https://linkedin.com/in/arthrod) · `contact@arthur.law` - [GitHub](https://github.com/jandira-tech/jubarte-redlines) - [crates.io](https://crates.io/crates/jubarte-redlines) - [docs.rs](https://docs.rs/jubarte-redlines) - [PyPI](https://pypi.org/project/jubarte-redlines/) - [npm](https://www.npmjs.com/package/jubarte-wasm) - [Releases](https://github.com/jandira-tech/jubarte-redlines/releases) - [npm CLI](https://www.npmjs.com/package/jubarte-redlines) DetailsTypePluginsTopicFiles & documentsPublisherjandira-techSourcegeminiCategoryToolsVersion0.11.3Stars7Proposed changes6Last update2026-10-30T20:15:40ZProject statusActiveLanguageRustLicenseAGPL-3.0Added2026-10-02 13:07:40Updated2026-10-02 13:07:40Source idjandira-tech/jubarte-redlinesMoreMore plugins on Files & documentsAll from jandira-techA–Z index