• Stars
    star
    1,425
  • Rank 33,019 (Top 0.7 %)
  • Language
    Python
  • Created over 8 years ago
  • Updated about 4 years ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

๐Ÿ’‰ Stuff which works in Chrome and maybe Acrobat and Foxit.

horrifying-pdf-experiments

New: For more details about this hack and how it works, check out my talk at !!con 2020, "Playing Breakout... inside a PDF!!"

If you're not viewing it right now, try the breakout.pdf file in Chrome.

Like many of you, I always thought of PDF as basically a benign format, where the author lays out some text and graphics, and then the PDF sits in front of the reader and doesn't do anything. I heard offhand about vulnerabilities in Adobe Reader years ago, but didn't think too much about why or how they might exist.

That was why Adobe made PDF at first1, but I think we've established that it's not quite true anymore. The 1,310-page PDF specification (actually a really clear and interesting read) specifies a bizarre amount of functionality, including:

but most interestingly...

Granted, most PDF readers (besides Adobe Reader) don't implement most of this stuff. But Chrome does implement JavaScript! If you open a PDF file like this one in Chrome, it will run the scripts. I found this fact out after following this blog post about how to make PDFs with JS.

There's a catch, though. Chrome only implements a tiny subset of the enormous Acrobat JavaScript API surface. The API implementation in Chrome's PDFium reader mostly consists of stubs like these:

FX_BOOL Document::addAnnot(IJS_Context* cc,
                           const CJS_Parameters& params,
                           CJS_Value& vRet,
                           CFX_WideString& sError) {
  // Not supported.
  return TRUE;
}
FX_BOOL Document::addField(IJS_Context* cc,
                           const CJS_Parameters& params,
                           CJS_Value& vRet,
                           CFX_WideString& sError) {
  // Not supported.
  return TRUE;
}
FX_BOOL Document::exportAsText(IJS_Context* cc,
                               const CJS_Parameters& params,
                               CJS_Value& vRet,
                               CFX_WideString& sError) {
  // Unsafe, not supported.
  return TRUE;
}

And I understand their concern -- that custom Adobe JavaScript API has an absolutely gigantic surface area. Scripts can supposedly do things like make arbitrary database connections, detect attached monitors, import external resources, and manipulate 3D objects.

So we have this strange situation in Chrome: we can do arbitrary computation, but we have this weird, constrained API surface, where it's annoying to do I/O and get data between the program and the user.23

It might be possible to embed a C compiler into a PDF by compiling it to JS with Emscripten, for example, but then your C compiler has to take input through a plain-text form field and spit its output back through a form field.

Breakout

So what can we do with the API surface that Chrome gives us?

I'm sorry, by the way, that the collision detection is not great and the game speed is inconsistent. (Not really the point, though!) I ripped off most of the game from a tutorial.

The first user-visible I/O points I could find in Chrome's implementation of the PDF API were in Field.cpp.

You can't set the fill color of a text field at runtime, but you can change its bounds rectangle and set its border style. You can't read the precise mouse position, but you can set mouse-enter and mouse-leave scripts on fields at PDF creation. And you can't add fields at runtime: you're stuck with what you put in the PDF at creation time.4. I'm actually curious why they chose those particular methods.

So the PDF file is generated by a script which emits a bunch of text fields upfront, including game elements:

  • Paddle
  • Bricks
  • Ball
  • Score
  • Lives

But we also do a few hacks here to get the game to work properly.

First, we emit a thin, long 'band' text field for each column of the lower half of the screen. Some band gets a mouse-enter event whenever you move your mouse along the x-axis, so the breakout paddle can move as you move your mouse.

And second, we emit a field called 'whole' which covers the whole top half of the screen. Chrome doesn't expect the PDF display to change, so if you move fields around in JS, you get pretty bad artifacts. This 'whole' field solves that problem when we toggle it on and off during frame rendering. That trick seems to force Chrome to clean out the artifacts.

Also, moving a field appears to discard its appearance stream. The fancy arbitrary PDF-graphics appearance you chose goes away, and it gets replaced with a basic filled and bordered rectangle. So my game objects generally rely on the simpler appearance characteristics dictionary. At the very least, a fill color specified there stays intact as a widget moves.

Useful resources

Footnotes

  1. In fact, I got interested in PDF a couple weeks ago because of PostScript; I'd been reading these random Don Hopkins posts about NeWS, the system supposedly like AJAX but done in the 80s on PostScript.

    Ironically, PDF was a reaction to PostScript, which was too expressive (being a full programming language) and too hard to analyze and reason about. PDF remains a big improvement there, I think, but it's still funny how it's grown all these features.

    It's also really interesting: like any long-lived digital format (I have a thing for the FAT filesystem, personally), PDF is itself a kind of historical document. You can see generations of engineers, adding things that they needed in their time, while trying not to break anything already out there. โ†ฉ

  2. I'm not sure why Chrome even bothered to expose the JS runtime. They took the PDF reader code from Foxit, so maybe Foxit had some particular client who relied on JavaScript form validation? โ†ฉ

  3. Chrome also uses the same runtime it does in the browser, even though it doesn't expose any browser APIs. That means you can use ES6 features like double-arrow functions and Proxies, as far as I can tell. โ†ฉ

  4. It's like some stereotype of programming in old-school FORTRAN. You have to declare all your variables upfront so the compiler can statically allocate them. โ†ฉ

More Repositories

1

TabFS

๐Ÿ—„ Mount your browser tabs as a filesystem.
JavaScript
3,816
star
2

Screenotate

๐Ÿ“ท Automatically recognize text in your screenshots.
269
star
3

cruncher

๐Ÿ“ˆ Scrubbing calculator.
JavaScript
185
star
4

tetris

๐Ÿ“ Disassembly of Tetris for Game Boy.
Assembly
74
star
5

tabssh

๐Ÿ“ก ssh into browser tab.
Go
74
star
6

markdown-it-jsx

๐Ÿ“„ Embed JSX components in Markdown.
JavaScript
54
star
7

gambo

๐ŸŽฎ Game Boy emulator.
Java
51
star
8

rpi-kernel

๐Ÿ’พ Pseudo-OS for the Raspberry Pi. Written in Rust.
Rust
45
star
9

little-editor

๐Ÿ“˜ Minimal (live-scriptable) text editor stub for macOS.
Objective-C
42
star
10

rpi-bitbang-ethernet

๐Ÿ“ถ Send Ethernet frames straight from a Raspberry Pi 4's GPIO pins.
C
39
star
11

livegb

๐Ÿ” Edit Game Boy games in-browser while they're running.
TypeScript
34
star
12

pinhole

โšช Falling ball toy. See the future!
Haskell
30
star
13

dagre-react

โคต๏ธ React component to lay out and render directed graphs.
JavaScript
23
star
14

reactions-spreadsheet

โค๏ธ Tiny multiplayer spreadsheet where you can react to cells.
HTML
21
star
15

otron

๐Ÿšซ UNSUPPORTED Chrome extension for end-to-end Facebook chat encryption.
JavaScript
16
star
16

cascading-content-sheets

๐ŸŽฉ Define page content and behavior in CSS. (???)
HTML
16
star
17

tex2png

Convert TeX and LaTeX to PNG images.
Shell
15
star
18

pretty-print

๐ŸŽž Animate from one string to another.
HTML
12
star
19

dewdrop

โ˜€๏ธ NeWS clone (very work in progress).
TypeScript
9
star
20

Birdhouse

๐Ÿฆ Functional reactive Twitter bots.
JavaScript
9
star
21

geom

๐Ÿ“ Geometry workspace.
JavaScript
8
star
22

little-fbdev-critcl-play

๐Ÿ–ฅ little Linux graphics w/o window system or GPU acceleration or anything.
Tcl
8
star
23

playgroundize-devtools-protocol

โ“ Chrome extension to turn Chrome DevTools protocol viewer into interactive playground.
JavaScript
7
star
24

waypatch

โœ‚๏ธ Move patches of a page backward in time.
JavaScript
7
star
25

metrodown

๐Ÿš‰ Metro map language.
JavaScript
7
star
26

waydiff

๐Ÿ“ฐ Chrome extension to visual diff pages in Wayback Machine.
JavaScript
7
star
27

ten-hundred

๐Ÿ“– Explain everything, starting from the 1000 most common words.
JavaScript
6
star
28

visible-web-page

๐Ÿ‘“ visible.
HTML
6
star
29

markov-music

๐ŸŽผ Markov chains for music generation.
C++
6
star
30

weird-pointer

๐Ÿ‘ฝ Weird mouse pointer animation.
HTML
6
star
31

PyVolvelle

๐Ÿงญ
Python
6
star
32

inline-c-in-nodejs

C
5
star
33

little-web-server

โ–ซ๏ธ little.
Lua
5
star
34

constitution

๐Ÿ“œ Simulating the Constitution.
JavaScript
5
star
35

att

Assembly
4
star
36

pixels-under-mouse

๐Ÿ‘ Example of getting pixels under mouse pointer on macOS.
Lua
4
star
37

computertop-desk

Python
4
star
38

golelm

๐Ÿ‘พ Conway's Game of Life in Elm.
Elm
4
star
39

critcl-pointer-play

๐Ÿ‘‰๐Ÿฝ pointer
Tcl
4
star
40

hs-drone

๐Ÿš Haskell library to control AR Drone quadcopters.
Haskell
4
star
41

JackScheme

๐Ÿ’ป Scheme interpreter in Jack (the nand2tetris language).
4
star
42

Browsereceipt

๐Ÿงพ Scroll Web page up into cat printer.
Swift
3
star
43

little-epoll-fiber-scheduler

๐Ÿ”ณ little.
Lua
3
star
44

transparent-overlay

Objective-C
3
star
45

template-matching

๐Ÿ”Ž Image template matching using Apple vDSP.
C
3
star
46

demo2014-kay

๐Ÿ’ญ Talk given by Alan Kay at DEMO Founder School 2014.
3
star
47

buffaloe

๐Ÿ’ฌ Online buffalo grammar.
JavaScript
3
star
48

pchrome

๐Ÿ“ฆ Promisified, typed Chrome API wrapper.
JavaScript
2
star
49

dotfiles

๐Ÿ“‹ dotfiles.
Emacs Lisp
2
star
50

display-stream-rects

Tcl
2
star
51

fiat

๐ŸŒ Parallel world generator.
JavaScript
2
star
52

react-markdown-loader

โœ๏ธ Webpack loader for Markdown w/ JSX.
JavaScript
2
star
53

rgbds.js

๐Ÿ”Œ Game Boy assembler/linker, ported to JS.
Assembly
2
star
54

lj2014-birdhouse-talk

๐Ÿ’ญ My talk on Birdhouse from Lambda Jam 2014.
JavaScript
1
star
55

db

C
1
star
56

multidrone

1
star
57

react-router-build

JavaScript
1
star
58

template-matching-play

Python
1
star
59

jumpgirl

๐Ÿ‘ง Khan Academy CS demo.
JavaScript
1
star
60

ElmCity

๐Ÿšง City-building game.
JavaScript
1
star
61

wiggly_ic_1

SystemVerilog
1
star
62

fluvium

๐ŸŒŠ Fluid physics game.
C
1
star