Code and Coffee

Code & Coffee is a hub for devs seeking balance. We cover everything from cutting-edge tech stacks to lifestyle tips. Whether you’re debugging code or savoring a fresh brew, find inspiration and practical advice here. Elevate your coding game while enjoying life’s simple pleasure

Featured

šŸŽÆ Print Me Baby One More Time

--

šŸŽµ ā€œOops!… I Did It Againā€ — Britney Spears

ā˜• Back to Ruby (And Back to PDFs)

It’s been a minute since I last tangoed with Ruby. Honestly? Years. Long enough to forget how bindings in blocks work, fumble my way through rspec, misuse simplecov, and set up CI pipelines that make me question my life choices.
Oh, and now we have rbs and one-liner method definitions? Cute.

But today, we’re diving headfirst into one of the oldest, gnarliest problems in the web dev trenches: generating PDFs from a Rails app.

And yes — it’s still a mess.

🧳 The Old Way: Wkhtmltopdf & Wicked Nostalgia

I needed to render a PDF from HTML. Reflex kicked in.
Grab wicked_pdf. Pair it with wkhtmltopdf. Back in the day, that combo was peak developer couture.

At first, things seemed fine. Then came the catch: I needed headers and footers that show up consistently — not just in pretty HTML preview, but in the actual printed PDF.

So I did what any nostalgic developer would do: went straight to the wkhtmltopdf GitHub repo.

What did I find?

🪦 Archived. Dead. Two years gone. No signs of life.

😬 Panic Mode & The JavaScript Jungle

Someone on X (RIP Twitter) said, ā€œJust use Prawn.ā€
Right. Let me handcraft every layout , manually paginate content, and write 200 lines of line-break handing code by candlelight. No, thanks. I’ve done my time.

Then someone whispered: ā€œGrover.ā€
It uses Puppeteer under the hood. Sounds promising — until you realize it hauls in half of npm and an entire browser just to print an invoice.

Y’all. I just wanted a PDF.

šŸ‘€ Going Deeper: Puppeteer & the BiDi Protocol

Grover is just a wrapper around Puppeteer, a JS library that controls Chrome (and Firefox ). It’s slick — but it’s still a Node.js.

Naturally, curiosity got the better of me. If Puppeteer can do it… could I ditch the JS layer and go native?

That led me to the BiDi (bidirectional) protocol — a modern, structured way to talk directly to the browser. I read through the chromium-bidi specs, explored Puppeteer’s internals, and spiraled into DevTools docs at 2AM.

Eventually, I got it working.
HTML → Chrome → PDF.
I danced. Briefly.

šŸ”’ The PDF Problem: Headers, Footers & Chrome’s Quirks

Here’s where it got tricky. I found this magical CSS:

@page {
@top-center {
content: element(pageHeader);
}
}
header.page-header {
position: running(pageHeader);
}

Looked legit. Sounded powerful. Except Chrome took one look and said:

ā€œI don’t know her.ā€

Turns out, even in 2025, Chrome still doesn’t support element() in print styles. This is one of those features that lives in every spec doc and Stack Overflow dream, but in practice? Chrome ghosts it.

In my search for a workaround, I found this fantastic write-up by Idan Co, titled ā€œThe Ultimate Print HTML Template with Header & Footerā€.
It’s a deep dive into the realities of getting printed layouts right in the browser.

Spoiler alert: the solution involves using a table and thead and tfoot as empty ā€œplace-holdersā€.

Yes. A table. Like it’s 2002.

But here’s the thing — it works. It’s a pragmatic hack that survives Chrome’s quirks and avoids the heartbreak of unsupported @page magic.

So if you’re still in the trenches, wrestling with disappearing footers and floaty headers, that article might just save your weekend.

🌟 Redemption: Paged.js to the Rescue

Just as despair set in, I found Paged.js — a polyfill for printed web content. And guess what?

✨ It. Actually. Works. ✨

With some setup, you get glorious, print-ready output with real page numbers, headers, and footers that don’t ghost you at the worst moment.

Here’s a sample setup:

@page {
size: A4;
margin: 30mm;
font-family: 'Roboto', sans-serif;

@top-center {
content: element(pageHeader);
}

@bottom-center {
content: element(pageFooter);
}

@bottom-right {
content: counter(page) " / " counter(pages);
}
}
<header class="page-header">
<h1>PDF Rendering Sample</h1>
<p class="text-muted">Styled with Bootstrap &amp; custom CSS</p>
</header>

<footer class="page-footer text-center">
<p class="small text-muted">Generated with ā¤ļø by Bidi2PDF – Page </p>
</footer>

Pro tip: yes, the header and footer go in the body and they need to be at the top of your page. No, I don’t know why. Just roll with it.

šŸ’” Bidi2PDF: The Birth of a Ruby Gem

All this chaos led to something useful:
šŸŽ‰ Bidi2PDF, a work-in-progress Ruby gem.

It gives you a clean CLI to print any website as a PDF. No Node.js. No giant wrappers. No tears.

Supported out of the box:

  • āœ… Basic Auth
  • āœ… Session cookies
  • āœ… Custom auth headers
  • āœ… Modern print styling via Paged.js
  • āœ… Chrome automation via BiDi

PDF printing remains one of the dark arts of web dev.
But with Bidi2PDF, you’ve got a better flashlight.

Footnote: Britney Spears’ ā€œOops!… I Did It Againā€ isn’t just a pop banger — it’s a mood. Especially in dev land. That moment you think, ā€œThis time it’ll workā€ as you reinstall a PDF gem for the fifth time? Britney gets it. The song is basically the emotional soundtrack of jumping back into legacy code, patching print styles, and pretending wkhtmltopdf will behave this time. It’s not just nostalgia — it’s dev dĆ©jĆ  vu. And like Britney, we always circle back. Not because it’s easy. But because we did it again. šŸ’»ā¤ļøšŸ–Øļø

Created in collaboration with OpenAI’s GPT-4.

--

--

Code and Coffee
Code and Coffee

Published in Code and Coffee

Code & Coffee is a hub for devs seeking balance. We cover everything from cutting-edge tech stacks to lifestyle tips. Whether you’re debugging code or savoring a fresh brew, find inspiration and practical advice here. Elevate your coding game while enjoying life’s simple pleasure

Dieter S.
Dieter S.

Written by Dieter S.

Fullstack developer for 25years now. Loves Agile, DevOps, Security.

No responses yet