Dev Log: Building a Whole PDF Toolkit in the Browser
2026-06-24
dev-logpdfbuilding-in-publicjavascript
This is the third post in the LocalPapa Notes dev-log series. The first two covered the earliest tool, PDF to Markdown, and the DORI Calculator. This time we zoom in on a major expansion in our development timeline — a whole set of PDF tools added in one go.
Why PDF is LocalPapa's core battleground
PDF is the poster child for "files you need to process but least want to leak": contracts, quotes, medical records, unpublished reports. And yet most online PDF tools ask you to upload the file first to their servers.
For LocalPapa, this is exactly the domain that most deserves to be "browser-ified." Beyond the earliest merge and split, this expansion added four advanced features at once: Rotate PDF, PDF to Image, Unlock PDF, and an enhanced Image to PDF with margins.
All of it, zero upload.
Three libraries, clear division of labour
The whole PDF suite stands on three mature client-side libraries — all lazy-loaded via CDN, so they're only fetched when you actually open a tool, never weighing down the homepage:
pdf-lib: the "writer" — create, modify, output PDFs (rotation, adding pages, merging).
pdf.js: the "reader" — parse and render PDFs (to image, to Markdown).
JSZip: bundles multiple outputs into a single .zip download.
Four features, four little engineering decisions
Behind each feature is a small decision worth recording:
1. Rotate PDF — reading and writing angles Read each page's existing rotation, visualise it on a Canvas / CSS Transform so you can preview, then write the new angle back via page.setRotation to export a correctly-oriented file. The key is WYSIWYG — what you rotate is what gets saved.
2. PDF to Image — Canvas as the developer fluid Use pdf.js to read each page's viewport at higher quality, render it onto an HTML5 Canvas, then convert to a .jpeg blob. Multiple pages? JSZip packs them into one archive for a single download — no right-click-save on each page.
3. Unlock PDF — using an "error" as the entry point This one is the cleverest. The flow: while processing the file, we deliberately catch the "Encrypted" error, use that to know a password is required, and open an input for it; once the password verifies, we re-save the content as an unprotected PDF. Exception handling becomes the feature's trigger.
It solves the "you have the password but must retype it every time you open the file" annoyance — not cracking. Without the password, it still won't open.
4. Image to PDF — margins are computed The enhancement here is margin support. When calling pdfDoc.addPage, the page size is computed dynamically as image.width + margin * 2 (and likewise for height), giving adjustable whitespace around the image and a more flexible layout.
What we learned
- Picking the right libraries solves half the problem.
pdf-lib / pdf.js / JSZip each chew through the hard bones of write / read / bundle; our job is assembling them into a smooth flow.
- Lazy-loading is a survival rule for a tool village. The more tools you have, the less you can afford to load them all on the homepage. Fetch on demand, and the homepage stays instant.
- An exception can be a feature. Unlock PDF turns "encrypted error" into a "please enter the password" entry point — sometimes the place where you catch the error is where the feature is born.
- For files like these, privacy isn't a bonus — it's a requirement. Contracts and medical records shouldn't have to be uploaded to a stranger's server just to convert a file.
The same question, again
Like the first two posts, this PDF suite answers the same question: can these advanced operations — the ones that "surely must" require a server upload — be done entirely in the browser?
Rotate, to-image, unlock, add margins — the answer is, every time: yes.
In the next dev log, we reach the step that makes all of this visible to search engines: the SPA-to-multipage SEO overhaul. Want to follow the series? Bookmark LocalPapa Notes.