TPTruPDF

@trupdf/annotations

40+ annotation types, XFDF round-trip, the interactive annotation canvas layer, undo/redo, threads + replies + status, measurement helpers, stylus input.

Core types

type AnnotationKind =
  // Text markup
  | 'highlight' | 'underline' | 'strikethrough' | 'squiggly'
  // Notes / text
  | 'sticky' | 'freetext' | 'callout' | 'typewriter' | 'caret'
  // Shapes
  | 'rectangle' | 'ellipse' | 'polygon' | 'polyline' | 'line'
  | 'arrow' | 'arc' | 'cloud'
  // Freehand
  | 'ink' | 'pencil' | 'freehand-highlight' | 'handwritten-signature'
  // Stamps
  | 'stamp' | 'rubber-stamp' | 'image-stamp'
  // Attachments
  | 'file-attachment' | 'sound'
  // Fields / redaction
  | 'signature-field' | 'redaction-mark'
  // Measurement
  | 'distance' | 'arc-measurement' | 'perimeter' | 'area'
  | 'rectangle-area' | 'ellipse-area' | 'count'
  // Fill & sign quick marks
  | 'check-mark' | 'cross-mark' | 'dot-mark' | 'date-stamp'
  // Misc
  | 'link' | 'image' | 'watermark' | 'reply';

AnnotationManager

The single source of truth. UI components subscribe to events and derive view state — never duplicate the records.

const m = new AnnotationManager({ defaultAuthor: 'Reviewer' });
const id = m.add({ kind: 'highlight', page: 1, rect: …, quadPoints: … });
m.update(id, { color: { r: 1, g: 0.85, b: 0 } });
m.undo();
m.redo();
m.on('annotation.added', (a) => /* … */);

Supporting classes: SelectionModel (multi-select), AnnotationClipboard (copy/paste across pages), PresetStore (per-tool style presets), CommandStack (AddCommand/UpdateCommand/RemoveCommand/BatchCommand).

Wire formats

  • serializeXFDF(records) / deserializeXFDF(xml) — Acrobat-compatible.
  • serializeJSON(records) / deserializeJSON(json) — REST API format.
  • writeAnnotationDict / readPdfAnnotations — write and read annotations directly into / out of the PDF itself, so TruPDF-saved files render identically in Acrobat, Chrome, Firefox, Edge, and Apple Preview.

The XFDF emitter uses an Adobe-namespaced root with a trupdf: extension for fields XFDF doesn't model (status, custom-data, font, measurement units).

Annotation canvas layer

new FabricLayer({ pageNumber, manager, viewport, container }) mounts the interactive annotation canvas onto a host <div>, subscribes to manager events, and re-renders on annotation.added/updated/removed/cleared. Coordinate helpers (pdfToFabricPoint, pdfToFabricRect, fabricToPdfPoint) convert between PDF user space and canvas space — never store canvas pixels in XFDF.

Tool controller

ToolController drives interactive creation — ~50 tool kinds are registered in TOOLS / TOOLS_BY_KIND, grouped into ribbon tabs (annotation, shapes, insert, measure, redact, edit, fill-sign, forms) via TOOL_MENUS / toolsForMenu. Bind to an annotation canvas layer and call setTool('highlight') etc. Hotkey map exported as TOOL_HOTKEYS. Legacy WebViewer tool names map via legacyToolNameToKind / kindToLegacyToolName.

Measurement

calibrate(a, b, realValue, unit)unitsPerPoint. Then measureDistance / measurePerimeter / measureArea apply that calibration in PDF user space. Arc helpers (arcLengthFromBezier, circularArcLength, ellipsePerimeter, …) and formatting (formatMeasurement, formatFractional) are exported alongside.

Stylus input

bindStylusInput(target, opts) with palm rejection, pressure, tilt, twist, coalesced events. applyPressureCurve ships soft / linear / firm curves.