DEV Community

sunshey
sunshey

Posted on

How to Edit PDF Metadata in the Browser with Vue 3 and pdf-lib

PDF metadata — the hidden document properties like title, author, subject, keywords, creator, and producer — is often overlooked but can expose more information than you want. With pdf-lib and a bit of Vue 3, you can build a fully browser-based metadata editor.

This post walks through the implementation details, including reading/writing PDF info dictionary fields, keyword array handling, and incremental updates.

Why client-side?

Traditional metadata tools often rely on server-side processing or desktop software. A browser-based approach:

  • Processes everything locally
  • Keeps files on the user's device
  • Works offline after loading
  • Avoids server-side bandwidth costs
  • Preserves all other PDF content untouched

The stack

  • Vue 3 + Composition API
  • pdf-lib for PDF manipulation and metadata editing
  • Vite for bundling

Reading metadata from a PDF

pdf-lib provides built-in getters for all standard metadata fields:

<script setup lang="ts">
import { ref, reactive } from 'vue'
import { PDFDocument } from 'pdf-lib'

const file = ref<File | null>(null)
const currentMeta = reactive({
  title: '',
  author: '',
  subject: '',
  keywords: '',
  creator: '',
  producer: '',
})
const meta = reactive({
  title: '',
  author: '',
  subject: '',
  keywords: '',
  creator: '',
  producer: '',
})

async function loadCurrentMetadata(bytes: Uint8Array) {
  const pdf = await PDFDocument.load(bytes, { ignoreEncryption: true })

  currentMeta.title = pdf.getTitle() || ''
  currentMeta.author = pdf.getAuthor() || ''
  currentMeta.subject = pdf.getSubject() || ''
  currentMeta.keywords = pdf.getKeywords() || ''
  currentMeta.creator = pdf.getCreator() || ''
  currentMeta.producer = pdf.getProducer() || ''

  // Pre-fill edit form
  Object.assign(meta, currentMeta)
}
</script>
Enter fullscreen mode Exit fullscreen mode

Writing metadata back

The setter methods follow the same pattern:

async function saveMetadata() {
  if (!file.value) return

  const bytes = await file.value.arrayBuffer()
  const pdf = await PDFDocument.load(bytes, { ignoreEncryption: true })

  // Only update non-empty fields; leave blanks as-is
  if (meta.title.trim()) pdf.setTitle(meta.title.trim())
  if (meta.author.trim()) pdf.setAuthor(meta.author.trim())
  if (meta.subject.trim()) pdf.setSubject(meta.subject.trim())

  // Keywords are special — they need to be stored as an array
  if (meta.keywords.trim()) {
    const keywords = meta.keywords.trim().split(/[,,]\s*/)
      .filter(k => k)
    pdf.setKeywords(keywords)
  }

  if (meta.creator.trim()) pdf.setCreator(meta.creator.trim())
  if (meta.producer.trim()) pdf.setProducer(meta.producer.trim())

  const blob = new Blob([await pdf.save()], { type: 'application/pdf' })
  downloadBlob(blob, file.value.name.replace(/\.pdf$/i, '') + '-modified.pdf')
}
Enter fullscreen mode Exit fullscreen mode

Key implementation detail: keyword arrays

pdf-lib stores keywords as an array of strings, not a single text field. When setting them:

// Correct: set an array
pdf.setKeywords(['finance', 'Q3', 'report'])

// pdf-lib handles this internally by building
// /Keywords [ /finance /Q3 /report ] in the Info dict
Enter fullscreen mode Exit fullscreen mode

When retrieving them, pdf-lib returns a joined string with spaces. To reconstruct comma-separated format for display:

const rawKeywords = pdf.getKeywords()
// Returns: "finance Q3 report" (space-separated)
// For display: join with commas
currentMeta.keywords = rawKeywords.split(' ').join(', ')
Enter fullscreen mode Exit fullscreen mode

In practice, users enter keywords separated by commas (or Chinese commas), so we split on both delimiters:

keywords.split(/[,,]\s*/).filter(k => k)
Enter fullscreen mode Exit fullscreen mode

What metadata doesn't do

It's important to understand what metadata editing doesn't touch:

  • Visible page content — text, images, layouts stay exactly the same
  • File attachments — embedded files are preserved
  • Annotations / comments — all markup survives
  • Form fields — interactive elements remain functional
  • Digital signatures — these may become invalid if the PDF changes even slightly

The only thing that changes is the Info Dictionary inside the PDF structure — the document-level metadata blob that most PDF readers don't show by default.

Edge cases to handle

Encrypted PDFs

Some PDFs have user passwords or owner permissions. Use { ignoreEncryption: true } when loading, but be aware that modification may require owner access.

Missing metadata

If a field was never set, pdf-lib returns an empty string. The UI should display "Not set" rather than a blank input so users know the difference between "empty" and "we haven't loaded yet."

Overwriting sensitive data

The Creator and Producer fields often contain internal software names or usernames. Editing these out before sharing is the most common use case, so the UI should make it easy to clear individual fields without touching others.

Summary

Building a browser-based PDF metadata editor with pdf-lib is straightforward:

  1. Load the PDF with PDFDocument.load()
  2. Read fields with getTitle(), getAuthor(), etc.
  3. Let users edit any subset of fields
  4. Write back with setTitle(), setAuthor(), etc.
  5. Handle keywords as arrays (not strings)
  6. Save and download via Blob

Try it at en.sotool.top/edit-metadata. It takes about 30 seconds and keeps your files completely private.

Top comments (0)