Find and Edit Content
These guides are about documents that already exist. Building one from nothing is
a matter of calling AddParagraph in the right order; changing one means first
finding the part you want, which is a different problem.
doc.Paragraphs() and doc.Tables() each answer half of it, and neither
preserves the ordering between them. doc.Nodes() returns both in document
order behind one handle, document.Node:
nodes := doc.Nodes()
headings := nodes.FindNodeByStyleName("heading 1")
nodes.ReplaceText("{{NAME}}", "Alice")nodes has to be a variable. Nodes is returned by value and every method on it
takes a pointer receiver, so doc.Nodes().ReplaceText(...) does not compile.
The shape of the tree
The top level is the document body: paragraphs and tables in order, with
structured document tag and custom XML wrappers already unwrapped. A table’s
Children are the paragraphs in its cells; a paragraph’s Children are its runs.
A table nested in a cell is a child of the outer table, not a top-level node. When
the body carries section properties, one extra node wrapping them is appended at
the end.
Node.X() returns the element behind the handle, and it is worth knowing all
three cases before writing a type switch:
switch t := node.X().(type) {
case *document.Paragraph:
case *document.Table:
case document.Run:
}Run is a value, not a pointer, and Node’s own type comment mentions only
paragraphs and tables, so a switch written from that comment misses runs
silently. Run nodes appear only when you ask FindNodeByCondition for them.
Choosing a finder
FindNodeByStyleId and FindNodeByStyleName select on style, which is how you
find headings. Watch the argument: Word’s Heading 1 has the id Heading1 and the
name heading 1, lowercase, and both are matched exactly.
FindNodeByText and FindNodeByRegexp select on content. Both work off
Node.Text(), which puts a newline after each run, so a sentence Word split
across runs does not read back as one string.
FindNodeByCondition takes a predicate for anything else, usually a type test.
It is the only finder that does not always recurse: its second argument decides
whether children are tested too. Passing false restricts the search to the level
you called it on, which is what you want for document-level structure. Passing
true flattens every descendant into the same result slice, runs included.
Reading, writing, deleting
A Node has Text() for reading, ReplaceText and ReplaceTextByRegexp for
substitution, and Remove() for deletion. Clear() looks like it belongs in that
list and does not; it detaches the node’s own reference and leaves the document
alone.
Replacement and deletion both work through the same handles, which is why a find-and-replace across a document reaches text inside table cells without any special casing, and why splitting a document at its headings or recombining parts of two files is a short program rather than a schema exercise.
Where to look
| Guide | Covers |
|---|---|
| Find Content | Walking the node tree and identifying paragraphs and tables. |
| Edit a Document | Opening a document and changing its existing content. |
| Find and Replace Text | Literal and regular expression substitution. |
| Remove Content | Deleting elements, for example every table. |
| Split by Heading | Using a heading style as a divider and writing one file per section. |
| Copy between Documents | Taking elements from two files into a new one. |
| Merge Documents | Appending whole documents together. |