Read Comments and Replies
Document.Comments() returns every comment in the document as a flat slice in
comments.xml order, replies included. Threading is stored as separate
metadata rather than as nesting, so a reply looks exactly like a top-level
comment until you ask.
| Getter | Returns |
|---|---|
ID() | The numeric comment ID. |
Author(), Initials() | The author name, and their initials or "" if unset. |
Date() | A *time.Time, nil when the comment carries no date. |
Text() | The plain text, paragraphs joined with newlines. |
Paragraphs() | The comment’s paragraphs, for reading formatting or runs. |
IsReply(), Parent() | Whether the comment is a reply, and the comment it replies to. |
Replies() | Direct replies, in the order Word displays them. |
Done() | Whether the thread is marked resolved. |
Listing threads
doc, err := document.Open("sample.docx")
if err != nil {
return err
}
defer doc.Close()
for _, c := range doc.Comments() {
if c.IsReply() {
continue
}
fmt.Printf("%d. %s: %s (resolved: %t)\n", c.ID(), c.Author(), c.Text(), c.Done())
for _, r := range c.Replies() {
fmt.Printf(" %d. %s: %s\n", r.ID(), r.Author(), r.Text())
}
}Skipping replies in the outer loop is what stops each reply being printed twice, once on its own and once under its root.
HasComments() reports whether the document has a comments part at all, not
whether that part holds anything. A document Word has stripped every comment
from can still answer true, with Comments() returning an empty slice; a
document that never had comments returns false and a nil slice.
Where threading comes from
IsReply(), Parent(), Replies() and Done() all read
commentsExtended.xml, which links comments by the w14:paraId of their last
paragraph. UniOffice loads that part along with commentsIds.xml when the
document is opened, so the threading survives a round trip.
A document produced by a tool that writes only comments.xml has no such links.
Every comment then reports IsReply() false, an empty Replies() and Done()
false, even where a human reader would see an obvious back-and-forth. There is
no way to recover the thread structure in that case, because it was never
recorded.
Limitations
Done() returning false does not distinguish “explicitly not resolved” from
“no threading entry for this comment”. Both look the same through the API.
Replies() returns direct replies only. Word writes flat threads, so in
practice one level is all there is, but recursing costs nothing and is what the
example does.
Text() concatenates run text and discards formatting, hyperlinks and images
inside the comment. Reach for Paragraphs() and its runs when the formatting
matters.
Run the example
The example opens the bundled sample.docx, which is what the
add comments example produces: four comments, one of them a
resolved thread with two replies. It takes no command-line arguments.
printThread does the indenting.
git clone https://github.com/unidoc/unioffice-examples.git
cd unioffice-examples/document/comment-list
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.