Edit My PDF now opens three kinds of PDF it used to show only in part: scanned documents, pages with Chinese, Japanese or Korean text, and files that carry their own colour profile. The privacy promise didn't move to make this happen. Every file the editor needs still comes from this site, and nothing you open leaves your browser.
What you'll notice
Scans show their pages.
Scanned PDFs often store each page as a JPEG 2000 or JBIG2 image, the formats of fax machines and document scanners. Those pages used to come up blank. They now appear.
Chinese, Japanese and Korean text appears.
A PDF can set these languages in a font it doesn't include, trusting the reader to know the character map. The editor now has the maps.
Colours follow the file.
Some PDFs include an ICC profile, a note saying exactly what their colours mean. The editor now honours it instead of taking a close guess.
Bullets and symbols use the right face.
Text in the classic Symbol or ZapfDingbats fonts, common for bullet points in older documents, draws with a proper stand-in.
You don't have to do anything. Open a PDF as usual and, if it's one of these kinds, it looks right.
How a PDF reader works, roughly
Edit My PDF reads PDFs with pdf.js, the open-source reader built into Firefox. pdf.js comes in two parts. There's the engine, which the editor has always shipped. And there's a set of helper files the engine loads only when a document needs them: two picture decoders, for JPEG 2000 and JBIG2 images, compiled to WebAssembly (a way of running programs written in languages like C inside a browser tab, quickly and safely); a colour engine, also WebAssembly, with a profile that describes printing inks; 168 character maps for Chinese, Japanese and Korean fonts; and fourteen stand-in fonts for PDFs that name a font without including it.
The engine has to be told where those files live. Until this release the editor shipped the engine on its own, so a scanned page had no decoder to call, a Japanese line had no map, and the colour engine stayed switched off. Now the helper files travel with the app and the engine knows exactly where they are.
Why I didn't point it at someone else's server
The quick fix is one line: point pdf.js at a public CDN that hosts these files for anyone. Most examples online do exactly that, and I was tempted for about a minute.
The reason I didn't is that it would quietly break the one promise this editor is built on. Edit My PDF makes no request to anybody else's server while you work. A CDN would learn that someone, at some address, opened a PDF with a Japanese font at a particular time. That isn't your document, but it isn't nothing either, and it's exactly the kind of thing I said I'd never send. I also want the editor to work offline eventually, which means every file it needs has to be part of the app.
How the helper files ship with the app
The work happens when the site is built, not when you use it. Every build:
- 1
Copies the helper files out of pdf.js.
The decoders, the colour engine and its ink profile, the 168 character maps and the 14 stand-in fonts come straight from the pdf.js package the editor already depends on.
- 2
Puts them in a folder with a fingerprint in its name.
The folder's name ends in a short code worked out from the files' contents. When pdf.js is updated, the contents change, the code changes, and no browser can confuse the old files with the new.
- 3
Tells the engine the address.
Whenever the editor opens a document, it hands pdf.js the folder's address on this site. Every request stays on editmypdf.dev.
The folder is about 3.5 megabytes, but almost nobody downloads it. pdf.js fetches a file the first time a document needs it, and a PDF with its own fonts embedded, ordinary JPEG pictures and screen colours fetches nothing. The app you load grew by 181 bytes, which is the note saying where the folder is.
The fingerprint in the folder's name is there because browsers keep copies of files they've downloaded. That's great until a file changes and the browser keeps using the old copy. With a fingerprint of the contents in the name, a changed file has a changed address and the stale copy is never asked for.
There's also a new rule in the code checker: the editor opens PDFs through exactly one door, and the checker refuses code that tries to go around it. I added that after finding that the little thumbnails on the documents screen had a door of their own, and had missed the helper files entirely.
How I tested it
Most PDFs never touch these files, so testing with ordinary documents proves nothing. I built four tiny PDFs with a Python script, each about a kilobyte, each asking one question: a gradient picture saved as JPEG 2000; black shapes saved as JBIG2; a colour profile with its red and blue primaries swapped, so a square that's red in the file turns blue only if the colour engine is really running; and a line of Japanese text in a font the file doesn't include, with some bullet points in the Symbol font.
Before this release: blank, blank, red, missing. Now: gradient, shapes, blue, 日本語テスト, and the browser console, which used to show a warning for each, is quiet. The JBIG2 one took two tries. Pillow stores 1-bit images with black and white the opposite way round from JBIG2, and my first test page was a white rectangle on a black page.
Also fixed: a one-byte font bug in exports
While checking the Symbol bullets I found something older. When you edit text and download, the editor writes a slimmed-down copy of each font into the new PDF, with just the characters your edit needs. For one family of fonts, the kind built on compact outlines, the library doing the slimming was writing a wrong value into one byte of the font's header.
Most PDF readers never look at that byte. Browsers do: Chrome and Firefox run every font through a checker called the OpenType Sanitizer before drawing with it, and it refused those fonts, so an edit in such a font opened in a fallback face in any browser-based reader, this editor included. I'd assumed the bug was something about Symbol, so I made a control PDF in an ordinary font of the same kind, edited a word and exported it. Same bad byte. It had been there for every font of that kind the editor ever exported.
The editor now checks that byte on the way out and sets it to a valid value. The exported fonts pass the sanitizer, load in the browser and open in the editor with the right glyphs.
What it means for your documents
Scans from a copier, forms from a government site, a paper in Japanese or Korean, a brochure with print-ready colours. These are everyday PDFs, and they now open the way their makers intended. Because the helper files are part of the app, they'll keep working when the app runs offline, which is where I'm heading next.
If you read code, the build step that ships the helper files is in vite.config.ts, the one door is openDocument in src/pdf.ts, and the byte repair is src/soundFontkit.ts.