Skip to content

Latest commit

 

History

History
334 lines (249 loc) · 15.3 KB

File metadata and controls

334 lines (249 loc) · 15.3 KB

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/point/arrow/ellipse/distance shapes, text highlight/underline/strikeout/squiggly/replacement, watermarks, image and link stamps, editable text fields, and threaded review comments (replies) -- then save back to the original format or export the annotations separately. Works across PDF, Word, Excel, PowerPoint, Visio, images, and more through one unified API, with no MS Office or external software installed.

Install

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:

{
  "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

from groupdocs.annotation import (
    Annotator, AnnotatorSettings, Document, License, Metered, FileType, IDocumentInfo,
)
from groupdocs.annotation.models.annotation_models import (
    AnnotationBase,
    # shapes
    AreaAnnotation, ArrowAnnotation, EllipseAnnotation, PointAnnotation,
    PolylineAnnotation, DistanceAnnotation,
    # text markup
    HighlightAnnotation, UnderlineAnnotation, StrikeoutAnnotation, SquigglyAnnotation,
    ReplacementAnnotation, TextRedactionAnnotation, ResourcesRedactionAnnotation,
    # content
    WatermarkAnnotation, ImageAnnotation, LinkAnnotation, TextFieldAnnotation,
)
from groupdocs.annotation.models import (
    Rectangle, Point, Reply, User, PageInfo, VersionsList,
    BorderStyle, BoxStyle, PenStyle, HorizontalAlignment, VerticalAlignment, RotationDocument,
)
from groupdocs.annotation.options import (
    LoadOptions, SaveOptions, PreviewOptions, PreviewFormats, AnnotationType,
    CreatePageStream, ReleasePageStream,
)
from groupdocs.annotation.exceptions import (
    AnnotatorException, FileTypeNotSupportedException,
    PasswordProtectedFileException, CorruptedOrDamagedFileException,
)
from groupdocs.annotation.logging import ConsoleLogger
from groupdocs.annotation.cache import FileCache

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.

from groupdocs.annotation import Annotator
from groupdocs.annotation.models.annotation_models import AreaAnnotation
from groupdocs.annotation.models import Rectangle

with Annotator("document.pdf") as annotator:
    area = AreaAnnotation()
    area.box = Rectangle(100, 100, 200, 80)   # x, y, width, height
    area.page_number = 0
    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 to control output paging / which annotation types to render. 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 (e.g. 65535), not Color objects.

Operations

Shape annotations (area / ellipse / arrow / point / distance / polyline)

from groupdocs.annotation.models.annotation_models import AreaAnnotation, EllipseAnnotation
from groupdocs.annotation.models import Rectangle

area = AreaAnnotation()
area.box = Rectangle(100, 100, 200, 80)
area.background_color = 65535     # ARGB int
area.pen_color = 255              # 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/PolylineAnnotation use the same box/points surface. (Note: in 26.6 the nullable pen_width (byte?) and pen_style (PenStyle?) properties cannot yet be set from Python — a plain int does not narrow to byte?/enum across the bridge.)

Text markup (highlight / underline / strikeout / squiggly / replacement)

from groupdocs.annotation.models.annotation_models import HighlightAnnotation
from groupdocs.annotation.models import Point

hl = HighlightAnnotation()
hl.font_color = 65535
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) follows the same shape.

Review comments (replies)

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, parent_reply, id.

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

with Annotator("annotated.pdf") as annotator:
    annotations = annotator.get()             # all annotations -> list
    annotator.update(annotations[0])          # push a modified annotation back
    annotator.remove(annotations[0])          # by object, or annotator.remove(id)
    annotator.save("edited.pdf")

Annotation versions

with Annotator("annotated.pdf") as annotator:
    for v in annotator.get_versions_list():
        for a in annotator.get_version(v):
            print(v, a.id, a.message)

Page previews & document info

from groupdocs.annotation.options import PreviewOptions, PreviewFormats

def create_page_stream(page_number):
    return open(f"page-{page_number}.png", "wb")

with Annotator("document.pdf") as annotator:
    info = annotator.document.get_document_info()      # format, page count, page sizes
    opts = PreviewOptions(create_page_stream)
    opts.preview_format = PreviewFormats.PNG
    opts.page_numbers = [0, 1]
    annotator.document.generate_preview(opts)

Import / export annotations

annotator.import_annotations_from_document(path) and annotator.export_annotations_from_xml_file(path) move annotations between a document/XML file and the current Annotator (each takes a single path string). Save afterwards to persist.

Save to a stream

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

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:

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, but output carries an evaluation watermark (PDF) or an equivalent mark, and a page/document-count cap applies. Set GROUPDOCS_LIC_PATH (or call License().set_license(...)) and re-run to clear these. 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.
get_versions_list() list Available annotation versions.
get_version(version) list Annotations in a given version.
save(path) / save(stream) / save(path, SaveOptions) None Render annotations and write the result.
import_annotations_from_document(path) None Pull annotations from a document/XML into this annotator.
export_annotations_from_xml_file(path) None Move annotations between an XML file and this annotator.
dispose() None Release native resources (handled by with).

Annotator properties: document (Documentget_document_info(), generate_preview(...)), 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. (pen_style/pen_width not settable from Python in 26.6 — see Shape annotations note.)
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=..., version=..., font_directories=...) Open protected / versioned input.
SaveOptions annotation_types, first_page, last_page, only_annotated_pages, version.
PreviewOptions(create_page_stream [, release_page_stream]) preview_format, page_numbers, width, height, resolution.
PreviewFormats PNG, JPEG, BMP.
AnnotationType Enum of annotation kinds (filter for get(...)).
Rectangle(x, y, width, height) / Point(x, y) Geometry (from ...models).
Reply comment, user, replied_on, parent_reply, id.
PenStyle, 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: e.g. annotation.background_color = 65535 (not a Color object)
  • Geometry: Rectangle(x, y, width, height), Point(x, y); text markup uses a points quad
  • 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., PreviewFormats.PNG, AnnotationType)
  • Exceptions: catch PasswordProtectedFileException / FileTypeNotSupportedException / CorruptedOrDamagedFileException / AnnotatorException (all subclass the product-root GroupDocsAnnotationException)

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

PasswordProtectedFileException -- the source is encrypted. Open it with Annotator(path, LoadOptions(password="...")).

FileTypeNotSupportedException / CorruptedOrDamagedFileException -- the format isn't supported or the file is damaged. 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.