fix(canvas): PDF export comes out blank in Safari - #9790
Conversation
html-to-image clones a <canvas> into an <img> nested inside the <foreignObject> it serializes, and WebKit paints that SVG before the nested image has decoded, so the first capture of a node holding a canvas comes back blank. captureCanvasBlocks rasterizes each block exactly once, which means that in Safari every chart in an exported PDF is empty; only the KPI sparklines survive, because they are inline SVG rather than Vega's canvas renderer. Probe once per capture with a tiny canvas and, where it comes back blank, capture canvas-backed blocks twice and discard the first result. Probing rather than matching the user agent keeps the extra pass off the browsers that do not need it, and lets the workaround retire itself once WebKit changes.
paginate scaled the capture up to the page content width, so a phone-width canvas was inflated by about 40%. That pushed rows past the page height, which sliced whole charts across pages and left most of each following page empty. Captures narrower than the page now keep their size and are centred.
The off-screen export render took its width from the width the dashboard happened to occupy on screen, so the same canvas produced a different document depending on the window it was exported from, and a phone produced a narrow capture stacked into a single column. Render it at the canvas's max_width instead.
html-to-image resolves and inlines the fonts on every capture, and cacheBust means nothing is reused between them. Sharing one precomputed stylesheet takes a pair of captures of a chart card from 3534ms to 414ms. The gap it opens also breaks the canvas warm-up: WebKit hands back the decoded nested image only on the capture immediately after the one that triggered it, so the two passes have to stay close together.
|
Hey @nishantmonu51 / @AdityaHegde Also, dark mode dashboards print as a wall of ink, Should the export always be light, on a white background page? The shape of it: switch to light before the export view mounts and restore afterwards, leaving the persisted preference untouched. Both the dark class and the theme store have to flip, since components are split between reading the class (chart colour resolution) and subscribing to the store (CanvasChart). The catch is that the switch is global, so the live dashboard visibly flips for the length of the run. We cover the dashboard region with an opaque layer showing the export progress, which also stops anyone clicking into a dashboard that is mid-capture. I have also been thinking about adding a small Just let me know how to proceed. |
In Safari every chart in a canvas PDF export comes out blank, headers only. Export twice without reloading and the second one is correct. Chrome and Firefox are unaffected.
html-to-imageclones a<canvas>into an<img>nested in the<foreignObject>it serializes, and WebKit paints that SVG before the nested image has decoded.captureCanvasBlocksrasterizes each block once, so every chart is always a first capture.Known upstream and unfixed. bubkoo/html-to-image#569 reports canvases blank on Safari across macOS, iOS and iPadOS; capturing twice is the standing workaround there (bubkoo/html-to-image#292), and the open fix (bubkoo/html-to-image#498) keys off a user agent regex. So it lives here for now, behind a probe instead.
Commits: probe for the affected browser and capture those blocks twice; stop
paginatemagnifying captures narrower than the page; render the export view at the canvas'smax_widthso the document does not depend on the exporter's window.Same capture path as #9770.
Checklist: