Guide

Export & Import

Export PDFs and save annotation drafts

KViewer supports exporting PDFs with annotations baked in, and saving/restoring annotation state as JSON for draft workflows.

Export a PDF

Use the template ref to export the current document with annotations:

<template>
  <div class="h-screen">
    <KViewer ref="viewer" :source="pdfUrl" />
    <button @click="exportPdf">Download PDF</button>
  </div>
</template>

<script setup lang="ts">
const viewer = ref()
const pdfUrl = '/sample.pdf'

async function exportPdf() {
  const bytes = await viewer.value?.exportPdf({
    flatten: true,
    download: true,
    fileName: 'annotated-document.pdf',
  })
}
</script>

Export Options

OptionTypeDefaultDescription
flattenbooleanfalseBurn annotations into page content (non-editable)
downloadbooleanfalseTrigger a browser file download
fileNamestringauto-generatedCustom filename for download
preserveOriginalAnnotationsbooleanfalseKeep unmodified native PDF annotations

Get Bytes for Upload

Export without downloading to get raw bytes for server upload:

const bytes = await viewer.value?.exportPdf({ flatten: true })

// Upload to your server
await fetch('/api/documents/save', {
  method: 'POST',
  body: bytes,
  headers: { 'Content-Type': 'application/pdf' },
})

Flatten Annotations

When flatten: true, annotations are burned into the PDF page content and become part of the rendered page. They can no longer be edited or removed.

When flatten: false (default), annotations are added as standard PDF annotation objects that can be edited in other PDF viewers.

Handle Native PDF Annotations

When a PDF has existing annotations that were auto-imported by KViewer:

// Recommended: don't preserve originals to avoid duplicates
const bytes = await viewer.value?.exportPdf({
  flatten: false,
  preserveOriginalAnnotations: false,
})
Setting preserveOriginalAnnotations: true when native annotations were auto-imported may create duplicate annotations in the exported PDF. Use false in this workflow.

Save Annotation Drafts

Save the current annotation state as JSON for later restoration:

// Save draft
const annotations = viewer.value?.getAnnotations() ?? []
const draft = JSON.stringify(annotations)
localStorage.setItem('document-draft', draft)

// Or save to your API
await fetch('/api/drafts', {
  method: 'POST',
  body: draft,
  headers: { 'Content-Type': 'application/json' },
})

Restore Annotation Drafts

Import previously saved annotations:

// Load draft
const draft = localStorage.getItem('document-draft')
if (draft) {
  const annotations = JSON.parse(draft)
  const result = await viewer.value?.importAnnotations(annotations, {
    mode: 'replace',
  })
  console.log(`Loaded ${result.loaded}, skipped ${result.skipped}`)
}

Import Modes

ModeDescription
replaceClear all existing annotations, then add the imported ones
mergeAdd imported annotations alongside existing ones, skipping collisions

Full Round-Trip Example

<template>
  <div class="h-screen">
    <KViewer ref="viewer" :source="pdfUrl" text-layer />
    <div class="flex gap-2 p-4">
      <button @click="saveDraft">Save Draft</button>
      <button @click="loadDraft">Load Draft</button>
      <button @click="exportFlattened">Export PDF</button>
    </div>
  </div>
</template>

<script setup lang="ts">
const viewer = ref()
const pdfUrl = '/sample.pdf'

function saveDraft() {
  const annotations = viewer.value?.getAnnotations() ?? []
  localStorage.setItem('draft', JSON.stringify(annotations))
}

async function loadDraft() {
  const saved = localStorage.getItem('draft')
  if (saved) {
    await viewer.value?.importAnnotations(JSON.parse(saved), { mode: 'replace' })
  }
}

async function exportFlattened() {
  await viewer.value?.exportPdf({
    flatten: true,
    download: true,
    fileName: 'final-document.pdf',
  })
}
</script>
Copyright © 2026