8

I am trying to generate multiple charts in a PDF using Google Charts. For the PDFs, I am using CakePDF with the Wkhtmltopdf engine. I appear to be having a problem with actually loading the Google Chart code into the PDF though. This is my current code for my Javascript.

<script type="text/javascript" src="https://www.google.com/jsapi">
</script>
<script type="text/javascript">
google.load('visualization', '1.0', {'packages':['corechart']});
//setTimeout(function() {google.load('visualization', '1.0', {'packages':['corechart']});}, 100);
google.setOnLoadCallback(drawChart);
function drawChart(doIt,taken, total, element) 
{
    if (typeof doIt === 'boolean')
    {
        var data = new google.visualization.DataTable();
        data.addColumn('string', 'Type');
        data.addColumn('number', 'Courses');
        data.addRows([
          ['Taken', taken],
          ['Remaining', total - taken]
        ]);
        var options = {
                       'width':40,
                       'height':40};
        var chart = new google.visualization.PieChart(document.getElementById(element));
        chart.draw(data, options);
    }
 }
</script>

The problem is that when I do the google.load for the visualization package, it causes Wkhtmltopdf to return with an error saying that the engine returned no data. I found a problem that I thought was similar at Why does google.load cause my page to go blank? so I tried to do setTimeout(function() {google.load('visualization', '1.0', {'packages':['corechart']});}, 100); The problem with this solution was that if the delay is too low, the page will return with no errors, but it will be completely blank; however, if I set the delay too high, the page will just not load the package and the Javascript will break at

var data = new google.visualization.DataTable();

when I call the function. Additionally, that poster stated problem with document.write(), but I have no issues if I add document.write() lines before or after I add the content to the page. I would appreciate it if anyone knew how to get Google Charts to work with Wkhtmltopdf and could help me.


Ok, In an attempt to give the API more time to load, I moved where I call the function to the end of the PHP. Now, it just sets an array of elements that need graphs drawn in them and the proper values, then it calls the function with the value from the arrays. I am not sure what the problem is now though because it is breaking on chart.draw(data, options); now. It appears to be getting the correct values and element passed to it though.

This will seem really roundabout since it is. For whatever reason, Wkhtmltopdf cannot read anything I put in a javascript tag. I have tried everything I can to get it to read it, but it just will not haha. The CakePDF plugin can read Javascript though, so my JS code is in the default PDF layout. Then in the actual view that is rendered by WkhtmltoPdf, I create an array of elements and values. I then do this (after many different possible solutions, this is the only way I was able to call the JS function)

for ($i = 0; $i < sizeof($grade_array); $i++)
{
    $element = $grade_array[$i][2];
    echo '<script type="text/javascript">drawChart(true, '.$this->Js->value($grade_array[$i][0]).', '.$this->Js->value($grade_array[$i][1]).','.json_encode($element).');</script>';
}

It appears to pass all of the correct data. Upon calling the function, I have debug lines printing the parameters, and all parameters are correctly printed. I also noticed that if I do document.write('test') in the same place as the chart.draw(), it will write 'test' without any error. For some reason, if I do chart.draw(), it just says Wkhtmltopdf didn't return any data.

Community
  • 1
  • 1
Isaac Senior
  • 91
  • 2
  • 7
  • Given the code you posted, there is no way for the `if (typeof doIt === 'boolean')` to return true, because the callback from the google.load call has no parameters (`doIt` will always be null). You must be calling the `drawChart` function somewhere else if you are getting an error on the line `var data = new google.visualization.DataTable();`. Is this the whole of your code or is there more to it? – asgallant Oct 19 '13 at 22:12
  • I'm sorry about that. I call the function itself later where I pass it the necessary parameters. I know that it is breaking at that line because I actually have a debugging line to print a message after each line so I know where it breaks; I just removed those to make the code easier for someone to read. I am certain that the function is being called with the correct parameters though. – Isaac Senior Oct 20 '13 at 00:41
  • You could be getting errors if the function is being called before the API is finished loading - the purpose of the callback is to ensure that everything is loaded first. Try arranging your code so that the trigger for the `drawChart` call happens inside the callback from the google loader (which need not be the `drawChart` function itself). – asgallant Oct 20 '13 at 02:05
  • I am not sure exactly how to do that. The function is called from within the content of the page. There is a PHP for loop where I get the proper information and then pass it to the Javascript function. Is there a way I can just delay the page from rendering the PHP until the API is loaded? – Isaac Senior Oct 20 '13 at 22:06
  • What triggers the function call? A document ready event? – asgallant Oct 21 '13 at 00:52
  • This will seem really roundabout since it is. For whatever reason, Wkhtmltopdf cannot read anything I put in a javascript tag for some reason. I have tried everything I can to get it to read it, but it just will not haha. The CakePDF plugin can read Javascript though, so my JS code is in the default PDF layout. Then in the actual view that is rendered by WkhtmltoPdf, I create an array of elements and values. – Isaac Senior Oct 21 '13 at 01:06
  • So the javascript is being read in PDF, not before the conversion to PDF? That is why it doesn't work. The Google Visualization API requires HTML, CSS, and SVG or VML support to work, none of which are present in PDF. – asgallant Oct 21 '13 at 01:11
  • If I do something like not adding a data set or not adding columns to the data set, it will print an error message in the correct location saying the chart has a problem though – Isaac Senior Oct 21 '13 at 01:14
  • If I recall correctly, PDF has an XML structure, and the syntax for writing to the document is the same as writing to HTML in a browser, so the Visualization API would be able to write text nodes (such as error messages) without any problems, but PDF can't render the SVG/VML nodes that would be required to show the charts. – asgallant Oct 21 '13 at 03:41
  • So, basically, I cannot generate a graph into a pdf? – Isaac Senior Oct 21 '13 at 17:10
  • You would have to convert it to an image before placing it in the PDF. – asgallant Oct 21 '13 at 17:18

8 Answers8

8

Problem: When using wkhtmltopdf / wkhtmltoimage the pdf / image on a html page (with Google Chart), it did not contain the Google chart. The output (pdf / png) was simply blank at the position the Google Chart should show up. The original html page was okay, of course.

Solution: I solved this issue (Google Charts + wkhtmltopdf) by replacing:

<script src="http://www.gstatic.com/charts/loader.js"></script>

by

<script src="http://www.google.com/jsapi"></script>

Somehow the wkhtmltopdf library could not handle a Google Chart html page which contains the first javascript include.

Ralf Claassens
  • 189
  • 1
  • 8
  • 1
    However by doing this you are missing out on any new functionality from the Google Charts API... – Adambean Feb 24 '17 at 12:57
  • Worked for me too. – jbmyid Oct 30 '17 at 05:42
  • 1
    @Adambean then what is the solution for it? – jbmyid Oct 30 '17 at 05:43
  • If you want to use the newer Google Charts API (from "gstatic.com") your only options right now are PhantomJS (discontinued but still works) or PDFcloud (free trial credits). Both will produce the same great result, though PhantomJS can be trickier to set up. – Adambean Oct 30 '17 at 21:00
3

I resolved this problem today myself, so here is what worked out for me. For a more detailed walkthrough, you are welcome to read my blog post about the subject.

I only had to make two simple changes to make it work:

  1. Supply the javascript-delay argument, e.g. javascript-delay 1000. This delays the execution of JavaScript code for 1000 milliseconds
  2. Add a callback when loading the Google Visualization API

function init() {
  google.load("visualization", "1.1", {
    packages: ["corechart"],
    callback: 'drawCharts'
  });
}

Then you can simply implement the drawCharts function where you can draw your charts exactly as you normally would.

Make sure your markup includes the following:

<body onload="init()">
    <div id="my-chart"></div>
</body>

Simply draw your chart to the div with the above ID, and you should be good to go.

Note: I am using the newest available binary (0.12.0 as of now). Tested on a 64-bit Ubuntu installation.

ba0708
  • 10,180
  • 13
  • 67
  • 99
  • Hi @Andy0708 - I've tried this method without any luck. Tested using the latest version of wkhtmltopdf on Ubuntu 14.04.4, Windows 10 & OSX 10.11.6 - Do you have any working examples I can look at? – BaronGrivet Oct 11 '16 at 21:16
  • This is how I dealt with the issue: https://github.com/wkhtmltopdf/wkhtmltopdf/issues/2855#issuecomment-408560725 – onassar Jul 27 '18 at 23:13
2

Instead of using the javascript-delay option, you should rather actually wait for the chart to be rendered by using window-status. window-status makes wkhtmltopdf wait until the window status has the desired value.

Add an event listener to the chart

google.visualization.events.addListener(tableChart, 'ready', myReadyHandler);
function myReadyHandler(){
  window.status = "ready";
}

Run wkhtmltopdf using --window-status ready

sk904861
  • 1,247
  • 1
  • 14
  • 31
2

Encountered this issue recently. I have tried all the workarounds to fix the loading issue - delays, setInterval, setTimeout. Also tried upgrading wkhtml version.

What worked for me is explicitly define the Google Charts version number to use. In my case version 44.

<script>google.load("visualization", "44", {packages:["corechart"]});</script>

Note: using version "1" now means you are using the current version. Per Google document - "All 'jsapi' requests are now being redirected to the new loader. If you were loading version '1' or '1.0', you will now be loading 'current'."

phpd
  • 551
  • 4
  • 10
1

I also have problems using

window.onload = function () {
   google.charts.load("current", {packages:["corechart", "timeline", "bar"]});
   google.charts.setOnLoadCallback(drawCharts);
};

drawCharts is never launched by the QT engine, thus no chart is rendered in the PDF (verified also with QTWeb Browser).

I finally solved with a not very elegant way

window.onload = function () {
   google.charts.load("current", {packages:["corechart", "timeline", "bar"]});
   setTimeout(function(){drawCharts();}, 500);
};
zeroquaranta
  • 392
  • 4
  • 16
1

Google Charts are not good at all. I've serval issues with google charts. I'm using D3 library https://d3js.org v5.9.2.

Everything is ok.

You can find examples how to do it. I used that example https://wizardace.com/d3-piechart/

Digital Legend
  • 149
  • 1
  • 13
0

It's working with the following code.

<script type="text/javascript" src="https://www.gstatic.com/charts/loader.js"></script>

And adding timeout/wait till its loaded.

 google.charts.load('current', {packages: ['corechart']});
    var interval = setInterval(function() {
      if (
        google.visualization !== undefined
        && google.visualization.DataTable !== undefined
        && google.visualization.PieChart !== undefined
      ){
        clearInterval(interval);
        window.status = 'ready';
        drawCharts();
      }
    }, 100);
jbmyid
  • 1,985
  • 19
  • 22
0

If you are facing this issue after ~may, july 2020 and your script was working, but not more now, for no reason, you must know why Google has deprecated the version of script in http://www.google.com/jsapi an now they are redirecting the requests for http://www.gstatic.com/charts/loader.js who isn't compatible with wkhtmltopdf.

So, for back to old version you must improve your code like that:

<script type="text/javascript" src="http://www.google.com/jsapi"></script>
        
<script type="text/javascript">
function init() {
    //the latest version who worked for me was the 44. 45 and above not worked anymore.
    google.load("visualization", "44", {packages:["corechart"]});
    var interval = setInterval(function() {
        if ( google.visualization !== undefined && google.visualization.DataTable !== undefined && google.visualization.PieChart !== undefined ){ clearInterval(interval);
        window.status = 'ready';
        drawCharts(); //call here your callback who will render the chart
        }
    }, 100);
}
</script>

And use this option to say to wkhtmltopdf to start render the pdf when window.status is ready:

--window-status ready

rafwell
  • 429
  • 1
  • 4
  • 12