Chartist-js: Charts get cut off on the bottom for print.

Created on 19 Dec 2014  ·  12Comments  ·  Source: gionkunz/chartist-js

The bottom of the charts is removed from the printable version of the page.

screenshot_121814_050716_pm

A fix I found for my charts was to include a print media query that gives padding to the bottom of all charts.

bug

Most helpful comment

Hey there. I've researched a bit and tried different things. One major issue is how browsers print today and there is no unified way. CSS print styles are applied in all browsers correctly but Media Query Listeners are handled totally different on different browsers. Firefox has also an open issue regarding this problem https://bugzilla.mozilla.org/show_bug.cgi?id=774398

On my Chrome 40 on windows the print MQL is handled correctly and the javascript that is executed in the same call stack as the MQL event will be reflected into the rendered DOM for printing. On my Ubuntu with Chrome 39 this does not work correctly.

I guess we'll need to wait until this stabilizes :-( But for Chrome on windows you could use the following temporary workaround to update your charts on the print view:

window.matchMedia('print').addListener(function() {
  chart.update();
});

All 12 comments

Maybe the intrinic containers dont work in print? What browser are you using? Can you create a jsbin to reproduce the issue?

I was testing this on Chrome. Here is a jsbin: http://jsbin.com/xisofuyeve/1/

The problem is that the print canvas has different dimensions / DPI. CSS is executed / re-applied but JavaScript isn't. So the internal Chart dimensions that depend on a re-draw when the container size changes don't get updated. This is the reason why the container is correctly set but the chart is overlapping and gets cut off.

I guess we could solve this somehow by using preserveAspectRatio and viewBox but I guess this goes against the separation of concerns a bit as we would like to keep all display relevant stuff in CSS.

As a workaround you could go with fixed dimensions on desktop and aspect ration on mobile (as there printing is less an issue)

Hey there. I've researched a bit and tried different things. One major issue is how browsers print today and there is no unified way. CSS print styles are applied in all browsers correctly but Media Query Listeners are handled totally different on different browsers. Firefox has also an open issue regarding this problem https://bugzilla.mozilla.org/show_bug.cgi?id=774398

On my Chrome 40 on windows the print MQL is handled correctly and the javascript that is executed in the same call stack as the MQL event will be reflected into the rendered DOM for printing. On my Ubuntu with Chrome 39 this does not work correctly.

I guess we'll need to wait until this stabilizes :-( But for Chrome on windows you could use the following temporary workaround to update your charts on the print view:

window.matchMedia('print').addListener(function() {
  chart.update();
});

With Chartist 0.7.0 you can also add a responsive option that contains a print configuration override. However, this is only working in Chrome 40 I believe.

My temporary workaround so far, I hope it helps anyone with the same problem:

// update and resize chart when printing
var chartScale = function (chart) {
  var ua = navigator.userAgent.toLowerCase();

  // Firefox printing bug workaround
  if (ua.indexOf('firefox') > -1) {
    var chart = document.getElementsByClassName('ct-chart-bar')[0], // change class name to your svg
      width = chart.getBBox().width,
      factor = 565 / width; // width of printable area(75dpi) / width of chart

    chart.setAttribute("transform", "scale("+factor+")");
    setTimeout(function () {
      chart.setAttribute("transform", "scale(1)");
    }, 10);
  } else {
    // this works in Chrome & latest Opera
    chart.update();
  }
};

// fix printing for different browsers
var preparePrintChart = function (chart) {
  if (window.matchMedia) {
    window.matchMedia('print').addListener(function() {
      chartScale(chart);
    });
  }

  window.onbeforeprint = function () {
    chartScale(chart);
  };
};

To use it:

var chart = new Chartist.Bar('.ct-chart', chartData, chartOptions);
preparePrintChart(chart);

Hi, I have the same problem in Firefox Dev Edit 41.0a2 (2015-07-15) , I have to set a width to make it works, but my layout is fluid… so 🐷

To get around this issue myself I've gone with setting the viewBox attribute on the SVG as suggested by @gionkunz. As mentioned this is probably going against SOC.

Since Chartist is recreating the SVG element on every update/redraw it seemed relatively safe to do for my purposes. In my own instance I'm developing with the understanding the users will want to print the chart quite frequently (for an audit of their activity).

In my own copy I've modified Chartist.createSvg to get the clientWidth and clientHeight of the container and set the viewBox accordingly.

  Chartist.createSvg = function (container, width, height, className) {
    var svg;

    //Get the expected width and height of the viewBox
    //This could be cleaner
    var viewBoxWidth = width || container.clientWidth;
    var viewBoxHeight = height || container.clientHeight;
    width = width || '100%';
    height = height || '100%';

    // Check if there is a previous SVG element in the container that contains the Chartist XML namespace and remove it
    // Since the DOM API does not support namespaces we need to manually search the returned list http://www.w3.org/TR/selectors-api/
    Array.prototype.slice.call(container.querySelectorAll('svg')).filter(function filterChartistSvgObjects(svg) {
      return svg.getAttributeNS('http://www.w3.org/2000/xmlns/', Chartist.xmlNs.prefix);
    }).forEach(function removePreviousElement(svg) {
      container.removeChild(svg);
    });

    // Create svg object with width and height or use 100% as default
    svg = new Chartist.Svg('svg').attr({
      width: width,
      height: height,
      //Set the viewbox attribute, this could be much cleaner
      viewBox: "0 0 " + viewBoxWidth + " " + viewBoxHeight
    }).addClass(className).attr({
      style: 'width: ' + width + '; height: ' + height + ';'
    });

    // Add the DOM node to our container
    container.appendChild(svg._node);

    return svg;
  };

If anyone requires a workaround that appears to work for IE & Chrome, you could do the same. I haven't pulled as this is likely a breaking pattern and not the nicest way to fix this issue, just one that's worked for me.

@jmacmi2 your workaround seems to kind of work for printing but it breaks resizing the page, as the whole graph gets scaled down instead of being redrawn at the same height as it happens without your patch.

Actually what happens is that the graph is scaled down along both axises when printing, so if you happen to have an HD screen and a full-width graph on it you will end up with ridiculously small fonds and thin lines when the graph is printed. If you reload the page while your browser window is kept small, then you have bigger text and lines.

Without that workaround the overflow also depends on the pixel size of the rendered graph.

Tested with Firefox 47

The suggestion from @gionkunz above, to use the media listener to update the chart on printing didn't quite work for me using Safari and iOS.

My issue is the narrow iOS screen (or on desktop a wide browser) would yield the chart printing unnecessarily narrow or cropped when printing from a wide browser window.

But, using the media listener to add a class or set the style directly to force the width, then update chartist worked for me. Specifically, calling chart.update(); alone does not resize the chart when using Safari. However forcing the width does help, e.g.:

window.matchMedia('print').addListener(function(mql) {
  var ct = document.querySelector('.ct-chart');
  if (mql.matches) {
    // While printing force the page width to the paper size
    ct.style.width = '7.25in';
    chart.update();
  } 
  else {
    // After printing revert the width
    ct.style.width = 'auto';
  }

We could generalize this approach so that a .ct-print class is added to each chart during printing, then it is up to chart users to set appropriate print widths for their charts during printing.

There are CSS page-break attributes as well, so using those may help to avoid splitting the chart at page boundaries.

Thoughts on pursuing this approach further?

The solutions above didn't quite work for me, as I needed it to work in every single browser, no exceptions.

So I came up with another workaround, and maybe this can be at help for someone else:

I put a button on the page that says "Print", which redirects the user to a new page with the contents that need to be printed, but on a print-page-view, like this fiddle: http://jsfiddle.net/2wk6Q/1/. Like in the fiddle, you can fire the print-event rightaway by window.print();, so no extra clicks for the user.

This is for a A4, but you can set the correct paper sizes in the CSS. As the charts don't need to be resized, they will look like expected.

This is just a workaround though, not a fix. I will keep waiting for a fix.

Was this page helpful?
0 / 5 - 0 ratings