14

I am using Highcharts in my application (without any internet connection)

I have multiple charts on a html page, and I want to generate a PDF report that contains all the charts from this page.

How can I do this without sending the data to any server on the internet ?

I will be thankful for any help or any example you can provide. Thank you in advance :)

Leon
  • 255
  • 1
  • 4
  • 11

5 Answers5

33

Yes this is possible but involves a few different libraries to get working. The first Library is jsPDF which allows the creation of PDF in the browser. The second is canvg which allows for the rendering and parsing of SVG's, the bit that is really cool though is it can render an svg on to canvas element. Lastly is Highcharts export module which will allow us to send the svg to the canvg to turn into a data URL which can then be given to jsPDF to turn into your pdf.

Here is an example http://fiddle.jshell.net/leighking2/dct9tfvn/ you can also see in there source files you will need to include in your project.

So to start highcharts provides an example of using canvg with it's export to save a chart as a png. because you want all the iamges in a pdf this has been slightly altered for our purpose to just return the data url

// create canvas function from highcharts example http://jsfiddle.net/highcharts/PDnmQ/
(function (H) {
    H.Chart.prototype.createCanvas = function (divId) {
        var svg = this.getSVG(),
            width = parseInt(svg.match(/width="([0-9]+)"/)[1]),
            height = parseInt(svg.match(/height="([0-9]+)"/)[1]),
            canvas = document.createElement('canvas');

        canvas.setAttribute('width', width);
        canvas.setAttribute('height', height);

        if (canvas.getContext && canvas.getContext('2d')) {

            canvg(canvas, svg);

            return canvas.toDataURL("image/jpeg");

        } 
        else {
            alert("Your browser doesn't support this feature, please use a modern browser");
            return false;
        }

    }
}(Highcharts));

Then for the example i have set up export on a button click. This will look for all elements of a certain class (so choose one to add to all of your chart elements) and then call their highcharts.createCanvas function.

$('#export_all').click(function () {
    var doc = new jsPDF();

    // chart height defined here so each chart can be palced
    // in a different position
    var chartHeight = 80;

    // All units are in the set measurement for the document
    // This can be changed to "pt" (points), "mm" (Default), "cm", "in"
    doc.setFontSize(40);
    doc.text(35, 25, "My Exported Charts");

    //loop through each chart
    $('.myChart').each(function (index) {
        var imageData = $(this).highcharts().createCanvas();

        // add image to doc, if you have lots of charts,
        // you will need to check if you have gone bigger 
        // than a page and do doc.addPage() before adding 
        // another image.

        /**
        * addImage(imagedata, type, x, y, width, height)
        */
        doc.addImage(imageData, 'JPEG', 45, (index * chartHeight) + 40, 120, chartHeight);
    });


    //save with name
    doc.save('demo.pdf');
});

important to note here that if you have lots of charts you will need to handle placing them on a new page. The documentation for jsPDF looks really outdated (they do have a good demos page though just not a lot to explain all the options possible), there is an addPage() function and then you can just play with widths and heights until you find something that works.

the last part is to just setup the graphs with an extra option to not display the export button on each graph that would normally display.

//charts
$('#chart1').highcharts({
    navigation: {
            buttonOptions: {
                enabled: false
            }
        },

//this is just normal highcharts setup form here for two graphs see fiddle for full details

The result isn't too bad i'm impressed with the quality of the graphs as I wasn't expecting much from this, with some playing of the pdf positions and sizes could look really good.

Here is a screen shot showing the network requests made before and after the export, when the export is made no requests are made https://i.stack.imgur.com/LXnU1.jpg

here is an example of what the pdf looks like https://i.stack.imgur.com/KW6Lr.png (looks better when view as actual pdf)

quick example to be tried on local https://github.com/leighquince/HighChartLocalExport

Quince
  • 14,790
  • 6
  • 60
  • 69
  • Thank you for your help, but in this solution the data are sent to the Highcharts Export Server. And I don't want this to happen as I said in the question :)) – Leon Sep 03 '14 at 07:52
  • No I have only used the export module to get the SVG data, i was watching the network tab and no data was sent – Quince Sep 03 '14 at 07:56
  • The conversion all happens in the browser, that is also why i have turned the export button off on each graph – Quince Sep 03 '14 at 07:58
  • @Leon added screen shot of network requests to the question to show no requests were sent to high chart – Quince Sep 03 '14 at 08:14
  • Hallo Quince, thanks again but I need to do it on my local server. Can you run the example on your local server, with no internet connection ? – Leon Sep 03 '14 at 08:55
  • Yep ill try and set that up now, will let you know the result – Quince Sep 03 '14 at 09:00
  • All good - http://i.imgur.com/Yrh8mUb.png - I have also made a quick repo so you download it and try for youself - https://github.com/leighquince/HighChartLocalExport – Quince Sep 03 '14 at 09:11
  • Thank you Quince ! Good job :) – Leon Sep 03 '14 at 09:59
  • Hi, The solution seems like to have been accepted bt it might be because its been a while but I am getting 404 error on two of the javascripts... Could anyone please comment with the correct js URLs? – user3115056 Jul 18 '16 at 16:06
  • @user3115056 all of the resources used can be located in, https://github.com/leighquince/HighChartLocalExport/tree/master/js , i have just seen the library served by google svn have been removed so will update the fiddle if i can find hosted replacements – Quince Jul 19 '16 at 08:27
  • @Quince - how would you preserve layout export with this if you had a document with some html tables, text and charts mixed together? – Toniq Mar 20 '18 at 14:47
  • With great difficulty, i imagine, from what i could tell the lib does all you to grab html elements but you would still need to well template it with the graph images so that it looked the same as what was in the browser – Quince Mar 20 '18 at 14:57
1

You need to setup your own exporting server, locally like in the article

Sebastian Bochan
  • 37,348
  • 3
  • 49
  • 75
1

Here is an example using the library pdfmake:

html:

<div id="chart_exchange" style="width: 450px; height: 400px; margin: 0 auto"></div>
<button id="export">export</button>
<canvas id="chart_exchange_canvas" width="450" height="400" style="display: none;"></canvas>

javascript:

function drawInlineSVG(svgElement, canvas_id, callback) {
  var can = document.getElementById(canvas_id);
  var ctx = can.getContext('2d');

  var img = new Image();
  img.setAttribute('src', 'data:image/svg+xml;base64,' + btoa(unescape(encodeURIComponent(svgElement))));
  img.onload = function() {
    ctx.drawImage(img, 0, 0);
    callback(can.toDataURL("image/png"));
  }
}

full working code: https://jsfiddle.net/dimitrisscript/f6sbdsps/

Dimitris Siakavelis
  • 1,048
  • 9
  • 10
0

Maybe this link can help you out.

http://bit.ly/1IYJIyF

Try refer to the exporting properties (fallbackToExportServer: false) and the necessary file that need to be include (offline-exporting.js).

Whereas for the export all at once part, currently I myself also still trying. Will update here if any.

William Kheng
  • 671
  • 11
  • 15
0

This question is a bit old but was something i was working on myself recently and had some trouble with it.

I used the jsPDF library: https://github.com/MrRio/jsPDF

Issues i ran into involved jsPDF not supporting the SVG image exported by the high chart + images being blurry and low quality.

Below is the solution I used to get two charts into one pdf document:

function createPDF() {
var doc = new jsPDF('p', 'pt', 'a4'); //Create pdf

if ($('#chart1').length > 0) {
    var chartSVG = $('#chart1').highcharts().getSVG();
    var chartImg = new Image();

    chartImg.onload = function () {

        var w = 762;
        var h = 600;

        var chartCanvas = document.createElement('canvas');
        chartCanvas.width = w * 2;
        chartCanvas.height = h * 2;
        chartCanvas.style.width = w + 'px';
        chartCanvas.style.height = h + 'px';
        var context = chartCanvas.getContext('2d');
        chartCanvas.webkitImageSmoothingEnabled = true;
        chartCanvas.mozImageSmoothingEnabled = true;
        chartCanvas.imageSmoothingEnabled = true;
        chartCanvas.imageSmoothingQuality = "high";
        context.scale(2, 2);
        chartCanvas.getContext('2d').drawImage(chartImg, 0, 0, 762, 600);

        var chartImgData = chartCanvas.toDataURL("image/png");
        doc.addImage(chartImgData, 'png', 40, 260, 250, 275);

        if ($('#chart2').length > 0) {
            var chart2SVG = $('#chart2').highcharts().getSVG(),
                chart2Img = new Image();

            chart2Img.onload = function () {

                var chart2Canvas = document.createElement('canvas');
                chart2Canvas.width = w * 2;
                chart2Canvas.height = h * 2;
                chart2Canvas.style.width = w + 'px';
                chart2Canvas.style.height = h + 'px';
                var context = chart2Canvas.getContext('2d');
                chart2Canvas.webkitImageSmoothingEnabled = true;
                chart2Canvas.mozImageSmoothingEnabled = true;
                chart2Canvas.imageSmoothingEnabled = true;
                chart2Canvas.imageSmoothingQuality = "high";
                context.scale(2, 2);
                chart2Canvas.getContext('2d').drawImage(chart2Img, 0, 0, 762, 600);

                var chart2ImgData = chart2Canvas.toDataURL("image/png");
                doc.addImage(chart2ImgData, 'PNG', 300, 260, 250, 275);

                doc.save('ChartReport.pdf');
            }

            chart2Img.src = "data:image/svg+xml;base64," + window.btoa(unescape(encodeURIComponent(chart2SVG)));
        }
    }
    chartImg.src = "data:image/svg+xml;base64," + window.btoa(unescape(encodeURIComponent(chartSVG)));
}
}

scripts to include:

 <script src="http://code.highcharts.com/highcharts.js"></script>
 <script src="http://code.highcharts.com/modules/exporting.js"></script>
 <script src="https://cdnjs.cloudflare.com/ajax/libs/jspdf/1.2.61/jspdf.min.js"></script>
Ryan Gavin
  • 689
  • 1
  • 8
  • 22