A PDF that writes... Slop

Fetches a relay from within Adobe Acrobat, try it now, download the pdf below! :D

Download slop.pdf How it works

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 yay.

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 pf, 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.