3

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?

alt
  • 13,357
  • 19
  • 80
  • 120
  • Have you considered rastering the SVG into an image and printing that instead? [html2canvas](https://github.com/niklasvh/html2canvas) works pretty well on SVG elements. – cvsguimaraes Dec 30 '16 at 01:11
  • @snolflake the chart is interactive so the rasterization would have to happen on print and I can't imagine it being faster than redrawing. thanks tho – alt Dec 30 '16 at 01:23
  • 1
    Yes thats how I imagined it to be. I'm suggesting this because Ive did the exact same thing in the past. I had a interactive chart with lots of dynamic elements and inline CSS. I had a print button that called html2canvas on the SVG element and then converted the returned canvas content into a [data url](https://developer.mozilla.org/en-US/docs/Web/API/HTMLCanvasElement/toDataURL) which I used to popup a window with the print action. IMHO its easier than it sounds. – cvsguimaraes Dec 30 '16 at 02:40
  • Use CSS media queries to change the CSS applied on printing. – Robert Longson Dec 30 '16 at 10:42
  • @kbtzr can you please provide more details on how you accomplished it with code example if possible. thanks in advance. – Ahmad Karim Nov 05 '19 at 08:57

2 Answers2

0

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.

jsruok
  • 545
  • 7
  • 10
0

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