Skip to content

fix(print): keep PDF exports readable in all themes - #359

Merged
PathGao merged 2 commits into
sftwrdotdev:masterfrom
PathGao:fix/pdf-export-print-layout
Aug 2, 2026
Merged

fix(print): keep PDF exports readable in all themes#359
PathGao merged 2 commits into
sftwrdotdev:masterfrom
PathGao:fix/pdf-export-print-layout

Conversation

@PathGao

@PathGao PathGao commented Aug 2, 2026

Copy link
Copy Markdown
Collaborator

Summary

PDF exports now use a paper layout of their own instead of inheriting the active screen theme, and diagrams are rebuilt for paper rather than recoloured.

Paper layout. The page gets its margin from the document body so a dark theme no longer leaves the sheet edges unpainted. Wide tables distribute columns from their content and wrap at word boundaries, alerts and table rows stay inside page boundaries, and the properties panel no longer overflows.

Diagrams. Mermaid bakes the active theme into the SVG it emits — fills, strokes and label colours are attributes on the generated elements, not variables a stylesheet can retarget. My first attempt overrode them from the print stylesheet, which means enumerating Mermaid's internal class names; those differ per diagram type and drift between releases. Flowchart nodes sit under g.node, sequence actors are a bare rect.actor, and flowchart labels moved into foreignObject in v11 while sequence labels are still <text>.

Missing one is worse than no override at all: the label rule lands, the fill rule does not, and dark text ends up painted on a near-black box. Measured against Mermaid 11.12 rendered with the dark theme, the enumerated rules left sequence diagrams at a contrast ratio of 1.18 — effectively invisible — while flowcharts reached 4.94.

So this now rebuilds the diagrams instead. The screen render keeps the diagram source on its container, and an export re-renders every diagram with Mermaid's light theme before printing, restoring the on-screen rendering afterwards. The same measurement gives 4.75 for sequence diagrams and leaves flowcharts unchanged. It stays correct for diagram types this version has never seen, and the 36 lines of recolouring CSS are gone. Diagram pagination rules stay.

Fixes #232

Validation

  • npm run check — 0 errors, 0 warnings
  • npm test — 161/161, including a new scripts/mermaidPrintTheme.test.ts covering the theme decision, source round-trip, re-render, idempotent restore, and the failure path where a diagram that cannot be rebuilt keeps its screen rendering
  • Contrast figures above were measured in Chromium against Mermaid 11.12.2 for flowchart, sequence and gantt diagrams, before and after the change
  • Page geometry checked across a 111-page export: top margin 54–60pt against a 0.75in target, no page below 40pt

PathGao and others added 2 commits August 3, 2026 00:20
Mermaid bakes the active theme into the SVG it emits — fills, strokes and
label colours are attributes on the generated elements, not variables a
stylesheet can retarget. Exporting from a dark preview therefore produced
diagrams that were unreadable on paper.

Recolouring that output from the print stylesheet means enumerating
Mermaid's internal class names, and those differ per diagram type and drift
between releases: flowchart nodes sit under `g.node`, sequence actors are a
bare `rect.actor`, flowchart labels moved into `foreignObject` in v11 while
sequence labels are still `<text>`. Missing one is worse than no override at
all — the label rule lands, the fill rule does not, and dark text ends up
painted on a near-black box. Measured against Mermaid 11.12 with the dark
theme, the enumerated rules left sequence diagrams at a contrast ratio of
1.18 while flowcharts reached 4.94.

Rebuild the diagrams instead. The screen render now keeps the diagram source
on its container, and an export re-renders every diagram with Mermaid's
light theme before printing, restoring the on-screen rendering afterwards.
The same contrast measurement gives 4.75 for sequence diagrams and leaves
flowcharts unchanged, and the result stays correct for diagram types this
version has never seen. The theme decision is shared by the screen render
and the restore so the two cannot drift.

The 36 lines of diagram recolouring in the print stylesheet are removed;
pagination rules for diagrams stay.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@PathGao
PathGao force-pushed the fix/pdf-export-print-layout branch from 0c5d810 to 665b476 Compare August 2, 2026 16:56
@PathGao
PathGao merged commit 37b3693 into sftwrdotdev:master Aug 2, 2026
5 checks passed
@PathGao
PathGao deleted the fix/pdf-export-print-layout branch August 2, 2026 18:17
PathGao added a commit that referenced this pull request Aug 2, 2026
**Find counted matches nobody could reach.** The tree walker skipped only
script, style, noscript and its own marks, so text inside a collapsed
heading section or a collapsed callout stayed in the DOM and stayed
countable — but both containers clip to zero height, so pressing Enter onto
one of those matches scrolled nowhere and looked like the search was broken.

Matching a match now reveals its folds. That is what the surrounding
software does: Chrome reveals `hidden=until-found` content and closed
`<details>` when find lands inside them, VS Code unfolds a region when
navigating to a match, and Obsidian tracks "counted but never revealed" as a
bug. Simply skipping folded text would have been the other consistent
answer, but it makes folding silently remove text from search with no
signal, which nothing does.

The reveal goes through the viewer's own fold toggle rather than stripping
the class. `collapsedHeaders` is the source of truth and is re-applied on
every render, so a class removed here would come back on the next one, and
the table of contents would disagree in the meantime. Dispatching the
toggle's own click keeps the DOM and that Set in step.

**Find highlights were printed into exported PDFs.** Nothing cleared them
before printing and `print-color-adjust: exact` made sure they came through.
They cannot go in the print block's `display: none` list — the mark wraps
document text, so hiding it would delete the matched words from the page —
so its background, shadow and colour are neutralised instead and the text
survives. Highlights authored with `==…==` are untouched.

**Images and diagrams taller than a page were clipped rather than scaled.**
`break-inside: avoid` was set with no height cap, and a replaced element
cannot fragment, so anything taller than the printable area simply lost its
bottom. They now cap at 9in with `object-fit: contain`, which fits both
Letter (9.5in printable) and A4 (10.19in). Mermaid containers also need
`overflow: visible` in print, because their on-screen `overflow-x: auto`
computes `overflow-y` to `auto` and would re-crop what the cap just fixed.

The size cap is purely additive to #359, whose mermaid work is a light-theme
re-render carrying deliberately zero mermaid print CSS. No existing
declaration in the print block was changed.

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
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.

Export to PDF Format is Broken

1 participant