Skip to content

feat: browser-native PDF export via Print with animations completed#105

Open
sonihemish wants to merge 2 commits into
zarazhangrui:mainfrom
sonihemish:feat/browser-native-pdf-export
Open

feat: browser-native PDF export via Print with animations completed#105
sonihemish wants to merge 2 commits into
zarazhangrui:mainfrom
sonihemish:feat/browser-native-pdf-export

Conversation

@sonihemish

Copy link
Copy Markdown

Summary

Makes the browser's built-in Print → Save as PDF (Cmd/Ctrl+P) produce a correct one-page-per-slide PDF with all entrance animations completed — no scripts, no Playwright, no dependencies. Addresses #28.

Generated decks also get a small floating 📥 PDF button wired to window.print(), so viewers can export without knowing the shortcut.

Why

The existing export-pdf.sh works, but it requires Node + Playwright (~150MB Chromium download), produces raster screenshots (no selectable text, large files), and can't be used by someone who just received the HTML file. The repo already had a @media print section in viewport-base.css, but in practice it was broken:

  1. No @page rule → slides printed on A4/Letter with margins, scaled and cut.
  2. Entrance animations never completed.reveal elements on slides the presenter hadn't visited stayed at opacity: 0, so printed pages were missing text and diagrams.
  3. No print-color-adjust → browsers stripped themed backgrounds.
  4. Blank page after every slide → any slide box even 1px taller than the page sheet (e.g. padding without border-box) makes browsers emit a trailing blank page per slide.

What changed

  • viewport-base.css@page sized to the 1920×1080 design canvas; print-color-adjust: exact; fast-forward .reveal variants and [data-anim] to their finished state; freeze transitions/animations mid-flight; hide deck/editing chrome; harden the slide box (border-box + max-height + zero margin) against the blank-page problem.
  • html-template.mdsetupPrintExport() in the SlidePresentation controller: beforeprint marks every slide .visible so class-gated animations resolve, afterprint restores the exact pre-print state. Documents the default floating PDF button.
  • bold-template-pack/deck-stage.js — same treatment for the web-component path: the head-injected print stylesheet (slide content is light DOM that ::slotted() can't reach) gains the animation fast-forward and hardening; beforeprint/afterprint handlers cover styles gated on [data-deck-active].
  • SKILL.md / README.md — browser-native export documented as the primary zero-dependency path; the Playwright script kept as the automation alternative.

On-screen rendering is untouched — every change is scoped to @media print/@page, and the JS handlers snapshot and fully restore deck state after the print dialog closes. Verified with byte-identical before/after screen screenshots.

Testing

Headless Chromium --print-to-pdf (same engine as the Cmd+P dialog) against a template-faithful test deck using .reveal, .reveal-scale, .reveal-left, and .reveal-blur on slides never visited on screen:

  • ✅ Exactly N pages for N slides, each 1920×1080, no blank pages
  • ✅ All animation-gated text and SVG diagrams present on unvisited slides
  • ✅ Deck chrome excluded; themed backgrounds preserved
  • ✅ Pathological case (content-box slide with padding, previously → blank page after every slide) fixed
  • ✅ Screen rendering byte-identical before/after (SHA-256 of full-page screenshots)
  • ✅ Also validated against 14 real-world generated decks (4–9 slides each): every PDF page count matched its slide count with zero empty pages

Notes

  • Complements rather than replaces export-pdf.sh — the script remains for automation and non-Chromium browsers.
  • Vector output means selectable/searchable text and much smaller files than the screenshot pipeline.

Closes #28.

Make Cmd/Ctrl+P -> Save as PDF produce a correct one-page-per-slide
PDF with no scripts or dependencies, addressing zarazhangrui#28:

- viewport-base.css: add @page sized to the 1920x1080 design canvas,
  force print-color-adjust so themed backgrounds survive printing,
  fast-forward entrance animations (.reveal variants, [data-anim]) to
  their finished state so slides never visited on screen print with
  all text and diagrams visible, freeze transitions/animations to
  avoid mid-flight capture, and hide deck/editing chrome.
- viewport-base.css: harden the print slide box (border-box +
  max-height + zero margin) — a slide even 1px taller than the page
  sheet makes browsers emit a blank page after every slide.
- html-template.md: add setupPrintExport() with beforeprint/afterprint
  handlers that mark all slides .visible for printing and restore the
  exact pre-print state, keeping on-screen behavior untouched.
- bold-template-pack/deck-stage.js: extend the injected document-level
  print stylesheet the same way (slide content is light DOM which
  ::slotted() cannot reach), mirror the blank-page hardening, and add
  beforeprint/afterprint handlers for [data-deck-active]-gated styles.
- SKILL.md/README.md: document browser-native export as the primary
  zero-dependency path (vector output, selectable text); keep the
  Playwright script as the automation alternative.

Verified with headless Chromium print-to-PDF: 3-slide deck with
.reveal/.reveal-scale/.reveal-left/.reveal-blur content on unvisited
slides renders 3 exact 1920x1080 pages with all content revealed and
chrome excluded; screen rendering is pixel-identical before/after.
Add a default floating PDF button wired to window.print() so viewers
can export without knowing the Cmd/Ctrl+P shortcut. Hidden while
printing via the print-hidden rule; styled to match the deck theme.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Request for HTML Slides to PDF Slides

1 participant