Annotations
Annotations are what people add on top of a page: highlights, sticky notes, shapes, stamps and text boxes. The annotation plugin shows the ones a PDF already has, lets people draw and edit new ones, and gives your code the same abilities. Every annotation is saved into the PDF, so it looks the same in every other PDF app.
Show the annotations in a PDF#
Register interactionPlugin() and annotationPlugin(), then put an <AnnotationLayer> above the
rendered page:
Loading live preview…
<script setup lang="ts">
import { DocumentGate, Viewer } from '@embedpdf/vue/runtime';
import type { OpenInput } from '@embedpdf/vue/runtime';
import { Stage, stagePlugin } from '@embedpdf/vue/stage';
import { RenderLayer, renderPlugin } from '@embedpdf/vue/render';
import { interactionPlugin } from '@embedpdf/vue/interaction';
import { AnnotationLayer, annotationPlugin } from '@embedpdf/vue/annotation';
import { localEngine } from '@embedpdf/engine';
import AddAnnotations from './AddAnnotations.vue';
import Status from './Status.vue';
import '../layer.css';
const engine = localEngine();
const plugins = [stagePlugin(), renderPlugin(), interactionPlugin(), annotationPlugin()];
const ebook = async (): Promise<OpenInput> => {
const response = await fetch('https://snippet.embedpdf.com/ebook.pdf');
return { kind: 'bytes', id: 'ebook', bytes: new Uint8Array(await response.arrayBuffer()) };
};
</script>
<template>
<Viewer :engine="engine" :plugins="plugins" :initial-documents="[{ source: ebook }]">
<DocumentGate>
<template #fallback><p class="loading">Loading…</p></template>
<AddAnnotations />
<Status />
<Stage class="stage">
<template #page>
<RenderLayer />
<AnnotationLayer />
</template>
</Stage>
</DocumentGate>
</Viewer>
</template>Try it: click the rectangle and drag it, or a corner to resize it. Double-click the text box to type in it.
<script setup lang="ts">
import { AnnotationLayer, annotationPlugin } from '@embedpdf/vue/annotation';
import { interactionPlugin } from '@embedpdf/vue/interaction';
import { RenderLayer, renderPlugin } from '@embedpdf/vue/render';
import { Viewer } from '@embedpdf/vue/runtime';
import { Stage, stagePlugin } from '@embedpdf/vue/stage';
import { engine } from './setup';
const plugins = [stagePlugin(), renderPlugin(), interactionPlugin(), annotationPlugin()];
</script>
<template>
<Viewer :engine="engine" :plugins="plugins">
<Stage>
<template #page>
<RenderLayer />
<AnnotationLayer />
</template>
</Stage>
</Viewer>
</template>While the annotation layer is on the page, the render layer leaves the annotations out of the page picture by itself. The annotation layer draws them instead, so they can be selected, moved and changed.
interactionPlugin() is the shared input system. Every tool, from text selection to drawing,
speaks through it; see Selecting & copying for how it works.
Let people draw#
A tool says what a click or a drag on the page creates. Activate one and people can draw:
Loading live preview…
<script setup lang="ts">
import { DocumentGate, Viewer } from '@embedpdf/vue/runtime';
import type { OpenInput } from '@embedpdf/vue/runtime';
import { Stage, stagePlugin } from '@embedpdf/vue/stage';
import { RenderLayer, renderPlugin } from '@embedpdf/vue/render';
import { interactionPlugin } from '@embedpdf/vue/interaction';
import { SelectionLayer, selectionPlugin } from '@embedpdf/vue/selection';
import { AnnotationLayer, annotationPlugin } from '@embedpdf/vue/annotation';
import { localEngine } from '@embedpdf/engine';
import Toolbar from './Toolbar.vue';
import '../draw.css';
const engine = localEngine();
// The selection plugin lets the highlight tool mark the text you select.
const plugins = [
stagePlugin(),
renderPlugin(),
interactionPlugin(),
selectionPlugin(),
annotationPlugin(),
];
const ebook = async (): Promise<OpenInput> => {
const response = await fetch('https://snippet.embedpdf.com/ebook.pdf');
return { kind: 'bytes', id: 'ebook', bytes: new Uint8Array(await response.arrayBuffer()) };
};
</script>
<template>
<Viewer :engine="engine" :plugins="plugins" :initial-documents="[{ source: ebook }]">
<DocumentGate>
<template #fallback><p class="loading">Loading…</p></template>
<Toolbar />
<Stage class="stage">
<template #page>
<RenderLayer />
<SelectionLayer />
<AnnotationLayer />
</template>
</Stage>
</DocumentGate>
</Viewer>
</template>Try it: drag on the page to draw a rectangle, then pick another tool. Highlight marks the text you select.
<script setup lang="ts">
import { useInteraction, useInteractionState } from '@embedpdf/vue/interaction';
const tools = ['pointer', 'square', 'ink', 'highlight', 'note'];
const interaction = useInteraction();
const { activeToolId } = useInteractionState();
</script>
<template>
<button
v-for="id in tools"
:key="id"
:aria-pressed="activeToolId === id"
@click="interaction.activateTool(id)"
>
{{ id }}
</button>
</template>pointerselects: click an annotation to select it, then move, resize or turn it.squaredraws a rectangle: drag, or just click for a default-size one.inkdraws freehand.highlighthighlights the text you select.notedrops a sticky note where you click.
After you draw, the new annotation is selected and the tool stays active, so you can draw another. You can change both; see What happens after drawing.
How it fits together#
Everything on these pages is one of five things:
| Thing | What it is | Where you use it |
|---|---|---|
| Annotation | A record in the PDF: its kind, position, colors and text. | annotation.list() |
| Tool | What a click or drag creates, with which defaults, cursor and preview. | annotationPlugin({ tools }) |
| Layer | What each page draws: the annotations, the selection box and handles, the preview. | <AnnotationLayer> |
| Overlay | Your own UI attached to an annotation, such as a menu or a hover card. | <AnnotationMenu> |
| API | Reading, creating, changing and deleting annotations, and listening for changes. | useAnnotation() |
An annotation here is the same record the engine reads and writes: the same kinds, the same fields and the same names. What you learn in the engine’s annotation pages holds here too.
The built-in tools#
| Tool | Makes | How |
|---|---|---|
square, circle | A rectangle or an ellipse | Drag, or click for a default size |
line | A line | Drag, or click for a default length |
polygon, polyline | A closed or open shape of points | Click each point, double-click to end |
ink, ink-highlight | Freehand drawing, or a see-through marker | Draw |
free-text, free-text-callout | A text box, or one with a line pointing out | Click or drag, then type |
highlight, underline, strikeout, squiggly | A mark over text | Select text |
insert-text, replace-text | A suggested insertion or replacement | Select text |
note | A sticky note | Click |
stamp | An image or a PDF drawing | Click, then pick a file |
attachment | A file pinned to a spot | Click, then pick a file |
link | An area that opens a page or a website | Drag |
Measurement tools (distance, perimeter, area) and redaction (redact) are annotation tools
too. Their plugins add the rest of what they need.
You can change any tool, or add your own; see Tools & defaults.
Who wrote it#
Every annotation records who made it and who last changed it (author, createdAt,
modifiedAt). You don’t set these on the annotation: they come from the user the document was
opened for.
Tell the viewer who the user is, once. Every document it opens uses it:
Loading live preview…
<script setup lang="ts">
import { DocumentGate, Viewer } from '@embedpdf/vue/runtime';
import type { OpenInput } from '@embedpdf/vue/runtime';
import { Stage, stagePlugin } from '@embedpdf/vue/stage';
import { RenderLayer, renderPlugin } from '@embedpdf/vue/render';
import { interactionPlugin } from '@embedpdf/vue/interaction';
import { AnnotationLayer, annotationPlugin } from '@embedpdf/vue/annotation';
import { localEngine } from '@embedpdf/engine';
import AddNote from './AddNote.vue';
import Authors from './Authors.vue';
import Toolbar from './Toolbar.vue';
import '../identity.css';
const engine = localEngine();
const plugins = [stagePlugin(), renderPlugin(), interactionPlugin(), annotationPlugin()];
const ebook = async (): Promise<OpenInput> => {
const response = await fetch('https://snippet.embedpdf.com/ebook.pdf');
return { kind: 'bytes', id: 'ebook', bytes: new Uint8Array(await response.arrayBuffer()) };
};
</script>
<template>
<Viewer
:engine="engine"
:plugins="plugins"
:identity="{ userId: 'u_381', displayName: 'Dana Smith' }"
:initial-documents="[{ source: ebook }]"
>
<DocumentGate>
<template #fallback><p class="loading">Loading…</p></template>
<AddNote />
<Toolbar />
<div class="viewer">
<Stage class="stage">
<template #page>
<RenderLayer />
<AnnotationLayer />
</template>
</Stage>
<Authors />
</div>
</DocumentGate>
</Viewer>
</template>Try it: draw a rectangle. The list says Dana Smith made it, and when.
<script setup lang="ts">
import { Viewer } from '@embedpdf/vue/runtime';
import { contract, engine, plugins } from './setup';
</script>
<template>
<Viewer
:engine="engine"
:plugins="plugins"
:identity="{ userId: 'u_381', displayName: 'Dana Smith' }"
:initial-documents="[{ source: contract }]"
/>
</template>displayName becomes the author other PDF apps show, and userId records who owns the
annotation. The same identity decides which annotations count as the user’s own when
permissions say “only their own”.
To open one document as someone else, give its entry in initialDocuments its own identity. It
replaces the viewer’s for that document:
const initialDocuments = [
{ source: contract, identity: { userId: 'bot', displayName: 'Import job' } },
];A document keeps the identity it was opened with. If you change the viewer’s identity, documents
you open afterwards use the new one.
Settings#
Set them when you register the plugin. snap, chrome, afterCreate and editable can change
while the app runs, with annotation.updateSettings({ … }); annotation.resetSettings() goes back to what you
registered, and useAnnotationSettings() gives you the current settings.
| Setting | What it does | Page |
|---|---|---|
tools | Change the built-in tools, or add your own | Tools & defaults |
afterCreate | Select a new annotation? Keep the tool active? | What happens after drawing |
chrome | The selection’s outline, handles and rotation handle, and their colors | Change the handles |
snap | Snapping to other annotations and to angles | Snapping |
editable | Which annotations people may change by hand: true, false, or kinds | What people may change |
Every color in chrome can also come from CSS, for dark mode or a different look per viewer: see
Colors & theming.
Next steps#
Your feedback goes directly to the documentation team.