Unable to generate Google Charts in PDF using Wkhtmltopdf

I am facing an issue while trying to create multiple charts in a PDF using Google Charts. I am utilizing CakePDF with the Wkhtmltopdf engine for generating the PDFs. The problem arises when attempting to load the Google Chart code into the PDF file. Below is the current JavaScript code snippet that I am working with:

<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>

There seems to be an issue arising when including the visualization package through google.load, as it causes Wkhtmltopdf to return an error stating that no data has been received from the engine. I came across a similar problem on Why does google.load cause my page to go blank? and attempted to resolve it by introducing a slight delay using setTimeout(function() {google.load('visualization', '1.0', {'packages':['corechart']});}, 100); However, balancing the delay between too low or high results in either a blank page or breaking of the Javascript.

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

The challenge now lies in solving the breakage caused at the line mentioned above. Despite trying various troubleshooting methods, such as moving the function call towards the end of PHP execution, the issue persists at the point of chart.draw(data, options);. The correct values and elements are being passed, but the function continues to malfunction.

Despite numerous attempts, it appears that Wkhtmltopdf fails to interpret any content within javascript tags, leading me to try different strategies without success. As a workaround, I have embedded the JS code within the default PDF layout recognized by CakePDF and then dynamically generate elements and values within the rendered view by WkhtmltoPdf. This approach, though convoluted, was the only way I could successfully invoke 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>';
}

All parameters are correctly passed to the function upon invocation, confirmed via debug statements printing the parameter values. Furthermore, inserting document.write('test') in place of chart.draw() functions properly, signifying a peculiar behavior specifically related to chart.draw(). Any efforts to resolve this result in the message "Wkhtmltopdf didn't return any data."

Answer №1

Issue: I encountered a problem when trying to convert an HTML page with a Google Chart to PDF or image using wkhtmltopdf / wkhtmltoimage. The resulting PDF or PNG file showed up blank instead of displaying the Google Chart that was present on the original HTML page.

Resolution: To resolve this issue, I made the following adjustment:

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

was replaced with:

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

It turns out that the wkhtmltopdf library had trouble handling a Google Chart in an HTML page that included the original script tag.

Answer №2

After successfully solving a problem today on my own, I wanted to share the solution that worked for me. If you'd like a more detailed explanation, feel free to check out my blog post.

Two small adjustments were all it took to make things function smoothly:

  1. Include the javascript-delay parameter, such as javascript-delay 1000. This helps with delaying JavaScript execution by 1000 milliseconds.
  2. Implement a callback while loading the Google Visualization API.

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

You can then proceed to create the drawCharts function where you would typically draw your charts.

Ensure that your HTML markup includes the following:

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

Once you draw your chart in the div with the specified ID, everything should work seamlessly.

Important note: The method was tested using the most recent binary version (0.12.0) and has been verified on a 64-bit Ubuntu setup.

Answer №3

To ensure the chart is fully rendered before converting it to a PDF, consider using the window-status option instead of the javascript-delay. This will make wkhtmltopdf wait until the window status matches the desired value.

Implement an event listener for the chart:

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

Execute wkhtmltopdf with the parameter --window-status ready

Answer №4

Recently faced a challenge with loading issues on Google Charts. Despite trying various workarounds like delays, setInterval, setTimeout, and upgrading wkhtml version, the problem persisted.

What eventually solved the issue for me was explicitly specifying the Google Charts version number to use - in my case, it was version 44.

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

Note: It's important to note that using version "1" now refers to the current version according to Google documentation. They redirected all 'jsapi' requests to the new loader, so if you were loading version '1' or '1.0', you will now be loading 'current'.

Answer №5

Having issues with the implementation of

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

The drawCharts function is not being triggered by the QT engine, resulting in no chart being displayed in the PDF (also confirmed with QTWeb Browser).

I managed to resolve this issue in a less-than-ideal manner

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

Answer №6

I have encountered several issues with Google Charts and do not find them up to par. Instead, I rely on the D3 library version 5.9.2 which has been serving me well. If you want a reliable charting solution, check out .

So far, everything is running smoothly with D3.

If you need help getting started, I suggest checking out this helpful example at

Answer №7

If you have encountered this issue post May or July 2020 and your previously functioning script has suddenly stopped working without any apparent reason, it is important to understand that Google has deprecated the version of the script at http://www.google.com/jsapi and is now redirecting requests to , which is not compatible with wkhtmltopdf.

To revert to the old version, you will need to update your code as shown below:

<script type="text/javascript" src="http://www.google.com/jsapi"></script>
        
<script type="text/javascript">
function init() {
    // The last version that worked for me was version 44. Versions 45 and above no longer work.
    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 your callback function here to render the chart
        }
    }, 100);
}
</script>

Additionally, use the following option to instruct wkhtmltopdf to begin rendering the pdf when the window status is ready:

--window-status ready

Answer №8

This code is successfully functioning as intended.

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

Additionally, a timeout/wait function has been implemented for loading.

 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);

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Guide to retrieving the value of property a within a referenced object using Mongoose in a Node.js web application

const mongoose = require("mongoose"); const jobsSchema = new mongoose.Schema({ jobNumber: Number, jobField: String, jobTitle: String, jobCity: String, jobArea: String, jobAddress: String, jobPhone: String, jobInsurance: Str ...

Is it necessary to configure Webpack or use a plugin to remove console.log() statements by default in an Angular Application, or does it do so automatically?

Welcome to my first post! I hope I can effectively communicate the question and the background that led me to ask it. I am relatively new to web programming, with about 1 and a half years of experience working with Java, JavaScript, and TypeScript using An ...

What is the most effective way to handle DOM events in Angular 8?

Looking to listen for the 'storage' event from the window in Angular 8. What is the recommended approach to achieving this in Angular? window.addEventListener('storage', () => { }); One method involves using Renderer2, but are ther ...

Tips for ensuring a cloned table header remains fixed when scrolling through a page

Check out my code snippet on Pastebin: http://pastebin.com/pUstKXJz I'm trying to make a sticky header that stays at the top of the page when I scroll past 100 pixels. The current JQuery code I have isn't working properly, so I need help modifyi ...

The issue of Fetch JSON causing an infinite loop arises due to the manipulation of React

the JSON data looks like this: { "orders": [ { id: 123456789, description: "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua." }, { id: 789456123, ...

Angularjs editable-form function onBlur is triggered when the user clicks or navigates away

Are there any directives for onblur in editable-form? I've been searching but haven't found one yet. Angular has ngTouch, but it only validates a single input. What I really need is to detect when the user loses focus on the entire form. This is ...

How can one create a hidden color box?

$.colorbox({ href:"/check.html", transition:"elastic", speed: 150, innerWidth:"910", iframe:true, fastIframe:false, fixedPosition:fixedPos, onComplete:function(){ var $ ...

Vue: utilizing shared methods in a JavaScript file

Currently, I am building a multipage website using Vue and I find myself needing the same methods for different views quite often. I came across a suggestion to use a shared .js file to achieve this. It works perfectly when my "test method" downloadModel i ...

Error: chunk.js encountered an unexpected token < and caused a syntax error

Hello friends, I find myself in need of some assistance. I recently deployed and built an application, but whenever I try to redirect to another page, I encounter this error: Uncaught SyntaxError: Unexpected token < I suspect that the issue lies in t ...

I am facing challenges with posting an XML document on my website

I've been troubleshooting an issue with posting my xml document to a URL using PHP and JavaScript codes. It works perfectly on my local server, but when I try it on the live site, nothing happens. Can anyone help me figure out why this is happening? B ...

When passing down data to a child component, the value is not accessible

I'm facing an issue where I am trying to pass down the isOpen prop to the Snackbar component. However, when I try to console.log this.props in either the componentDidMount or componentDidUpdate methods, all I see is this.props.message. I would really ...

Is it possible to reference a .js file within an HTML file using AngularJS?

Having a slight issue with an email function. I experimented with the 'nodemailer' package and successfully sent an email when coding in a .js file. Upon calling the .js file (node emailfile.js), the email was received as expected (code provided ...

Guide to sending a specialized SPL token using the libraries '@solana/web3.js' and '@solana/sol-wallet-adapter'

Attempting to transfer a custom SPL token using the solana-wallet adapter is proving to be challenging due to difficulty in obtaining the wallet's secret key for signing the transaction. Although I have reviewed resources on writing the transfer code ...

Creating an outlined effect on a transparent image in a canvas: step-by-step guide

Currently, I am working on creating transparent images using canvas in HTML5 and I would like to incorporate borders into them. The issue I'm facing is that the "Stroke" property does not consider the transparency of the image and applies it as if it ...

How can DataTables (JQuery) filter multiple columns using a text box with data stored in an array?

I've been attempting to create a multi-column filter similar to what's shown on this page () using an array containing all the data (referred to as 'my_array_data'). However, I'm facing issues with displaying those filter text boxe ...

Discussing the importance of setting up variables in an Angular Factory

I am trying to develop a factory that will generate a PlayerList, but I am encountering issues with accessing the variables I have defined in the initialize function. Below is the code snippet: app.factory("PlayerList", function(){ // Define the Play ...

Tips for creating a mobile-responsive React + MUI component

A React component utilizing Material-UI (MUI) has been created and I am working on making it mobile responsive. The current appearance is as follows: https://i.stack.imgur.com/8z0T8.png My goal is to have it look like this: https://i.stack.imgur.com/L8g ...

Transferring a variable from template.php to a javascript file

Within my THEME_preprocess_page function, I am using the following code: drupal_add_js(array('variableName' => 'value'), 'setting'); Then, in my JavaScript file: alert(Drupal.settings.variableName); However, I am receiv ...

Behavior Subject in RxJS is able to emit a value even without explicitly calling the `next

I am in the process of developing a multi select filter using RxJs and an angular service to exchange values between various components. @Injectable({ providedIn: 'root' }) export class SomeService{ private readonly defaulFilterSelect: ...

Implementing React: Dynamically Assigning a className to a New Component

I'm attempting to include a className property in a newly created component in the following way: const component = <Icons.RightArrowIcon /> // I intend to add a className to this component // Then... // ... return ( <>{component}&l ...