Annotation Plugin
The Annotation Plugin provides a comprehensive framework for adding, editing, and managing annotations within a PDF document. It supports a wide range of common annotation types, including text markups (highlight, underline), free-hand drawings (ink), shapes (squares, circles), and more.
The plugin is built on an extensible “Tool” system, allowing you to define and customize different annotation behaviors and appearances.
Installation
This plugin has several dependencies that must be installed to handle user interactions and manage state. The history plugin is optional but recommended for undo/redo functionality.
npm install @embedpdf/plugin-annotation @embedpdf/plugin-interaction-manager @embedpdf/plugin-selection @embedpdf/plugin-historyRegistration
Import AnnotationPluginPackage and its dependencies. It is crucial to register the dependencies before the annotation plugin itself.
import { createPluginRegistration } from '@embedpdf/core'
import { EmbedPDF } from '@embedpdf/core/svelte'
// ... other imports
import { InteractionManagerPluginPackage } from '@embedpdf/plugin-interaction-manager/svelte'
import { SelectionPluginPackage } from '@embedpdf/plugin-selection/svelte'
import { HistoryPluginPackage } from '@embedpdf/plugin-history/svelte'
import { AnnotationPluginPackage } from '@embedpdf/plugin-annotation/svelte'
const plugins = [
// ... other essential plugins
createPluginRegistration(DocumentManagerPluginPackage, { /* ... */ }),
createPluginRegistration(RenderPluginPackage),
// Register dependencies first
createPluginRegistration(InteractionManagerPluginPackage),
createPluginRegistration(SelectionPluginPackage),
createPluginRegistration(HistoryPluginPackage),
// Register and configure the annotation plugin
createPluginRegistration(AnnotationPluginPackage, {
// Optional: Set the author name for created annotations
annotationAuthor: 'Jane Doe',
}),
]Usage
The plugin works by combining a UI component to render the annotations with a store to manage the annotation tools and state.
1. The <AnnotationLayer /> Component
This component is responsible for rendering all annotations and handling user interactions like creating, selecting, moving, and resizing. It must be placed inside the <Scroller />’s renderPage snippet and wrapped by a <PagePointerProvider> to correctly process pointer events.
Note that documentId is required for all components to link them to the correct document state.
<script lang="ts">
import { PagePointerProvider } from '@embedpdf/plugin-interaction-manager/svelte';
import { AnnotationLayer } from '@embedpdf/plugin-annotation/svelte';
let { documentId }: { documentId: string } = $props();
</script>
{#snippet renderPage(page)}
<PagePointerProvider {documentId} pageIndex={page.pageIndex}>
<RenderLayer {documentId} pageIndex={page.pageIndex} />
<SelectionLayer {documentId} pageIndex={page.pageIndex} />
<AnnotationLayer
{documentId}
pageIndex={page.pageIndex}
/>
</PagePointerProvider>
{/snippet}
<Scroller {documentId} {renderPage} />2. Building an Annotation Toolbar
The useAnnotation store provides all the necessary methods to control the plugin for a specific document. You can build a toolbar that allows users to select an “active tool” (like a pen or highlighter) and perform actions like deleting a selected annotation.
<script lang="ts">
import { useAnnotation } from '@embedpdf/plugin-annotation/svelte';
let { documentId }: { documentId: string } = $props();
const annotation = useAnnotation(() => documentId);
const deleteSelected = () => {
const selection = annotation.provides?.getSelectedAnnotation();
if (selection) {
annotation.provides?.deleteAnnotation(selection.object.pageIndex, selection.object.id);
}
};
</script>
<div>
<button onclick={() => annotation.provides?.setActiveTool('highlight')}>Highlighter</button>
<button onclick={() => annotation.provides?.setActiveTool('ink')}>Pen</button>
<button onclick={deleteSelected} disabled={!annotation.state.selectedUid}>Delete</button>
</div>3. Adding a Selection Menu
When an annotation is selected, you can display a contextual menu (e.g., for deleting, editing properties). Use the selectionMenu snippet on <AnnotationLayer />. This snippet receives props including the context object, which contains the selected annotation.
<script lang="ts">
import { AnnotationLayer, useAnnotation } from '@embedpdf/plugin-annotation/svelte';
let { documentId }: { documentId: string } = $props();
const annotation = useAnnotation(() => documentId);
const handleDelete = (pageIndex: number, id: number) => {
annotation.provides?.deleteAnnotation(pageIndex, id);
};
</script>
<AnnotationLayer {documentId} pageIndex={page.pageIndex}>
{#snippet selectionMenuSnippet({ selected, context, menuWrapperProps, rect })}
{#if selected}
<span style={menuWrapperProps.style} use:menuWrapperProps.action>
<div
style:position="absolute"
style:top="{rect.size.height + 8}px"
style:pointer-events="auto"
style:cursor="default"
>
<button onclick={() => handleDelete(context.annotation.object.pageIndex, context.annotation.object.id)}>
Delete
</button>
</div>
</span>
{/if}
{/snippet}
</AnnotationLayer>The menuWrapperProps provides a style string and an action for your wrapper element—the style handles positioning relative to page rotation, while the action handles event propagation to prevent interaction conflicts with underlying layers.
Live Example
Try using the annotation tools below. Select a tool from the toolbar to start creating annotations. Click on an annotation to select it, and use the delete button or the contextual menu to remove it.
Default Annotation Tools
The plugin comes with a set of pre-configured tools. You can activate any of these tools by passing its id to the setActiveTool method.
| Tool Name | id | Description |
|---|---|---|
| Highlight | highlight | Creates text highlight annotations. |
| Underline | underline | Creates text underline annotations. |
| Strikeout | strikeout | Creates text strikeout annotations. |
| Squiggly | squiggly | Creates squiggly text underline annotations. |
| Pen | ink | Free-hand drawing tool. |
| Ink Highlighter | inkHighlighter | Free-hand highlighter with a multiply blend mode. |
| Circle | circle | Draws ellipse annotations. |
| Square | square | Draws rectangle annotations. |
| Line | line | Draws straight line annotations. |
| Arrow | lineArrow | Draws a straight line with an arrowhead. |
| Polyline | polyline | Draws multi-segment lines. |
| Polygon | polygon | Draws closed, multi-sided shapes. |
| Free Text | freeText | Adds a text box annotation. |
| Image | stamp | Adds an image stamp. Opens a file picker by default. |
API Reference
Configuration (AnnotationPluginConfig)
| Option | Type | Description |
|---|---|---|
annotationAuthor | string | Sets the author name for all created annotations. Default: 'Guest' |
autoCommit | boolean | If true, annotation changes are automatically saved to the engine. Default: true |
tools | AnnotationTool[] | An array of custom annotation tools to add or override default tools. |
colorPresets | string[] | A list of hex color strings to be used in a color picker UI. |
deactivateToolAfterCreate | boolean | If true, the active tool is deselected after an annotation is created. Default: false |
selectAfterCreate | boolean | If true, a newly created annotation is automatically selected. Default: true |
Component: <AnnotationLayer />
The primary component for rendering and interacting with annotations.
| Prop | Type | Description |
|---|---|---|
documentId | string | (Required) The ID of the document this layer belongs to. |
pageIndex | number | (Required) The page index this layer corresponds to. |
selectionOutlineColor | string | The color of the outline around a selected annotation. Default: '#007ACC' |
selectionMenu | Snippet | (Optional) A snippet to render a contextual menu when an annotation is selected. |
Snippet: selectionMenu
| Prop | Type | Description |
|---|---|---|
context | object | The selection context containing the annotation object. |
selected | boolean | true if an annotation is currently selected. |
menuWrapperProps | MenuWrapperProps | An object containing style: string for positioning and action: Action to attach to your wrapper element via use:menuWrapperProps.action. |
rect | Rect | The bounding box of the selected annotation. |
Store: useAnnotation(documentId)
Connects your components to the annotation plugin’s state and methods for a specific document.
Parameters
| Parameter | Type | Description |
|---|---|---|
documentId | () => string | A getter function that returns the document ID to track. |
Returns
| Property | Type | Description |
|---|---|---|
provides | AnnotationScope | null | An object with methods to control the plugin, or null if not ready. |
state | AnnotationState | Reactive state object containing properties like activeToolId, selectedUid. |
AnnotationScope Methods
A selection of key methods available on the provides object:
| Method | Description |
|---|---|
setActiveTool(toolId) | Activates an annotation tool (e.g., 'ink', 'highlight'). Pass null to deactivate. |
getActiveTool() | Returns the currently active AnnotationTool object, or null. |
addTool(tool) | Registers a new custom AnnotationTool. |
createAnnotation(..) | Programmatically creates a new annotation on a page. |
updateAnnotation(..) | Updates the properties of an existing annotation. |
deleteAnnotation(..) | Deletes an annotation from a page. |
selectAnnotation(..) | Programmatically selects an annotation. |
getSelectedAnnotation() | Returns the currently selected TrackedAnnotation object, or null. |
importAnnotations(..) | Imports an array of annotations into the viewer. |
commit() | Manually saves all pending annotation changes to the PDF document. |
onStateChange(cb) | Subscribes to any change in the annotation state. |
onAnnotationEvent(cb) | Subscribes to events like annotation creation, updates, or deletion. |
Need Help?
Join our community for support, discussions, and to contribute to EmbedPDF's development.