Tech · Tools · Crypto

Lock the Content, Leave the Label: Optional Metadata Encryption in pdf-encrypt-lib

pdf-encrypt-lib 0.1.10 and the PDFfile Manager

September 23, 2026, 8:07 PM UTC · 4 min read
This article is also available auf Deutsch.
Lock the Content, Leave the Label: Optional Metadata Encryption in pdf-encrypt-lib

When I published pdf-encrypt-lib this past July, the library could do one thing: encrypt the file with AES-256, completely, so that the result would open in every spec-compliant reader. However, everything in the file got encrypted, including the parts that maybe should have stayed out of it.

With this update, that changes a little now.

The all-or-nothing problem

A PDF is not just the individual pages a user reads or prints. Every file also carries metadata: title, author, subject, keywords, creation date, information about the file itself. In modern PDFs, that data sits in what is called an XMP stream, the same format the EXIF Editor can already detect and strip from your photos. ISO 32000-2 has a dedicated switch for it, the EncryptMetadata flag. Set it to false and the content of the file stays encrypted, while the metadata stream stays readable in plaintext.

And what is that good for? Well, that actually explains itself. Think of a document management system, a search indexer, an archive, or some other kind of scanner: they need to find a filed document again and categorize it without having to read it completely, but you do not want to hand the password, and the workload of decrypting, to every process that only needs to read the metadata. That is why the PDF standard has offered the choice to leave the metadata out of the encryption for years.

The little pdf-encrypt-lib could not do that so far. The flag was hardcoded to the default value true, so XMP metadata ended up encrypted behind the password too. For professional workflows that depend on readable metadata, the library simply was not an option.

What is new

encryptPdf now accepts an encryptMetadata option. The default is true, so nothing changes for existing code:

await encryptPdf(bytes, 'password', { encryptMetadata: false });

With false, though, the XMP metadata stream stays in plaintext while every page, every stream and every other string gets AES-256 encrypted. The flag is written into the encryption dictionary and into the encrypted permissions block, so a reader knows what to expect. decryptPdf reads the flag back, and changePdfPassword now keeps your choice on any password rotation, instead of silently resetting it to the default.

There was still a catch, and it got fixed too

The standard only exempts the XMP stream from encryption. Many PDFs, including everything pdf-lib writes with setTitle() or setAuthor(), keep title and author in the classic Info dictionary instead, and that is made of ordinary strings that still get encrypted regardless of the flag.

Version 0.1.10 closes that gap. When a file is now encrypted with the metadata meant to stay unencrypted, it checks whether an XMP stream is available. If it does not exist yet, one is built from the classic PDF metadata. Should XMP already exist, it stays untouched and gets carried over.

In the browser

The library practically exists only because of the PDFfile Manager, so naturally the tool got the option too. Whether a PDF is encrypted or its password changed, a checkbox now decides how the metadata is handled. As always, the tool runs offline and your data never leaves your browser.

One thing to keep in mind

Whatever ends up in that unencrypted metadata is readable by anyone who gets the file. Only tick the box if you are fine with your title, author and keywords being public. If in doubt, leave it off, which was and still is the default.

Where to get it

Version 0.1.10 is out on Codeberg and GitHub, with the full changelog in the repository. Since that first post the library has also gotten quite a bit more robust, with clean password normalization, clear errors for PDFs it cannot process, and a check of the permission bits. It is still the same small library, and there is still no promise beyond "works for me and gets tested hard". So still no npm package either, download it, test it and use it, without warranty.

Cheers Alexander