feat: flatten CSS gradients at PDF print time for fast Preview.app rendering#144
Merged
Conversation
…ndering Chrome's Skia backend converts CSS gradients into Type 4 shading functions + soft masks, which macOS CoreGraphics renders pathologically slowly (measured: 14.2s for one slide) and incorrectly (radial-gradient stars mostly invisible). pdf.html now embeds a script that replaces gradient background-images with PNGs rasterized by Chrome itself (SVG foreignObject → canvas → data URL) before printing; text stays fully vector (selectable/searchable). run_chrome_print passes --virtual-time-budget so the script completes before print. Targets whose capture cannot be guaranteed faithful are skipped and keep their vector gradient (slow but correct): url() layers, background-attachment: fixed, background-clip: text, non-normal background-blend-mode, color-clip changes, fragmented inline elements, non-px dimensions, padded/bordered pseudo-elements, oversized canvases. Measured on a real 11-slide deck: page 1 render 14,204ms → 33ms, total 15,109ms → 273ms, /Shading 19 → 0, identical visual output. Closes #142 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
mizzy
added a commit
that referenced
this pull request
Jul 6, 2026
Resolves conflicts with PR #144 (gradient flatten for PDF export): - render.rs: keep both PDF_FLATTEN_JS and BUILTIN_MEASURE_JS consts - main.rs: adopt chrome_print_args helper atop our run_one_shot_chrome runner - tests: union both sides Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Merged
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #142
問題
peitho export pdfで生成したPDFをmacOSのプレビュー.appで開くと、各ページが真っ白の状態から部分的に描画されていく(実測: 表紙1ページの描画に14.2秒)。根本原因(実測で特定)
CSSグラデーション。ChromeのSkiaバックエンドはCSSグラデーションをType 4シェーディング(PostScript計算関数)+ソフトマスクとしてPDF化し、macOSのCoreGraphicsはこれをピクセル毎に関数評価するため極端に遅い。さらに「遅い」だけでなく「正しく描画されない」(radial-gradientで描いた星がほぼ消える)ことも確認。対照実験でグラデーションだけをフラット色化するとType3フォントが残っていても5.8ms/ページになり、フォントは無関係と確定。
対策
印刷用
pdf.htmlにフラット化スクリプトを埋め込み、グラデーション背景だけをChrome自身にラスタライズさせてdata URL画像に置き換える(SVGforeignObject→ canvas →toDataURL。Chromeの実レンダリングを使うためCSS解釈のズレが原理的に生じない)。テキストは完全にベクターのまま(選択・検索可能)。run_chrome_printに--virtual-time-budget=10000を追加してスクリプト完了後に印刷する。忠実なキャプチャを保証できない対象はスキップしてベクターを温存する(劣化方向は常に「遅いが正しい」):
url()レイヤー混在 /background-attachment: fixed/background-clip: text/ 非normalのbackground-blend-mode/ 色のclip変化 / 複数フラグメントのインライン要素 / pxに解決できない寸法 / padding・border付き疑似要素 / canvas上限超過。実測結果(decks/carina、11スライド)
/Shading/SMask同一背景を共有するスライドは同一data URLになりSkiaが重複排除するため、埋め込み画像は実質1枚分(11スライドで2オブジェクト/789KB)。
設計記録
docs/plans/2026-07-06-pdf-gradient-flatten.md(計測事実・スキップ条件・設計判断)テスト
chrome_print_argsの引数列固定、スクリプト埋め込み、</scriptガード/Shading不在+画像存在+フォント存在、既存E2Eも緑検証中に発見した別問題(別Issue化予定)
--headless=newのChromeが印刷完了後に終了せずpeitho export pdfがハングする(本変更とは無関係の既存問題、--virtual-time-budgetなしでも再現)🤖 Generated with Claude Code