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.
| Step | Call |
|---|---|
| Find a mark and its id | run.IsFootnote(), run.IsEndnote() over the runs of each paragraph |
| Delete the note and the mark | para.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.goIf this is your first time using UniOffice, follow the getting started guide to create an API key and set up your development environment.