The technical side of PageVera: how a save works, what Chrome’s PDF engine can and can’t do, and how to run the tests.
The extension attaches Chrome’s debugger to the tab and calls the DevTools command Page.printToPDF. That is the same PDF engine as Print → Save as PDF, without the dialog. In that engine:
The quality gains come from preparing the page first:
!important, so autoplay timers and resize handlers can’t move the slides back.vh units are measured against the paper height, which would make a full-screen hero section as tall as the whole PDF. The extension briefly emulates a viewport as tall as the paper, finds every style that changes, and pins it to its on-screen value.<canvas> (Google Docs, Sheets, Figma, maps) comes out as an image, not text.filter, backdrop-filter or blurred shadow are rasterized at 300 DPI by Chrome’s PDF engine. Do-nothing filters such as brightness(1) are removed automatically, and so are filters and shadows on boxes taller than two screens, where they would turn into huge images.chrome:// pages, the Chrome Web Store and other extensions’ pages. If the tab is already showing a PDF, the popup offers to download the original file.Requirements: Node 22, Python 3 with Pillow and numpy, and poppler (brew install poppler) for the PDF checks.
npm install # playwright-core, used only by the tests
npm run fixtures # generate test images (already committed)
npm run icons # redraw the icons
node tools/make_screenshots.mjs # redraw the README images from docs/demo/article.html
npm test # unit tests + end-to-end tests
npm run package # zip only the files the extension needs
The end-to-end tests launch Chrome for Testing with the unpacked extension (branded Chrome ignores --load-extension). By default they use the Playwright build at ~/Library/Caches/ms-playwright/chromium-1243. Set CHROME_PATH to use another Chrome for Testing binary.
node tests/run-one.mjs <fixture.html | URL> '{"layout":"paged"}' [--headed] [--out file.pdf] saves one page and prints the job report, embedded fonts and embedded image sizes.
Code layout:
background.js: service worker. Routes popup, shortcut, context menu and picker requests.lib/job.js: one save from start to finish.lib/cdp.js, lib/agent-host.js: debugger session and the isolated world the page script runs in.content/agent.js: page preparation. Every change is recorded on the element so it can be undone.content/picker.js: the click-to-remove toolbar.lib/paper.js, lib/pdf-stream.js, lib/offscreen-client.js, offscreen.js, lib/download.js: paper size math, reading the PDF stream, and turning it into a downloaded file.