@@ -68,10 +68,11 @@ empty string if not configured. *date* is also optional, but always set by Word
6868UTC date and time the comment was added, with seconds resolution (no milliseconds or
6969microseconds).
7070
71- **Additional Features. ** Later versions of Word allow a comment to be *resolved *. A
72- comment in this state will appear grayed-out in the Word UI. Later versions of Word also
73- allow a comment to be *replied to *, forming a *comment thread *. Neither of these
74- features is supported by the initial implementation of comments in *python-docx *.
71+ **Additional Features. ** Later versions of Word allow a top-level comment to be
72+ *resolved *. A comment in this state will appear grayed-out in the Word UI. Later
73+ versions of Word also allow a comment to be *replied to *, forming a *comment thread *.
74+ *python-docx * supports both replies and resolved-state metadata, but only for the
75+ top-level comment in a thread.
7576
7677**Applicability. ** Note that comments cannot be added to a header or footer and cannot
7778be nested inside a comment itself. In general the *python-docx * API will not allow these
@@ -109,6 +110,48 @@ A simple example is adding a comment to a paragraph::
109110
110111The API documentation for :meth: `.Document.add_comment ` provides further details.
111112
113+ For convenience, a comment can also be added directly from a paragraph, run, or
114+ table cell::
115+
116+ >>> paragraph = document.add_paragraph("Hello, world!")
117+ >>> comment = paragraph.add_comment("Please reword this.", author="Steve Canny")
118+ >>> run = paragraph.runs[0]
119+ >>> comment = run.add_comment("Comment on just this run.", author="Steve Canny")
120+ >>> table = document.add_table(rows=1, cols=1)
121+ >>> cell = table.cell(0, 0)
122+ >>> cell.text = "Cell text"
123+ >>> comment = cell.add_comment("Comment on this cell.", author="Steve Canny")
124+
125+ Tracked changes also provide a convenience API. A comment can be anchored directly to
126+ an insertion or deletion::
127+
128+ >>> paragraph = document.add_paragraph("Hello")
129+ >>> insertion = paragraph.add_tracked_insertion(" world", author="Editor")
130+ >>> comment = insertion.add_comment("Please justify this insertion.", author="Reviewer")
131+
132+ When added from a cell, the comment is anchored from the first run in the first
133+ paragraph of the cell to the last run in the last paragraph of the cell. This matches
134+ Word's XML model, where a so-called "cell comment" is really a comment range anchored
135+ inside the cell's paragraph content rather than on the cell element itself.
136+
137+ For run-level tracked changes, Word stores the comment as an ordinary comment range
138+ that brackets the ``<w:ins> `` or ``<w:del> `` wrapper itself. For block-level tracked
139+ changes such as inserted or deleted paragraphs and tables, the comment is anchored to
140+ the first and last paragraph content inside the tracked block because comment markers
141+ still have to live on paragraph/run boundaries.
142+
143+ When you need to anchor a comment to only part of a paragraph's text, use
144+ ``Paragraph.add_comment_range(start, end, ...) `` with offsets measured against
145+ ``paragraph.accepted_text ``::
146+
147+ >>> paragraph = document.add_paragraph("South")
148+ >>> comment = paragraph.add_comment_range(1, 3, "Comment on just 'ou'.")
149+
150+ The method will split runs as needed so the comment range lands on proper run
151+ boundaries. In this first pass, range comments are limited to plain paragraph runs;
152+ selections that include deleted text, hyperlinks, or other non-run content raise
153+ ``ValueError `` rather than guessing.
154+
112155
113156Accessing and using the Comments collection
114157-------------------------------------------
@@ -166,3 +209,28 @@ The author and initials metadata can be updated as desired::
166209 'John Smith'
167210 >>> comment.initials
168211 'JS'
212+
213+
214+ Resolving and reopening comments
215+ --------------------------------
216+
217+ A top-level comment can be marked resolved or reopened using either the ``resolved ``
218+ property or the convenience methods ``resolve() `` and ``reopen() ``::
219+
220+ >>> comment.resolved
221+ False
222+ >>> comment.resolve()
223+ >>> comment.resolved
224+ True
225+ >>> comment.resolved_at is not None
226+ True
227+ >>> comment.reopen()
228+ >>> comment.resolved
229+ False
230+
231+ The ``resolved_at `` value records the UTC timestamp associated with the resolved-state
232+ metadata when that information is available in the document.
233+
234+ Reply comments do not support independent resolved-state operations. This matches Word's
235+ review UI, which treats resolution as a property of the thread root rather than each
236+ individual reply.
0 commit comments