-
Notifications
You must be signed in to change notification settings - Fork 1
Annotations
modern-pdf-lib v0.15.1 — 18 annotation types with auto-generated appearances.
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.
// 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';| 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 |
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);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';
};
}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. |
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).
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);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. |
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'.
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 },
});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 },
});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'. |
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,
});import { PdfStampAnnotation } from 'modern-pdf-lib';
const stamp = PdfStampAnnotation.create({
rect: [72, 50, 300, 120],
stampName: 'Approved', // 'Approved' | 'Experimental' | 'NotApproved' | 'Draft' | 'Final' | 'Confidential' | ...
});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
],
});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);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. |
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. |
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
}// 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);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.