Use shape annotations to add structured visual markup to PDF files.

Common use cases include:

  • Architectural markups
  • Diagram and technical drawing tools
  • Review workflows with callouts
  • Region highlighting and boundary marking

In this guide, you’ll add:

  • Lines with configurable ending styles
  • Circles and ellipses
  • Squares and rectangles
  • Custom colors and line widths
Download sample

How Nutrient helps

Nutrient Python SDK handles shape annotation structures and rendering.

The SDK handles:

  • Parsing shape annotation dictionaries and path construction
  • Managing stroke and fill operations with appearance streams
  • Handling geometric calculations and coordinate transformations
  • Complex line ending styles and cap rendering

Complete implementation

This example adds multiple shape annotations to a PDF file:

from nutrient_sdk import Document
from nutrient_sdk import PdfEditor
from nutrient_sdk import Color
from nutrient_sdk import PdfLineEndingStyle
from nutrient_sdk import NutrientException

Working with shape annotations

Open the document in a context manager(opens in a new tab) to ensure cleanup after processing.

Then:

  • Create a PDF editor.
  • Get the page collection.
  • Add a letter-size page (612 × 792) if the document is empty.
  • Get the annotation collection from the first page.
def main():
try:
with Document.open("input.pdf") as document:
editor = PdfEditor.edit(document)
pages = editor.get_page_collection()
if pages.get_count() == 0:
pages.add(612.0, 792.0)
page = pages.get_first()
annotations = page.get_annotation_collection()

Adding a line annotation

Create a line with add_line(start_x, start_y, end_x, end_y, author, content).

This example adds a horizontal line from (50, 700) to (200, 700):

By default, the SDK uses:

  • Black stroke color (ARGB 255, 0, 0, 0)
  • 1.0 point line width
line = annotations.add_line(
50.0, 700.0, 200.0, 700.0, # start_x, start_y, end_x, end_y
"Author",
"Simple horizontal line"
)

Adding a line with arrow endings

Create a second line and customize its style.

This example:

  • Draws a diagonal line from (50, 650) to (200, 600).
  • Sets end_cap to PdfLineEndingStyle.CLOSED_ARROW.
  • Sets the color to blue with Color.from_argb(255, 0, 0, 255).
  • Sets the line width to 2.0.

Set ending styles on both start_cap and end_cap:

arrow = annotations.add_line(
50.0, 650.0, 200.0, 600.0, # start_x, start_y, end_x, end_y
"Reviewer",
"Arrow pointing to important area"
)
# Optionally customize the line
arrow.end_cap = PdfLineEndingStyle.CLOSED_ARROW
arrow.color = Color.from_argb(255, 0, 0, 255)
arrow.line_width = 2.0

Available line ending styles from the PdfLineEndingStyle enumeration include:

  • NONE — No ending (default)
  • SQUARE — Square ending for boundary indicators
  • CIRCLE — Circular ending for connection points
  • DIAMOND — Diamond shape for decision points
  • OPEN_ARROW — Open arrowhead for directional indicators
  • CLOSED_ARROW — Filled arrowhead for emphasis
  • BUTT — Perpendicular line for measurement marks
  • REVERSE_OPEN_ARROW — Reverse open arrow for backward direction
  • REVERSE_CLOSED_ARROW — Reverse closed arrow for backward emphasis
  • SLASH — Slash mark for termination indicators

Adding a circle annotation

Create a circle with add_circle(x, y, width, height, author, content).

In this sample:

  • The bounds are x=250, y=550, width=100, and height=100.
  • Equal width and height produce a circle.
  • Unequal values produce an ellipse.
  • The style is updated to green and a 2.0 point line width.
circle = annotations.add_circle(
250.0, 550.0, 100.0, 100.0, # x, y, width, height
"Editor",
"Area of interest"
)
# Optionally customize the appearance
circle.color = Color.from_argb(255, 0, 128, 0)
circle.line_width = 2.0

Adding a square annotation

Create a square or rectangle with add_square(x, y, width, height, author, content).

In this sample:

  • The bounds are x=400, y=550, width=150, and height=100.
  • Equal width and height produce a square.
  • Unequal values produce a rectangle.
  • The style is updated to purple and a 2.0 point line width.
square = annotations.add_square(
400.0, 550.0, 150.0, 100.0, # x, y, width, height
"Reviewer",
"Section to review"
)
# Optionally customize the appearance
square.color = Color.from_argb(255, 128, 0, 128)
square.line_width = 2.0

Combining multiple shapes

Use consistent styling across multiple shapes to build one markup set.

This sample creates:

  • A callout line with an open arrow
  • An ellipse highlight
  • A rectangular selection box

All three use:

  • An orange stroke (ARGB 255, 255, 165, 0).
  • 1.5 point line width.
callout_line = annotations.add_line(
50.0, 500.0, 150.0, 450.0, # start_x, start_y, end_x, end_y
"Author", "Callout line"
)
callout_line.end_cap = PdfLineEndingStyle.OPEN_ARROW
callout_line.color = Color.from_argb(255, 255, 165, 0)
callout_line.line_width = 1.5
highlight_circle = annotations.add_circle(
150.0, 420.0, 100.0, 60.0, # x, y, width, height
"Author", "Highlighted area"
)
highlight_circle.color = Color.from_argb(255, 255, 165, 0)
highlight_circle.line_width = 1.5
selection_box = annotations.add_square(
260.0, 420.0, 100.0, 60.0, # x, y, width, height
"Author", "Selection box"
)
selection_box.color = Color.from_argb(255, 255, 165, 0)
selection_box.line_width = 1.5

Saving the document

Save the output PDF and close the editor.

The except block catches NutrientException if processing fails:

editor.save_as("output.pdf")
editor.close()
except NutrientException as e:
print(f"Error: {e}")
if __name__ == "__main__":
main()

Conclusion

Use this workflow to add shape annotations:

  1. Open the document using a context manager(opens in a new tab) for automatic resource cleanup.
  2. Create an editor and access the page collection.
  3. Ensure at least one page exists by adding a letter-size page if needed.
  4. Retrieve the annotation collection from the target page.
  5. Create line annotations with add_line() specifying start and end coordinates.
  6. Customize line endings using start_cap and end_cap properties with PdfLineEndingStyle values.
  7. Create circle annotations with add_circle() for highlighting regions (equal dimensions create perfect circles).
  8. Create square annotations with add_square() for boundary marking (equal dimensions create perfect squares).
  9. Combine multiple shapes with consistent styling for unified visual markup systems.
  10. Save and close the editor.

For related annotation workflows, refer to the Python SDK annotation guides.