Skip to content

Remove Notes

Removing a note is two deletions that have to happen together: the reference run in the body and the note body in its own part. RemoveFootnote and RemoveEndnote do both, and they hang off the paragraph rather than the document because the paragraph is what owns the mark. Finding that paragraph is the part you write yourself.

StepCall
Find a mark and its idrun.IsFootnote(), run.IsEndnote() over the runs of each paragraph
Delete the note and the markpara.RemoveFootnote(id), para.RemoveEndnote(id)

Removing a note

for _, para := range doc.Paragraphs() {
    for _, run := range para.Runs() {
        if ok, id := run.IsFootnote(); ok && id == 2 {
            para.RemoveFootnote(id)
            break
        }

        if ok, id := run.IsEndnote(); ok && id == 1 {
            para.RemoveEndnote(id)
            break
        }
    }
}

The break is not tidiness. Removal renumbers every note that is left, so any id read before the call is stale from that point on, and the runs collected for the loop no longer describe the paragraph. One removal per pass, then scan again.

Pass the id you just read from the run in front of you. Both calls search the paragraph for a run carrying that id and drop it, then delete the note from the part whether or not the search found anything.

What removal does to the numbering

After deleting the entry, UniOffice renumbers the notes that remain from 1 in the order they now sit in the part, rewrites the id in every reference run in the body to match, and restates the separator ids in the document settings. Nothing is left pointing at a note that is gone.

The sample file carries footnotes 2 and 3. Remove footnote 2 and the survivor becomes footnote 1, with the mark in the body rewritten to 1 as well.

Slice order is where this gets sharp. The deletion moves the last entry in the part into the freed slot before the renumbering runs, so with three notes, removing the first leaves the third holding id 1 and the second holding id 2. Word still prints the numbers in the order the marks appear in the text, so the document reads correctly; it is only the ids that stop following document order. Anything you cached before the removal is wrong afterwards.

Limitations

An id that is not in the part removes the first entry instead, which is the separator, and the notes are then renumbered around the loss. Only ever pass an id that came from IsFootnote or IsEndnote on the document you are editing.

Calling either removal on a document that has no notes of that kind panics with a nil pointer. HasFootnotes and HasEndnotes are the guard.

The removal deletes the note from the part even when the mark is not on the paragraph you called it on. Calling RemoveFootnote on the wrong paragraph leaves the mark in the body with its note gone.

Renumbering rewrites the references reachable from doc.Paragraphs(), which is the body and the paragraphs inside table cells. Marks in headers and footers are not visited, so they keep the ids they had while everything else moves.

The endnote branch of the example looks for id 1, and in the sample file id 1 is the continuation separator; its only real endnote is id 2. Run it as shipped and the footnote goes while the endnote stays. It is a fair illustration of the previous point: ids are per document, and hardcoding one is how you miss.

Run the example

The example opens footnotes_endnotes.docx, walks every paragraph looking for a footnote mark with id 2 and an endnote mark with id 1, and writes removed_footnote_endnote.docx. The scan in main is the pattern to copy; the ids are specific to that file.

git clone https://github.com/unidoc/unioffice-examples.git
cd unioffice-examples/document/endnotes_footnotes_remove
go run main.go

If this is your first time using UniOffice, follow the getting started guide to create an API key and set up your development environment.

View the full source
Last updated on