Skip to content

Annotation tools and appearance

This page is a reference for the built-in annotation types, tool switching, creation modes, and appearance settings. To create an annotation through a complete UI interaction first, follow Create your first annotation. To add a new type, see Custom annotation types.

Built-in annotation types

Every built-in type supports printing and PDF export. native writes a standard PDF annotation dictionary. appearance-stream preserves behavior without an equivalent standard PDF type through a Stamp appearance stream.

The Geometry, Creation, and PDF strategy columns contain values from Annotation Type Definitions and are mainly useful when extending the type system. For ordinary use, focus on the Type ID and Purpose columns.

Type IDPurposeGeometryCreationPDF strategy
highlightText highlighttext-markuptext-selection · continuousnative
strikeoutText strikeouttext-markuptext-selection · continuousnative
underlineText underlinetext-markuptext-selection · continuousnative
free-textPositioned text boxtext-boxtext-input · one-shotnative
rectangleRectangle shapeboxdrag-box · one-shotnative
circleCircle or ellipse shapeboxdrag-box · one-shotnative
freehandMulti-stroke inkpathfreehand · one-shotnative
free-highlightCorrected free highlightpathfreehand · one-shotappearance-stream
signatureImage or ink signatureimageimage-placement · one-shotappearance-stream
stampImage stampimageimage-placement · one-shotnative
notePoint notepointpoint · one-shotnative
lineLine with editable endpointslineline · one-shotnative
arrowArrow with editable endpointslineline · one-shotappearance-stream
polygonClosed polygonpolylinepolyline · one-shotnative
polylineOpen polylinepolylinepolyline · one-shotnative
cloudClosed cloud outlinepolylinepolyline · one-shotappearance-stream

Tools and creation modes

ts
core.annotations.setTool('rectangle')
core.annotations.setTool('text-select')
core.annotations.setTool('select')

rectangle starts drawing a rectangle, text-select lets the user select PDF text, and select edits existing annotations.

Most creation tools return to select after one annotation. highlight, underline, and strikeout remain active by default so users can create more than one text markup. You can change this when creating the Core instance:

ts
const core = await createInkLayer({
  root,
  annotation: {
    creationModes: { rectangle: 'continuous' }
  }
})

Update toolbar state from the emitted toolChanged event, because a one-shot tool can switch back to select after creation. Text markup follows the rule “select text, then create the annotation”; the complete button interaction is shown in Create your first annotation.

Appearance

AnnotationAppearanceInput accepts only the fields you want to change. Omitted fields keep their current values; setting stroke, fill, or text to null disables that appearance component.

ts
core.annotations.setToolAppearance('highlight', {
  stroke: null,
  fill: { color: '#74d13d', opacity: 0.45 }
})

core.annotations.setToolAppearance('rectangle', {
  stroke: { color: '#175cd3', width: 2, dash: [] },
  fill: null
})

Use getAppearanceCapabilities(type) to render valid inspector controls for each annotation type. Hit target width and transformer internals are Core-owned and are intentionally not persisted appearance fields.

FreeText, Signature, and Stamp

FreeText uses the configured TextInputProvider. The browser default creates and manages an in-place textarea. An application can provide another implementation through a Capability without changing annotation semantics.

Signature and Stamp are image annotations. The application creates or selects a PNG or JPEG data URL, then gives Core the asset to place:

ts
core.annotations.setImageAsset('signature', {
  image: signatureDataUrl,
  width: 180,
  height: 60,
  text: 'Ada signature'
})
core.annotations.setTool('signature')

Core handles the cursor preview, placement, selection, transforms, rendering, and PDF output. If no asset has been set, clicking the page emits imageAssetRequired, allowing the application to open its picker.

Annotation data and collaboration

core.annotations.repository is the current instance's annotation data store and the single source of truth for annotations, selection, comments, references, and permissions. Pass your own repository when state must outlive an engine instance, or install it with createAnnotationRepositoryCapability().

Persist canonical Annotation values only. Validate untrusted input before inserting it. Framework UI may keep panel state and optimistic network status separately, but must not create a second annotation model. Follow the complete save and restore guide.

Custom annotation types

Register namespaced definitions through @inklayer-dev/core/annotation-types. Definitions receive validated data and return controlled renderer-neutral scene values; they never receive Konva nodes or PDF.js internals. See the custom annotation type tutorial for a complete Definition and lifecycle example, and the Public API for registration, missing definition behavior, transform reducers, and PDF appearance streams.

Attach pages manually

Page Flow handles this automatically. If your adapter owns page layout, add an empty annotation layer above each page canvas and attach it after the page dimensions are known:

ts
await core.annotations.attachPage({
  pageIndex: 0,
  container: annotationLayer,
  width: unscaledPageWidth,
  height: unscaledPageHeight,
  scale: currentScale
})

Update or reattach the layer when scale changes. When the page unmounts, call core.annotations.detachPage(pageIndex). Annotation coordinates remain in unscaled page units.

Released under the MIT License.