A bookmarklet to make a web page print to a single page
Sometimes I want to save a web page as a PDF for some reason or another. However, this can be problematic, because most (all?) browsers’ HTML-to-PDF conversion is an offshoot of the process for printing a web page onto paper. This means that the default behavior is for the page to get broken up into 8.5-by-11-inch chunks, which often wreaks havoc on the layout of pages that have large images or are much taller than they are wide (i.e., basically all modern web pages).
There are, however, workarounds for getting a web page to “print” as a columnar single-page PDF, usually by using JavaScript to fiddle with the CSS. I learned one such method from StackOverflow a few years ago when creating a workflow to archive email newsletters using Mailbagit. At the time, I embedded the necessary code into a Python program and moved on. However, every few months since, I’ve found myself wanting to use that trick on a page in my browser.1
So, I’ve gone ahead and made it into a bookmarklet!
const style1 = document.createElement('style');
style1.innerText = 'html, body { width: fit-content; height: fit-content; margin: 0px; padding: 0px; }';
document.head.appendChild(style1);
const style2 = document.createElement('style');
style2.id = 'page_style';
//Set an arbitrary starting page size. This will be changed in a moment
style2.innerText = '@page { size: 1000px 1000px ; margin : 0px }';
document.head.appendChild(style2);
// Get the height of rendered page in pixels
const renderBlock = document.getElementsByTagName("html")[0];
const renderBlockInfo = window.getComputedStyle(renderBlock);
// fix chrome page sizing bug
const fixHeight = parseInt(renderBlockInfo.height) + 1 + "px";
// Change CSS in <head> so that printing page size is set to render size
const pageCss = `@page { size: ${renderBlockInfo.width} ${fixHeight}; margin:0;}`;
document.getElementById("page_style").innerText = pageCss;
One note: as far as I can tell, this only works properly in Google Chrome (though I haven’t tested other Chromium-based browsers, it may work there as well). I don’t love this fact; I generally try to avoid Chrome on the principle that there at least some parts of my online life I want to keep Google out of. However, Chrome does seem to just have better support for rendering HTML as printed pages. Before Zotero added the ability to annotate saved web snapshots in 2024 (thank you Zotero team!), I would print any HTML-based grad school readings to PDF. I found that I got much better results in Chrome and had more ability to control things with CSS via support for things like the widows property.
Anyway, if you want this bookmarklet as one big URL-escaped string ready to be bookmarked, here you are:
javascript:(function()%7Bconst%20style1%20%3D%20document.createElement('style')%0Astyle1.innerText%20%3D%20'html%2C%20body%20%7B%20width%3A%20%20fit-content%3B%20height%3A%20fit-content%3B%20margin%3A%20%200px%3B%20padding%3A%200px%3B%20%7D'%0Adocument.head.appendChild(style1)%0A%0Aconst%20style2%20%3D%20document.createElement('style')%0Astyle2.id%20%3D%20'page_style'%0A%2F%2FSet%20an%20arbitrary%20starting%20page%20size.%20This%20will%20be%20changed%20in%20a%20moment%0Astyle2.innerText%20%3D%20'%40page%20%7B%20size%3A%201000px%201000px%20%3B%20margin%20%3A%200px%20%7D'%0Adocument.head.appendChild(style2)%0A%0A%2F%2F%20Get%20the%20height%20of%20rendered%20page%20in%20pixels%0Aconst%20renderBlock%20%3D%20document.getElementsByTagName(%22html%22)%5B0%5D%3B%0Aconst%20renderBlockInfo%20%3D%20window.getComputedStyle(renderBlock)%0A%0A%2F%2F%20fix%20chrome%20page%20sizing%20bug%0Aconst%20fixHeight%20%3D%20parseInt(renderBlockInfo.height)%20%2B%201%20%2B%20%22px%22%20%20%20%0A%0A%2F%2F%20Change%20CSS%20in%20%3Chead%3E%20so%20that%20printing%20page%20size%20is%20set%20to%20render%20size%0Aconst%20pageCss%20%3D%20%60%40page%20%7B%20size%3A%20%24%7BrenderBlockInfo.width%7D%20%24%7BfixHeight%7D%20%3B%20margin%3A0%3B%7D%60%3B%0Adocument.getElementById(%22page_style%22).innerText%20%3D%20pageCss%3B%7D)()%3B
-
Yes, I could just use one of the million browser extensions that do this, but A), where’s the fun in that, and B), a lot of the extensions for this feel a little dodgy. ↩