Skip to content

Footnotes and endnotes

notes/write.py exposes add_footnote and add_endnote, both with identical shape: append a reference marker run to the paragraph, then append a content entry in the corresponding separate part. The content entry uses Word's FootnoteText / EndnoteText paragraph style and FootnoteReference / EndnoteReference run style for the leading reference glyph. The body text run carries xml:space="preserve".

For the calls, see the notes guide.

edit_footnote(doc, id, text) and edit_endnote(doc, id, text) mutate the body of an existing note in place. They strip every <w:p> child of the matching <w:footnote> / <w:endnote> element and append a fresh paragraph built by the shared _build_note_paragraph helper (used by both add and edit paths). The body-side reference marker in the main document body is untouched, so the in-text superscript stays put. Reserved separator ids (-1, 0) raise ValueError; missing ids raise NoteNotFoundError.

read_footnotes(doc) and read_endnotes(doc) walk the corresponding part and pair each note with the paragraph index of its body-side reference marker. Reserved entries (ids -1 for separator, 0 for continuation separator, or any entry with w:type of "separator" / "continuationSeparator") are filtered out before results are returned, so callers only ever see user-authored notes.

FootnoteIdRegistry and EndnoteIdRegistry are two more disjoint namespaces. The shared _NoteIdRegistryBase (notes/registry.py) parameterises the relationship type and the note tag; the underlying _IdRegistryBase.reserve(value) rejects values outside [1, 2**31 - 1] on a range check, so ids 0 and -1 are unissuable — the range check fires before any duplicate check, so no special pre-seeding is needed.