Two YouTube videos, one PDF feature Adobe forgot to kill, and a field-engineer reporting system that actually works
Pete Gypps20 May 2026 · updated 27 August 2026
10 min read1,280 words
I needed to give a UPS maintenance firm a reporting tool that worked offline, in a data-centre basement, on a laptop with no signal. The answer was a feature buried in Adobe Acrobat’s JavaScript API that has, I checked, exactly two YouTube videos explaining it.
The job: build a reporting tool that works in a basement data centre with no signal. The answer was older than you think.
If you Google “Adobe PDF template spawn JavaScript”, you get two YouTube videos. Two. Both short. Both faintly amateurish. Both from around 2014. One of them has 2,300 views.
That feature is now running on field engineers’ laptops at a UPS specialist, replacing a £5,000-a-year vendor tool. We shipped twenty-three builds of it in the last six weeks. The client wanted three different service-report types from one PDF, signed by the customer on-site, locked before delivery, all working offline in a basement data centre with no signal.
The standard answer in 2026 is: build a web app. Next.js, Supabase, mobile-responsive, cloud sync. That is the SaaS playbook everyone reaches for. The standard answer was wrong. This is what I learned building the thing the standard answer would have missed.
What the engineers actually need
Picture the job. An engineer drives to a data centre. There is a UPS the size of a fridge humming in the corner, a Riello Sentryum, three-phase, fifteen years old, propping up a server room that costs more per hour than the engineer earns in a month.
The engineer’s checklist is long. Inspect the unit. Run diagnostics. Photograph the burn marks on the relay if there are any. Measure battery voltages. Write recommendations. Hand the customer a signed report before they leave site so the customer can raise the next purchase order.
The engineer is in a basement. There is no signal. The customer’s IT department has not been told to add the engineer’s laptop to the WiFi allow-list, and even if they had, it would take an hour to clear. The engineer needs to walk in, fill in a report, get a signature, generate a clean PDF for the customer, and leave. A web app fails the first ten metres into the building.
What actually fits the job is something so unfashionable it almost circles back to being fashionable: a file you double-click. A PDF that runs locally, has form fields, accepts input offline, looks identical on every Windows laptop, and produces a signed deliverable the customer can keep on their phone. The question was never “what stack should we build”. The question was: which obscure corner of Adobe’s product surface is going to let us do this without writing our own viewer.
The forgotten feature
Adobe Acrobat has a JavaScript API. Most developers know app.alert() and stop there. Buried deeper in that API is something called Template Spawning. Here is the bit that breaks brains: a PDF can contain hidden template pages. They live inside the file but do not appear in the page count. JavaScript on a button can clone those templates into the visible page tree, in any order, on demand:
javascript
var t = this.getTemplate("NEWSVR_P1");
t.spawn();
That single call drops the hidden template “NEWSVR_P1” into the document as a real visible page. Form field names auto-increment if they collide. The file size does not change whether you spawn one report or all three. So I built one PDF with eighteen hidden template pages inside it and three buttons on the front:
SERVICE RPT: spawns 6 pages of service-report layout
BATTERY INSTALL: spawns 5 pages of battery-installation layout
COMMISSION RPT: spawns 7 pages of commissioning layout
One file. Three report types. The engineer picks whichever one matches the visit. It is elegant. It is well documented. It also belongs to an era of Adobe’s history when the company believed in JavaScript-extensible PDFs as a platform, before they pivoted to AEM Forms and quietly stopped talking about it. The documentation is intact but nobody is writing new tutorials. The internet contains exactly two YouTube videos.
Making Adobe trust you
Spawning templates is the easy half. The hard half is making the engineer’s workflow feel like a real application. When the engineer clicks SAVE, the file needs to save as SVR-{PO}.pdf in their working folder, auto-named from the PO Number they just typed. When they click CREATE CLIENT COPY, the file needs to lock every field readonly, save a sibling file as SVR-{PO}-CLIENT.pdf, and leave their working copy untouched so they can carry on editing.
None of that works from a normal form-button script. Adobe’s security model (for good reason) blocks file writes initiated by user-supplied JavaScript. You cannot call doc.saveAs() from a button click and expect it to work. Unless you write a trusted function.
A trusted function is a small JavaScript file (in our case, pwm_save.js, fifty lines) that lives on the engineer’s machine, gets loaded by Adobe at startup, and is marked as privileged. Buttons in the PDF can call into it. The trusted function can do the things buttons cannot. Where it lives matters a lot. Acrobat only treats a script as privileged if it sits in:
Not DC\JavaScripts\. Not the top-level JavaScripts\. The Privileged subfolder. Which does not exist by default. Which the Windows installer has to create. I lost an afternoon to that one detail. The trusted function loaded. The button called it. Nothing happened. No error. Adobe silently ignores app.trustedFunction() calls in scripts loaded from the wrong folder. You only get the error when the trusted function actually executes a privileged operation, and even then the message is misleading.
What is shipping
Three weeks of work, twenty-three numbered builds, four visible report pages, eighteen hidden template pages, three report types, a custom NSIS installer that creates the Privileged subfolder and drops the trusted JavaScript file in the right place, a desktop shortcut, an uninstaller properly registered with Windows’ Add/Remove Programs.
The engineer’s workflow is:
Double-click the desktop icon
Fill in the Job Details: PO Number, Site, Customer, Engineer, Date
Click the report type (SERVICE RPT / BATTERY INSTALL / COMMISSION RPT). Six-to-seven pages spawn.
Fill in everything. Take photos straight from the laptop camera into the embedded photo buttons.
Hand the laptop to the customer. They click the curly Draw Free Hand icon. They sign their name with the trackpad.
Engineer clicks CREATE CLIENT COPY.
Two files appear in Documents\PWM Power\Service Reports\: SVR-{PO}.pdf (working copy, fields editable) and SVR-{PO}-CLIENT.pdf (locked, fields readonly).
Email the locked copy. Keep editing the working copy if anything changes.
Acrobat Standard runs it. No Pro licence needed. No cloud. No login. No internet. Works in a basement.
The boring-tech reflection
I keep coming back to this: the right tool for the job was the tool the YouTube algorithm forgot. The standard playbook would have given me a Next.js app deployed to Vercel, a Supabase database, OAuth login, a React form with conditional rendering, jsPDF generating the report, AWS S3 for photo uploads, Stripe metering for licensing, a Sentry integration. Twelve dependencies, three cloud providers, four monthly bills, and a workflow that breaks the moment the engineer walks into a server room.
Instead it is: PyMuPDF (Python library, builds the PDF), Acrobat (already installed on every engineer’s laptop), an NSIS installer (no recurring cost, one .exe), a fifty-line trusted JavaScript file, and a hidden Adobe feature with two YouTube videos. Total monthly recurring cost: £0.
If you ever find yourself building on this stack, these are the four sentences I would hand my past self:
PyMuPDF, never pikepdf. Pikepdf rewrites button widgets in ways that pass validation but break interaction.
The Privileged subfolder is mandatory. Anywhere else and app.trustedFunction() is silently ignored.
Do not trust flattenPages(). Enhanced Security blocks it regardless of script trust. Use readonly = true instead.
Signatures are annotations, not widgets. PDF_WIDGET_TYPE_SIGNATURE is for digital certificates. Customers want to draw, not authenticate.
Those four lines are roughly the four days you would otherwise spend learning them the hard way. Maybe I will record the third YouTube video.
How a single hour with a new M5 Max and an MLX inference job sent me down a thirty-six-hour rabbit hole that ended with a custom fan-control daemon, a CFRunLoop power-event monitor, and the discovery of a macOS daemon Apple does not want you to know about.
Our native macOS app for running many coding agents at once: Claude Code, Codex, Cursor, Aider, Copilot and Windsurf side by side in one multi-pane deck, with sessions that survive closing the app.
Cloud AI can connect to your inbox. It cannot actually read it. Here is what happened when I gave up waiting for the vendors to fix that, and built a local-first AI assistant for my own mailbox on an M5 Max, 125,000 messages, a knowledge graph, all the AI inference running on my own machine, and the things you only learn when you try.