Skip to content

docx_plus.notes.write

Insert footnotes and endnotes. Both share the same shape: a reference marker run in the body, plus a content entry in the corresponding separate part (word/footnotes.xml or word/endnotes.xml, created on first use via core.get_or_create_part). edit_footnote / edit_endnote replace the body text of an existing note in place; reserved ids (-1 separator, 0 continuation-separator) are not editable.

Architecture walkthrough: ARCHITECTURE.md §7.9.

docx_plus.notes.write

Insert footnotes and endnotes.

Footnotes and endnotes share the same shape: a reference marker <w:r><w:rPr><w:rStyle val=".."/></w:rPr><w:footnoteReference|w:endnoteReference w:id=N/></w:r> in the body, plus a content entry in the corresponding separate part (word/footnotes.xml or word/endnotes.xml).

Insert-only is sufficient for v0.2 — in-place edits of existing notes are deferred to v0.3.

This module imports only from docx_plus.core and the sibling docx_plus.notes.registry (SPEC §9.1).

FootnoteRef dataclass

FootnoteRef(note_id: int, body_element: _Element)

Handle for an inserted footnote.

EndnoteRef dataclass

EndnoteRef(note_id: int, body_element: _Element)

Handle for an inserted endnote.

NoteNotFoundError

Bases: DocxPlusError, KeyError

Raised when no footnote / endnote with the requested id exists.

Subclasses :class:KeyError so existing except KeyError: clauses still catch it; also :class:DocxPlusError per SPEC §9.7.

add_footnote

add_footnote(
    paragraph: Paragraph,
    text: str,
    *,
    id_registry: FootnoteIdRegistry | None = None,
) -> FootnoteRef

Append a footnote reference to paragraph and a footnote body to footnotes.xml.

Parameters:

Name Type Description Default
paragraph Paragraph

A python-docx :class:~docx.text.paragraph.Paragraph in the main document body. The reference marker run is appended after the paragraph's existing runs.

required
text str

Footnote body text. Whitespace is preserved.

required
id_registry FootnoteIdRegistry | None

Pre-existing :class:FootnoteIdRegistry to share across an editing session.

None

Returns:

Name Type Description
A FootnoteRef

class:FootnoteRef with the assigned note id and a handle

FootnoteRef

to the note body element.

Example

from docx import Document from docx_plus.notes import add_footnote doc = Document() p = doc.add_paragraph("See the footnote.") add_footnote(p, "Explanatory text.")

Source code in docx_plus/notes/write.py
def add_footnote(
    paragraph: Paragraph,
    text: str,
    *,
    id_registry: FootnoteIdRegistry | None = None,
) -> FootnoteRef:
    """Append a footnote reference to ``paragraph`` and a footnote body to ``footnotes.xml``.

    Args:
        paragraph: A python-docx :class:`~docx.text.paragraph.Paragraph`
            in the main document body. The reference marker run is
            appended after the paragraph's existing runs.
        text: Footnote body text. Whitespace is preserved.
        id_registry: Pre-existing :class:`FootnoteIdRegistry` to share
            across an editing session.

    Returns:
        A :class:`FootnoteRef` with the assigned note id and a handle
        to the note body element.

    Example:
        >>> from docx import Document
        >>> from docx_plus.notes import add_footnote
        >>> doc = Document()
        >>> p = doc.add_paragraph("See the footnote.")
        >>> add_footnote(p, "Explanatory text.")
    """
    registry = (
        id_registry
        if id_registry is not None
        else FootnoteIdRegistry(body_document_for(paragraph, operation="add_footnote"))
    )
    note_id, note_body = _add_note(
        paragraph,
        text,
        spec=FOOTNOTES_SPEC,
        ref_tag="w:footnoteReference",
        body_tag="w:footnote",
        rstyle_ref="FootnoteReference",
        body_inner_ref_tag="w:footnoteRef",
        para_style="FootnoteText",
        registry=registry,
    )
    return FootnoteRef(note_id=note_id, body_element=note_body)

add_endnote

add_endnote(
    paragraph: Paragraph,
    text: str,
    *,
    id_registry: EndnoteIdRegistry | None = None,
) -> EndnoteRef

Append an endnote reference to paragraph and an endnote body to endnotes.xml.

Same shape as :func:add_footnote but writes the endnote variants of the reference elements and the endnotes part.

Parameters:

Name Type Description Default
paragraph Paragraph

A python-docx :class:~docx.text.paragraph.Paragraph in the main document body.

required
text str

Endnote body text.

required
id_registry EndnoteIdRegistry | None

Pre-existing :class:EndnoteIdRegistry.

None

Returns:

Name Type Description
A EndnoteRef

class:EndnoteRef with the assigned note id and a handle

EndnoteRef

to the note body element.

Source code in docx_plus/notes/write.py
def add_endnote(
    paragraph: Paragraph,
    text: str,
    *,
    id_registry: EndnoteIdRegistry | None = None,
) -> EndnoteRef:
    """Append an endnote reference to ``paragraph`` and an endnote body to ``endnotes.xml``.

    Same shape as :func:`add_footnote` but writes the endnote variants
    of the reference elements and the endnotes part.

    Args:
        paragraph: A python-docx :class:`~docx.text.paragraph.Paragraph`
            in the main document body.
        text: Endnote body text.
        id_registry: Pre-existing :class:`EndnoteIdRegistry`.

    Returns:
        A :class:`EndnoteRef` with the assigned note id and a handle
        to the note body element.
    """
    registry = (
        id_registry
        if id_registry is not None
        else EndnoteIdRegistry(body_document_for(paragraph, operation="add_endnote"))
    )
    note_id, note_body = _add_note(
        paragraph,
        text,
        spec=ENDNOTES_SPEC,
        ref_tag="w:endnoteReference",
        body_tag="w:endnote",
        rstyle_ref="EndnoteReference",
        body_inner_ref_tag="w:endnoteRef",
        para_style="EndnoteText",
        registry=registry,
    )
    return EndnoteRef(note_id=note_id, body_element=note_body)

edit_footnote

edit_footnote(doc: Document, note_id: int, text: str) -> None

Replace the body text of an existing footnote in place.

Removes all child block-level content of the matching <w:footnote> element and appends a fresh paragraph with text. The reference glyph run is rebuilt as part of the new paragraph; the body-side reference marker run in the main document is untouched.

Parameters:

Name Type Description Default
doc Document

The python-docx :class:~docx.document.Document to mutate.

required
note_id int

The w:id of the footnote to edit. Must be >= 1; ids -1 and 0 are reserved separator entries and are not editable.

required
text str

New footnote body text. Whitespace is preserved.

required

Raises:

Type Description
ValueError

If note_id is <= 0.

NoteNotFoundError

If no footnote with note_id exists, including the case where the footnotes part is absent. Subclasses :class:KeyError, so except KeyError also catches it (SPEC §16).

Source code in docx_plus/notes/write.py
def edit_footnote(doc: Document, note_id: int, text: str) -> None:
    """Replace the body text of an existing footnote in place.

    Removes all child block-level content of the matching ``<w:footnote>``
    element and appends a fresh paragraph with ``text``. The reference
    glyph run is rebuilt as part of the new paragraph; the body-side
    reference marker run in the main document is untouched.

    Args:
        doc: The python-docx :class:`~docx.document.Document` to mutate.
        note_id: The ``w:id`` of the footnote to edit. Must be ``>= 1``;
            ids ``-1`` and ``0`` are reserved separator entries and are
            not editable.
        text: New footnote body text. Whitespace is preserved.

    Raises:
        ValueError: If ``note_id`` is ``<= 0``.
        NoteNotFoundError: If no footnote with ``note_id`` exists,
            including the case where the footnotes part is absent.
            Subclasses :class:`KeyError`, so ``except KeyError`` also
            catches it (SPEC §16).
    """
    _edit_note(
        doc,
        note_id,
        text,
        spec=FOOTNOTES_SPEC,
        body_tag="w:footnote",
        rstyle_ref="FootnoteReference",
        body_inner_ref_tag="w:footnoteRef",
        para_style="FootnoteText",
    )

edit_endnote

edit_endnote(doc: Document, note_id: int, text: str) -> None

Replace the body text of an existing endnote in place.

Same shape as :func:edit_footnote but targets the endnotes part.

Parameters:

Name Type Description Default
doc Document

The python-docx :class:~docx.document.Document to mutate.

required
note_id int

The w:id of the endnote. Must be >= 1.

required
text str

New endnote body text.

required

Raises:

Type Description
ValueError

If note_id is <= 0.

NoteNotFoundError

If no endnote with note_id exists. Subclasses :class:KeyError, so except KeyError also catches it (SPEC §16).

Source code in docx_plus/notes/write.py
def edit_endnote(doc: Document, note_id: int, text: str) -> None:
    """Replace the body text of an existing endnote in place.

    Same shape as :func:`edit_footnote` but targets the endnotes part.

    Args:
        doc: The python-docx :class:`~docx.document.Document` to mutate.
        note_id: The ``w:id`` of the endnote. Must be ``>= 1``.
        text: New endnote body text.

    Raises:
        ValueError: If ``note_id`` is ``<= 0``.
        NoteNotFoundError: If no endnote with ``note_id`` exists.
            Subclasses :class:`KeyError`, so ``except KeyError`` also
            catches it (SPEC §16).
    """
    _edit_note(
        doc,
        note_id,
        text,
        spec=ENDNOTES_SPEC,
        body_tag="w:endnote",
        rstyle_ref="EndnoteReference",
        body_inner_ref_tag="w:endnoteRef",
        para_style="EndnoteText",
    )