DEV Community

Allen Yang
Allen Yang

Posted on

How to Add Annotations and Markups to PDFs Using Python

Adding Annotations and Markups to PDF Documents Using Python

In PDF document processing scenarios, adding annotations is a practical and common requirement. Whether for document review, collaborative editing, or content marking, annotation functionality enables us to add notes, highlight key points, or insert comments without modifying the original text. This article will provide a comprehensive guide on how to add various types of annotations and markups to PDF documents using Python.

Why Add Annotations to PDF Documents

Manually adding annotations to PDF documents is not only inefficient but also makes it difficult to maintain consistent formatting. Implementing this functionality programmatically offers the following advantages:

  • Batch Processing - Add annotations with identical formatting to multiple documents at once
  • Precise Control - Pixel-level positioning of annotations to ensure perfect alignment with document content
  • Rich Types - Programmatically create various annotations including highlights, underlines, free text, stamps, and more
  • Style Consistency - Uniformly configure colors, opacity, borders, and other properties to maintain a professional appearance

Environment Setup

First, install a Python library that supports PDF operations. This article uses Spire.PDF for Python, which provides a complete PDF annotation API.

pip install Spire.PDF
Enter fullscreen mode Exit fullscreen mode

This library supports multiple annotation types including text markup, free text, stamps, lines, attachments, and more, meeting various document annotation requirements.

Core Annotation Types

Spire.PDF supports a rich variety of annotation types, each with its specific application scenarios:

1. Free Text Annotation

Free text annotations allow adding editable text boxes on PDF pages, suitable for adding detailed explanations or comments.

from spire.pdf import *

# Create PDF document
doc = PdfDocument()
page = doc.Pages.Add()

# Define annotation area
rect = RectangleF(100.0, 200.0, 200.0, 50.0)

# Create free text annotation
textAnnotation = PdfFreeTextAnnotation(rect)
textAnnotation.Text = "\nThis is a free text annotation"

# Set font and border
font = PdfTrueTypeFont("Arial", 12.0, PdfFontStyle.Regular, True)
textAnnotation.Font = font

# Set border style
border = PdfAnnotationBorder(1.5)
textAnnotation.Border = border
textAnnotation.BorderColor = PdfRGBColor(Color.get_Blue())

# Set line ending style
textAnnotation.LineEndingStyle = PdfLineEndingStyle.Circle

# Set background color and opacity
textAnnotation.Color = PdfRGBColor(Color.get_LightBlue())
textAnnotation.Opacity = 0.8

# Add to page
page.AnnotationsWidget.Add(textAnnotation)

# Save document
doc.SaveToFile("FreeTextAnnotation.pdf")
doc.Close()
Enter fullscreen mode Exit fullscreen mode

Key API Explanation:

  • PdfFreeTextAnnotation - Creates editable text annotation, requires specifying a rectangular area
  • PdfAnnotationBorder - Defines border width to make the annotation appearance clearer
  • PdfLineEndingStyle - Controls the ending style of annotation borders (circle, arrow, square, etc.)
  • Opacity - Sets transparency level (0.0-1.0), where 0.0 is fully transparent and 1.0 is fully opaque

2. Text Markup Annotation

Text markup annotations are used to highlight, underline, or strikethrough specific text, making them the most commonly used feature in document review.

from spire.pdf import *

# Load existing PDF document
doc = PdfDocument()
doc.LoadFromFile("template.pdf")
page = doc.Pages[0]

# Get text position to mark
font = PdfTrueTypeFont("Arial", 12.0, PdfFontStyle.Regular, True)
format = PdfStringFormat()

# Draw prompt text
prompt = "Text to highlight:"
page.Canvas.DrawString(prompt, font, PdfBrushes.get_DodgerBlue(), 0.0, 50.0)

# Calculate target text position
x = font.MeasureString(prompt, format).Width
label = "This is an important paragraph"
page.Canvas.DrawString(label, font, PdfBrushes.get_OrangeRed(), x, 50.0)

# Create highlight annotation
incorrectWordLocation = PointF(x, 50.0)
markupText = "Important content reminder"
annotation = PdfTextMarkupAnnotation(
    markupText, 
    "Important paragraph",
    RectangleF(x, 50.0, 100.0, 20.0), 
    font
)

# Set highlight type to highlight
annotation.TextMarkupAnnotationType = PdfTextMarkupAnnotationType.Highlight

# Set highlight color
annotation.TextMarkupColor = PdfRGBColor(Color.get_Yellow())

# Add to page
page.AnnotationsWidget.Add(annotation)

# Save document
doc.SaveToFile("TextMarkupAnnotation.pdf")
doc.Close()
Enter fullscreen mode Exit fullscreen mode

Supported Markup Types:

  • Highlight - Highlight display, similar to highlighter pen effect
  • Underline - Add underline
  • StrikeOut - Add strikethrough
  • Squiggly - Add wavy line

3. Line Annotation

Line annotations are used to draw straight lines with arrows in documents, suitable for indicating, connecting, or measuring.

from spire.pdf import *

doc = PdfDocument()
page = doc.Pages.Add()

# Define start and end point coordinates of the line [x1, y1, x2, y2]
linePoints = [100, 400, 300, 400]

# Create line annotation
lineAnnotation = PdfLineAnnotation(linePoints, "Dimension measurement")

# Set line intent (affects how the reader displays it)
lineAnnotation.LineIntent = PdfLineIntent.LineDimension

# Set start and end styles
lineAnnotation.BeginLineStyle = PdfLineEndingStyle.ClosedArrow
lineAnnotation.EndLineStyle = PdfLineEndingStyle.Diamond

# Set line color and background color
lineAnnotation.InnerLineColor = PdfRGBColor(Color.get_Green())
lineAnnotation.BackColor = PdfRGBColor(Color.get_Green())

# Set border style
lineAnnotation.lineBorder.BorderStyle = PdfBorderStyle.Solid
lineAnnotation.lineBorder.BorderWidth = 2

# Add to page
page.AnnotationsWidget.Add(lineAnnotation)

# Save document
doc.SaveToFile("LineAnnotation.pdf")
doc.Close()
Enter fullscreen mode Exit fullscreen mode

Line Style Options:

  • PdfLineEndingStyle.None - No line ending decoration
  • PdfLineEndingStyle.Butt - Butt endpoint
  • PdfLineEndingStyle.ClosedArrow - Closed arrow
  • PdfLineEndingStyle.OpenArrow - Open arrow
  • PdfLineEndingStyle.Diamond - Diamond endpoint
  • PdfLineEndingStyle.Circle - Circle endpoint

4. Rubber Stamp Annotation

Rubber stamp annotations are used to add predefined or custom status markers, such as "Approved", "Confidential", "Draft", etc.

from spire.pdf import *
from datetime import datetime

# Load PDF document
document = PdfDocument()
document.LoadFromFile("document.pdf")
page = document.Pages[0]

# Create custom stamp template
template = PdfTemplate(150.0, 60.0)

# Set font and color
font1 = PdfTrueTypeFont("Arial", 14.0, PdfFontStyle.Bold, True)
brush = PdfSolidBrush(PdfRGBColor(Color.get_Red()))

# Draw rounded rectangle border
rectangle = RectangleF(PointF(5.0, 5.0), SizeF(140.0, 50.0))
path = PdfPath()
path.AddArc(rectangle.X, rectangle.Y, 10.0, 10.0, 180.0, 90.0)
path.AddArc(rectangle.X + rectangle.Width - 10.0, rectangle.Y, 10.0, 10.0, 270.0, 90.0)
path.AddArc(rectangle.X + rectangle.Width - 10.0, rectangle.Y + rectangle.Height - 10.0, 
            10.0, 10.0, 0.0, 90.0)
path.AddArc(rectangle.X, rectangle.Y + rectangle.Height - 10.0, 10.0, 10.0, 90.0, 90.0)
path.CloseFigure()
template.Graphics.DrawPath(PdfPen(brush, 2.0), path)

# Draw stamp text
stampText = "✓ Approved\n" + datetime.now().strftime("%Y-%m-%d")
template.Graphics.DrawString(stampText, font1, brush, PointF(10.0, 15.0))

# Create rubber stamp annotation
stamp = PdfRubberStampAnnotation(RectangleF(PointF(400.0, 50.0), SizeF(150.0, 60.0)))
appearance = PdfAppearance(stamp)
appearance.Normal = template
stamp.Appearance = appearance

# Set metadata
stamp.Author = "Reviewer"
stamp.Subject = "Document Approval"
stamp.ModifiedDate = datetime.now()

# Add to page
page.AnnotationsWidget.Add(stamp)

# Save document
document.SaveToFile("StampAnnotation.pdf")
document.Close()
Enter fullscreen mode Exit fullscreen mode

Predefined Stamp Icons:

If you don't want to customize stamps, you can use built-in icons:

# Use predefined "Draft" stamp
stamp = PdfRubberStampAnnotation(
    RectangleF(100.0, 100.0, 100.0, 40.0), 
    "Draft status"
)
stamp.Icon = PdfRubberStampAnnotationIcon.Draft
stamp.Color = PdfRGBColor(Color.get_Plum())
page.AnnotationsWidget.Add(stamp)
Enter fullscreen mode Exit fullscreen mode

Common predefined icons include:

  • Draft - Draft
  • Approved - Approved
  • Confidential - Confidential
  • Final - Final version
  • Experimental - Experimental

5. Popup Annotation

Popup annotations are typically used in conjunction with other annotations, displaying detailed information when hovering the mouse over them.

from spire.pdf import *

doc = PdfDocument()
page = doc.Pages.Add()

# First create a text markup
font = PdfTrueTypeFont("Arial", 12.0, PdfFontStyle.Regular, True)
page.Canvas.DrawString("Hover over the annotation to view notes", font, 
                       PdfBrushes.get_Black(), 50.0, 100.0)

# Create popup annotation
popupRect = RectangleF(PointF(100.0, 150.0), SizeF.Empty())
popupAnnotation = PdfPopupAnnotation(popupRect, "This is detailed annotation information\nCan contain multiple lines of text")

# Set icon style
popupAnnotation.Icon = PdfPopupIcon.Paragraph

# Set whether to expand by default
popupAnnotation.Open = True

# Set color
popupAnnotation.Color = PdfRGBColor(Color.get_Yellow())

# Add to page
page.AnnotationsWidget.Add(popupAnnotation)

# Save document
doc.SaveToFile("PopupAnnotation.pdf")
doc.Close()
Enter fullscreen mode Exit fullscreen mode

6. Polygon and Polyline Annotation

Polygon and polyline annotations are used to create annotation areas with custom shapes.

from spire.pdf import *
from datetime import datetime

doc = PdfDocument()
page = doc.Pages.Add()

# Create polygon annotation (closed shape)
polygon = PdfPolygonAnnotation(
    page, 
    [PointF(100.0, 200.0), PointF(150.0, 180.0), PointF(200.0, 200.0), 
     PointF(180.0, 250.0), PointF(120.0, 250.0)]
)

# Set polygon properties
polygon.Color = PdfRGBColor(Color.get_PaleVioletRed())
polygon.Text = "Key focus area"
polygon.Author = "Reviewer"
polygon.Subject = "Area marking"
polygon.BorderEffect = PdfBorderEffect.BigCloud  # Cloud border effect
polygon.ModifiedDate = datetime.now()

# Add to page
page.AnnotationsWidget.Add(polygon)

# Save document
doc.SaveToFile("PolygonAnnotation.pdf")
doc.Close()
Enter fullscreen mode Exit fullscreen mode

Comprehensive Example: Creating a Review Document with Multiple Annotation Types

In practical applications, multiple annotation types are often used in combination. The following example demonstrates how to add multiple types of annotations to a single document:

from spire.pdf import *
from datetime import datetime

# Create document
doc = PdfDocument()
margin = PdfMargins()
margin.Top = 50
margin.Bottom = 50
margin.Left = 50
margin.Right = 50
page = doc.Pages.Add(PdfPageSize.A4(), margin)

# Add title
titleFont = PdfTrueTypeFont("Arial", 16.0, PdfFontStyle.Bold, True)
page.Canvas.DrawString("Document Review Example", titleFont, 
                       PdfBrushes.get_Black(), 0.0, 30.0)

# Add body text
contentFont = PdfTrueTypeFont("Arial", 12.0, PdfFontStyle.Regular, True)
content = "This is a text content that needs review. Some parts need to be highlighted, and some parts need comments added."
page.Canvas.DrawString(content, contentFont, 
                       PdfBrushes.get_Black(), 0.0, 80.0)

# 1. Add free text annotation
freeTextRect = RectangleF(50.0, 150.0, 250.0, 60.0)
freeText = PdfFreeTextAnnotation(freeTextRect)
freeText.Text = "\nReview comments:\nThe wording here needs to be clearer and more precise"
freeText.Font = PdfTrueTypeFont("Arial", 12.0, PdfFontStyle.Regular, True)
freeText.Color = PdfRGBColor(Color.get_LightYellow())
freeText.Opacity = 0.9
page.AnnotationsWidget.Add(freeText)

# 2. Add rubber stamp annotation
stampTemplate = PdfTemplate(120.0, 50.0, True)
stampFont = PdfTrueTypeFont("Arial", 12.0, PdfFontStyle.Bold, True)
stampBrush = PdfSolidBrush(PdfRGBColor(Color.get_Red()))
stampTemplate.Graphics.DrawString("Needs Revision", stampFont, stampBrush, PointF(10.0, 15.0))

stamp = PdfRubberStampAnnotation(RectangleF(PointF(350.0, 150.0), SizeF(120.0, 50.0)))
stampAppearance = PdfAppearance(stamp)
stampAppearance.Normal = stampTemplate
page.AnnotationsWidget.Add(stamp)

# 3. Add line annotation pointing to specific location
linePoints = [320, 200, 350, 180]
lineAnnot = PdfLineAnnotation(linePoints, "Refer to this section")
lineAnnot.BeginLineStyle = PdfLineEndingStyle.Butt
lineAnnot.EndLineStyle = PdfLineEndingStyle.ClosedArrow
lineAnnot.InnerLineColor = PdfRGBColor(Color.get_Blue())
page.AnnotationsWidget.Add(lineAnnot)

# Save document
doc.SaveToFile("ComprehensiveReview.pdf")
doc.Close()
Enter fullscreen mode Exit fullscreen mode

Generated Result Preview:

Python Create PDF Annotations and Markups

Practical Tips

Setting Annotation Author and Timestamp

annotation = PdfFreeTextAnnotation(rect)
annotation.Author = "John Smith"  # Set author
annotation.CreationDate = datetime.now()  # Creation time
annotation.ModifiedDate = datetime.now()  # Modification time
Enter fullscreen mode Exit fullscreen mode

Controlling Annotation Display Hierarchy

# Set annotation flags
annotation.Flags = PdfAnnotationFlags.Locked  # Lock annotation to prevent deletion
# Or use other flags
# PdfAnnotationFlags.Hidden - Hide annotation
# PdfAnnotationFlags.NoView - Do not display in view
# PdfAnnotationFlags.ReadOnly - Read-only mode
Enter fullscreen mode Exit fullscreen mode

Extracting All Annotations from a Page

# Load document and get annotation collection
doc = PdfDocument()
doc.LoadFromFile("annotated.pdf")
annotations = doc.Pages[0].AnnotationsWidget

# Iterate through all annotations
if annotations.Count > 0:
    for i in range(annotations.Count):
        annotation = annotations.get_Item(i)

        # Handle different annotations based on type
        if isinstance(annotation, PdfFreeTextAnnotationWidget):
            print(f"Free text: {annotation.Text}")
        elif isinstance(annotation, PdfTextMarkupAnnotationWidget):
            print(f"Highlight text: {annotation.Text}")

        print(f"Author: {annotation.Author}")
        print(f"Modification date: {annotation.ModifiedDate}")
Enter fullscreen mode Exit fullscreen mode

Summary

This article has introduced methods for adding various types of annotations to PDF documents using Python. Through the rich API provided by Spire.PDF, developers can:

  • Create multiple annotation types including free text, highlights, lines, stamps, and more
  • Precisely control annotation properties such as position, color, opacity, and more
  • Batch process document review and annotation tasks
  • Extract and manage existing annotation information

These capabilities make PDF annotation automation possible, significantly improving the efficiency of document collaboration and review. In actual projects, different annotation types can be combined according to specific requirements to create professional interactive PDF documents.

Top comments (0)