A PDF that writes... Slop
Fetches a relay from within Adobe Acrobat, try it now, download the pdf below! :D
Overview
A PDF that calls Qwen via a relay to generate slop stories and displays the result in Adobe Acrobat.
Working because the PDF spec's supports javascript, and can recieve inputs through form handling.
this only works on Adobe Acrobat
This document is Adobe desktop/mac only by nature -- mainly because adobe was the only pdf viewing company that fully implemented the spec :p
Your mileage may vary if you use something else (email me if you find something else that works!).
Requirements
- Adobe Acrobat or Acrobat Reader, on Windows or macOS.
- A network connection
Adobe thinks I'm trying to attack you when it realises a request is being sent out. So it'll ask for your permission. Choose Allow -- I won't do anything too bad >:)
How it works
Acrobat exposes exactly one network API to an ordinary document:
Doc.submitForm(), which posts the form's fields to a URL,
I think it was maybe meant as a low tech alternative to google forms.
Acrobat doens't allow for standard JSON responses, so we need to be a bit creative.
Acrobat does however, support FDF responses, which is a format intended for PDF form data.
Thus when we send a response with Content-Type: application/vnd.fdf, Acrobat parses the FDF
and imports the values into our document
.
Note
I did cook.
So WTF is FDF?
An FDF file looks complicated, but deep down its super simple. It's a set of key-value pairs, that relate to form names and data respectively.
%FDF-1.2
% an example FDF file
1 0 obj
<< /FDF << /Fields [
<< /T(status) /V(OK - 1284 chars in 17s) >>
<< /T(response) /V(The vending machine had opinions...) >>
<< /T(job) /V() >>
] >> >>
endobj
trailer
<< /Root 1 0 R >>
%%EOF
With that context, this code actually makes a bit more sense. /T values relate to the field name, and /V values relate to the data.
It is also important to note that an FDF response can also carry javascript, but Acrobat's security features block that unless the file sits in a privileged location, so to make this a bit more portable nothing here depends on it.
Polling???
Acrobat will not keep a submitForm connection open for a long time. The first working version took
15–35 seconds to respond
, and Acrobat dropped the socket before the reply could be
written and sent so the document would just always show nothing.
So I added job ids, and made the document poll. Now the inital request responds with an id, and later requests use the id to ask for the right response.
There is also a Poll now button. In case the reponse isn't available yet.
Why relay?
I didn't want to hit Qwen directly for two reasons, other than the fact that would be boring af:
1. I didn't want to leak my api keys
Anyone who has the pdf can read all of it's code, I didn't wanna be the idiot to put my secrets in the frontend. By adding in the relay I can protect my keys + change stuff on the backend easily.
2. Like me using my cellphone, it answers quickly.
LLM api calls take a while.. Like longer than I could keep the request going, so I needed something that responded quicker.
Credits & licence
Built by me, Alex Van Doren, one of the developers behind Hack Club's Stardance Challenge, and super cool sailor, because I thought it would be stupid, and also because I wanted to have something funny to show my new friends at Brown.
BTW I made this as a fork of
linuxpdf by
ading2210, which is super cool and runs an
entire RISC-V Linux emulator inside a PDF using similar tricks (exploiting pdf javascript).
pdfform.py is derived from that project's
gen_pdf.py. It is 100% worth your time and it was actually made by a High Schooler for a previous Hack Club program. See also
DoomPDF (also by ading2210), and
TinyEMU by Fabrice Bellard,
which linuxpdf's emulator is based on.
Licensed GNU GPL v3, inherited from linuxpdf. Copyright © 2026 vandorena. Derived from ading2210/linuxpdf, copyright © 2025 ading2210.