'Redrawing a D3/SVG chart for printing

I have a D3.js chart on my website.

I'd like it to redraw itself for printing. The only way I know of to do this is to use a combination of window.matchMedia('print') and 'onbeforeprint' to redraw the chart based on the size of the window, since the browser will make the window the size of the page in that event.

However, D3 does not draw the chart fast enough in Safari and Firefox. The result is that D3 re-renders the charts, but it occurs too late. This does, however, work in Chrome:

let beforePrint = () => {
  this.handleResize();

};
let afterPrint = () => {
  this.handleResize();
};

if (window.matchMedia) {
  window.matchMedia('print').addListener(mql => {
    if (mql.matches) {
      beforePrint();
    } else {
      afterPrint();
    }
  });
}

window.onbeforeprint = beforePrint;
window.onafterprint = afterPrint;

Are there any other options for re-drawing D3/SVG charts for printing?



Solution 1:[1]

I used duplicate elements for the graph.

First, a print version for the graph is rendered with width to match the paper size. Then it's hidden, and a screen version is shown instead.

Details for AngularJS solution in this answer.

Solution 2:[2]

Same problem here in 2022 (1).

I ended up delaying printing by milliseconds (2):

function print()
{
    window.onafterprint = afterPrint; // This one occurs at the right time, keep it.
    beforePrint();
    window.setTimeout(window.print, 500);
}

(plugged to a "Print" button, and to Ctrl-P as shown by How to Disable the CTRL+P using javascript or Jquery? ; alas beforeprint could not be trapped like Ctrl-P)

(contrary to @jsruok 's solution, my print-oriented shadow SVG is pretty big so I did not want to keep it hidden while handling the "main" SVG, I need it to be generated once at print time then discarded)


(1) To print a "big" SVG, I replace it with multiple, page-sized SVGs (think mosaic) which each show part of the big picture by <use transform="translate()">'ing it.

(2) It seems to me that Firefox' SVG rendering is delayed, as if in a setTimeout(0), contrary to HTML DOM elements:

  • Ctrl-P
    • onbeforeprint called
      • add DOM elements
      • add SVG elements
      • return
    • Firefox grabs DOM to generate the print preview: new DOM elements are taken into account, but new SVGs display as blank
    • onafterprint called
  • new loop iteration, SVG gets rendered
  • now if Ctrl-P'rinting, the generated mosaic appears in print preview

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 jsruok
Solution 2 Guillaume Outters