/** * ODF tracked-changes emitter: paragraph-granularity (Slice 1) + intra-paragraph modify pairs * (issue #356). * * Mutates the REVISED `content.xml` DOM in place, adding a `text:tracked-changes` container (first * child of `office:text`) plus the lightweight in-body markers that reference it. The exact markup * shapes were confirmed by driving LibreOffice to author each change and inspecting its output * (see the `add-odf-compare` and `add-odf-intra-paragraph-compare` design notes): * * - Insertion: `text:change-start` / `text:change-end` brackets the inserted run, referencing a * `text:insertion` region; inserted content stays inline. Forward (a following kept paragraph * exists): start at the inserted run's first paragraph, end at the following paragraph's start. * End-of-document: start at the preceding paragraph's end, end at the inserted run's last * paragraph's end — UNLESS the preceding paragraph lives inside a table cell, where the * bracket stays within the inserted run (start at its first paragraph's start). A span from a * table-cell paragraph into a body paragraph encodes a paragraph-break merge LibreOffice * cannot perform across a table boundary, so rejecting it strands an empty body paragraph * (issue #380). * - Deletion (paragraph-break merge): the deleted paragraphs live out-of-line in a `text:deletion` * region; an inline `text:change` point marker sits in the nearest SURVIVING paragraph — at the * start of the following one (forward) or the end of the preceding one (backward, for a run * reaching end-of-document). A run whose following paragraph belongs to an inserted run that * itself reaches end-of-document (a dissimilar whole-paragraph replacement of the LAST * paragraph) also anchors BACKWARD: the insertion's bracket is end-anchored there, so a forward * marker would sit inside the insertion span and rejecting the insertion would remove the * deletion's restore point (issue #367). When that composition's backward anchor would be a * table-cell paragraph, the insertion bracket stays within the inserted run instead (see * above), so the deletion anchors at the inserted run's first paragraph's START — before the * co-located `text:change-start`, hence still outside the insertion span — and its region * stores NO merge-artifact paragraph: rejecting the content-only insertion leaves one * residual empty paragraph behind, and that residual paragraph is the merge slot the * artifact normally provides (issue #380). A PURE end-of-document deletion (no paired * insertion) whose backward anchor is a table-cell paragraph likewise cannot anchor in the * cell — LibreOffice would restore the deleted paragraph INSIDE the cell (issue #540); instead * a fresh empty body `text:p` is appended after the table to host the marker and serve as the * (no-artifact) merge slot, so rejecting restores the paragraph after the table. Consecutive * deletions coalesce into ONE region with * all deleted paragraphs plus one empty merge-artifact paragraph (artifact last for forward, * first for backward). `text:change` is inline and is never a direct block child of * `office:text`. * - Modify pair (intra-paragraph): the revised paragraph stays in place. An inserted span keeps * its content inline bracketed by `text:change-start`/`text:change-end`; a deleted span leaves * one `text:change` point marker and its content is stored out-of-line in a `text:deletion` * region holding ONE block mirroring the host (`text:p`/`text:h` + style/outline-level) — no * merge-artifact paragraph (no paragraph break died). A replace orders the insertion bracket * first and the deletion point after its `text:change-end` (LibreOffice's authored order); at * one offset the document order is `change-end`, `change`, `change-start`. A pair whose spans * cannot be mapped degrades to the Slice-1 whole-paragraph delete+insert, decided before any * markup is written. * - A run with no surviving paragraph to anchor to (every paragraph deleted) fails closed. * * All element creation/matching is by `namespaceURI` + `localName` (ODF prefixes are not guaranteed). */ import type { EditOp } from './diff.js'; export type EmitParams = { /** The revised `content.xml` DOM; mutated in place. */ revisedDoc: Document; /** Revised body paragraphs in document order (excludes `text:tracked-changes`). */ revisedBlocks: Element[]; /** Original body paragraphs in document order (source of deleted content). */ originalBlocks: Element[]; /** The edit script from `diffParagraphs(original, revised)`. */ ops: EditOp[]; /** Change author for `dc:creator`. */ author: string; /** Change date for `dc:date` (ODF 8601, no fractional seconds). */ date: string; }; export declare class OdfEmitError extends Error { } /** Per-emission accounting so reported stats always match the emitted markup. */ export type EmitResult = { /** Modify pairs that survived as inline markup. */ modifications: number; /** Modify pairs that fell back to whole-paragraph delete+insert. */ degradedModifications: number; /** Inserted spans inside surviving modify pairs (one `text:insertion` region each). */ inlineInsertions: number; /** Deleted spans inside surviving modify pairs (one `text:deletion` region each). */ inlineDeletions: number; }; /** Apply the tracked-changes markup for `ops` to `revisedDoc`. */ export declare function emitTrackedChanges(params: EmitParams): EmitResult; //# sourceMappingURL=emit.d.ts.map