Skip to content

Annotations

ABCrimson edited this page Mar 1, 2026 · 5 revisions

Annotations

modern-pdf-lib v0.15.1 — 18 annotation types with auto-generated appearances.


Overview

PDF annotations are interactive or visual elements overlaid on a page. modern-pdf-lib supports the full set of standard PDF annotation subtypes, each backed by a dedicated class with typed getters/setters and an appearance generator that produces valid /AP /N streams so annotations render without requiring a viewer to generate them.


Imports

// Base class and factory
import {
  PdfAnnotation,
  createAnnotation,
  AnnotationFlags,
} from 'modern-pdf-lib';

// Specific annotation types
import {
  PdfHighlightAnnotation,
  PdfUnderlineAnnotation,
  PdfSquigglyAnnotation,
  PdfStrikeOutAnnotation,
} from 'modern-pdf-lib';

import {
  PdfLineAnnotation,
  PdfSquareAnnotation,
  PdfCircleAnnotation,
  PdfPolygonAnnotation,
  PdfPolyLineAnnotation,
} from 'modern-pdf-lib';

import { PdfTextAnnotation }           from 'modern-pdf-lib';
import { PdfFreeTextAnnotation }       from 'modern-pdf-lib';
import { PdfStampAnnotation }          from 'modern-pdf-lib';
import { PdfInkAnnotation }            from 'modern-pdf-lib';
import { PdfLinkAnnotation }           from 'modern-pdf-lib';
import { PdfRedactAnnotation }         from 'modern-pdf-lib';
import { PdfPopupAnnotation }          from 'modern-pdf-lib';
import { PdfCaretAnnotation }          from 'modern-pdf-lib';
import { PdfFileAttachmentAnnotation } from 'modern-pdf-lib';

Annotation Types

Type Class Subtype (/Subtype) Appearance
Sticky note PdfTextAnnotation Text Icon with popup
Free text PdfFreeTextAnnotation FreeText Text box in-place
Line PdfLineAnnotation Line Line with end caps
Rectangle PdfSquareAnnotation Square Rectangle outline
Ellipse PdfCircleAnnotation Circle Ellipse outline
Polygon PdfPolygonAnnotation Polygon Closed polygon
Open polygon PdfPolyLineAnnotation PolyLine Open polyline
Highlight PdfHighlightAnnotation Highlight Translucent fill
Underline PdfUnderlineAnnotation Underline Line under text
Squiggly PdfSquigglyAnnotation Squiggly Wavy underline
Strike-out PdfStrikeOutAnnotation StrikeOut Line through text
Stamp PdfStampAnnotation Stamp Rubber-stamp label
Ink PdfInkAnnotation Ink Freehand strokes
Link PdfLinkAnnotation Link Clickable region
Redaction PdfRedactAnnotation Redact Redaction mark
Popup PdfPopupAnnotation Popup Comment popup window
Caret PdfCaretAnnotation Caret Text insertion point
File attachment PdfFileAttachmentAnnotation FileAttachment Embedded file icon

Creating Annotations

Generic Factory

import { createAnnotation, AnnotationFlags } from 'modern-pdf-lib';

const annot = createAnnotation('Text', {
  rect:     [72, 700, 100, 728],  // [x1, y1, x2, y2] in user space
  contents: 'Review this section',
  author:   'Alice',
  color:    { r: 1, g: 1, b: 0 },
  opacity:  0.8,
  flags:    AnnotationFlags.Print,
});

page.addAnnotation(annot);

AnnotationOptions (shared by all types)

interface AnnotationOptions {
  rect:              [number, number, number, number]; // required
  contents?:         string;
  author?:           string;
  modificationDate?: Date;
  color?:            { r: number; g: number; b: number };
  opacity?:          number;   // 0–1
  flags?:            number;   // AnnotationFlags bitmask
  border?: {
    width:  number;
    style?: 'solid' | 'dashed' | 'beveled' | 'inset' | 'underline';
  };
}

Annotation Flags

Combine flags with bitwise OR.

import { AnnotationFlags } from 'modern-pdf-lib';

const annot = createAnnotation('Stamp', {
  rect:  [50, 50, 200, 100],
  flags: AnnotationFlags.Print | AnnotationFlags.Locked,
});
Flag Bit Description
Invisible 0 Do not display if viewer does not recognise the subtype.
Hidden 1 Do not display or print.
Print 2 Print when the page is printed.
NoZoom 3 Do not scale with the page zoom level.
NoRotate 4 Do not rotate when the page rotates.
NoView 5 Do not display on screen (print-only annotation).
ReadOnly 6 Cannot be interacted with by the user.
Locked 7 Cannot be moved, resized, or deleted.
ToggleNoView 8 Invert NoView flag when selected.
LockedContents 9 Contents cannot be edited.

Text Markup Annotations

Highlight, Underline, Squiggly, and StrikeOut annotations use QuadPoints — arrays of 8 numbers per quad defining the four corners of a text region. Each quad is [x1,y2, x2,y2, x1,y1, x2,y1] (bottom-left, bottom-right, top-left, top-right).

Highlight

const highlight = PdfHighlightAnnotation.create({
  rect:  [72, 690, 400, 710],
  color: { r: 1, g: 1, b: 0 },   // yellow
  // quadPoints auto-derived from rect if omitted
});

// Or specify quad points explicitly (multiple regions)
const multiRegion = PdfHighlightAnnotation.create({
  rect:       [72, 680, 400, 720],
  color:      { r: 0.2, g: 0.8, b: 1 },
  quadPoints: [
    72, 710, 400, 710, 72, 690, 400, 690,   // first line
    72, 690, 250, 690, 72, 670, 250, 670,   // second line
  ],
});

// Quick factory from rect
const quick = PdfHighlightAnnotation.createForRect(
  [72, 690, 400, 710],
  { r: 1, g: 0.8, b: 0 },
);

page.addAnnotation(highlight);

Underline, Squiggly, StrikeOut

All three follow the same API:

const underline = PdfUnderlineAnnotation.create({
  rect:  [72, 690, 400, 710],
  color: { r: 0, g: 0, b: 1 },
});

const squiggly = PdfSquigglyAnnotation.create({
  rect:  [72, 690, 400, 710],
  color: { r: 1, g: 0, b: 0 },
});

const strikeOut = PdfStrikeOutAnnotation.create({
  rect:  [72, 690, 400, 710],
  color: { r: 0.5, g: 0.5, b: 0.5 },
});

// Access / modify quad points after creation
highlight.getQuadPoints(); // number[]
highlight.setQuadPoints([72, 710, 400, 710, 72, 690, 400, 690]);
Method Returns Description
getQuadPoints() number[] Raw flat array of quad point coordinates.
setQuadPoints(pts) void Replace the quad points array.
generateAppearance() PdfStream Auto-generates the visual appearance stream.

Shape Annotations

Line

import { PdfLineAnnotation } from 'modern-pdf-lib';

const line = PdfLineAnnotation.create({
  rect:          [72, 700, 300, 720],
  startPoint:    [72, 710],
  endPoint:      [300, 710],
  color:         { r: 1, g: 0, b: 0 },
  startStyle:    'None',
  endStyle:      'ClosedArrow',
  interiorColor: { r: 1, g: 0, b: 0 },
});

page.addAnnotation(line);

Line ending styles: 'None', 'Square', 'Circle', 'Diamond', 'OpenArrow', 'ClosedArrow', 'Butt', 'ROpenArrow', 'RClosedArrow', 'Slash'.

Rectangle and Ellipse

const square = PdfSquareAnnotation.create({
  rect:          [72, 650, 200, 720],
  color:         { r: 0, g: 0.5, b: 1 },    // border
  interiorColor: { r: 0.9, g: 0.95, b: 1 }, // fill
  border:        { width: 2 },
});

const circle = PdfCircleAnnotation.create({
  rect:          [72, 580, 200, 640],
  color:         { r: 0.8, g: 0, b: 0 },
  interiorColor: { r: 1, g: 0.9, b: 0.9 },
});

Polygon and PolyLine

const polygon = PdfPolygonAnnotation.create({
  rect:   [72, 500, 300, 600],
  points: [150, 600, 72, 500, 300, 500],  // flat [x,y, x,y, ...]
  color:  { r: 0, g: 0.6, b: 0 },
});

const polyline = PdfPolyLineAnnotation.create({
  rect:   [72, 400, 300, 500],
  points: [72, 400, 150, 500, 300, 450],
  color:  { r: 0.5, g: 0, b: 0.5 },
});

Link Annotations

import { PdfLinkAnnotation } from 'modern-pdf-lib';

// External URL
const urlLink = PdfLinkAnnotation.create({
  rect:          [72, 700, 300, 720],
  url:           'https://example.com',
  highlightMode: 'Invert',
});

// Internal page destination
const pageLink = PdfLinkAnnotation.create({
  rect:      [72, 660, 300, 680],
  pageIndex: 4,
  fit:       'FitH',
});

page.addAnnotation(urlLink);
page.addAnnotation(pageLink);
Method Description
getUrl() / setUrl(url) URI action for external links.
getDestination() / setDestination(pageIndex, fit?) Explicit page destination.
getHighlightMode() / setHighlightMode(mode) Visual effect on click: 'None', 'Invert', 'Outline', 'Push'.

Other Annotation Types

Text (Sticky Note)

import { PdfTextAnnotation } from 'modern-pdf-lib';

const note = PdfTextAnnotation.create({
  rect:     [500, 750, 520, 770],
  contents: 'Please confirm figures.',
  author:   'Reviewer',
  iconName: 'Note',   // 'Note' | 'Comment' | 'Key' | 'Help' | 'NewParagraph' | 'Paragraph' | 'Insert'
  isOpen:   false,
});

Stamp

import { PdfStampAnnotation } from 'modern-pdf-lib';

const stamp = PdfStampAnnotation.create({
  rect:      [72, 50, 300, 120],
  stampName: 'Approved',  // 'Approved' | 'Experimental' | 'NotApproved' | 'Draft' | 'Final' | 'Confidential' | ...
});

Ink (Freehand Drawing)

import { PdfInkAnnotation } from 'modern-pdf-lib';

const ink = PdfInkAnnotation.create({
  rect:  [72, 400, 300, 500],
  color: { r: 0, g: 0, b: 1 },
  inkLists: [
    [72, 450, 100, 480, 130, 460, 160, 490],  // stroke 1
    [200, 420, 220, 500, 260, 440],            // stroke 2
  ],
});

Redaction

import { PdfRedactAnnotation } from 'modern-pdf-lib';

const redact = PdfRedactAnnotation.create({
  rect:           [72, 690, 300, 710],
  overlayText:    'REDACTED',
  fillColor:      { r: 0, g: 0, b: 0 },
});

// Apply the redaction (removes underlying content)
await redact.apply(page);

Caret (Text Insertion Point)

import { PdfCaretAnnotation } from 'modern-pdf-lib';

const caret = PdfCaretAnnotation.create({
  rect:   [150, 700, 170, 720],
  color:  { r: 0, g: 0, b: 1 },
  symbol: 'P',          // 'None' | 'P' (paragraph)
  contents: 'Insert new paragraph here',
});

page.addAnnotation(caret);
Method Description
getSymbol() / setSymbol(symbol) Caret symbol: 'None' or 'P' (paragraph).
getCaretRect() / setCaretRect(rect) Optional tight bounding box for the caret glyph.

File Attachment

import { PdfFileAttachmentAnnotation } from 'modern-pdf-lib';

const attachment = PdfFileAttachmentAnnotation.create({
  rect:            [72, 700, 100, 728],
  fileName:        'report.csv',
  fileData:        csvBytes,       // Uint8Array
  mimeType:        'text/csv',
  fileDescription: 'Q4 revenue data',
  icon:            'Paperclip',    // 'GraphPushPin' | 'PaperclipTag' | 'Paperclip' | 'Tag'
});

page.addAnnotation(attachment);
Method Description
getIcon() / setIcon(icon) Display icon for the attachment.
getFileName() Name of the embedded file.
getFileData() Raw file bytes (Uint8Array).
buildFileSpec(registry) Builds the /FS file specification dictionary.

Base Class API

All annotation classes extend PdfAnnotation.

class PdfAnnotation {
  // Type
  getType(): AnnotationType

  // Geometry
  getRect(): [number, number, number, number]
  setRect(rect: [number, number, number, number]): void

  // Content
  getContents(): string | undefined
  setContents(contents: string): void

  // Authoring
  getAuthor(): string | undefined
  setAuthor(author: string): void

  // Colour
  getColor(): { r: number; g: number; b: number } | undefined
  setColor(color: { r: number; g: number; b: number }): void

  // Opacity
  getOpacity(): number   // defaults to 1
  setOpacity(opacity: number): void

  // Flags
  isHidden(): boolean
  setHidden(hidden: boolean): void
  isPrintable(): boolean
  setPrintable(printable: boolean): void
  isLocked(): boolean
  setLocked(locked: boolean): void

  // Appearance
  generateAppearance(): PdfStream | undefined

  // Serialization
  toDict(registry: PdfObjectRegistry): PdfDict
  getDict(): PdfDict
}

Adding Annotations to a Page

// Add a single annotation
page.addAnnotation(highlight);

// Add multiple at once
page.addAnnotations([highlight, underline, link]);

// Get all annotations on a page
const annotations = page.getAnnotations();
annotations.forEach(a => console.log(a.getType(), a.getRect()));

// Remove an annotation
page.removeAnnotation(highlight);

Appearance Generation

Every subclass that overrides generateAppearance() produces a complete /AP /N form XObject stream. This means the annotation will render correctly in any conformant PDF viewer without requiring the viewer to construct an appearance itself — important for embedded viewers, printing pipelines, and PDF/A archival.

Class Appearance Function
PdfHighlightAnnotation generateHighlightAppearance()
PdfUnderlineAnnotation generateUnderlineAppearance()
PdfSquigglyAnnotation generateSquigglyAppearance()
PdfStrikeOutAnnotation generateStrikeOutAppearance()
PdfLineAnnotation generateLineAppearance()
PdfSquareAnnotation generateSquareAppearance()
PdfCircleAnnotation generateCircleAppearance()

Appearances are generated automatically when toDict(registry) is called, so you do not need to invoke the generation functions directly.

Clone this wiki locally