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>
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')
}
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
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(', ')
In practice, users enter keywords separated by commas (or Chinese commas), so we split on both delimiters:
keywords.split(/[,,]\s*/).filter(k => k)
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:
- Load the PDF with
PDFDocument.load() - Read fields with
getTitle(),getAuthor(), etc. - Let users edit any subset of fields
- Write back with
setTitle(),setAuthor(), etc. - Handle keywords as arrays (not strings)
- 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)