# GroupDocs.Annotation for Python via .NET — Complete Documentation > Native Python library that adds, edits, and removes annotations and markup — area and shape annotations, text highlight/underline/strikeout, watermarks, image and link stamps, and threaded comments — on PDF, Word, Excel, PowerPoint, images, CAD, Visio, and email files on Windows, Linux, and macOS. No Microsoft Office or Adobe Acrobat required. --- ## Add annotations Path: https://docs.groupdocs.com/annotation/python-net/add-annotations/ GroupDocs.Annotation lets you add many kinds of annotations to PDF, Word, Excel, PowerPoint, image, and other documents. You create an annotation object, set its properties, call [`add`](https://reference.groupdocs.com/annotation/python-net/groupdocs.annotation/annotator/) on the [`Annotator`](https://reference.groupdocs.com/annotation/python-net/groupdocs.annotation/annotator/), and `save` the result. A few rules apply to every annotation: - **Position.** Box-style annotations (area, ellipse, point, arrow, distance, polyline, text field, watermark, resources redaction, image) are positioned with a `Rectangle(x, y, width, height)` assigned to the `.box` property. Text-markup annotations (highlight, underline, strikeout, squiggly, replacement, text redaction, link) are positioned with a list of corner `Point` objects assigned to the `.points` property, in the order top-left, top-right, bottom-left, bottom-right. - **Colors.** Every color property (`background_color`, `font_color`, `pen_color`, `underline_color`, `squiggly_color`) is a packed **ARGB integer**, not a `Color` object. Use `Color..to_argb()` or `Color.from_argb(a, r, g, b).to_argb()`. - **Page index.** `page_number` is **0-based** — `0` is the first page. ## Add an area annotation An area annotation draws a filled rectangle over a region of the page. {{< tabs "code-example-add-area-annotation" >}} {{< tab "add_area_annotation.py" >}} ```python from groupdocs.annotation import Annotator from groupdocs.annotation.models import Rectangle, PenStyle from groupdocs.annotation.models.annotation_models import AreaAnnotation from groupdocs.pydrawing import Color def add_area_annotation(): # Load the document to be annotated with Annotator("./sample.pdf") as annotator: # Configure an area (rectangle) annotation area = AreaAnnotation() area.box = Rectangle(100, 100, 100, 100) # x, y, width, height area.background_color = Color.yellow.to_argb() area.pen_color = Color.red.to_argb() area.pen_width = 3 area.pen_style = PenStyle.SOLID area.opacity = 0.7 area.page_number = 0 area.message = "This is an area annotation" # Add the annotation and save the result annotator.add(area) annotator.save("./output.pdf") if __name__ == "__main__": add_area_annotation() ``` {{< /tab >}} {{< tab "sample.pdf" >}} {{< tab-text >}} `sample.pdf` is the sample file used in this example. Click [here](https://docs.groupdocs.com/annotation/python-net/_sample_files/developer-guide/basic-usage/add-annotations/sample.pdf) to download it. {{< /tab-text >}} {{< /tab >}} {{< tab "output.pdf" >}} ```text Binary file (PDF, 90 KB) ``` [Download full output](https://docs.groupdocs.com/annotation/python-net/_output_files/developer-guide/basic-usage/add-annotations/add_area_annotation/output.pdf) {{< /tab >}} {{< /tabs >}} ## Add an ellipse annotation An ellipse annotation draws a filled oval inside the bounding box. {{< tabs "code-example-add-ellipse-annotation" >}} {{< tab "add_ellipse_annotation.py" >}} ```python from groupdocs.annotation import Annotator from groupdocs.annotation.models import Rectangle, PenStyle from groupdocs.annotation.models.annotation_models import EllipseAnnotation from groupdocs.pydrawing import Color def add_ellipse_annotation(): with Annotator("./sample.pdf") as annotator: ellipse = EllipseAnnotation() ellipse.box = Rectangle(100, 100, 200, 120) ellipse.background_color = Color.from_argb(255, 144, 238, 144).to_argb() ellipse.pen_color = Color.green.to_argb() ellipse.pen_width = 2 ellipse.pen_style = PenStyle.SOLID ellipse.opacity = 0.7 ellipse.page_number = 0 ellipse.message = "This is an ellipse annotation" annotator.add(ellipse) annotator.save("./output.pdf") if __name__ == "__main__": add_ellipse_annotation() ``` {{< /tab >}} {{< tab "sample.pdf" >}} {{< tab-text >}} `sample.pdf` is the sample file used in this example. Click [here](https://docs.groupdocs.com/annotation/python-net/_sample_files/developer-guide/basic-usage/add-annotations/sample.pdf) to download it. {{< /tab-text >}} {{< /tab >}} {{< tab "output.pdf" >}} ```text Binary file (PDF, 90 KB) ``` [Download full output](https://docs.groupdocs.com/annotation/python-net/_output_files/developer-guide/basic-usage/add-annotations/add_ellipse_annotation/output.pdf) {{< /tab >}} {{< /tabs >}} ## Add a point annotation A point annotation marks a single location on the page. It is positioned by the origin of its box, so the width and height are `0`. {{< tabs "code-example-add-point-annotation" >}} {{< tab "add_point_annotation.py" >}} ```python from groupdocs.annotation import Annotator from groupdocs.annotation.models import Rectangle from groupdocs.annotation.models.annotation_models import PointAnnotation def add_point_annotation(): with Annotator("./sample.pdf") as annotator: point = PointAnnotation() point.box = Rectangle(100, 100, 0, 0) # a point is positioned by its box origin point.page_number = 0 point.message = "This is a point annotation" annotator.add(point) annotator.save("./output.pdf") if __name__ == "__main__": add_point_annotation() ``` {{< /tab >}} {{< tab "sample.pdf" >}} {{< tab-text >}} `sample.pdf` is the sample file used in this example. Click [here](https://docs.groupdocs.com/annotation/python-net/_sample_files/developer-guide/basic-usage/add-annotations/sample.pdf) to download it. {{< /tab-text >}} {{< /tab >}} {{< tab "output.pdf" >}} ```text Binary file (PDF, 90 KB) ``` [Download full output](https://docs.groupdocs.com/annotation/python-net/_output_files/developer-guide/basic-usage/add-annotations/add_point_annotation/output.pdf) {{< /tab >}} {{< /tabs >}} ## Add an arrow annotation An arrow annotation draws a directed line across the bounding box. {{< tabs "code-example-add-arrow-annotation" >}} {{< tab "add_arrow_annotation.py" >}} ```python from groupdocs.annotation import Annotator from groupdocs.annotation.models import Rectangle, PenStyle from groupdocs.annotation.models.annotation_models import ArrowAnnotation from groupdocs.pydrawing import Color def add_arrow_annotation(): with Annotator("./sample.pdf") as annotator: arrow = ArrowAnnotation() arrow.box = Rectangle(100, 100, 100, 100) arrow.pen_color = Color.blue.to_argb() arrow.pen_width = 2 arrow.pen_style = PenStyle.SOLID arrow.opacity = 0.9 arrow.page_number = 0 arrow.message = "This is an arrow annotation" annotator.add(arrow) annotator.save("./output.pdf") if __name__ == "__main__": add_arrow_annotation() ``` {{< /tab >}} {{< tab "sample.pdf" >}} {{< tab-text >}} `sample.pdf` is the sample file used in this example. Click [here](https://docs.groupdocs.com/annotation/python-net/_sample_files/developer-guide/basic-usage/add-annotations/sample.pdf) to download it. {{< /tab-text >}} {{< /tab >}} {{< tab "output.pdf" >}} ```text Binary file (PDF, 90 KB) ``` [Download full output](https://docs.groupdocs.com/annotation/python-net/_output_files/developer-guide/basic-usage/add-annotations/add_arrow_annotation/output.pdf) {{< /tab >}} {{< /tabs >}} ## Add a distance annotation A distance annotation measures the span between two points on the page. {{< tabs "code-example-add-distance-annotation" >}} {{< tab "add_distance_annotation.py" >}} ```python from groupdocs.annotation import Annotator from groupdocs.annotation.models import Rectangle, PenStyle from groupdocs.annotation.models.annotation_models import DistanceAnnotation from groupdocs.pydrawing import Color def add_distance_annotation(): with Annotator("./sample.pdf") as annotator: distance = DistanceAnnotation() distance.box = Rectangle(100, 100, 100, 100) distance.pen_color = Color.blue.to_argb() distance.pen_width = 2 distance.pen_style = PenStyle.SOLID distance.opacity = 0.7 distance.page_number = 0 distance.message = "This is a distance annotation" annotator.add(distance) annotator.save("./output.pdf") if __name__ == "__main__": add_distance_annotation() ``` {{< /tab >}} {{< tab "sample.pdf" >}} {{< tab-text >}} `sample.pdf` is the sample file used in this example. Click [here](https://docs.groupdocs.com/annotation/python-net/_sample_files/developer-guide/basic-usage/add-annotations/sample.pdf) to download it. {{< /tab-text >}} {{< /tab >}} {{< tab "output.pdf" >}} ```text Binary file (PDF, 90 KB) ``` [Download full output](https://docs.groupdocs.com/annotation/python-net/_output_files/developer-guide/basic-usage/add-annotations/add_distance_annotation/output.pdf) {{< /tab >}} {{< /tabs >}} ## Add a polyline annotation A polyline annotation draws a free-form shape described by an SVG path inside the bounding box. {{< tabs "code-example-add-polyline-annotation" >}} {{< tab "add_polyline_annotation.py" >}} ```python from groupdocs.annotation import Annotator from groupdocs.annotation.models import Rectangle, PenStyle from groupdocs.annotation.models.annotation_models import PolylineAnnotation from groupdocs.pydrawing import Color def add_polyline_annotation(): with Annotator("./sample.pdf") as annotator: polyline = PolylineAnnotation() polyline.box = Rectangle(100, 100, 200, 100) polyline.svg_path = "M 0 0 L 50 50 L 100 0 L 150 50" polyline.pen_color = Color.purple.to_argb() polyline.pen_width = 2 polyline.pen_style = PenStyle.SOLID polyline.opacity = 0.9 polyline.page_number = 0 polyline.message = "This is a polyline annotation" annotator.add(polyline) annotator.save("./output.pdf") if __name__ == "__main__": add_polyline_annotation() ``` {{< /tab >}} {{< tab "sample.pdf" >}} {{< tab-text >}} `sample.pdf` is the sample file used in this example. Click [here](https://docs.groupdocs.com/annotation/python-net/_sample_files/developer-guide/basic-usage/add-annotations/sample.pdf) to download it. {{< /tab-text >}} {{< /tab >}} {{< tab "output.pdf" >}} ```text Binary file (PDF, 90 KB) ``` [Download full output](https://docs.groupdocs.com/annotation/python-net/_output_files/developer-guide/basic-usage/add-annotations/add_polyline_annotation/output.pdf) {{< /tab >}} {{< /tabs >}} ## Add a highlight annotation Highlight, underline, strikeout, and squiggly are text-markup annotations: they are positioned by the corner `Point` objects of the text region rather than a box. {{< tabs "code-example-add-highlight-annotation" >}} {{< tab "add_highlight_annotation.py" >}} ```python from groupdocs.annotation import Annotator from groupdocs.annotation.models import Point from groupdocs.annotation.models.annotation_models import HighlightAnnotation from groupdocs.pydrawing import Color def add_highlight_annotation(): with Annotator("./sample.pdf") as annotator: highlight = HighlightAnnotation() # Text-markup annotations are positioned by the corner points of the # text region: top-left, top-right, bottom-left, bottom-right highlight.points = [ Point(80, 600), Point(300, 600), Point(80, 575), Point(300, 575), ] highlight.background_color = Color.yellow.to_argb() highlight.opacity = 0.7 highlight.page_number = 0 highlight.message = "This is a highlight annotation" annotator.add(highlight) annotator.save("./output.pdf") if __name__ == "__main__": add_highlight_annotation() ``` {{< /tab >}} {{< tab "sample.pdf" >}} {{< tab-text >}} `sample.pdf` is the sample file used in this example. Click [here](https://docs.groupdocs.com/annotation/python-net/_sample_files/developer-guide/basic-usage/add-annotations/sample.pdf) to download it. {{< /tab-text >}} {{< /tab >}} {{< tab "output.pdf" >}} ```text Binary file (PDF, 90 KB) ``` [Download full output](https://docs.groupdocs.com/annotation/python-net/_output_files/developer-guide/basic-usage/add-annotations/add_highlight_annotation/output.pdf) {{< /tab >}} {{< /tabs >}} ## Add an underline annotation An underline annotation draws a line beneath the selected text region. {{< tabs "code-example-add-underline-annotation" >}} {{< tab "add_underline_annotation.py" >}} ```python from groupdocs.annotation import Annotator from groupdocs.annotation.models import Point from groupdocs.annotation.models.annotation_models import UnderlineAnnotation from groupdocs.pydrawing import Color def add_underline_annotation(): with Annotator("./sample.pdf") as annotator: underline = UnderlineAnnotation() underline.points = [ Point(80, 600), Point(300, 600), Point(80, 575), Point(300, 575), ] underline.underline_color = Color.red.to_argb() underline.opacity = 0.9 underline.page_number = 0 underline.message = "This is an underline annotation" annotator.add(underline) annotator.save("./output.pdf") if __name__ == "__main__": add_underline_annotation() ``` {{< /tab >}} {{< tab "sample.pdf" >}} {{< tab-text >}} `sample.pdf` is the sample file used in this example. Click [here](https://docs.groupdocs.com/annotation/python-net/_sample_files/developer-guide/basic-usage/add-annotations/sample.pdf) to download it. {{< /tab-text >}} {{< /tab >}} {{< tab "output.pdf" >}} ```text Binary file (PDF, 90 KB) ``` [Download full output](https://docs.groupdocs.com/annotation/python-net/_output_files/developer-guide/basic-usage/add-annotations/add_underline_annotation/output.pdf) {{< /tab >}} {{< /tabs >}} ## Add a strikeout annotation A strikeout annotation draws a line through the selected text region. {{< tabs "code-example-add-strikeout-annotation" >}} {{< tab "add_strikeout_annotation.py" >}} ```python from groupdocs.annotation import Annotator from groupdocs.annotation.models import Point from groupdocs.annotation.models.annotation_models import StrikeoutAnnotation from groupdocs.pydrawing import Color def add_strikeout_annotation(): with Annotator("./sample.pdf") as annotator: strikeout = StrikeoutAnnotation() strikeout.points = [ Point(80, 600), Point(300, 600), Point(80, 575), Point(300, 575), ] strikeout.font_color = Color.red.to_argb() strikeout.opacity = 0.9 strikeout.page_number = 0 strikeout.message = "This is a strikeout annotation" annotator.add(strikeout) annotator.save("./output.pdf") if __name__ == "__main__": add_strikeout_annotation() ``` {{< /tab >}} {{< tab "sample.pdf" >}} {{< tab-text >}} `sample.pdf` is the sample file used in this example. Click [here](https://docs.groupdocs.com/annotation/python-net/_sample_files/developer-guide/basic-usage/add-annotations/sample.pdf) to download it. {{< /tab-text >}} {{< /tab >}} {{< tab "output.pdf" >}} ```text Binary file (PDF, 90 KB) ``` [Download full output](https://docs.groupdocs.com/annotation/python-net/_output_files/developer-guide/basic-usage/add-annotations/add_strikeout_annotation/output.pdf) {{< /tab >}} {{< /tabs >}} ## Add a squiggly annotation A squiggly annotation draws a wavy line under the selected text region. {{< tabs "code-example-add-squiggly-annotation" >}} {{< tab "add_squiggly_annotation.py" >}} ```python from groupdocs.annotation import Annotator from groupdocs.annotation.models import Point from groupdocs.annotation.models.annotation_models import SquigglyAnnotation from groupdocs.pydrawing import Color def add_squiggly_annotation(): with Annotator("./sample.pdf") as annotator: squiggly = SquigglyAnnotation() squiggly.points = [ Point(80, 600), Point(300, 600), Point(80, 575), Point(300, 575), ] squiggly.squiggly_color = Color.red.to_argb() squiggly.opacity = 0.9 squiggly.page_number = 0 squiggly.message = "This is a squiggly annotation" annotator.add(squiggly) annotator.save("./output.pdf") if __name__ == "__main__": add_squiggly_annotation() ``` {{< /tab >}} {{< tab "sample.pdf" >}} {{< tab-text >}} `sample.pdf` is the sample file used in this example. Click [here](https://docs.groupdocs.com/annotation/python-net/_sample_files/developer-guide/basic-usage/add-annotations/sample.pdf) to download it. {{< /tab-text >}} {{< /tab >}} {{< tab "output.pdf" >}} ```text Binary file (PDF, 90 KB) ``` [Download full output](https://docs.groupdocs.com/annotation/python-net/_output_files/developer-guide/basic-usage/add-annotations/add_squiggly_annotation/output.pdf) {{< /tab >}} {{< /tabs >}} ## Add a text field annotation A text field annotation places editable text inside a box, with font and alignment control. {{< tabs "code-example-add-text-field-annotation" >}} {{< tab "add_text_field_annotation.py" >}} ```python from groupdocs.annotation import Annotator from groupdocs.annotation.models import Rectangle, HorizontalAlignment from groupdocs.annotation.models.annotation_models import TextFieldAnnotation from groupdocs.pydrawing import Color def add_text_field_annotation(): with Annotator("./sample.pdf") as annotator: text_field = TextFieldAnnotation() text_field.box = Rectangle(100, 100, 150, 50) text_field.text = "Some text in a field" text_field.font_family = "Arial" text_field.font_size = 12.0 text_field.font_color = Color.black.to_argb() text_field.background_color = Color.yellow.to_argb() text_field.text_horizontal_alignment = HorizontalAlignment.CENTER text_field.opacity = 0.9 text_field.page_number = 0 text_field.message = "This is a text field annotation" annotator.add(text_field) annotator.save("./output.pdf") if __name__ == "__main__": add_text_field_annotation() ``` {{< /tab >}} {{< tab "sample.pdf" >}} {{< tab-text >}} `sample.pdf` is the sample file used in this example. Click [here](https://docs.groupdocs.com/annotation/python-net/_sample_files/developer-guide/basic-usage/add-annotations/sample.pdf) to download it. {{< /tab-text >}} {{< /tab >}} {{< tab "output.pdf" >}} ```text Binary file (PDF, 90 KB) ``` [Download full output](https://docs.groupdocs.com/annotation/python-net/_output_files/developer-guide/basic-usage/add-annotations/add_text_field_annotation/output.pdf) {{< /tab >}} {{< /tabs >}} ## Add a text replacement annotation A text replacement annotation marks a text region and provides replacement text. {{< tabs "code-example-add-replacement-annotation" >}} {{< tab "add_replacement_annotation.py" >}} ```python from groupdocs.annotation import Annotator from groupdocs.annotation.models import Point from groupdocs.annotation.models.annotation_models import ReplacementAnnotation from groupdocs.pydrawing import Color def add_replacement_annotation(): with Annotator("./sample.pdf") as annotator: replacement = ReplacementAnnotation() replacement.points = [ Point(80, 600), Point(300, 600), Point(80, 575), Point(300, 575), ] replacement.text_to_replace = "replacement text" replacement.font_color = Color.black.to_argb() replacement.font_size = 12.0 replacement.opacity = 0.9 replacement.page_number = 0 replacement.message = "This is a text replacement annotation" annotator.add(replacement) annotator.save("./output.pdf") if __name__ == "__main__": add_replacement_annotation() ``` {{< /tab >}} {{< tab "sample.pdf" >}} {{< tab-text >}} `sample.pdf` is the sample file used in this example. Click [here](https://docs.groupdocs.com/annotation/python-net/_sample_files/developer-guide/basic-usage/add-annotations/sample.pdf) to download it. {{< /tab-text >}} {{< /tab >}} {{< tab "output.pdf" >}} ```text Binary file (PDF, 90 KB) ``` [Download full output](https://docs.groupdocs.com/annotation/python-net/_output_files/developer-guide/basic-usage/add-annotations/add_replacement_annotation/output.pdf) {{< /tab >}} {{< /tabs >}} ## Add a text redaction annotation A text redaction annotation hides a text region behind a solid block. {{< tabs "code-example-add-text-redaction-annotation" >}} {{< tab "add_text_redaction_annotation.py" >}} ```python from groupdocs.annotation import Annotator from groupdocs.annotation.models import Point from groupdocs.annotation.models.annotation_models import TextRedactionAnnotation from groupdocs.pydrawing import Color def add_text_redaction_annotation(): with Annotator("./sample.pdf") as annotator: redaction = TextRedactionAnnotation() redaction.points = [ Point(80, 600), Point(300, 600), Point(80, 575), Point(300, 575), ] redaction.font_color = Color.black.to_argb() redaction.page_number = 0 redaction.message = "This is a text redaction annotation" annotator.add(redaction) annotator.save("./output.pdf") if __name__ == "__main__": add_text_redaction_annotation() ``` {{< /tab >}} {{< tab "sample.pdf" >}} {{< tab-text >}} `sample.pdf` is the sample file used in this example. Click [here](https://docs.groupdocs.com/annotation/python-net/_sample_files/developer-guide/basic-usage/add-annotations/sample.pdf) to download it. {{< /tab-text >}} {{< /tab >}} {{< tab "output.pdf" >}} ```text Binary file (PDF, 90 KB) ``` [Download full output](https://docs.groupdocs.com/annotation/python-net/_output_files/developer-guide/basic-usage/add-annotations/add_text_redaction_annotation/output.pdf) {{< /tab >}} {{< /tabs >}} ## Add a resources redaction annotation A resources redaction annotation blacks out a rectangular region, removing the underlying page resources. {{< tabs "code-example-add-resources-redaction-annotation" >}} {{< tab "add_resources_redaction_annotation.py" >}} ```python from groupdocs.annotation import Annotator from groupdocs.annotation.models import Rectangle from groupdocs.annotation.models.annotation_models import ResourcesRedactionAnnotation def add_resources_redaction_annotation(): with Annotator("./sample.pdf") as annotator: redaction = ResourcesRedactionAnnotation() redaction.box = Rectangle(100, 100, 200, 80) redaction.page_number = 0 redaction.message = "This is a resources redaction annotation" annotator.add(redaction) annotator.save("./output.pdf") if __name__ == "__main__": add_resources_redaction_annotation() ``` {{< /tab >}} {{< tab "sample.pdf" >}} {{< tab-text >}} `sample.pdf` is the sample file used in this example. Click [here](https://docs.groupdocs.com/annotation/python-net/_sample_files/developer-guide/basic-usage/add-annotations/sample.pdf) to download it. {{< /tab-text >}} {{< /tab >}} {{< tab "output.pdf" >}} ```text Binary file (PDF, 90 KB) ``` [Download full output](https://docs.groupdocs.com/annotation/python-net/_output_files/developer-guide/basic-usage/add-annotations/add_resources_redaction_annotation/output.pdf) {{< /tab >}} {{< /tabs >}} ## Add a watermark annotation A watermark annotation places rotated, scalable text over the page. {{< tabs "code-example-add-watermark-annotation" >}} {{< tab "add_watermark_annotation.py" >}} ```python from groupdocs.annotation import Annotator from groupdocs.annotation.models import Rectangle, HorizontalAlignment, VerticalAlignment from groupdocs.annotation.models.annotation_models import WatermarkAnnotation from groupdocs.pydrawing import Color def add_watermark_annotation(): with Annotator("./sample.pdf") as annotator: watermark = WatermarkAnnotation() watermark.box = Rectangle(100, 100, 200, 100) watermark.text = "Watermark" watermark.font_family = "Arial" watermark.font_size = 24.0 watermark.font_color = Color.red.to_argb() watermark.angle = 45.0 watermark.auto_scale = True watermark.horizontal_alignment = HorizontalAlignment.CENTER watermark.vertical_alignment = VerticalAlignment.CENTER watermark.opacity = 0.5 watermark.page_number = 0 watermark.message = "This is a watermark annotation" annotator.add(watermark) annotator.save("./output.pdf") if __name__ == "__main__": add_watermark_annotation() ``` {{< /tab >}} {{< tab "sample.pdf" >}} {{< tab-text >}} `sample.pdf` is the sample file used in this example. Click [here](https://docs.groupdocs.com/annotation/python-net/_sample_files/developer-guide/basic-usage/add-annotations/sample.pdf) to download it. {{< /tab-text >}} {{< /tab >}} {{< tab "output.pdf" >}} ```text Binary file (PDF, 90 KB) ``` [Download full output](https://docs.groupdocs.com/annotation/python-net/_output_files/developer-guide/basic-usage/add-annotations/add_watermark_annotation/output.pdf) {{< /tab >}} {{< /tabs >}} ## Add a link annotation A link annotation turns a text region into a clickable hyperlink. {{< tabs "code-example-add-link-annotation" >}} {{< tab "add_link_annotation.py" >}} ```python from groupdocs.annotation import Annotator from groupdocs.annotation.models import Point from groupdocs.annotation.models.annotation_models import LinkAnnotation from groupdocs.pydrawing import Color def add_link_annotation(): with Annotator("./sample.pdf") as annotator: link = LinkAnnotation() link.points = [ Point(80, 600), Point(300, 600), Point(80, 575), Point(300, 575), ] link.url = "https://www.groupdocs.com" link.background_color = Color.azure.to_argb() link.opacity = 0.7 link.page_number = 0 link.message = "This is a link annotation" annotator.add(link) annotator.save("./output.pdf") if __name__ == "__main__": add_link_annotation() ``` {{< /tab >}} {{< tab "sample.pdf" >}} {{< tab-text >}} `sample.pdf` is the sample file used in this example. Click [here](https://docs.groupdocs.com/annotation/python-net/_sample_files/developer-guide/basic-usage/add-annotations/sample.pdf) to download it. {{< /tab-text >}} {{< /tab >}} {{< tab "output.pdf" >}} ```text Binary file (PDF, 90 KB) ``` [Download full output](https://docs.groupdocs.com/annotation/python-net/_output_files/developer-guide/basic-usage/add-annotations/add_link_annotation/output.pdf) {{< /tab >}} {{< /tabs >}} ## Add an image annotation An image annotation stamps a picture from disk onto the page. Set `image_path` to a local image file. {{< tabs "code-example-add-image-annotation" >}} {{< tab "add_image_annotation.py" >}} ```python from groupdocs.annotation import Annotator from groupdocs.annotation.models import Rectangle from groupdocs.annotation.models.annotation_models import ImageAnnotation def add_image_annotation(): with Annotator("./sample.pdf") as annotator: image = ImageAnnotation() image.box = Rectangle(100, 100, 100, 100) image.image_path = "./stamp.png" image.opacity = 0.9 image.angle = 0.0 image.page_number = 0 image.message = "This is an image annotation" annotator.add(image) annotator.save("./output.pdf") if __name__ == "__main__": add_image_annotation() ``` {{< /tab >}} {{< tab "sample.pdf" >}} {{< tab-text >}} `sample.pdf` is the document used in this example. Click [here](https://docs.groupdocs.com/annotation/python-net/_sample_files/developer-guide/basic-usage/add-annotations/sample.pdf) to download it. {{< /tab-text >}} {{< /tab >}} {{< tab "stamp.png" >}} {{< tab-text >}} `stamp.png` is the image stamped onto the page. Click [here](https://docs.groupdocs.com/annotation/python-net/_sample_files/developer-guide/basic-usage/add-annotations/stamp.png) to download it. {{< /tab-text >}} {{< /tab >}} {{< tab "output.pdf" >}} ```text Binary file (PDF, 95 KB) ``` [Download full output](https://docs.groupdocs.com/annotation/python-net/_output_files/developer-guide/basic-usage/add-annotations/add_image_annotation/output.pdf) {{< /tab >}} {{< /tabs >}} --- ## Features Overview Path: https://docs.groupdocs.com/annotation/python-net/features-overview/ GroupDocs.Annotation for Python via .NET adds annotations, markup, and review comments across a wide range of [supported document formats](https://docs.groupdocs.com/annotation/python-net/supported-document-formats/). Every annotation follows the same workflow: open a document with `Annotator`, add one or more annotations with `add()`, then `save()` the result — back to the original format or to another supported format. The capabilities below can be combined freely in a single pass. ## Text markup Mark up the text of a document with highlight, underline, strikeout, and squiggly-underline annotations, or replace and redact text. Text-markup annotations are positioned with a list of `Point` objects that describe the region they cover, and you can set colors such as `font_color`, `underline_color`, and `squiggly_color` (all passed as packed ARGB integers). See [Add Annotations](https://docs.groupdocs.com/annotation/python-net/developer-guide/basic-usage/add-annotations/). ## Graphic (shape) annotations Draw geometric shapes over a page — area (rectangle), ellipse, point, arrow, distance, and polyline. Box-style shapes use a `Rectangle` (`box`) for position and size, while line-based shapes use `Point` coordinates. You can configure fill and pen colors (as ARGB integers) and the pen style. See [Add Annotations](https://docs.groupdocs.com/annotation/python-net/developer-guide/basic-usage/add-annotations/). ## Watermarks, text fields, links, and image stamps Add a `WatermarkAnnotation` over a page, an editable `TextFieldAnnotation`, a clickable `LinkAnnotation`, or stamp an `ImageAnnotation` onto the document. These rich annotations let you label, brand, or augment a document without altering its underlying content. See [Add Annotations](https://docs.groupdocs.com/annotation/python-net/developer-guide/basic-usage/add-annotations/). ## Comments and replies Attach an author and threaded review comments to any annotation. Each annotation carries a `user` (`User` with a `Role` of viewer or editor) and a list of `replies` (`Reply` objects with a comment, an author, and a timestamp), so you can model a full review conversation. See [Comments and Replies](https://docs.groupdocs.com/annotation/python-net/developer-guide/basic-usage/comments-and-replies/). ## Managing annotations Add, update, get, and remove annotations. Retrieve all annotations, or filter by type with `get(type=AnnotationType.X)`; remove a single annotation by id or instance, or clear them all. See [Get Annotations](https://docs.groupdocs.com/annotation/python-net/developer-guide/basic-usage/get-annotations/) and [Remove Annotations](https://docs.groupdocs.com/annotation/python-net/developer-guide/basic-usage/remove-annotations/). ## Document information Inspect a document before annotating it — read the page count, file type, page dimensions, and size through `get_document_info()`, and list every format the API supports with `FileType.get_supported_file_types()`. See [Get Document Info](https://docs.groupdocs.com/annotation/python-net/developer-guide/basic-usage/get-document-info/) and [Supported File Formats](https://docs.groupdocs.com/annotation/python-net/developer-guide/basic-usage/get-supported-file-formats/). ## Loading and saving Load documents from a local path or from a stream, including password-protected files, by passing `LoadOptions`. When saving, write back to the original format, save to a different path, restrict the output to specific pages with `SaveOptions.first_page`/`last_page` (1-based), or save only certain annotation types. See [Loading Documents](https://docs.groupdocs.com/annotation/python-net/developer-guide/advanced-usage/loading-documents/) and [Saving Documents](https://docs.groupdocs.com/annotation/python-net/developer-guide/advanced-usage/saving-documents/). ## On-premise GroupDocs.Annotation for Python via .NET runs entirely on your own infrastructure — your documents never leave your environment. The package is a self-contained wheel that bundles everything it needs, so no Microsoft Office, OpenOffice, Adobe Acrobat, or separate runtime has to be installed. See [System Requirements](https://docs.groupdocs.com/annotation/python-net/system-requirements/) for the supported platforms and native dependencies. --- ## Install GroupDocs.Annotation for Python via .NET Path: https://docs.groupdocs.com/annotation/python-net/installation/ GroupDocs.Annotation for Python via .NET is distributed as a pre-built wheel on [PyPI](https://pypi.org/project/groupdocs-annotation-net/). The PyPI index hosts a separate wheel for each supported platform, and `pip` picks the correct one automatically. Each wheel is self-contained: it bundles the embedded runtime and every managed dependency, so no Microsoft Office, OpenOffice, Adobe Acrobat, or separate runtime install is required. Before installing, confirm your environment matches the supported platforms and Python versions listed in the [System Requirements](https://docs.groupdocs.com/annotation/python-net/system-requirements/) topic. ## Install Package from PyPI Open a terminal and run the install command for your platform: {{< tabs "install-pypi">}} {{< tab "Windows" >}} ```ps py -m pip install groupdocs-annotation-net ``` {{< /tab >}} {{< tab "Linux" >}} ```bash python3 -m pip install groupdocs-annotation-net ``` {{< /tab >}} {{< tab "macOS" >}} ```bash python3 -m pip install groupdocs-annotation-net ``` {{< /tab >}} {{< /tabs >}} After running the command you should see output similar to: ```bash Collecting groupdocs-annotation-net Downloading groupdocs_annotation_net-26.6.0-py3-none-win_amd64.whl.metadata (3.0 kB) Downloading groupdocs_annotation_net-26.6.0-py3-none-win_amd64.whl (40.0 MB) ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 40.0/40.0 MB 2.8 MB/s eta 0:00:00 Installing collected packages: groupdocs-annotation-net Successfully installed groupdocs-annotation-net-26.6.0 ``` The wheel file name will include a platform suffix that matches your operating system — for example `manylinux1_x86_64` on Ubuntu/Debian, `macosx_11_0_arm64` on Apple Silicon, or `win_amd64` on 64-bit Windows. ## Add the Package to `requirements.txt` For reproducible environments, pin the package version in your `requirements.txt`: ```txt groupdocs-annotation-net==26.6.0 ``` Then install all dependencies in one step: ```bash pip install -r requirements.txt ``` ## Install from a Pre-Downloaded Wheel If your build environment cannot reach PyPI, download the appropriate wheel from the [GroupDocs Releases website](https://releases.groupdocs.com/annotation/python-net/) and install it locally. Wheels are published for the following four platforms: - **Windows 64-bit**: file name ends with `win_amd64.whl` - **Linux x64 (glibc)**: file name ends with `manylinux1_x86_64.whl` - **macOS Intel**: file name ends with `macosx_10_14_x86_64.whl` - **macOS Apple Silicon**: file name ends with `macosx_11_0_arm64.whl` Place the downloaded wheel into your project folder, then install it: {{< tabs "install-wheel">}} {{< tab "Windows (64-bit)" >}} ```ps py -m pip install ./groupdocs_annotation_net-26.6.0-py3-none-win_amd64.whl ``` {{< /tab >}} {{< tab "Linux (glibc)" >}} ```bash python3 -m pip install ./groupdocs_annotation_net-26.6.0-py3-none-manylinux1_x86_64.whl ``` {{< /tab >}} {{< tab "macOS (Intel)" >}} ```bash python3 -m pip install ./groupdocs_annotation_net-26.6.0-py3-none-macosx_10_14_x86_64.whl ``` {{< /tab >}} {{< tab "macOS (Apple Silicon)" >}} ```bash python3 -m pip install ./groupdocs_annotation_net-26.6.0-py3-none-macosx_11_0_arm64.whl ``` {{< /tab >}} {{< /tabs >}} Expected output: ```bash Processing ./groupdocs_annotation_net-26.6.0-py3-none-*.whl Installing collected packages: groupdocs-annotation-net Successfully installed groupdocs-annotation-net-26.6.0 ``` ## Platform Prerequisites On Windows no extra steps are required. On Linux and macOS, install the native libraries the rendering engine depends on: {{< tabs "platform-prereqs">}} {{< tab "Linux" >}} ```bash apt install libgdiplus libfontconfig1 libicu-dev ttf-mscorefonts-installer ``` {{< /tab >}} {{< tab "macOS" >}} ```bash brew install mono-libgdiplus ``` {{< /tab >}} {{< /tabs >}} {{< alert style="info" >}} The package runs on Windows, Linux, and macOS. On Linux and macOS the native libraries above provide graphics, fonts, and globalization support for the bundled engine; installing fonts (for example `ttf-mscorefonts-installer`) helps annotated output match the original document. See [System Requirements](https://docs.groupdocs.com/annotation/python-net/system-requirements/) for the full list. {{< /alert >}} ## Verify the Installation Confirm the package imported correctly: ```bash python -c "import groupdocs.annotation; print('GroupDocs.Annotation is ready')" ``` You can also list the installed package with `pip show groupdocs-annotation-net` to confirm the version and location. ## Next Steps - Follow the [Hello, World!](https://docs.groupdocs.com/annotation/python-net/hello-world/) guide to add your first annotation. - Read the [Features Overview](https://docs.groupdocs.com/annotation/python-net/features-overview/) to see everything you can annotate. - Clone the [examples repository](https://github.com/groupdocs-annotation/GroupDocs.Annotation-for-Python-via-.NET) and read [How to Run Examples](https://docs.groupdocs.com/annotation/python-net/how-to-run-examples/) to try every documented scenario locally. --- ## Loading documents Path: https://docs.groupdocs.com/annotation/python-net/loading-documents/ The [`Annotator`](https://reference.groupdocs.com/annotation/python-net/groupdocs.annotation/annotator/) class can open a document from a file path or any readable binary stream, including password-protected files. The examples below show each loading scenario. ## Load from local disk When the document is on the local disk, pass its path to the `Annotator` constructor. {{< tabs "code-example-load-from-local-disk" >}} {{< tab "load_from_local_disk.py" >}} ```python from groupdocs.annotation import Annotator from groupdocs.annotation.models import Rectangle from groupdocs.annotation.models.annotation_models import AreaAnnotation from groupdocs.pydrawing import Color def load_from_local_disk(): # Load a document directly from a local file path with Annotator("./sample.pdf") as annotator: area = AreaAnnotation() area.box = Rectangle(100, 100, 100, 100) area.background_color = Color.yellow.to_argb() area.page_number = 0 area.message = "Loaded from local disk" annotator.add(area) annotator.save("./output.pdf") if __name__ == "__main__": load_from_local_disk() ``` {{< /tab >}} {{< tab "sample.pdf" >}} {{< tab-text >}} `sample.pdf` is the sample file used in this example. Click [here](https://docs.groupdocs.com/annotation/python-net/_sample_files/developer-guide/advanced-usage/loading-documents/sample.pdf) to download it. {{< /tab-text >}} {{< /tab >}} {{< tab "output.pdf" >}} ```text Binary file (PDF, 90 KB) ``` [Download full output](https://docs.groupdocs.com/annotation/python-net/_output_files/developer-guide/advanced-usage/loading-documents/load_from_local_disk/output.pdf) {{< /tab >}} {{< /tabs >}} ## Load from stream As an alternative to a local file, pass an open binary stream to the `document` parameter of the `Annotator` constructor. {{< tabs "code-example-load-from-stream" >}} {{< tab "load_from_stream.py" >}} ```python from groupdocs.annotation import Annotator from groupdocs.annotation.models import Rectangle from groupdocs.annotation.models.annotation_models import AreaAnnotation from groupdocs.pydrawing import Color def load_from_stream(): # Load a document from an open binary stream with open("./sample.pdf", "rb") as stream: with Annotator(document=stream) as annotator: area = AreaAnnotation() area.box = Rectangle(100, 100, 100, 100) area.background_color = Color.yellow.to_argb() area.page_number = 0 area.message = "Loaded from stream" annotator.add(area) annotator.save("./output.pdf") if __name__ == "__main__": load_from_stream() ``` {{< /tab >}} {{< tab "sample.pdf" >}} {{< tab-text >}} `sample.pdf` is the sample file used in this example. Click [here](https://docs.groupdocs.com/annotation/python-net/_sample_files/developer-guide/advanced-usage/loading-documents/sample.pdf) to download it. {{< /tab-text >}} {{< /tab >}} {{< tab "output.pdf" >}} ```text Binary file (PDF, 90 KB) ``` [Download full output](https://docs.groupdocs.com/annotation/python-net/_output_files/developer-guide/advanced-usage/loading-documents/load_from_stream/output.pdf) {{< /tab >}} {{< /tabs >}} ## Load a password-protected file To open an encrypted document, set the `password` property of [`LoadOptions`](https://reference.groupdocs.com/annotation/python-net/groupdocs.annotation.options/loadoptions/) and pass it to the `Annotator` through the `load_options` parameter. {{< tabs "code-example-load-password-protected-document" >}} {{< tab "load_password_protected_document.py" >}} ```python from groupdocs.annotation import Annotator from groupdocs.annotation.options import LoadOptions from groupdocs.annotation.models import Rectangle from groupdocs.annotation.models.annotation_models import AreaAnnotation from groupdocs.pydrawing import Color def load_password_protected_document(): # Provide the password through LoadOptions load_options = LoadOptions() load_options.password = "secret" with Annotator("./protected.docx", load_options=load_options) as annotator: area = AreaAnnotation() area.box = Rectangle(100, 100, 100, 100) area.background_color = Color.yellow.to_argb() area.page_number = 0 area.message = "Annotated a password-protected document" annotator.add(area) annotator.save("./output.docx") if __name__ == "__main__": load_password_protected_document() ``` {{< /tab >}} {{< tab "protected.docx" >}} {{< tab-text >}} `protected.docx` is the sample file used in this example. Click [here](https://docs.groupdocs.com/annotation/python-net/_sample_files/developer-guide/advanced-usage/loading-documents/protected.docx) to download it. {{< /tab-text >}} {{< /tab >}} {{< tab "output.docx" >}} ```text Binary file (DOCX, 10 KB) ``` [Download full output](https://docs.groupdocs.com/annotation/python-net/_output_files/developer-guide/advanced-usage/loading-documents/load_password_protected_document/output.docx) {{< /tab >}} {{< /tabs >}} --- ## GroupDocs.Annotation for Python via .NET Overview Path: https://docs.groupdocs.com/annotation/python-net/product-overview/ ## What is GroupDocs.Annotation? GroupDocs.Annotation for Python via .NET is a native Python library that **adds, edits, and removes annotations and markup** on documents — across PDF, Microsoft Word, Excel, PowerPoint, images, CAD, Visio, and email formats — through a single, format-independent API. It runs entirely on-premise, requires no Microsoft Office or Adobe Acrobat installation, and ships as a pre-built wheel on Windows, Linux, and macOS. Typical uses include: - **Document review & collaboration** — add area, shape, and text-markup annotations and attach threaded reviewer comments so teams can discuss a document in place. - **Legal & contract markup** — highlight clauses, strike out obsolete text, and flag regions that need attention before a document is signed or shared. - **Engineering & design review** — annotate CAD drawings and Visio diagrams with area, arrow, and distance markups. - **Content & e-learning feedback** — mark up images and scanned pages with points, watermarks, and image stamps. - **Automated annotation pipelines** — stamp watermarks, links, and notes across many documents and save only the annotation types or page ranges you need. ## Key Capabilities | Capability | Description | |---|---| | **Shape Annotations** | Draw area, ellipse, arrow, point, distance, and polyline annotations with configurable color and opacity. | | **Text Markup** | Highlight, underline, strikeout, and squiggly-mark text, plus replacement, text-redaction, and resources-redaction annotations. | | **Content Annotations** | Stamp watermarks, image annotations, hyperlinks, and editable text fields onto a document. | | **Comments & Replies** | Attach threaded review comments — with user and timestamp — to any annotation. | | **Manage Annotations** | List, update, and remove annotations, all of them or filtered by annotation type. | | **Save Filters** | Render only selected annotation types, or a specific page range, when saving the result. | | **Document Inspection** | Read file type, page count, and size without modifying the document. | Every capability is covered with runnable, copy-paste examples in the [Developer Guide](https://docs.groupdocs.com/annotation/python-net/developer-guide/). ## Quick Example Add an annotation and save the result with just a few lines of code. The example draws a yellow area annotation on the first page of `sample.pdf` and writes the result to `annotated.pdf`: {{< tabs "product-overview-add-area" >}} {{< tab "annotate_area.py" >}} ```python from groupdocs.annotation import Annotator from groupdocs.annotation.models import Rectangle from groupdocs.annotation.models.annotation_models import AreaAnnotation from groupdocs.pydrawing import Color def annotate_area(): # Open the document with Annotator("./sample.pdf") as annotator: # Build an area annotation on the first page area = AreaAnnotation() area.box = Rectangle(100, 100, 200, 80) # x, y, width, height area.page_number = 0 # 0-based page index area.background_color = Color.yellow.to_argb() # ARGB int, not a Color object area.message = "Review this section" annotator.add(area) # Save the annotated document annotator.save("./annotated.pdf") if __name__ == "__main__": annotate_area() ``` {{< /tab >}} {{< tab "sample.pdf" >}} {{< tab-text >}} `sample.pdf` is the sample file used in this example. Click [here](https://docs.groupdocs.com/annotation/python-net/_sample_files/product-overview/sample.pdf) to download it. {{< /tab-text >}} {{< /tab >}} {{< tab "annotated.pdf" >}} ```text Binary file (PDF, 1.0 MB) ``` [Download full output](https://docs.groupdocs.com/annotation/python-net/_output_files/product-overview/annotate_area/annotated.pdf) {{< /tab >}} {{< /tabs >}} For richer review workflows, add several annotations, attach a comment thread, and save only the area annotations with a page-range filter using `SaveOptions`: {{< tabs "product-overview-add-options" >}} {{< tab "annotate_with_options.py" >}} ```python from groupdocs.annotation import Annotator from groupdocs.annotation.models import Rectangle, Point, Reply from groupdocs.annotation.models.annotation_models import AreaAnnotation, HighlightAnnotation from groupdocs.annotation.options import SaveOptions, AnnotationType from groupdocs.pydrawing import Color def annotate_with_options(): with Annotator("./sample.pdf") as annotator: # An area annotation carrying a threaded review comment area = AreaAnnotation() area.box = Rectangle(100, 100, 200, 80) area.page_number = 0 area.background_color = Color.yellow.to_argb() area.message = "Please review" reply = Reply() reply.comment = "Confirmed, looks good" area.replies = [reply] # A text highlight described by a quad of points highlight = HighlightAnnotation() highlight.page_number = 0 highlight.font_color = Color.lime.to_argb() highlight.points = [Point(80, 730), Point(240, 730), Point(240, 750), Point(80, 750)] annotator.add([area, highlight]) # Render only area annotations, and only the first page options = SaveOptions() options.annotation_types = AnnotationType.AREA options.first_page = 1 # SaveOptions pages are 1-based options.last_page = 1 annotator.save("./annotated.pdf", save_options=options) if __name__ == "__main__": annotate_with_options() ``` {{< /tab >}} {{< tab "sample.pdf" >}} {{< tab-text >}} `sample.pdf` is the sample file used in this example. Click [here](https://docs.groupdocs.com/annotation/python-net/_sample_files/product-overview/sample.pdf) to download it. {{< /tab-text >}} {{< /tab >}} {{< tab "annotated.pdf" >}} ```text Binary file (PDF, 1.0 MB) ``` [Download full output](https://docs.groupdocs.com/annotation/python-net/_output_files/product-overview/annotate_with_options/annotated.pdf) {{< /tab >}} {{< /tabs >}} ## Where to next 1. **Install the package** — [Installation](https://docs.groupdocs.com/annotation/python-net/getting-started/installation/) walks through PyPI and offline wheel installation for Windows, Linux, and macOS. 2. **Run your first annotation** — [Hello, World!](https://docs.groupdocs.com/annotation/python-net/getting-started/hello-world/) annotates a document in under five minutes. 3. **Explore runnable examples** — [How to Run Examples](https://docs.groupdocs.com/annotation/python-net/getting-started/how-to-run-examples/) clones the GitHub repository and runs every documented scenario locally or in Docker. 4. **Use it in depth** — the [Developer Guide](https://docs.groupdocs.com/annotation/python-net/developer-guide/) covers every API surface with runnable, copy-paste code examples. 5. **Plug it into AI pipelines** — [AI Agents & LLM Integration]({{< ref "annotation/python-net/agents-and-llm-integration.md" >}}) explains the bundled `AGENTS.md`, the MCP server, and machine-readable docs. --- ## Hello, World! Path: https://docs.groupdocs.com/annotation/python-net/hello-world/ ## Introduction A "Hello, World!" example is often the first step when exploring GroupDocs.Annotation for Python via .NET. It serves as a simple test to confirm that your development environment is correctly set up and that the library is functioning as expected. ## Overview GroupDocs.Annotation for Python via .NET lets you add annotations, markup, and review comments to a wide range of document and image formats. A wide range of [supported formats](https://docs.groupdocs.com/annotation/python-net/supported-document-formats/) makes it versatile for different use cases. ## How to annotate a document The following steps demonstrate how to add an annotation to a document using GroupDocs.Annotation for Python via .NET: 1. Import the `groupdocs.annotation` classes you need. 2. Create an annotation and configure it (here, an area annotation with a yellow fill). 3. Open the document with an `Annotator`, pointing it at the sample file. 4. Add the annotation. 5. Save the result. ## Complete example The example below adds a yellow area (rectangle) annotation to the first page of a PDF and saves the annotated document: {{< tabs "code-example-hello-world" >}} {{< tab "hello_world.py" >}} ```python from groupdocs.annotation import Annotator from groupdocs.annotation.models import Rectangle from groupdocs.annotation.models.annotation_models import AreaAnnotation from groupdocs.pydrawing import Color def hello_world(): # Describe a yellow area (rectangle) annotation on the first page area = AreaAnnotation() area.box = Rectangle(100, 100, 100, 100) # x, y, width, height area.background_color = Color.yellow.to_argb() # packed ARGB int, not a Color object area.page_number = 0 # page numbers are 0-based area.message = "Welcome to GroupDocs.Annotation!" # Open the document, add the annotation, and save the result with Annotator("./sample.pdf") as annotator: annotator.add(area) annotator.save("./output.pdf") print("Annotation added successfully. Output saved to ./output.pdf.") if __name__ == "__main__": hello_world() ``` {{< /tab >}} {{< tab "sample.pdf" >}} {{< tab-text >}} `sample.pdf` is the sample file used in this example. Click [here](https://docs.groupdocs.com/annotation/python-net/_sample_files/getting-started/hello-world/sample.pdf) to download it. {{< /tab-text >}} {{< /tab >}} {{< tab "output.pdf" >}} ```text Binary file (PDF) ``` [Download full output](https://docs.groupdocs.com/annotation/python-net/_output_files/getting-started/hello-world/hello_world/output.pdf) {{< /tab >}} {{< /tabs >}} This example writes the annotated document to `output.pdf`. Colors are passed as packed ARGB integers (for example `Color.yellow.to_argb()`), not as `Color` objects, and page numbers are 0-based, so `page_number = 0` targets the first page. To annotate a different format, simply open a file with another extension — the same code works across every [supported format](https://docs.groupdocs.com/annotation/python-net/supported-document-formats/). ## Additional resources This demo references the GroupDocs.Annotation for Python via .NET [code samples](https://github.com/groupdocs-annotation/GroupDocs.Annotation-for-Python-via-.NET/). --- ## Manage annotations Path: https://docs.groupdocs.com/annotation/python-net/manage-annotations/ Once a document contains annotations you can read them, remove a single one by its id, or remove them all. The [`Annotator`](https://reference.groupdocs.com/annotation/python-net/groupdocs.annotation/annotator/) class exposes `get()` to list annotations and `remove(...)` to delete them. ## Get all annotations Call `get()` to return the list of annotations stored in the document. Each item exposes common properties such as `id`, `message`, `page_number`, and `replies`. {{< tabs "code-example-get-all-annotations" >}} {{< tab "get_all_annotations.py" >}} ```python from groupdocs.annotation import Annotator def get_all_annotations(): # Open a document that already contains annotations and list them with Annotator("./annotated.pdf") as annotator: annotations = annotator.get() print(f"Found {len(annotations)} annotation(s):") for annotation in annotations: print(f" [{annotation.id}] {type(annotation).__name__}: {annotation.message}") if __name__ == "__main__": get_all_annotations() ``` {{< /tab >}} {{< tab "annotated.pdf" >}} {{< tab-text >}} `annotated.pdf` is the sample file used in this example. Click [here](https://docs.groupdocs.com/annotation/python-net/_sample_files/developer-guide/basic-usage/manage-annotations/annotated.pdf) to download it. {{< /tab-text >}} {{< /tab >}} {{< /tabs >}} ## Remove an annotation by id Pass the `annotation_id` of the annotation you want to delete to `remove(...)`, then save the document. {{< tabs "code-example-remove-annotation-by-id" >}} {{< tab "remove_annotation_by_id.py" >}} ```python from groupdocs.annotation import Annotator def remove_annotation_by_id(): # Open an annotated document and remove a single annotation by its id with Annotator("./annotated.pdf") as annotator: annotations = annotator.get() if annotations: annotator.remove(annotation_id=annotations[0].id) annotator.save("./output.pdf") if __name__ == "__main__": remove_annotation_by_id() ``` {{< /tab >}} {{< tab "annotated.pdf" >}} {{< tab-text >}} `annotated.pdf` is the sample file used in this example. Click [here](https://docs.groupdocs.com/annotation/python-net/_sample_files/developer-guide/basic-usage/manage-annotations/annotated.pdf) to download it. {{< /tab-text >}} {{< /tab >}} {{< tab "output.pdf" >}} ```text Binary file (PDF, 90 KB) ``` [Download full output](https://docs.groupdocs.com/annotation/python-net/_output_files/developer-guide/basic-usage/manage-annotations/remove_annotation_by_id/output.pdf) {{< /tab >}} {{< /tabs >}} ## Remove all annotations To clear every annotation, read the list with `get()` and pass it to `remove(...)` through the `annotations_to_delete` parameter. {{< tabs "code-example-remove-all-annotations" >}} {{< tab "remove_all_annotations.py" >}} ```python from groupdocs.annotation import Annotator def remove_all_annotations(): # Open an annotated document and remove every annotation with Annotator("./annotated.pdf") as annotator: annotations = annotator.get() if annotations: annotator.remove(annotations_to_delete=annotations) annotator.save("./output.pdf") if __name__ == "__main__": remove_all_annotations() ``` {{< /tab >}} {{< tab "annotated.pdf" >}} {{< tab-text >}} `annotated.pdf` is the sample file used in this example. Click [here](https://docs.groupdocs.com/annotation/python-net/_sample_files/developer-guide/basic-usage/manage-annotations/annotated.pdf) to download it. {{< /tab-text >}} {{< /tab >}} {{< tab "output.pdf" >}} ```text Binary file (PDF, 88 KB) ``` [Download full output](https://docs.groupdocs.com/annotation/python-net/_output_files/developer-guide/basic-usage/manage-annotations/remove_all_annotations/output.pdf) {{< /tab >}} {{< /tabs >}} --- ## Saving documents Path: https://docs.groupdocs.com/annotation/python-net/saving-documents/ By default `save()` writes every annotation back into the document. With [`SaveOptions`](https://reference.groupdocs.com/annotation/python-net/groupdocs.annotation.options/saveoptions/) you can persist only certain annotation types or limit the output to a range of pages. Pass the configured `SaveOptions` to `save()` through the `save_options` parameter. ## Save specific annotation types Set `annotation_types` to an [`AnnotationType`](https://reference.groupdocs.com/annotation/python-net/groupdocs.annotation.options/annotationtype/) value to write only annotations of that type, even if the document contains others. {{< tabs "code-example-save-specific-annotation-types" >}} {{< tab "save_specific_annotation_types.py" >}} ```python from groupdocs.annotation import Annotator from groupdocs.annotation.models import Rectangle from groupdocs.annotation.models.annotation_models import AreaAnnotation, EllipseAnnotation from groupdocs.annotation.options import SaveOptions, AnnotationType from groupdocs.pydrawing import Color def save_specific_annotation_types(): with Annotator("./sample.pdf") as annotator: area = AreaAnnotation() area.box = Rectangle(100, 100, 100, 100) area.background_color = Color.yellow.to_argb() area.page_number = 0 area.message = "Area" ellipse = EllipseAnnotation() ellipse.box = Rectangle(100, 250, 150, 80) ellipse.background_color = Color.from_argb(255, 144, 238, 144).to_argb() ellipse.page_number = 0 ellipse.message = "Ellipse" annotator.add(area) annotator.add(ellipse) # Persist only the area annotations save_options = SaveOptions() save_options.annotation_types = AnnotationType.AREA annotator.save("./output.pdf", save_options=save_options) if __name__ == "__main__": save_specific_annotation_types() ``` {{< /tab >}} {{< tab "sample.pdf" >}} {{< tab-text >}} `sample.pdf` is the sample file used in this example. Click [here](https://docs.groupdocs.com/annotation/python-net/_sample_files/developer-guide/advanced-usage/saving-documents/sample.pdf) to download it. {{< /tab-text >}} {{< /tab >}} {{< tab "output.pdf" >}} ```text Binary file (PDF, 90 KB) ``` [Download full output](https://docs.groupdocs.com/annotation/python-net/_output_files/developer-guide/advanced-usage/saving-documents/save_specific_annotation_types/output.pdf) {{< /tab >}} {{< /tabs >}} ## Save a page range Set `first_page` and `last_page` to write only a range of pages. These values are **1-based** — page `1` is the first page. {{< tabs "code-example-save-specific-pages" >}} {{< tab "save_specific_pages.py" >}} ```python from groupdocs.annotation import Annotator from groupdocs.annotation.models import Rectangle from groupdocs.annotation.models.annotation_models import AreaAnnotation from groupdocs.annotation.options import SaveOptions from groupdocs.pydrawing import Color def save_specific_pages(): with Annotator("./multipage_sample.pdf") as annotator: area = AreaAnnotation() area.box = Rectangle(100, 100, 100, 100) area.background_color = Color.yellow.to_argb() area.page_number = 0 area.message = "Page 1 annotation" annotator.add(area) # first_page / last_page are 1-based for SaveOptions save_options = SaveOptions() save_options.first_page = 1 save_options.last_page = 2 annotator.save("./output.pdf", save_options=save_options) if __name__ == "__main__": save_specific_pages() ``` {{< /tab >}} {{< tab "multipage_sample.pdf" >}} {{< tab-text >}} `multipage_sample.pdf` is the sample file used in this example. Click [here](https://docs.groupdocs.com/annotation/python-net/_sample_files/developer-guide/advanced-usage/saving-documents/multipage_sample.pdf) to download it. {{< /tab-text >}} {{< /tab >}} {{< tab "output.pdf" >}} ```text Binary file (PDF, 92 KB) ``` [Download full output](https://docs.groupdocs.com/annotation/python-net/_output_files/developer-guide/advanced-usage/saving-documents/save_specific_pages/output.pdf) {{< /tab >}} {{< /tabs >}} --- ## Supported Document Formats Path: https://docs.groupdocs.com/annotation/python-net/supported-document-formats/ ## Supported File Formats The following tables list the file formats that GroupDocs.Annotation for Python via .NET can open, annotate, and save. You can add annotations, markup, and review comments to any of the formats below. ### Word Processing | Format | Description | | --- | --- | | [DOC](https://docs.fileformat.com/word-processing/doc/) | Microsoft Word 97-2007 Document | | [DOCX](https://docs.fileformat.com/word-processing/docx/) | Office Open XML WordprocessingML Document (macro-free) | | [DOCM](https://docs.fileformat.com/word-processing/docm/) | Office Open XML WordprocessingML Macro-Enabled Document | | [DOT](https://docs.fileformat.com/word-processing/dot/) | Microsoft Word 97-2007 Template | | [DOTX](https://docs.fileformat.com/word-processing/dotx/) | Office Open XML WordprocessingML Template (macro-free) | | [DOTM](https://docs.fileformat.com/word-processing/dotm/) | Office Open XML WordprocessingML Macro-Enabled Template | | [RTF](https://docs.fileformat.com/word-processing/rtf/) | Rich Text Format | | [ODT](https://docs.fileformat.com/word-processing/odt/) | OpenDocument Text Document | ### Spreadsheets | Format | Description | | --- | --- | | [XLS](https://docs.fileformat.com/spreadsheet/xls/) | Excel Workbook 97-2003 | | [XLSX](https://docs.fileformat.com/spreadsheet/xlsx/) | Office Open XML Workbook (2007 and later) | | [XLSM](https://docs.fileformat.com/spreadsheet/xlsm/) | Office Open XML Macro-Enabled Workbook | | [XLSB](https://docs.fileformat.com/spreadsheet/xlsb/) | Excel Binary Workbook | | [ODS](https://docs.fileformat.com/spreadsheet/ods/) | OpenDocument Spreadsheet | ### Presentations | Format | Description | | --- | --- | | [PPT](https://docs.fileformat.com/presentation/ppt/) | PowerPoint Presentation 97-2003 | | [PPTX](https://docs.fileformat.com/presentation/pptx/) | Office Open XML Presentation | | [PPS](https://docs.fileformat.com/presentation/pps/) | PowerPoint Slide Show 97-2003 | | [PPSX](https://docs.fileformat.com/presentation/ppsx/) | Office Open XML Slide Show | | [ODP](https://docs.fileformat.com/presentation/odp/) | OpenDocument Presentation | ### Portable Documents | Format | Description | | --- | --- | | [PDF](https://docs.fileformat.com/pdf/) | Adobe Portable Document Format | ### Images | Format | Description | | --- | --- | | [TIF/TIFF](https://docs.fileformat.com/image/tiff/) | Tagged Image File Format | | [JPG/JPEG](https://docs.fileformat.com/image/jpeg/) | Joint Photographic Experts Group image | | [PNG](https://docs.fileformat.com/image/png/) | Portable Network Graphics image | | [BMP](https://docs.fileformat.com/image/bmp/) | Bitmap image file | ### CAD | Format | Description | | --- | --- | | [DWG](https://docs.fileformat.com/cad/dwg/) | AutoCAD Drawing Database file | | [DXF](https://docs.fileformat.com/cad/dxf/) | AutoCAD Drawing Exchange Format | ### Diagrams | Format | Description | | --- | --- | | [VSD](https://docs.fileformat.com/visio/vsd/) | Microsoft Visio Drawing | | [VSDX](https://docs.fileformat.com/visio/vsdx/) | Microsoft Visio 2013 Drawing | | [VSDM](https://docs.fileformat.com/visio/vsdm/) | Visio Macro-Enabled Drawing | | [VSS](https://docs.fileformat.com/visio/vss/) | Visio Stencil | | [VSX](https://docs.fileformat.com/visio/vsx/) | Visio Stencil XML | | [VSSX](https://docs.fileformat.com/visio/vssx/) | Visio 2013 Stencil | | [VST](https://docs.fileformat.com/visio/vst/) | Visio Drawing Template | | [VSTM](https://docs.fileformat.com/visio/vstm/) | Visio Macro-Enabled Drawing Template | ### Email | Format | Description | | --- | --- | | [EML](https://docs.fileformat.com/email/eml/) | E-mail message file | | [EMLX](https://docs.fileformat.com/email/emlx/) | Apple Mail message file | ### Web | Format | Description | | --- | --- | | [HTM/HTML](https://docs.fileformat.com/web/html/) | HyperText Markup Language document | {{< alert style="tip" >}} **Can't find your file format?** We're here to help! Please post a request on our [Free Support Forum](https://forum.groupdocs.com/c/annotation/), and our team will assist you. {{< /alert >}} --- ## Comments and replies Path: https://docs.groupdocs.com/annotation/python-net/comments-and-replies/ Every annotation can carry a discussion thread. You build the thread as a list of [`Reply`](https://reference.groupdocs.com/annotation/python-net/groupdocs.annotation.models/reply/) objects and assign it to the annotation's `replies` property. Each reply has a `comment` and a [`User`](https://reference.groupdocs.com/annotation/python-net/groupdocs.annotation.models/user/), and each user has a `name` and a `Role` (`Role.EDITOR` or `Role.VIEWER`). ## Add replies to an annotation The example below creates an area annotation and attaches a two-message conversation between two users before saving the document. {{< tabs "code-example-add-replies-to-annotation" >}} {{< tab "add_replies_to_annotation.py" >}} ```python from groupdocs.annotation import Annotator from groupdocs.annotation.models import Rectangle, Reply, User, Role from groupdocs.annotation.models.annotation_models import AreaAnnotation from groupdocs.pydrawing import Color def add_replies_to_annotation(): with Annotator("./sample.pdf") as annotator: area = AreaAnnotation() area.box = Rectangle(100, 100, 100, 100) area.background_color = Color.yellow.to_argb() area.page_number = 0 area.message = "Please review this section" # Attach a threaded discussion to the annotation first_reply = Reply() first_reply.comment = "Good catch, I'll take a look." first_reply.user = User(name="Tom", role=Role.EDITOR) second_reply = Reply() second_reply.comment = "Agreed, looks resolved now." second_reply.user = User(name="Jack", role=Role.VIEWER) area.replies = [first_reply, second_reply] annotator.add(area) annotator.save("./output.pdf") if __name__ == "__main__": add_replies_to_annotation() ``` {{< /tab >}} {{< tab "sample.pdf" >}} {{< tab-text >}} `sample.pdf` is the sample file used in this example. Click [here](https://docs.groupdocs.com/annotation/python-net/_sample_files/developer-guide/basic-usage/comments-and-replies/sample.pdf) to download it. {{< /tab-text >}} {{< /tab >}} {{< tab "output.pdf" >}} ```text Binary file (PDF, 91 KB) ``` [Download full output](https://docs.groupdocs.com/annotation/python-net/_output_files/developer-guide/basic-usage/comments-and-replies/add_replies_to_annotation/output.pdf) {{< /tab >}} {{< /tabs >}} --- ## Generate document preview Path: https://docs.groupdocs.com/annotation/python-net/generate-document-preview/ Use [`Document.generate_preview`](https://reference.groupdocs.com/annotation/python-net/groupdocs.annotation/document/) to render document pages to image files (PNG, JPEG, or BMP). You configure the render with [`PreviewOptions`](https://reference.groupdocs.com/annotation/python-net/groupdocs.annotation.options/previewoptions/) and supply two callbacks: one that returns a writable stream for each page, and one that releases it after the page is written. ## Render pages to PNG images Pass the two callables directly to `PreviewOptions(create_page_stream, release_page_stream)` — the library invokes `create_page_stream(page_number)` to obtain a stream for each page and `release_page_stream(page_number, page_stream)` once the page has been written. Set `preview_format` and, optionally, `page_numbers` to limit which pages are rendered. {{< tabs "code-example-generate-document-preview" >}} {{< tab "generate_document_preview.py" >}} ```python import os from groupdocs.annotation import Annotator from groupdocs.annotation.options import PreviewOptions, PreviewFormats def generate_document_preview(): output_dir = "./preview" os.makedirs(output_dir, exist_ok=True) # Track the open streams so each page's file can be closed after it is written open_streams = {} def create_page_stream(page_number): stream = open(os.path.join(output_dir, f"page_{page_number}.png"), "wb") open_streams[page_number] = stream return stream def release_page_stream(page_number, page_stream): stream = open_streams.pop(page_number, None) if stream: stream.close() with Annotator("./sample.pdf") as annotator: preview_options = PreviewOptions(create_page_stream, release_page_stream) preview_options.preview_format = PreviewFormats.PNG preview_options.page_numbers = [1] annotator.document.generate_preview(preview_options) print(f"Generated page preview image(s) in {output_dir}.") if __name__ == "__main__": generate_document_preview() ``` {{< /tab >}} {{< /tabs >}} `PreviewFormats` supports `PNG`, `JPEG`, and `BMP`. Other useful `PreviewOptions` properties include `width`/`height`, `resolution`, `render_comments`, and `render_annotations`. --- ## Import and export annotations Path: https://docs.groupdocs.com/annotation/python-net/import-export-annotations/ GroupDocs.Annotation can serialize a document's annotations to a standalone XML file and load them back into any document. This is useful for transferring a review between copies of a document, storing annotations separately from the source file, or applying the same set of annotations to multiple documents. - [`export_annotations_to_xml_file(output_path)`](https://reference.groupdocs.com/annotation/python-net/groupdocs.annotation/annotator/) — write the open document's annotations to an XML file. - [`import_annotations_from_xml_file(file_path)`](https://reference.groupdocs.com/annotation/python-net/groupdocs.annotation/annotator/) — read annotations from an XML file into the open document. ## Export annotations to XML Open an annotated document and write its annotations to an XML file. {{< tabs "code-example-export-annotations" >}} {{< tab "export_annotations_to_xml.py" >}} ```python from groupdocs.annotation import Annotator def export_annotations_to_xml(): with Annotator("./annotated.pdf") as annotator: annotator.export_annotations_to_xml_file(output_path="./exported_annotations.xml") print("Exported annotations to ./exported_annotations.xml.") if __name__ == "__main__": export_annotations_to_xml() ``` {{< /tab >}} {{< /tabs >}} ## Import annotations from XML Load annotations from an XML file into a document, then save the result. {{< tabs "code-example-import-annotations" >}} {{< tab "import_annotations_from_xml.py" >}} ```python from groupdocs.annotation import Annotator def import_annotations_from_xml(): with Annotator("./sample.pdf") as annotator: annotator.import_annotations_from_xml_file(file_path="./annotations.xml") annotator.save("./output.pdf") print("Imported annotations from ./annotations.xml. Output saved to ./output.pdf.") if __name__ == "__main__": import_annotations_from_xml() ``` {{< /tab >}} {{< /tabs >}} --- ## Showcases Path: https://docs.groupdocs.com/annotation/python-net/showcases/ {{< alert style="info" >}}Want to try GroupDocs.Annotation for Python via .NET by yourself? Explore the Python code examples, the real-world use cases below, and the free online demonstration to learn more about the document annotation features.{{< /alert >}} ## GitHub Examples To get started with GroupDocs.Annotation for Python via .NET, explore the runnable code examples on GitHub. The repository provides standalone scripts that showcase every documented capability — shape annotations, text markup, watermarks, image and link stamps, comments and replies, as well as loading documents, listing and removing annotations, and saving with page-range and annotation-type filters. [GroupDocs.Annotation for Python via .NET GitHub Repository](https://github.com/groupdocs-annotation/GroupDocs.Annotation-for-Python-via-.NET) See [How to Run Examples](https://docs.groupdocs.com/annotation/python-net/getting-started/how-to-run-examples/) to set up the project and run everything locally, and the [Developer Guide](https://docs.groupdocs.com/annotation/python-net/developer-guide/) for step-by-step explanations. ## Use cases GroupDocs.Annotation fits a wide range of document-review and markup scenarios: - **Document review & collaboration** — add area, ellipse, arrow, and text-markup annotations, then attach threaded reviewer comments so teams can discuss a document in place before it is finalized. See the [Developer Guide](https://docs.groupdocs.com/annotation/python-net/developer-guide/). - **Legal & contract markup** — highlight key clauses, strike out obsolete language, and flag regions that need attention with area annotations and notes, keeping the original document format intact. See the [Developer Guide](https://docs.groupdocs.com/annotation/python-net/developer-guide/). - **Engineering, CAD & Visio review** — annotate technical drawings and diagrams with area, arrow, and distance markups, and stamp watermarks or image annotations onto pages for sign-off. See the [Developer Guide](https://docs.groupdocs.com/annotation/python-net/developer-guide/). - **Automated annotation pipelines** — programmatically stamp watermarks, hyperlinks, and notes across many documents in an automated workflow, then save only the annotation types or page ranges you need. See the [Developer Guide](https://docs.groupdocs.com/annotation/python-net/developer-guide/). ## Online Demo To experience GroupDocs.Annotation without any installation, try the free online annotation app. It lets you upload a document, add annotations, and download the annotated result right in your browser: [Free Online Document Annotation App](https://products.groupdocs.app/annotation) The online demo is a convenient way to evaluate the library's capabilities in a real-world environment and decide whether it fits your document-review and markup needs. --- ## Licensing and evaluation Path: https://docs.groupdocs.com/annotation/python-net/licensing-and-subscription/ To explore the system effectively, you may want immediate access to the API. GroupDocs.Annotation simplifies this by offering various purchase plans, along with an evaluation mode and a 30-day Temporary License for evaluation. {{< alert style="info" >}} To learn more about licensing options, purchasing, and evaluation policies, refer to the [Purchase Policies and FAQ](https://purchase.groupdocs.com/policies) section. {{< /alert >}} ## Purchased License After purchasing GroupDocs.Annotation for Python via .NET, you will receive a license file that unlocks the full functionality of the API. A few rules apply: - Apply the license **only once** per process, in your start-up code. - Apply it **before** constructing any `Annotator` or other GroupDocs.Annotation object. - A license can be applied from a file path, from a binary stream (handy when the license is an embedded resource), or as a [Metered License](https://purchase.groupdocs.com/faqs/licensing/metered/) that bills by usage. Use the [set_license](https://reference.groupdocs.com/annotation/python-net/groupdocs.annotation/license/set_license/) method to license the component. Calling it more than once is harmless — it simply wastes a little processor time. ### Set a license from a file The example below applies a license from a file path. It reads the path from the `GROUPDOCS_LIC_PATH` environment variable and falls back to a local `GroupDocs.Annotation.lic` file: {{< tabs "code-example-set-license-from-file" >}} {{< tab "set_license_from_file.py" >}} ```python import os from groupdocs.annotation import License def set_license_from_file(): # Resolve the license path from the environment, with a local fallback license_path = os.environ.get("GROUPDOCS_LIC_PATH", "./GroupDocs.Annotation.lic") # Apply the license before using any annotation features if os.path.exists(license_path): License().set_license(license_path=license_path) print("License set successfully.") else: print("License file not found. Running in evaluation mode.") if __name__ == "__main__": set_license_from_file() ``` {{< /tab >}} {{< /tabs >}} ### Set a license from a stream You can also apply a license from any readable binary stream — useful when the license ships as an embedded resource: {{< tabs "code-example-set-license-from-stream" >}} {{< tab "set_license_from_stream.py" >}} ```python import os from groupdocs.annotation import License def set_license_from_stream(): # Resolve the license path from the environment, with a local fallback license_path = os.environ.get("GROUPDOCS_LIC_PATH", "./GroupDocs.Annotation.lic") # Apply the license from an open binary stream if os.path.exists(license_path): with open(license_path, "rb") as license_stream: License().set_license(license_stream=license_stream) print("License set successfully from stream.") else: print("License file not found. Running in evaluation mode.") if __name__ == "__main__": set_license_from_stream() ``` {{< /tab >}} {{< /tabs >}} ### Set a metered license A Metered License lets you pay for what you use. Set the public and private keys with [set_metered_key](https://reference.groupdocs.com/annotation/python-net/groupdocs.annotation/metered/set_metered_key/) before using the API, then query consumption at any time: {{< tabs "code-example-set-metered-license" >}} {{< tab "set_metered_license.py" >}} ```python import os from groupdocs.annotation import Metered def set_metered_license(): # Read the metered public and private keys from the environment public_key = os.environ.get("GROUPDOCS_METERED_PUBLIC_KEY", "") private_key = os.environ.get("GROUPDOCS_METERED_PRIVATE_KEY", "") # Apply the metered keys before using any annotation features if public_key and private_key: metered = Metered() metered.set_metered_key(public_key=public_key, private_key=private_key) print("Metered license set successfully.") # Query the current metered consumption print(f"Consumption quantity: {Metered.get_consumption_quantity()}") print(f"Consumption credit: {Metered.get_consumption_credit()}") else: print("Metered keys not provided. Running in evaluation mode.") if __name__ == "__main__": set_metered_license() ``` {{< /tab >}} {{< /tabs >}} ### Changing the license file name You are not required to keep the license file name as `GroupDocs.Annotation.lic`. You can rename it to any preferred name and use that name when applying the license in your application. ### "Cannot find license filename" exception When you download the license from the GroupDocs website it is saved as `GroupDocs.Annotation.lic`. However, some web browsers may automatically append `.xml`, resulting in `GroupDocs.Annotation.lic.xml`. If your Windows settings are configured to hide file extensions (the default), the file may still appear as `GroupDocs.Annotation.lic` in File Explorer even though the actual name is `GroupDocs.Annotation.lic.xml`. This discrepancy can cause `set_license` to throw an exception. To fix it, manually rename the file to remove the `.xml` extension, or disable "Hide extensions for known file types" in Windows. ## How to evaluate GroupDocs.Annotation You can evaluate GroupDocs.Annotation for Python via .NET without purchasing a license. The evaluation version is identical to the purchased one; it becomes fully licensed once you set a license. ### Evaluation mode Without a license, GroupDocs.Annotation runs in evaluation mode: - The API is **fully functional** — you can open documents, add, get, update, and remove annotations, and save the result. Every operation completes successfully and no exception is thrown. - There is **no limit** on the number of documents you can open in a process and **no cap** on the number of annotations you can add. You can run as many `Annotator` operations as you like, one after another, in the same process. - An **evaluation watermark** is added to the output document, so the saved file is slightly larger than a licensed one. To remove the evaluation watermark, apply a purchased or temporary license as shown above. ### Temporary License To produce output without the evaluation watermark while you assess the full features of GroupDocs.Annotation, you can request a 30-day ["Temporary License"](https://purchase.groupdocs.com/temporary-license). --- ## AI agents and LLM integration Path: https://docs.groupdocs.com/annotation/python-net/agents-and-llm-integration/ GroupDocs.Annotation for Python via .NET is designed to work smoothly with AI coding assistants such as Claude Code, Cursor, and GitHub Copilot in agent mode. ## Built into the package The `groupdocs-annotation-net` wheel ships a bundled `AGENTS.md` reference. Once the package is installed, AI tools discover it automatically at `groupdocs/annotation/AGENTS.md`. It covers the canonical imports, the open → add → save workflow, per-operation recipes, licensing, the full API-surface tables, and troubleshooting — everything an agent needs to write correct annotation code without guessing. ## MCP server For on-demand documentation lookups, point your AI tool at the GroupDocs MCP server: ```json { "mcpServers": { "groupdocs-docs": { "url": "https://docs.groupdocs.com/mcp" } } } ``` This works with Claude Code (`~/.claude/settings.json`), Cursor (`.cursor/mcp.json`), VS Code Copilot (`.vscode/mcp.json`), and any MCP-compatible client. ## Machine-readable documentation LLM-optimized documentation for retrieval-augmented generation and context loading is available at [`https://docs.groupdocs.com/annotation/python-net/llms-full.txt`](https://docs.groupdocs.com/annotation/python-net/llms-full.txt). ## AGENTS.md reference The complete reference bundled inside the wheel is reproduced below. ````markdown # GroupDocs.Annotation for Python via .NET -- AGENTS.md > Instructions for AI agents working with this package. Add, edit, and remove annotations and markup on documents and images -- area/ellipse/arrow/point/distance/polyline shapes, text highlight/underline/strikeout/squiggly, replacement and redaction markups, watermarks, image and link stamps, editable text fields, and threaded review comments (replies) -- then save back to the original format. Works across PDF, Word, Excel, PowerPoint, Visio, CAD, images, email, and more through one unified API, with no MS Office or external software installed. ## Install ```bash pip install groupdocs-annotation-net ``` **Python**: 3.5 - 3.14 | **Platforms**: Windows, Linux, macOS ## Resources | Resource | URL | |---|---| | Documentation | https://docs.groupdocs.com/annotation/python-net/ | | LLM-optimized docs | https://docs.groupdocs.com/annotation/python-net/llms-full.txt | | API reference | https://reference.groupdocs.com/annotation/python-net/ | | Code examples | https://docs.groupdocs.com/annotation/python-net/developer-guide/ | | Release notes | https://releases.groupdocs.com/annotation/python-net/release-notes/ | | PyPI | https://pypi.org/project/groupdocs-annotation-net/ | | Free support forum | https://forum.groupdocs.com/c/annotation/ | | Temporary license | https://purchase.groupdocs.com/temporary-license | ## MCP Server If your environment has MCP configured, you can connect your AI tool to the GroupDocs documentation server for on-demand API lookups: ```json { "mcpServers": { "groupdocs-docs": { "url": "https://docs.groupdocs.com/mcp" } } } ``` Works with Claude Code (`~/.claude/settings.json`), Cursor (`.cursor/mcp.json`), VS Code Copilot (`.vscode/mcp.json`), and any MCP-compatible client. If MCP is unavailable, fall back to the LLM-optimized docs URL above and this file -- both are shipped inside the wheel. ## Imports ```python from groupdocs.annotation import Annotator, AnnotatorSettings, Document, FileType, License, Metered from groupdocs.annotation.options import ( AnnotationType, LoadOptions, SaveOptions, ) from groupdocs.annotation.models import ( Rectangle, Point, Reply, User, Role, PageInfo, BorderStyle, BoxStyle, HorizontalAlignment, VerticalAlignment, RotationDocument, ) from groupdocs.annotation.models.annotation_models import ( AnnotationBase, # shapes AreaAnnotation, ArrowAnnotation, DistanceAnnotation, EllipseAnnotation, PointAnnotation, PolylineAnnotation, # text markup HighlightAnnotation, UnderlineAnnotation, StrikeoutAnnotation, SquigglyAnnotation, ReplacementAnnotation, TextRedactionAnnotation, ResourcesRedactionAnnotation, # content WatermarkAnnotation, ImageAnnotation, LinkAnnotation, TextFieldAnnotation, ) from groupdocs.pydrawing import Color # use Color..to_argb() for ARGB ints ``` ## Open + annotate + save (the core workflow) `Annotator` is the entry point. The flow is always: **open → one or more `add(...)` calls → `save()`**. Each annotation is a plain object you configure with properties, then hand to `add`. Use `Annotator` as a context manager so the native document handle is released. ```python from groupdocs.annotation import Annotator from groupdocs.annotation.models import Rectangle from groupdocs.annotation.models.annotation_models import AreaAnnotation from groupdocs.pydrawing import Color with Annotator("document.pdf") as annotator: area = AreaAnnotation() area.box = Rectangle(100, 100, 200, 80) # x, y, width, height area.page_number = 0 # 0-based page index area.background_color = Color.yellow.to_argb() # ARGB int, NOT a Color object area.message = "Review this" annotator.add(area) annotator.save("annotated.pdf") ``` **`Annotator(...)` constructor.** `Annotator(file_path)` or `Annotator(stream)`, optionally with `LoadOptions` and/or `AnnotatorSettings`: `Annotator("doc.pdf", LoadOptions(password="..."))`, `Annotator(stream, LoadOptions(), AnnotatorSettings(...))`. **`add(...)`** accepts a single annotation or a list of annotations. **`save(...)`** writes to a path or a writable stream; pass `SaveOptions` (as `save_options=...`) to control which annotation types and pages are rendered. By default `save()` renders all annotations onto the document. **Coordinates & colors.** Geometry uses `Rectangle(x, y, width, height)` and `Point(x, y)` from `groupdocs.annotation.models`. **Colors are ARGB integers**, not `Color` objects — always pass `Color..to_argb()` (or a literal ARGB int) to `background_color`, `font_color`, `pen_color`, `underline_color`, and `squiggly_color`. **Pages.** Annotation `page_number` is **0-based**, but `SaveOptions.first_page` / `last_page` are **1-based**. ## Operations ### Shape annotations (area / ellipse / arrow / point / distance / polyline) ```python from groupdocs.annotation import Annotator from groupdocs.annotation.models import Rectangle from groupdocs.annotation.models.annotation_models import AreaAnnotation, EllipseAnnotation from groupdocs.pydrawing import Color area = AreaAnnotation() area.box = Rectangle(100, 100, 200, 80) area.background_color = Color.yellow.to_argb() # ARGB int area.pen_color = Color.red.to_argb() # ARGB int area.opacity = 0.7 area.page_number = 0 area.message = "Flagged region" ell = EllipseAnnotation() ell.box = Rectangle(50, 200, 120, 60) ell.page_number = 0 with Annotator("document.pdf") as annotator: annotator.add([area, ell]) # add several at once annotator.save("annotated.pdf") ``` Shape types share `box`, `background_color` / `pen_color`, and `opacity`. `ArrowAnnotation`, `DistanceAnnotation`, and `PolylineAnnotation` use the same box/points surface; `PointAnnotation` anchors a comment at a single point. ### Text markup (highlight / underline / strikeout / squiggly / replacement) ```python from groupdocs.annotation import Annotator from groupdocs.annotation.models import Point from groupdocs.annotation.models.annotation_models import HighlightAnnotation from groupdocs.pydrawing import Color hl = HighlightAnnotation() hl.font_color = Color.yellow.to_argb() hl.page_number = 0 # Quad points around the target text (top-left, top-right, bottom-right, bottom-left): hl.points = [Point(80, 730), Point(240, 730), Point(240, 750), Point(80, 750)] with Annotator("document.pdf") as annotator: annotator.add(hl) annotator.save("highlighted.pdf") ``` Text-markup types expose `points` (a list of `Point` describing the marked region) and `font_color`; the rest of the markup family (`UnderlineAnnotation`, `StrikeoutAnnotation`, `SquigglyAnnotation`, `ReplacementAnnotation`, `TextRedactionAnnotation`, `ResourcesRedactionAnnotation`) follows the same shape. ### Review comments (replies) ```python from groupdocs.annotation.models import Reply r1 = Reply(); r1.comment = "Please double-check this" r2 = Reply(); r2.comment = "Confirmed" area.replies = [r1, r2] # attach a comment thread to any annotation ``` `Reply` has `comment`, `user` (a `User`), `replied_on`, and `id`. A `User(id, name, role)` carries a `Role` (`VIEWER` / `EDITOR`). ### Content annotations (watermark / image / link / text field) `WatermarkAnnotation`, `ImageAnnotation` (set `image_path`), `LinkAnnotation` (set `url`), and `TextFieldAnnotation` are created and added the same way — configure their properties, then `annotator.add(...)`. ### List, update, remove ```python from groupdocs.annotation import Annotator from groupdocs.annotation.options import AnnotationType with Annotator("annotated.pdf") as annotator: all_annotations = annotator.get() # every annotation -> list areas = annotator.get(AnnotationType.AREA) # filter by type -> list annotator.update(all_annotations[0]) # push a modified annotation back annotator.remove(all_annotations[0]) # by object, or annotator.remove(id) annotator.save("edited.pdf") ``` ### Save options (page range + annotation-type filter) ```python from groupdocs.annotation import Annotator from groupdocs.annotation.options import SaveOptions, AnnotationType with Annotator("document.pdf") as annotator: annotator.add(area) options = SaveOptions() options.annotation_types = AnnotationType.AREA # render only area annotations options.first_page = 1 # 1-based options.last_page = 2 annotator.save("filtered.pdf", save_options=options) ``` ### Document info ```python with Annotator("document.pdf") as annotator: info = annotator.document.get_document_info() # format, page count, size print(info.file_type.file_format, info.page_count, info.size) ``` ### Save to a stream ```python import io with Annotator("document.pdf") as annotator: annotator.add(area) buf = io.BytesIO() annotator.save(buf) # BytesIO is updated after save data = buf.getvalue() ``` ## Licensing ```python from groupdocs.annotation import License # From file License().set_license("path/to/license.lic") # From stream with open("license.lic", "rb") as f: License().set_license(f) ``` Or auto-apply: `export GROUPDOCS_LIC_PATH="path/to/license.lic"` Metered licensing is also available: ```python from groupdocs.annotation import Metered Metered().set_metered_key("public-key", "private-key") print(Metered().get_consumption_quantity(), Metered().get_consumption_credit()) ``` **Evaluation vs licensed.** Without a license the library still runs and `save()` completes normally, but the output carries an evaluation watermark/banner (the output file is noticeably larger). There is no per-process document-open cap. Set `GROUPDOCS_LIC_PATH` (or call `License().set_license(...)`) and re-run to clear the watermark. A 30-day full license is free: https://purchase.groupdocs.com/temporary-license ## API Reference ### Annotator | Method | Returns | Description | |---|---|---| | `Annotator(file_path / stream [, load_options [, settings]])` | | Open by path or binary stream; optional `LoadOptions` / `AnnotatorSettings`. Context manager. | | `add(annotation)` / `add([annotations])` | `None` | Add one annotation or a list. | | `update(annotation)` | `None` | Replace an existing annotation (matched by id). | | `remove(annotation)` / `remove(id)` | `None` | Remove by object or by id; a list is also accepted. | | `get()` | `list` | All annotations on the document. | | `get(annotation_type)` | `list` | Annotations of one `AnnotationType`. | | `save(path)` / `save(path, save_options=...)` / `save(stream)` | `None` | Render annotations and write the result. | | `dispose()` | `None` | Release native resources (handled by `with`). | `Annotator` properties: `document` (`Document` — `get_document_info()`), `rotation`, `process_pages`. `AnnotatorSettings` carries `logger` / `cache`. ### Annotation types (`groupdocs.annotation.models.annotation_models`) | Type | Notes | |---|---| | `AreaAnnotation`, `EllipseAnnotation` | `box` (`Rectangle`), `background_color`, `pen_color`, `opacity` (ARGB-int colors). | | `ArrowAnnotation`, `DistanceAnnotation`, `PolylineAnnotation` | Line/shape markups; box + points. | | `PointAnnotation` | A single comment anchor at a point. | | `HighlightAnnotation`, `UnderlineAnnotation`, `StrikeoutAnnotation`, `SquigglyAnnotation` | Text markup; `points` (quad) + `font_color`. | | `ReplacementAnnotation`, `TextRedactionAnnotation`, `ResourcesRedactionAnnotation` | Replace / redact text or resources. | | `WatermarkAnnotation` | Text watermark stamp. | | `ImageAnnotation` | Image stamp (`image_path`). | | `LinkAnnotation` | Hyperlink over a region (`url`). | | `TextFieldAnnotation` | Editable text field. | | All | Inherit `id`, `message`, `page_number`, `replies`, `created_on`, `user`, `type` from `AnnotationBase`. | ### Options, models & enums | Type | Notes | |---|---| | `LoadOptions(password=...)` | Open protected input. | | `SaveOptions` | `annotation_types`, `first_page` (1-based), `last_page`, `only_annotated_pages`. | | `AnnotationType` | Enum of annotation kinds (filter for `get(...)` / `SaveOptions`). | | `Rectangle(x, y, width, height)` / `Point(x, y)` | Geometry (from `...models`). | | `Reply` | `comment`, `user`, `replied_on`, `id`. | | `User(id, name, role)` / `Role` | `VIEWER`, `EDITOR`. | | `BorderStyle`, `BoxStyle`, `HorizontalAlignment`, `VerticalAlignment`, `RotationDocument` | Styling enums. | ### License / Metered `License().set_license(path_or_stream)` · `Metered().set_metered_key(public, private)` · `Metered().get_consumption_quantity()` · `Metered().get_consumption_credit()` ## Key Patterns - **Properties**: use `snake_case` -- auto-mapped to .NET `PascalCase` - **Context managers**: `with Annotator(...) as a:` ensures the document handle is released - **Build then add**: construct an annotation object, set its properties, then `add(it)` — or `add([...])` for several - **Colors are ARGB ints**: always pass `Color..to_argb()` (e.g. `annotation.background_color = Color.yellow.to_argb()`), never a `Color` object - **Geometry**: `Rectangle(x, y, width, height)`, `Point(x, y)`; text markup uses a `points` quad - **Pages**: annotation `page_number` is **0-based**; `SaveOptions.first_page` / `last_page` are **1-based** - **Replies**: attach a `list[Reply]` to any annotation's `replies` for review comments - **Streams**: pass `open("file", "rb")` or `io.BytesIO(data)` where .NET expects a Stream; `BytesIO` is updated after `save(stream)` - **Enums**: case-insensitive, lazy-loaded (e.g., `AnnotationType.AREA`, `Role.EDITOR`) ## Platform Requirements | Platform | Requirements | |---|---| | Windows | None | | Linux | `apt install libgdiplus libfontconfig1 ttf-mscorefonts-installer` | | macOS | `brew install mono-libgdiplus` | ## Troubleshooting **Evaluation watermark on output** -- no license. Apply one with `License().set_license(...)` or set `GROUPDOCS_LIC_PATH`; a free 30-day license is at https://purchase.groupdocs.com/temporary-license **Password-protected source** -- the input is encrypted. Open it with `Annotator(path, LoadOptions(password="..."))`. **Unsupported or damaged file** -- the format isn't supported or the file is corrupted. Check it against the supported-formats list. **`System.Drawing.Common is not supported`** -- install libgdiplus: `sudo apt install libgdiplus` (Linux) / `brew install mono-libgdiplus` (macOS) **`Gdip` type initializer exception** -- outdated libgdiplus: `brew reinstall mono-libgdiplus` (macOS) **Garbled text / missing fonts** -- install fonts: `sudo apt install ttf-mscorefonts-installer fontconfig && sudo fc-cache -f` **`DllNotFoundException: libSkiaSharp`** -- stale system copy conflicts with bundled version. Rename it: `sudo mv /usr/local/lib/libSkiaSharp.dylib /usr/local/lib/libSkiaSharp.dylib.bak` **`DOTNET_SYSTEM_GLOBALIZATION_INVARIANT` errors** -- do NOT set this. Install ICU: `sudo apt install libicu-dev` **`TypeLoadException`** -- reinstall: `pip install --force-reinstall groupdocs-annotation-net` **Still stuck?** Post your question at https://forum.groupdocs.com/c/annotation/ -- the development team responds directly. ```` --- ## System Requirements Path: https://docs.groupdocs.com/annotation/python-net/system-requirements/ {{< alert style="info" >}} GroupDocs.Annotation for Python via .NET operates independently of external software like Microsoft Word, Excel, PowerPoint, or Adobe Acrobat. To install it, simply follow one of the methods described in the [Installation](https://docs.groupdocs.com/annotation/python-net/installation/) section. {{< /alert >}} ## Overview GroupDocs.Annotation for Python via .NET does not require Microsoft Office, OpenOffice, Adobe Acrobat, or any other external software to be installed. The package is a self-contained wheel that bundles everything it needs, so the only prerequisites are a supported version of Python and the operating-system packages listed below. ## Supported Python Versions GroupDocs.Annotation for Python via .NET supports the following Python versions: * Python 3.5 * Python 3.6 * Python 3.7 * Python 3.8 * Python 3.9 * Python 3.10 * Python 3.11 * Python 3.12 * Python 3.13 * Python 3.14 ## Supported Operating Systems The package is distributed as a self-contained wheel that runs on the following platforms: ### Windows * Windows x64 No additional dependencies are required on Windows. ### Linux * Linux x64 On Linux you need to install a few system packages for graphics, fonts, and globalization: ```bash apt install libgdiplus libfontconfig1 libicu-dev ttf-mscorefonts-installer ``` ### macOS * macOS x64 (Intel) * macOS ARM64 (Apple Silicon) On macOS install the graphics library via Homebrew: ```bash brew install mono-libgdiplus ``` ## No Third-Party Software Required Unlike many document-processing tools, GroupDocs.Annotation for Python via .NET does not depend on Microsoft Office, OpenOffice, Adobe Acrobat, or any other application being installed on the machine. All loading, annotating, and saving of Word Processing documents, Spreadsheets, Presentations, PDFs, images, and the other [supported formats](https://docs.groupdocs.com/annotation/python-net/supported-document-formats/) is performed entirely by the bundled engine. --- ## How to Run Examples Path: https://docs.groupdocs.com/annotation/python-net/how-to-run-examples/ {{< alert style="warning" >}}Before running an example make sure that GroupDocs.Annotation for Python via .NET has been installed successfully.{{< /alert >}} The complete examples project for **GroupDocs.Annotation for Python via .NET** is hosted on [GitHub](https://github.com/groupdocs-annotation/GroupDocs.Annotation-for-Python-via-.NET). It contains standalone, runnable scripts together with the sample documents they use, so the examples work out of the box. ## Prerequisites - [Python](https://www.python.org/) 3.5 – 3.14 installed and on your `PATH`. - [git](https://git-scm.com/) to clone the repository (or download the ZIP). - On Linux and macOS, the native libraries listed in the [System Requirements](https://docs.groupdocs.com/annotation/python-net/system-requirements/) (`libgdiplus`, `libfontconfig1`, `libicu-dev`, and fonts). - Optionally, a GroupDocs.Annotation license to remove the [evaluation watermark](https://docs.groupdocs.com/annotation/python-net/licensing-and-subscription/). ## Get the code Clone the repository with your favourite git client, or download the ZIP from GitHub: ```bash git clone https://github.com/groupdocs-annotation/GroupDocs.Annotation-for-Python-via-.NET.git cd GroupDocs.Annotation-for-Python-via-.NET ``` ## Project Structure The examples live under the `Examples/` folder, organized by topic. Directory names are kebab-case and each script is standalone: ```text GroupDocs.Annotation-for-Python-via-.NET/ ├── Dockerfile └── Examples/ ├── requirements.txt ├── run_all_examples.py ├── licensing/ │ ├── set_license_from_file.py │ ├── set_license_from_stream.py │ └── set_metered_license.py ├── getting-started/ │ └── hello-world/ │ └── hello_world.py └── developer-guide/ ├── basic-usage/ │ ├── add-annotations/ │ ├── comments-and-replies/ │ ├── get-annotations/ │ ├── get-document-info/ │ ├── get-supported-file-formats/ │ └── remove-annotations/ └── advanced-usage/ ├── loading-documents/ └── saving-documents/ ``` ## Setup Create and activate a virtual environment, then install the dependencies listed in `Examples/requirements.txt`: {{< tabs "setup-venv">}} {{< tab "Windows" >}} ```ps py -m venv .venv .venv\Scripts\activate py -m pip install -r Examples/requirements.txt ``` {{< /tab >}} {{< tab "Linux" >}} ```bash python3 -m venv .venv source .venv/bin/activate python3 -m pip install -r Examples/requirements.txt ``` {{< /tab >}} {{< tab "macOS" >}} ```bash python3 -m venv .venv source .venv/bin/activate python3 -m pip install -r Examples/requirements.txt ``` {{< /tab >}} {{< /tabs >}} To run the examples without the evaluation watermark, point the `GROUPDOCS_LIC_PATH` environment variable at your license file. The example scripts read this variable and apply the license automatically: {{< tabs "setup-license">}} {{< tab "Windows" >}} ```ps $env:GROUPDOCS_LIC_PATH = "C:\path\to\GroupDocs.Annotation.lic" ``` {{< /tab >}} {{< tab "Linux" >}} ```bash export GROUPDOCS_LIC_PATH="/path/to/GroupDocs.Annotation.lic" ``` {{< /tab >}} {{< tab "macOS" >}} ```bash export GROUPDOCS_LIC_PATH="/path/to/GroupDocs.Annotation.lic" ``` {{< /tab >}} {{< /tabs >}} See [Licensing and Evaluation](https://docs.groupdocs.com/annotation/python-net/licensing-and-subscription/) for details on obtaining and applying a license. ## Run Run every example at once with the runner script: ```bash python Examples/run_all_examples.py ``` Or run a single example by passing its path directly: ```bash python Examples/developer-guide/basic-usage/add-annotations/add_area_annotation.py ``` The repository ships with all the sample documents and resources used by the examples, so the scripts run out of the box. {{< alert style="info" >}}Without a license the examples run in evaluation mode, which adds a watermark to each output document. There is no document-open limit, so every example completes successfully whether or not a license is applied. The `run_all_examples.py` runner launches each example in its own process and keeps the working directory set to the example's folder, so each script finds its input and output files.{{< /alert >}} ## Run with Docker The repository includes a `Dockerfile` that installs the native dependencies and Python packages so you can run the examples in a clean, reproducible container. From the repository root: ```bash docker build -t groupdocs-annotation-examples . docker run --rm groupdocs-annotation-examples ``` To use a license inside the container, mount it and pass `GROUPDOCS_LIC_PATH`: ```bash docker run --rm \ -v /path/to/GroupDocs.Annotation.lic:/app/GroupDocs.Annotation.lic:ro \ -e GROUPDOCS_LIC_PATH=/app/GroupDocs.Annotation.lic \ groupdocs-annotation-examples ``` ## Continuous integration Because the examples run headlessly and exit with a non-zero status on failure, they fit naturally into a CI pipeline. Install `Examples/requirements.txt`, supply the license through the `GROUPDOCS_LIC_PATH` environment variable (store the license as a protected secret), make sure the Linux native dependencies are present on the runner, and invoke `python Examples/run_all_examples.py` as a build step. The provided `Dockerfile` is a convenient base image for such jobs. ## Troubleshooting - **Evaluation watermark on the output** — you are running unlicensed in evaluation mode. Set `GROUPDOCS_LIC_PATH` to a valid license to produce output without the watermark. See [Licensing and Evaluation](https://docs.groupdocs.com/annotation/python-net/licensing-and-subscription/). - **Missing or substituted fonts** — install fonts so annotated output matches the original: `apt install libfontconfig1 ttf-mscorefonts-installer`. - **ICU / globalization errors on Linux** — install ICU: `apt install libicu-dev`. - **`ModuleNotFoundError: No module named 'groupdocs'`** — the package is not installed in the active environment. Activate your virtual environment and re-run `pip install -r Examples/requirements.txt`. ## Contribute If you would like to add or improve an example, we encourage you to contribute to the project. All examples in this repository are open source and can be freely used in your own applications. To contribute, fork the repository, edit the code, and create a pull request. We will review the changes and include them if found helpful. --- ## Technical Support Path: https://docs.groupdocs.com/annotation/python-net/technical-support/ GroupDocs provides unlimited free technical support for all of its products. Support is available to all users, including evaluation. The support is provided at the [Free Support Forum](https://forum.groupdocs.com/) and the [Paid Support Helpdesk](https://helpdesk.groupdocs.com/). {{< alert style="info" >}} Please note that GroupDocs does not provide technical support over the phone. Phone support is only available for sales and purchase questions. {{< /alert >}} ## GroupDocs Free Support Forum If you need help with GroupDocs.Annotation, consider the following: * Make sure you are using the latest GroupDocs.Annotation version before reporting an issue. See [PyPI](https://pypi.org/project/groupdocs-annotation-net) to find out about the latest version. * Have a look through the forums, this documentation, and the [API Reference](https://reference.groupdocs.com/annotation/python-net/) before reporting an issue – perhaps your question has already been answered. * Post your question at the [GroupDocs.Annotation Free Support Forum](https://forum.groupdocs.com/c/annotation), and we'll assist you. Questions are answered directly by the GroupDocs.Annotation development team. * When expecting a reply on the forums, please allow for time zone differences. ## Paid Support Helpdesk Paid support issues have higher priority compared to free support requests. * Post your question at the [Paid Support Helpdesk](https://helpdesk.groupdocs.com/) to set a higher priority for the issue. ## Report an Issue or Feature Request When posting your issue, question, or feature request with GroupDocs.Annotation, follow these simple steps to make sure it is resolved in the most efficient way: * Include the original document and, if possible, the code snippet that is causing the problem. If you need to attach a few files, zip them into one. It is safe to attach your documents to the GroupDocs forums because only you and the GroupDocs developers will have access to the attached files. * Add information about the environment you are facing the issue in — operating system, Python version, and GroupDocs.Annotation version. * Try to report one issue per thread. If you have another issue, question, or feature request, please report it in a separate thread.