README

bobzhang/docx2html/docx does not have a README file

#
AnnotationAnchor

pub struct AnnotationAnchor {
// private fields
}

One anchor of a comment: a paired range (start+end), a dangling half-range, or a point (references only). Paths are full agent paths ("/body/p[2]"); references are the comment-reference positions attached to this anchor, in document order.

#
AnnotationAnchor::end

fn AnnotationAnchor::end(self : AnnotationAnchor) -> String?

The range end's containing projection path, when present.

#
AnnotationAnchor::end_boundary

The end marker's boundary relative to end, when present.

#
AnnotationAnchor::references

fn AnnotationAnchor::references(self : AnnotationAnchor) -> Array[String]

Comment-reference positions attached to this anchor, in document order.

#
AnnotationAnchor::start

fn AnnotationAnchor::start(self : AnnotationAnchor) -> String?

The range start's containing projection path, when present.

#
AnnotationAnchor::start_boundary

The start marker's boundary relative to start, when present.

#
AnnotationAnchor::story

fn AnnotationAnchor::story(self : AnnotationAnchor) -> String

The story this anchor lives in ("/body", "/header[1]", ...).

#
AnnotationBoundary

pub enum AnnotationBoundary {
Before
InsideStart
InsideEnd
After
} derive(Eq)

Where a marker sits relative to its rendered projection node — the plan's boundary distinction, preserved so "before p[3]" and "after p[2]" do not collapse into the same position.

#
AnnotationBoundary::equal

#
AnnotationBoundary::not_equal

#
AnnotationBoundary::output

fn AnnotationBoundary::output(self : AnnotationBoundary, logger : &Logger) -> Unit

#
AnnotationBoundary::to_string

fn AnnotationBoundary::to_string(self : AnnotationBoundary) -> String

#
AnnotationIndex

pub struct AnnotationIndex {
// private fields
}

The annotation index. Opaque: fields stay private so the surface can evolve additively.

#
AnnotationIndex::comments

Comments: one entry per definition (comments.xml order), then marker-only ids.

#
AnnotationIndex::covering_comment_ids

fn AnnotationIndex::covering_comment_ids(self : AnnotationIndex, path : String) -> Array[String]

The ids of comments whose anchors INTERSECT the projection element at path ("/body/p[2]" style), in definition order — the reverse link J2's comment_ids exposes. Point anchors cover their reference's paragraph.

#
AnnotationIndex::endnotes

Endnotes with their reference positions.

#
AnnotationIndex::footnotes

fn AnnotationIndex::footnotes(self : AnnotationIndex) -> Array[NoteInfo]

Footnotes with their reference positions.

#
AnnotationIndex::revisions

Tracked changes across every publicly addressable story, in document order (story order, then position within the story).

#
AnnotationIndex::warnings

fn AnnotationIndex::warnings(self : AnnotationIndex) -> Array[String]

Non-fatal diagnostics gathered while building the index.

#
CommentInfo

pub struct CommentInfo {
// private fields
}

One comment: identity and metadata from comments.xml (defined is false for a dangling id that only appears in markers), threading from commentsExtended when present, and anchors from the story scans in the total order the plan fixes (story rank, then position).

#
CommentInfo::anchors

Anchors in the plan's total order (story rank, then position).

#
CommentInfo::author

fn CommentInfo::author(self : CommentInfo) -> String?

The w:author attribute, when present.

#
CommentInfo::body_paragraphs

fn CommentInfo::body_paragraphs(self : CommentInfo) -> Int

Number of body paragraphs in the comment definition.

#
CommentInfo::date

fn CommentInfo::date(self : CommentInfo) -> String?

The w:date attribute, LEXICAL (never converted), when present.

#
CommentInfo::defined

fn CommentInfo::defined(self : CommentInfo) -> Bool

False when the id appears only in markers (no definition).

#
CommentInfo::done

fn CommentInfo::done(self : CommentInfo) -> Bool?

w15 resolution state; None when commentsExtended is absent.

#
CommentInfo::id

fn CommentInfo::id(self : CommentInfo) -> String

The comment id, as spelled in comments.xml.

#
CommentInfo::initials

fn CommentInfo::initials(self : CommentInfo) -> String?

The w:initials attribute, when present.

#
CommentInfo::last_para_id

fn CommentInfo::last_para_id(self : CommentInfo) -> String?

The definition's LAST body paragraph w14:paraId (the commentsExtended key), or None when the paragraph is unstamped.

#
CommentInfo::parent_id

fn CommentInfo::parent_id(self : CommentInfo) -> String?

The parent comment id for replies (w15 paraIdParent, last-paragraph rule).

#
CommentSpec

pub struct CommentSpec {
// private fields
}

One comment to attach to the written body: WHO (author, optional initials and xsd:dateTime date), WHERE (an inclusive range of top-level body block indexes with both endpoints paragraphs — or, for a reply, the earlier comment it answers), optionally a done resolution flag, and WHAT (paragraph-only body content). Construct with comment_spec (anchored) or comment_reply (anchorless), which fail-close on everything a spec can get wrong in isolation.

#
DocxAnnotatedResult

pub struct DocxAnnotatedResult {
// private fields
}

The package read plus its annotation index. Opaque. Carries the annotation BODIES as (id, body) pairs in reader order — exactly the shape @paths.resolve_annotation_path consumes (J2's read surface).

#
DocxAnnotatedResult::annotation_sidecars

fn DocxAnnotatedResult::annotation_sidecars(self : DocxAnnotatedResult) -> Array[String]

Relationship types of annotation SIDECARS this tooling cannot keep consistent when mutating comments (Word's modern comment identity and people parts). Per the L0 matrix rule these are detected by RELATIONSHIP TYPE from the main part — their presence makes mutating commands fail closed. commentsExtended is NOT here: the add operation never touches it (matrix row), and L2 owns it.

#
DocxAnnotatedResult::annotations

The annotation index built from the same bytes.

#
DocxAnnotatedResult::body_paragraph_span

fn DocxAnnotatedResult::body_paragraph_span(self : DocxAnnotatedResult, relative_path : String) -> NodeSpan?

The span of the body-story paragraph at relative_path — the story-relative ORDINAL path exactly as the index emits it ("p[3]", "tbl[1]/tr[2]/tc[1]/p[1]") — or None when no such paragraph was projected. Offsets index into main_story_part's original bytes.

#
DocxAnnotatedResult::body_run_span

fn DocxAnnotatedResult::body_run_span(self : DocxAnnotatedResult, relative_path : String) -> NodeSpan?

The span of the body-story RUN at relative_path ("p[3]/r[2]", the index's ordinal form), or None. Runs carry the same offsets as paragraphs (content_start past a leading rPr) so L1 can rewrite a self-closing <w:r/> by its own extent too.

#
DocxAnnotatedResult::comment_bodies

Comment (id, body) pairs in comments.xml order.

#
DocxAnnotatedResult::comments_extended_part

fn DocxAnnotatedResult::comments_extended_part(self : DocxAnnotatedResult) -> String?

The zip entry holding the commentsExtended part (resolved by RELATIONSHIP TYPE), or None when the package has none — L2 then creates it.

#
DocxAnnotatedResult::comments_part

fn DocxAnnotatedResult::comments_part(self : DocxAnnotatedResult) -> String?

The zip entry holding the comments part (resolved by RELATIONSHIP TYPE from the main part, never by filename), or None when the package has no comments part yet — L1 then creates it.

#
DocxAnnotatedResult::comments_story_source

fn DocxAnnotatedResult::comments_story_source(self : DocxAnnotatedResult) -> DocxStoryPartSource?

Physical source for the comments story, when one was read.

#
DocxAnnotatedResult::endnote_bodies

Endnote (id, body) pairs in reader order.

#
DocxAnnotatedResult::endnotes_story_source

fn DocxAnnotatedResult::endnotes_story_source(self : DocxAnnotatedResult) -> DocxStoryPartSource?

Physical source for the endnotes story, when one was read.

#
DocxAnnotatedResult::footer_story_sources

Physical sources for rendered footer stories in package-result order.

#
DocxAnnotatedResult::footnote_bodies

Footnote (id, body) pairs in reader order.

#
DocxAnnotatedResult::footnotes_story_source

fn DocxAnnotatedResult::footnotes_story_source(self : DocxAnnotatedResult) -> DocxStoryPartSource?

Physical source for the footnotes story, when one was read.

#
DocxAnnotatedResult::header_story_sources

Physical sources for rendered header stories in package-result order.

#
DocxAnnotatedResult::main_story_part

fn DocxAnnotatedResult::main_story_part(self : DocxAnnotatedResult) -> String

The zip entry name of the MAIN document part the body-story spans index into (resolved through the officeDocument relationship, never hardcoded).

#
DocxAnnotatedResult::main_story_source

Physical source and authority for the body story.

#
DocxAnnotatedResult::result

The package-level read (identical to read_docx_package).

#
DocxAnnotatedResult::revision_spans

Every tracked-change element across every scanned story, in story order then document order. Offsets index into the part that backs each story.

The list is complete BY CONSTRUCTION: the scanner records a site for every tracked-change element it walks, without the suppression and projection gates the reading index applies, and retraction never removes one. A resolver can therefore treat an empty result as proof that the package carries no tracked changes at all.

#
DocxAnnotatedResult::story_paragraph_span

fn DocxAnnotatedResult::story_paragraph_span(self : DocxAnnotatedResult, story : String, relative_path : String) -> NodeSpan?

The span of a paragraph in ANY scanned story ("/comments" included), by its story-relative ordinal path ("comment[2]/p[1]") — offsets index into that story's part bytes. L2's paraId retrofit edits the parent comment's LAST body paragraph through this.

#
DocxAnnotatedResult::story_root_span

fn DocxAnnotatedResult::story_root_span(self : DocxAnnotatedResult, story : String) -> NodeSpan?

The ROOT element's span for a scanned story ("/body", "/comments", "/footnotes", "/endnotes"), or None when the story was not scanned. L1 splices a new comment definition just before the comments story's close_tag_start; a self-closing root reports no interior and must be rewritten by its own extent first.

#
DocxAnnotatedResult::story_row_span

fn DocxAnnotatedResult::story_row_span(self : DocxAnnotatedResult, story : String, relative_path : String) -> NodeSpan?

The span of one table ROW in any scanned story, addressed by the story key ("/body", "/header[1]", ...) and the story-relative ordinal path ("tbl[1]/tr[2]"). The scanner already records tr nodes with full spans; template repetition replaces a row's byte region with filled clones through this.

#
DocxAnnotatedResult::story_run_span

fn DocxAnnotatedResult::story_run_span(self : DocxAnnotatedResult, story : String, relative_path : String) -> NodeSpan?

The span of one text RUN in any scanned story, addressed by the story key ("/body", "/header[1]", ...) and the story-relative ordinal path ("p[3]/r[2]", "p[1]/hyperlink[1]/r[1]"). The scanner already records run nodes with full spans in every story; this generalizes the body-only lookup so template merging can rewrite header and footer runs through the same byte-span contract.

#
DocxPackageResult

Everything read_docx_with_messages returns, plus sections and header/footer stories.

#
DocxPackageResult::equal

#deprecated("implicit trait-method promotion is being removed; call via the trait")
fn DocxPackageResult::equal(DocxPackageResult, DocxPackageResult) -> Bool

#
DocxPackageResult::not_equal

#deprecated("implicit trait-method promotion is being removed; call via the trait")
fn DocxPackageResult::not_equal(x : DocxPackageResult, y : DocxPackageResult) -> Bool

#
DocxPackageResult::to_repr

#deprecated("implicit trait-method promotion is being removed; call via the trait")
fn DocxPackageResult::to_repr(DocxPackageResult) ->
Repr

#
DocxReadResult

Document tree plus diagnostics produced while reading a DOCX package.

#
DocxReadResult::equal

#deprecated("implicit trait-method promotion is being removed; call via the trait")
fn DocxReadResult::equal(DocxReadResult, DocxReadResult) -> Bool

#
DocxReadResult::not_equal

#deprecated("implicit trait-method promotion is being removed; call via the trait")
fn DocxReadResult::not_equal(x : DocxReadResult, y : DocxReadResult) -> Bool

#
DocxReadResult::to_repr

#deprecated("implicit trait-method promotion is being removed; call via the trait")
fn DocxReadResult::to_repr(DocxReadResult) ->
Repr

#
DocxStoryPartAuthority

pub enum DocxStoryPartAuthority {
RelationshipBacked
LegacyFilenameFallback
}

How the reader selected a physical story part.

#
DocxStoryPartAuthority::name

Stable protocol spelling for story-part authority.

#
DocxStoryPartSource

pub struct DocxStoryPartSource {
// private fields
}

The physical ZIP entry used for one logical DOCX story and how it was selected. The type is opaque so more provenance can be added later without exposing mutable reader internals.

#
DocxStoryPartSource::authority

Whether this part was relationship-backed or a legacy filename fallback.

#
DocxStoryPartSource::part

fn DocxStoryPartSource::part(self : DocxStoryPartSource) -> String

The exact physical ZIP entry read for this story.

#
NodeSpan

pub struct NodeSpan {
// private fields
}

The byte extent of one BODY-story projection node (paragraph or run) in the original word-processing part, plus the two insertion offsets annotation surgery uses: content_start (just past the open tag AND past a leading pPr/rPr — the property container leads its sequence, so content and markers must land after it) and close_tag_start (the '<' of the close tag). Both are None for a self-closing form, which has no interior — the splice layer must first rewrite it to the open form (a byte edit of this node's own span).

#
NodeSpan::byte_end

fn NodeSpan::byte_end(self : NodeSpan) -> Int

Offset just past the '>' of the paragraph's close tag (exclusive).

#
NodeSpan::byte_start

fn NodeSpan::byte_start(self : NodeSpan) -> Int

Offset of the '<' of the paragraph's open tag.

#
NodeSpan::close_tag_start

fn NodeSpan::close_tag_start(self : NodeSpan) -> Int?

Offset of the '<' of the close tag, or None for a self-closing paragraph.

#
NodeSpan::content_start

fn NodeSpan::content_start(self : NodeSpan) -> Int?

Earliest interior insertion offset (past the open tag and any leading pPr), or None for a self-closing paragraph.

#
NodeSpan::self_closing

fn NodeSpan::self_closing(self : NodeSpan) -> Bool

True when the paragraph is the self-closing <w:p/> form.

#
NoteInfo

pub struct NoteInfo {
// private fields
}

One footnote/endnote: its id and every body position referencing it (multi-reference notes are representable per the plan).

#
NoteInfo::id

fn NoteInfo::id(self : NoteInfo) -> String

The note id, as spelled.

#
NoteInfo::references

fn NoteInfo::references(self : NoteInfo) -> Array[String]

Every body position referencing this note, in document order.

#
NoteSpec

pub struct NoteSpec {
// private fields
}

One footnote or endnote body: plain-content, paragraph-only, and no note references of its own (notes do not nest). Construct with note_spec.

#
RevisionInfo

pub struct RevisionInfo {
// private fields
}

One tracked change: what kind of revision it is, who made it, when they said they made it, and the projection path of the paragraph that contains it.

The reader flattens w:ins into the accepted text and drops w:del entirely, so a revision has no representation in the parsed document at all. This record is the only place the identity survives, and it is read-only: text still returns the accepted view, unchanged.

id, author and date are the attributes AS SPELLED and stay absent when the source omits them. All three are optional in CT_TrackChange; a defaulted author or date would make the index assert authorship the document never recorded.

#
RevisionInfo::author

fn RevisionInfo::author(self : RevisionInfo) -> String?

The w:author attribute, when present.

#
RevisionInfo::date

fn RevisionInfo::date(self : RevisionInfo) -> String?

The w:date attribute, LEXICAL (never converted), when present.

#
RevisionInfo::id

fn RevisionInfo::id(self : RevisionInfo) -> String?

The w:id attribute, as spelled, when present.

#
RevisionInfo::kind

fn RevisionInfo::kind(self : RevisionInfo) -> String

"ins" for an insertion, "del" for a deletion.

#
RevisionInfo::path

fn RevisionInfo::path(self : RevisionInfo) -> String

The story-qualified projection path of the containing paragraph ("/body/p[2]"), degrading to the nearest verifiable ancestor.

#
RevisionNameEdit

pub struct RevisionNameEdit {
// private fields
}

One element-name rewrite: the byte range of an element's LOCAL NAME in the original part, and the name it becomes. Rejecting a deletion turns every w:delText back into w:t through these ranges, which leaves the namespace prefix, xml:space, and every other byte of the tag untouched.

#
RevisionNameEdit::end

fn RevisionNameEdit::end(self : RevisionNameEdit) -> Int

Offset just past the last byte of the local name (exclusive).

#
RevisionNameEdit::replacement

fn RevisionNameEdit::replacement(self : RevisionNameEdit) -> String

The local name that replaces the range ("t" or "instrText").

#
RevisionNameEdit::start

fn RevisionNameEdit::start(self : RevisionNameEdit) -> Int

Offset of the first byte of the local name.

#
RevisionSpan

pub struct RevisionSpan {
// private fields
}

One tracked-change ELEMENT in the original bytes: what it is, who recorded it, and the byte extent a resolver needs to unwrap or remove it.

This is the MUTATION view of a revision and is deliberately wider than AnnotationIndex::revisions, which is the READING view. Reading reports the content insertions and deletions the reader's projection retains; resolving must additionally see every construct it cannot act on, because a construct it cannot see is a construct it would silently leave behind. supported separates the two: it is true only for content w:ins and w:del, false for property revisions, moves, and every *PrChange.

#
RevisionSpan::author

fn RevisionSpan::author(self : RevisionSpan) -> String?

The w:author attribute, as spelled, when present.

#
RevisionSpan::byte_end

fn RevisionSpan::byte_end(self : RevisionSpan) -> Int

Offset just past the '>' that ends the element (exclusive).

#
RevisionSpan::byte_start

fn RevisionSpan::byte_start(self : RevisionSpan) -> Int

Offset of the '<' of the element's open tag.

#
RevisionSpan::close_tag_start

fn RevisionSpan::close_tag_start(self : RevisionSpan) -> Int?

Offset of the '<' of the close tag, or None for the self-closing form.

#
RevisionSpan::container_path

fn RevisionSpan::container_path(self : RevisionSpan) -> String?

The story-relative projection path of the innermost containing node ("p[2]", "tbl[1]/tr[1]/tc[1]/p[1]"), or None at story level.

#
RevisionSpan::content_start

fn RevisionSpan::content_start(self : RevisionSpan) -> Int?

Offset just past the '>' of the open tag, or None for the self-closing form — which has no content to keep, so all four resolutions of it collapse to removing the element.

#
RevisionSpan::date

fn RevisionSpan::date(self : RevisionSpan) -> String?

The w:date attribute, LEXICAL (never converted), when present.

#
RevisionSpan::declares_namespaces

fn RevisionSpan::declares_namespaces(self : RevisionSpan) -> Bool

True when the element's own start tag binds a namespace prefix. Unwrapping it would drop the binding while its former children still use it, so an unwrap resolution must refuse; removing the whole element stays safe.

#
RevisionSpan::id

fn RevisionSpan::id(self : RevisionSpan) -> String?

The w:id attribute, as spelled, when present.

#
RevisionSpan::kind

fn RevisionSpan::kind(self : RevisionSpan) -> String

The WML local name of the element ("ins", "del", "moveFrom", "rPrChange").

#
RevisionSpan::name_edits

fn RevisionSpan::name_edits(self : RevisionSpan) -> Array[RevisionNameEdit]

The w:delText/w:delInstrText names inside this deletion, in document order; empty for an insertion. Rejecting the deletion applies all of them.

#
RevisionSpan::story

fn RevisionSpan::story(self : RevisionSpan) -> String

The story key the span's offsets index into ("/body", "/header[1]").

#
RevisionSpan::supported

fn RevisionSpan::supported(self : RevisionSpan) -> Bool

True only for a content w:ins/w:del a resolver can accept or reject.

#
MAX_AUTHORED_MEDIA_BYTES

let MAX_AUTHORED_MEDIA_BYTES : Int64

Maximum aggregate emitted-media bytes, summed over image occurrences rather than unique assets.

#
MAX_AUTHORED_MEDIA_PARTS

let MAX_AUTHORED_MEDIA_PARTS : Int

Maximum distinct media parts a fresh-authoring script may emit (one per image OCCURRENCE, not per unique asset), and the aggregate emitted-media byte ceiling summed over occurrences. Enforced BEFORE the writer graph is built, so a script that references one asset thousands of times is refused as a typed media limit rather than after materializing every part.

#
author_docx_bytes

Builds a fresh DOCX from an already-parsed docx.batch/2 script and the image bytes its image_paths() referenced. Body/comment/note construction errors surface as Unsupported carrying the script op address; a bounded serialization or emitted-media ceiling breach surfaces as WriteResourceLimit. Nothing is written — the caller publishes the returned bytes through its own transaction/atomic-write boundary.

#
comment_anchor_fragments

fn comment_anchor_fragments(id~ : String, wordprocessing_namespace? : String) -> (String, String)

The two anchor fragments for comment id: the range start (spliced at the from-paragraph's content_start) and the range end plus the reference run (spliced at the to-paragraph's close_tag_start) — the same canonical shape the K1 writer emits, in self-contained form.

#
comment_definition_fragment

fn comment_definition_fragment(spec : CommentSpec, id~ : String, para_ids? : Array[String], max_output_bytes? : Int, wordprocessing_namespace? : String) -> String raise
DocxError

The w:comment definition fragment for spec (whose anchored range is ignored here — the splice layer owns placement). The body is re-validated (the arrays stay caller-mutable) and serialized through the same body writers as the packaging path, under the same fail-closed guard against relationship-bearing content.

#
comment_reply

fn comment_reply(author~ : String, initials? : String, date? : String, reply_to~ : Int, done? : Bool, body : Array[
DocumentElement
]) -> CommentSpec raise
DocxError

Validates and builds an anchorless REPLY: reply_to is the 0-based index of an EARLIER spec in the array passed to write_docx_with_comments (checked against the array there; chains are allowed — a reply may answer another reply). Per the locked policy a reply emits NO range or reference markers: its parent's anchor is logically its own, and the thread linkage lives in word/commentsExtended.xml (w15:paraIdParent). Everything else — author/initials/date rules, the plain-content paragraph-only body — matches comment_spec.

#
comment_spec

fn comment_spec(author~ : String, initials? : String, date? : String, from~ : Int, to~ : Int, done? : Bool, body : Array[
DocumentElement
]) -> CommentSpec raise
DocxError

Validates and builds a CommentSpec. from/to are 0-based indexes into the body array later passed to write_docx_with_comments (the inclusive anchored range); bounds against that body are checked at write time, everything spec-local is checked here so callers can attribute the failure to the exact comment: non-empty attribute-safe author, non-empty attribute-safe initials (when given), a lexically valid xsd:dateTime date (when given), an ordered non-negative range, and a non-empty paragraph-only body. Bodies are PLAIN CONTENT by the roadmap's locked rule — hyperlinks (even relationship-free anchor-only ones) and images are rejected here, and again at write time because the arrays stay caller-mutable.

#
convert

fn convert(docx : BytesView, output_format? :
OutputFormat
, style_map? : Array[String], include_default_style_map? : Bool, include_embedded_style_map? : Bool, ignore_empty_paragraphs? : Bool, id_prefix? : String, pretty_print? : Bool, convert_image? : (
Image
) ->
ImageConversion
, transform_document? : (
DocumentElement
) ->
DocumentElement
, external_file_access? : Bool, read_external_file? : (String) -> Bytes?) ->
ConversionResult
raise
DocxError

Converts DOCX bytes using the requested output format.

#
convert_to_html

fn convert_to_html(docx : BytesView, style_map? : Array[String], include_default_style_map? : Bool, include_embedded_style_map? : Bool, ignore_empty_paragraphs? : Bool, id_prefix? : String, pretty_print? : Bool, convert_image? : (
Image
) ->
ImageConversion
, transform_document? : (
DocumentElement
) ->
DocumentElement
, external_file_access? : Bool, read_external_file? : (String) -> Bytes?) ->
ConversionResult
raise
DocxError

Converts DOCX bytes to HTML.

#
convert_to_markdown

fn convert_to_markdown(docx : BytesView, style_map? : Array[String], include_default_style_map? : Bool, include_embedded_style_map? : Bool, ignore_empty_paragraphs? : Bool, id_prefix? : String, convert_image? : (
Image
) ->
ImageConversion
, transform_document? : (
DocumentElement
) ->
DocumentElement
, external_file_access? : Bool, read_external_file? : (String) -> Bytes?) ->
ConversionResult
raise
DocxError

Converts DOCX bytes to Markdown.

#
embed_style_map

fn embed_style_map(docx : BytesView, style_map : String) -> Bytes raise
DocxError

Embeds or replaces the DOCX style map part.

#
extract_raw_text

fn extract_raw_text(docx : BytesView, external_file_access? : Bool, read_external_file? : (String) -> Bytes?) ->
ConversionResult
raise
DocxError

Extracts raw text from DOCX bytes.

#
new_blank_docx

fn new_blank_docx() -> Bytes

Builds a minimal, schema-valid blank docx: one empty paragraph, a body-final section (Letter, one-inch margins), and a Normal style. All inputs are compile-time constants, so builder failures are programming errors — hence the abort rather than a raise.

#
new_blank_docx_limited

Like new_blank_docx, but serialized under limits so a fresh-creation transaction can bound the candidate BEFORE allocation (the blank package is a small constant, so this never actually breaches — it keeps the create path on the same bounded-writer contract as authored documents). An over- limit ceiling surfaces as the typed WriteResourceLimit.

#
note_spec

Validates and builds a NoteSpec. The body rules match comment bodies (non-empty, paragraph-only, plain content) plus the no-nesting rule; they are re-checked at write time because the arrays stay caller-mutable. Which KIND the note is (footnote or endnote) is decided by which array it is passed in, so one spec type serves both.

#
plan_comment_addition

fn plan_comment_addition(annotated : DocxAnnotatedResult, original : BytesView, at_relative~ : String, to_relative~ : String, spec : CommentSpec, max_fragment_bytes? : Int) -> (
SplicePlan
, String) raise
DocxError

Builds the splice plan that adds one comment to the ORIGINAL package: anchor markers into the main part at scanner offsets (self-closing paragraphs rewritten by their own extent), the definition into the existing comments part (self-closing roots rewritten too) or a fresh part derived from the MAIN part's directory, wired with a parsed-not-guessed relationship id and a content-type Override located at the BYTE level. Returns the plan and the allocated comment id.

#
plan_comment_addition_archive

fn plan_comment_addition_archive(annotated : DocxAnnotatedResult, archive :
Archive
, at_relative~ : String, to_relative~ : String, spec : CommentSpec, xml_budget? :
XmlReadBudget
, max_fragment_bytes? : Int) -> (
SplicePlan
, String) raise
DocxError

Archive-backed form used by preservation-safe edit sessions. It reuses the caller's already bounded, materialized package and never inflates the DOCX a second time.

#
plan_comment_reply

fn plan_comment_reply(annotated : DocxAnnotatedResult, original : BytesView, parent_id~ : String, spec : CommentSpec, max_fragment_bytes? : Int) -> (
SplicePlan
, String) raise
DocxError

Builds the splice plan for docx annotate reply: the reply's definition (paraId-stamped) into comments.xml plus the whole-part commentsExtended update (parent retrofit included). Returns the plan and the reply's new comment id.

#
plan_comment_reply_archive

fn plan_comment_reply_archive(annotated : DocxAnnotatedResult, archive :
Archive
, parent_id~ : String, spec : CommentSpec, xml_budget? :
XmlReadBudget
, max_fragment_bytes? : Int) -> (
SplicePlan
, String) raise
DocxError

Archive-backed reply planner for bounded preservation sessions.

#
plan_comment_resolution

fn plan_comment_resolution(annotated : DocxAnnotatedResult, original : BytesView, comment_id~ : String, done~ : Bool) ->
SplicePlan
raise
DocxError

Builds the splice plan for docx annotate resolve|unresolve: the comment's commentsExtended entry flips (or gains) its w15:done flag, with the same paraId retrofit as replies when the definition is unstamped.

#
plan_comment_resolution_archive

Archive-backed resolution planner for bounded preservation sessions.

#
read_docx

fn read_docx(docx : BytesView, external_file_access? : Bool, read_external_file? : (String) -> Bytes?) ->
DocumentElement
raise
DocxError

Reads DOCX bytes into a document tree.

#
read_docx_annotated

fn read_docx_annotated(docx : BytesView, external_file_access? : Bool, read_external_file? : (String) -> Bytes?) -> DocxAnnotatedResult raise
DocxError

Reads DOCX bytes into the package representation PLUS the annotation index. The package half is identical to read_docx_package.

#
read_docx_annotated_archive

fn read_docx_annotated_archive(archive :
Archive
, external_file_access? : Bool, read_external_file? : (String) -> Bytes?) -> DocxAnnotatedResult raise
DocxError

Builds the same package and annotation indexes from a caller-owned archive snapshot. Payload buffers are shared; the DOCX reader does not inflate the package a second time. Duplicate entry names fail before the map-backed view is constructed.

#
read_docx_annotated_archive_limited

fn read_docx_annotated_archive_limited(archive :
Archive
, xml_budget :
XmlReadBudget
, external_file_access? : Bool, read_external_file? : (String) -> Bytes?, max_diagnostics? : Int, max_diagnostic_chars? : Int, expected_main_document_path? : String) -> DocxAnnotatedResult raise
DocxError

Builds package and annotation indexes from an existing archive while every XML part shares one cumulative parser budget. Source bytes are charged before UTF-8 decoding and parser tokens before DOM allocation. Reader diagnostics are deduplicated in first-seen order and bounded during production by max_diagnostics and max_diagnostic_chars.

#
read_docx_annotated_archive_tolerant_limited

fn read_docx_annotated_archive_tolerant_limited(archive :
Archive
, xml_budget :
XmlReadBudget
, external_file_access? : Bool, read_external_file? : (String) -> Bytes?, max_diagnostics? : Int, max_diagnostic_chars? : Int, expected_main_document_path? : String) -> DocxAnnotatedResult raise
DocxError

Builds the bounded annotation-aware projection used by tolerant read-only commands. Broken section header/footer references are already warned and omitted by DocxPackageResult; they do not activate mutation-only identity gates. Edit transactions continue to use read_docx_annotated_archive_limited and fail closed on the same input.

#
read_docx_package

fn read_docx_package(docx : BytesView, external_file_access? : Bool, read_external_file? : (String) -> Bytes?) -> DocxPackageResult raise
DocxError

Reads DOCX bytes into the package-level representation: the body document tree (identical to read_docx_with_messages), header/footer parts (each a block-level story, deduplicated by part path in first-reference order), and the sections that reference them.

#
read_docx_with_messages

fn read_docx_with_messages(docx : BytesView, external_file_access? : Bool, read_external_file? : (String) -> Bytes?) -> DocxReadResult raise
DocxError

Reads DOCX bytes into a document tree and diagnostics.

#
read_embedded_style_map

fn read_embedded_style_map(docx : BytesView) -> String? raise
DocxError

Reads the embedded style map from DOCX bytes, if present.

#
validate_annotation_identity_state

fn validate_annotation_identity_state(annotated : DocxAnnotatedResult) -> Unit raise
DocxError

Rejects annotation state that cannot safely serve as the result of a comment mutation. This is the candidate-side counterpart to the planners' source gate and catches duplicate or ambiguous identities introduced by a caller-supplied generic splice plan.

#
validate_global_para_id_state_archive_limited

fn validate_global_para_id_state_archive_limited(annotated : DocxAnnotatedResult, archive :
Archive
, xml_budget :
XmlReadBudget
) -> Unit raise
DocxError

Validates every reachable story's w14:paraId values under one cumulative XML budget. This is the candidate-side gate for generic splice plans; the reply/resolution planners use the same traversal before allocating IDs.

#
write_docx

Serializes body content into a complete docx package. Supported today: paragraphs (style id, alignment, list numbering) containing runs (bold, italic, underline, strikethrough, caps, super/subscript, font, size, highlight), text, tabs, line breaks; and tables with spans. Headings use the Heading1..Heading6 style ids, emitted into the styles part so Word's outline and this repo's own outline command both recognize them; list paragraphs reference the fixed bullet/decimal definitions in the numbering part.

#
write_docx_with_annotations

The full annotation writer: write_docx plus comments (see write_docx_with_comments) plus footnotes and endnotes. Notes are referenced from body runs by note_reference(kind, index) where index is the 0-based position in the matching array here — each supplied note must be referenced EXACTLY ONCE (the canonical shape; unreferenced notes would be invisible orphans, duplicate references are not what Word produces). Note bodies are plain-content paragraph-only, may not carry note references themselves, and land in word/footnotes.xml / word/endnotes.xml as MAIN-part relationships, complete with the separator/continuationSeparator plumbing notes and the in-note footnoteRef/endnoteRef mark run.

#
write_docx_with_comments

write_docx plus comments. The body serializes exactly as write_docx would (an empty comments array is byte-identical); each comment then decorates its anchored paragraphs — range start markers right after pPr in the from paragraph, range end plus the id's reference run appended to the to paragraph — and its definition is written to word/comments.xml. Both anchor endpoints must be top-level paragraphs (tables cannot carry the canonical intra-paragraph shape). REPLIES (comment_reply) must answer an EARLIER spec in this array and emit no markers at all. Comment BODIES reuse the body writers, with a fail-closed guard: content that allocates relationships or media (hyperlinks, images) raises instead of emitting references that would dangle in the comments part. Threading or resolution anywhere adds w14:paraId stamps and word/commentsExtended.xml; otherwise output is byte-identical to the threading-free writer.