Signature Plugin
The Signature Plugin extends the Annotation Plugin to provide a dedicated workflow for electronic signatures. It includes ready-to-use UI components for drawing and typing signatures, helper composables for image uploads, and an API for saving them as reusable entries.
Once a signature is saved as an entry, users can easily click to arm the tool and stamp their signature anywhere on the PDF document.
Signatures are placed as standard PDF annotations (Ink for drawn signatures, Stamps for typed/uploaded signatures). They render automatically through your existing <AnnotationLayer />.
Installation
This plugin depends on the Annotation Plugin and its interaction dependencies.
npm install @embedpdf/plugin-signature @embedpdf/plugin-annotation @embedpdf/plugin-interaction-manager @embedpdf/plugin-selection @embedpdf/plugin-historyRegistration
Import SignaturePluginPackage and add it to your plugins array after the Annotation Plugin dependencies and the Annotation Plugin itself.
import { createPluginRegistration } from '@embedpdf/core'
import { EmbedPDF } from '@embedpdf/core/vue'
// ... other imports
import { InteractionManagerPluginPackage } from '@embedpdf/plugin-interaction-manager/vue'
import { SelectionPluginPackage } from '@embedpdf/plugin-selection/vue'
import { HistoryPluginPackage } from '@embedpdf/plugin-history/vue'
import { AnnotationPluginPackage } from '@embedpdf/plugin-annotation/vue'
import { SignaturePluginPackage, SignatureMode } from '@embedpdf/plugin-signature/vue'
const plugins = [
// ... essential viewer plugins (DocumentManager, Viewport, Render, Scroll)
createPluginRegistration(DocumentManagerPluginPackage, { /* ... */ }),
// Annotation dependencies
createPluginRegistration(InteractionManagerPluginPackage),
createPluginRegistration(SelectionPluginPackage),
createPluginRegistration(HistoryPluginPackage),
// Annotation plugin
createPluginRegistration(AnnotationPluginPackage),
// Signature plugin
createPluginRegistration(SignaturePluginPackage, {
mode: SignatureMode.SignatureOnly, // or SignatureAndInitials
defaultSize: { width: 150, height: 50 }, // Default placement size
}),
]Usage
1. Rendering Signatures
Because signatures are specialized annotations, you do not need a special layer. Simply render the <AnnotationLayer /> inside your page scroller.
<script setup>
import { PagePointerProvider } from '@embedpdf/plugin-interaction-manager/vue'
import { AnnotationLayer } from '@embedpdf/plugin-annotation/vue'
</script>
<template>
<Scroller :document-id="documentId">
<template #default="{ page }">
<PagePointerProvider :document-id="documentId" :page-index="page.pageIndex">
<RenderLayer :document-id="documentId" :page-index="page.pageIndex" />
<SelectionLayer :document-id="documentId" :page-index="page.pageIndex" />
<AnnotationLayer :document-id="documentId" :page-index="page.pageIndex" />
</PagePointerProvider>
</template>
</Scroller>
</template>2. Creating Signatures (The UI Pads)
The plugin exports <SignatureDrawPad /> and <SignatureTypePad /> components. These handle the complexities of tracking pointer events, rendering canvas strokes, applying fonts, and auto-cropping the results to remove transparent whitespace.
You can place these inside modals or sidebars.
<script setup>
import { SignatureDrawPad, SignatureTypePad } from '@embedpdf/plugin-signature/vue'
function handleResult(result) {
// result is either SignatureInkFieldDefinition or SignatureStampFieldDefinition
}
</script>
<template>
<SignatureDrawPad
strokeColor="#0000FF"
:strokeWidth="3"
@result="handleResult"
/>
<SignatureTypePad
color="#0000FF"
fontFamily="'Dancing Script', cursive"
@result="handleResult"
/>
</template>3. Uploading Signatures
For image uploads, use the useSignatureUpload composable to handle the file picker, drag-and-drop, and automatic image-to-canvas extraction.
<script setup>
import { useSignatureUpload } from '@embedpdf/plugin-signature/vue'
const { openFilePicker, inputRef, handleFileInputChange } = useSignatureUpload({
onResult: (result) => {
// Save to the capability (see step 4)
},
})
</script>
<template>
<button type="button" @click="openFilePicker">
Upload Image
</button>
<input
type="file"
ref="inputRef"
accept="image/png,image/jpeg,image/svg+xml"
class="hidden"
@change="handleFileInputChange"
/>
</template>4. Saving and Placing Signatures
Once you have a result from one of the pads or the upload hook, save it to the plugin’s state using useSignatureCapability.
<script setup>
import { useSignatureCapability, useSignatureEntries, useActivePlacement } from '@embedpdf/plugin-signature/vue'
const props = defineProps<{ documentId: string }>()
const { entries } = useSignatureEntries();
const { provides: signatureCapability } = useSignatureCapability();
const activePlacement = useActivePlacement(() => props.documentId);
const handleSaveNew = (result) => {
signatureCapability.value?.addEntry({ signature: result });
}
</script>
<template>
<div>
<div v-for="entry in entries" :key="entry.id" :class="{ 'ring-2 ring-blue-500': activePlacement?.entryId === entry.id }">
<img :src="entry.signature.previewDataUrl" alt="Signature" />
<button @click="signatureCapability?.forDocument(documentId).activateSignaturePlacement(entry.id)">
Use
</button>
<button @click="signatureCapability?.removeEntry(entry.id)">
Delete
</button>
</div>
</div>
</template>5. Persisting Signatures (localStorage / Backend)
The plugin ships with serializeEntries and deserializeEntries utilities that convert entries to and from a JSON-safe format, and an onEntriesChange event hook that fires whenever entries are added, removed, or loaded. Together they make auto-persisting signatures trivial.
<script setup>
import { watchEffect, onUnmounted } from 'vue'
import {
useSignatureCapability,
serializeEntries,
deserializeEntries,
} from '@embedpdf/plugin-signature/vue'
const STORAGE_KEY = 'my-signatures'
const { provides: signatureCapability } = useSignatureCapability()
let unsubscribe
watchEffect(() => {
if (!signatureCapability.value) return
// Load previously saved entries on mount
const raw = localStorage.getItem(STORAGE_KEY)
if (raw) {
signatureCapability.value.loadEntries(deserializeEntries(JSON.parse(raw)))
}
// Auto-save whenever entries change
unsubscribe = signatureCapability.value.onEntriesChange((entries) => {
localStorage.setItem(STORAGE_KEY, JSON.stringify(serializeEntries(entries)))
})
})
onUnmounted(() => unsubscribe?.())
</script>Live Example
This example demonstrates the full signature workflow: drawing, typing, and uploading signatures, saving them to your collection, and placing them on the document.
API Reference
Configuration (SignaturePluginConfig)
| Option | Type | Description |
|---|---|---|
mode | SignatureMode | Determines if the plugin tracks only signatures (SignatureMode.SignatureOnly) or both signatures and initials (SignatureMode.SignatureAndInitials). |
defaultSize | { width: number, height: number } | The default bounding box size applied when placing a signature. Default: { width: 150, height: 50 } |
Hook: useSignatureCapability()
Returns the global plugin API.
SignatureCapability Methods
| Method | Description |
|---|---|
getEntries() | Returns all saved SignatureEntry objects. |
addEntry(entry) | Saves a new signature entry. Returns the generated UUID. |
removeEntry(id) | Deletes a signature entry. |
exportEntries() | Returns all SignatureEntry objects including their binary data. Use with serializeEntries() for persistence. |
loadEntries(entries) | Hydrates the plugin with previously exported entries. Use with deserializeEntries() when loading from JSON. |
onEntriesChange | Event hook that fires whenever entries are added, removed, or loaded. Subscribe with onEntriesChange((entries) => { ... }) — returns an unsubscribe function. |
forDocument(documentId) | Returns a SignatureScope scoped to the current document for placement actions. |
Hook: useSignatureEntries()
Reactive hook that returns { entries }. The entries array automatically updates when signatures are added or removed.
Hook: useActivePlacement(documentId)
Reactive hook that returns an ActivePlacementInfo object if a signature is currently armed for placement, or null.
interface ActivePlacementInfo {
entryId: string;
kind: SignatureFieldKind; // 'signature' | 'initials'
}SignatureScope Methods (Returned by forDocument)
| Method | Description |
|---|---|
activateSignaturePlacement(entryId) | Arms a specific signature entry for placement on the PDF. |
activateInitialsPlacement(entryId) | Arms a specific initials entry for placement on the PDF. |
deactivatePlacement() | Cancels placement mode. |
Component: <SignatureDrawPad />
An auto-cropping canvas component that tracks pointer events to capture hand-drawn signatures.
| Prop/Event | Type | Description |
|---|---|---|
@result | (result) => void | Fires whenever the user draws on the canvas. Provides the SignatureInkFieldDefinition. |
strokeColor | string | The color of the ink line. |
strokeWidth | number | The thickness of the ink line. |
You can call .clear() on the component template ref to reset it.
Component: <SignatureTypePad />
An auto-cropping text input component that converts typed text into a high-resolution stamp image.
| Prop/Event | Type | Description |
|---|---|---|
@result | (result) => void | Fires when text changes. Provides a SignatureStampFieldDefinition including imageData. |
fontFamily | string | The CSS font-family to use (e.g. 'Dancing Script', cursive). |
color | string | The text color. |
fontSize | number | The base font size used for the canvas generation. |
placeholder | string | The placeholder text. |
You can call .clear() on the component template ref to reset it.
Need Help?
Join our community for support, discussions, and to contribute to EmbedPDF's development.