Wkhtmltopdf does not render Chart.JS 2.5.0 graph
Asked Answered
S

9

6

Using:
WKpdftohml version 0.12.3.2
PHPwkhtmltopdf version 2.2.0
Chart.JS version 2.5.0

I'm trying to print a line graph using the above libraries. I can reproduce a pdf using the shell command: wkhtmltopdf --javascript-delay 5000 " http://netdna.webdesignerdepot.com/uploads7/easily-create-stunning-animated-charts-with-chart-js/chartjs-demo.html" test2.pdf
So there is no problem with WKhtmltopdf.

The problem is when I do it in my app, using the PHPwkhtmltopdf library. I get a blank page.
From my research these are the things I tried:

  • Added 'javascript-delay' => 500 to Chart.JS options;
  • Added animation:{onComplete: function () {window.JSREPORT_READY_TO_START =true} to Chart.JS options;
  • Added <div style="width:800px;height:200;font-size:10px;"> to the parent div of canvas html tag
  • Added ctx.canvas.width = 800;ctx.canvas.height = 200; to javascript initialization of the chart.

Well nothing worked. I love Chart.JS and WKhtmltopdf, but if I can't print I'll have to drop one of them. Is there any solution?

This is my php code for the PHPwkhtmltopdf:

public function imprimir ($request, $response)
{
    // include_once 'config/constants.php';
    // include_once 'resources/auxiliar/helpers.php';

    $folha = $_POST['printit'];
    $variaveis = explode(',', $folha);

    $nomeFicheiro = $variaveis[0];

    $printName = substr($nomeFicheiro, 5);

    if (isset($variaveis[2])) {

        $_SESSION['mesNumero'] = $variaveis[2];
        $_SESSION['mes'] = $variaveis[1];

    } else {

        $mesNumero = 0;
        $mes = '';

    }

    ob_start();
    if ($nomeFicheiro == 'printPpiam') {
        require ('C:/xampp/htdocs/.../'.$nomeFicheiro.'.php');
    } else {
        require ('C:/xampp/htdocs/.../'.$nomeFicheiro.'.php');
    }
    $content = ob_get_clean();

    // You can pass a filename, a HTML string, an URL or an options array to the constructor
    $pdf = new Pdf($content);

    // On some systems you may have to set the path to the wkhtmltopdf executable
    $pdf->binary = 'C:/Program Files/wkhtmltopdf/bin/wkhtmltopdf';

    $pdf -> setOptions(['orientation' => 'Landscape',
                        'javascript-delay' => 500,
                        // 'enable-javascript' => true,
                        // 'no-stop-slow-scripts' => true]
                    ]);

    if (!$pdf->send($printName.'.pdf')) {
        throw new Exception('Could not create PDF: '.$pdf->getError());
    }

    $pdf->send($printName.'.pdf');

}  

# Update 1
Made a php file with the page output. Run it in the browser and the graph rendered. When I do it in the console it renders everything except the graph!
How can it be wkhtmltopdf renders the graphics in this page : http://netdna.webdesignerdepot.com/uploads7/easily-create-stunning-animated-charts-with-chart-js/chartjs-demo.html but not my own?!

# Update 2
After Quince's comment, I tried just turning the animations off, but I'm not sure on how to do that. I tried:

$pdf -> setOptions(['orientation' => 'Landscape',
                            'javascript-delay' => 500,
                             // 'window-status' => 'myrandomstring ',
                             'animation' => false,
                             'debug-javascript',
                             'no-stop-slow-scripts',
                         ]); 

But it fails.

Sivan answered 2/3, 2017 at 16:40 Comment(1)
If anybody is running into this on 0.12.5, try and upgrade to 0.12.6. It fixed our issue.Jeremie
C
21

Here's the code that works with wkhtmltopdf version 0.12.5:

chart.html

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.8.0/Chart.min.js"></script>
<style>
    .reportGraph {width:900px}
</style>
</head>
<body>

<div class="reportGraph"><canvas id="canvas"></canvas></div>

<script type="text/javascript">
// wkhtmltopdf 0.12.5 crash fix.
// https://github.com/wkhtmltopdf/wkhtmltopdf/issues/3242#issuecomment-518099192
'use strict';
(function(setLineDash) {
    CanvasRenderingContext2D.prototype.setLineDash = function() {
        if(!arguments[0].length){
            arguments[0] = [1,0];
        }
        // Now, call the original method
        return setLineDash.apply(this, arguments);
    };
})(CanvasRenderingContext2D.prototype.setLineDash);
Function.prototype.bind = Function.prototype.bind || function (thisp) {
    var fn = this;
    return function () {
        return fn.apply(thisp, arguments);
    };
};

function drawGraphs() {
    new Chart(
        document.getElementById("canvas"), {
            "responsive": false,
            "type":"line",
            "data":{"labels":["January","February","March","April","May","June","July"],"datasets":[{"label":"My First Dataset","data":[65,59,80,81,56,55,40],"fill":false,"borderColor":"rgb(75, 192, 192)","lineTension":0.1}]},
            "options":{}
        }
    );
}
window.onload = function() {
    drawGraphs();
};
</script>
</body>
</html>

Run:

$ wkhtmltopdf chart.html chart.pdf:

Loading pages (1/6)
Counting pages (2/6)
Resolving links (4/6)
Loading headers and footers (5/6)
Printing pages (6/6)
Done
Coeval answered 11/8, 2019 at 23:42 Comment(4)
Thank, your solution woks for me in console and with Symfony with KnpSnappyBundle with OSx. I haven't try on debian server I gess it will works.Transform
Great! Just don't forget to turn off the animation, otherwise yAxis ticks appear incorrect (because it is rendering while still doing the animation).Oldline
This answer worked for me, https://mcmap.net/q/1630341/-charts-js-not-showingDulles
I noticed that this impairs your chart resolution. I don't know how to fix that. I tried to strip your (great, +1) code of all that has nothing to do with the problem and made a community wiki. I could also change your answer accordingly, but that felt a bit intrusive.Sosthina
S
17

Found the answer. After I created a separate file, outside the framework, i did some tests again. It rendered the graph in the browser so I tried to use the command tool WKhtmltopdf, and it did not worked, when it did with other examples (see Update #1). So there is something wrong with my php page.
Ran the same tests that I did in the framework, and got the answer for my problem. By introducing a parent div tag width dimensions in the canvas tag it made the graph render in the page.

<div style="width:800px;height:200;">
    <canvas id="myChart" style="width:800px;height:200;"></canvas>
</div>

The proposition was found in this site: Github, so thanks laguiz.

Sivan answered 3/3, 2017 at 9:14 Comment(0)
S
7

Try adding this, as according to this github source

<script src="https://cdnjs.cloudflare.com/ajax/libs/babel-polyfill/7.0.0/polyfill.min.js"></script>
Scaler answered 1/11, 2018 at 12:42 Comment(0)
E
2

Solved it by downgrading wkhtmltopdf: 0.12.4 > 0.12.2.1 chart.js version seemed to have no influence. I used 2.7.0. Fixed width and height seem to be required as well.

Edit: Since wkhtmltopdf is dead, I switched to Puppeteer recently.

Eliason answered 13/3, 2018 at 12:34 Comment(0)
U
2

I was dealing with the same issue using rotativa to export my ASP.NET MVC page with Chart.JS to PDF with no luck.

After a couple of days I finally found a super-easy solution to achieve my goal. What I did is simply to use the .toBase64Image() method of Chart.JS to encode the chart to a base64 string variable in Javascript. Then I saved this string into a model and then on the PDF html page a used tag where i put the base64encoded string to a scr property and the result is great :-)

javascript:

//save Chart as Image
var url_base64 = document.getElementById('myChart').toDataURL('image/png');

//set the string as a value of a hidden element
document.getElementById('base64graph').value = url_base64;

PDF view:

<img style='display:block; width:900px;height:400px;position:relative;margin:auto;text-align:center;' id='base64image'
         src='@Model.base64graph' />
Unstoppable answered 25/8, 2020 at 16:29 Comment(0)
S
1

I'm trying to improve on the answer by temuri, which is great, but a bit bloated. I ran into the OP's issues (even same WKpdftohml version) and this did the trick for me:

<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.8.0/Chart.min.js"></script>

<div style="width: 400px;"><canvas id="canvas"></canvas></div>

<script type="application/javascript">
    // wkhtmltopdf 0.12.5 crash fix.
    // https://github.com/wkhtmltopdf/wkhtmltopdf/issues/3242#issuecomment-518099192

    Function.prototype.bind = Function.prototype.bind || function (thisp) {
        const fn = this;
        return function () {
            return fn.apply(thisp, arguments);
        };
    };

    new Chart(
        document.getElementById("canvas"), {
            "responsive": false,
            "type":"line",
            "data":{"labels":["January","February","March","April","May","June","July"],"datasets":[{"label":"My First Dataset","data":[65,59,80,81,56,55,40],"fill":false,"borderColor":"rgb(75, 192, 192)","lineTension":0.1}]},
            "options":{}
        }
    );
</script>

I'm yet to figure out how to get the chart library via ordinary tools like npm, instead of getting it via ajax like here in the first line. Note that this can impact your chart resolution.

Sosthina answered 2/3, 2017 at 16:41 Comment(0)
C
0

I was strugling with that too and you self-answer did not help my case. I am using symfony 3.3 and Chart.js 2 and whatever I did, did not work properly. So I have solved it in a different manner (maybe not a clean one) and I wanted to post it here for inspiration to others.

I needed to export a page, that I was presenting to the user in a browser. In browser, I used Javascript to get picture out of the rendered graph with

animation: {
                onComplete: function(animation) {
                    console.log('done');
                    var url=document.getElementById("barChartByCountryWeight{{ part }}{{ subsetKey }}").toDataURL();
                    $.ajax({
                        url: 'saveChartImages',
                        type: 'POST',
                        data: { 'barChartByCountryWeight{{ part }}{{ subsetKey }}': url },
                        success: function(result) {
                            console.log(result);
                            console.log('the request was successfully sent to the server');
                        },
                        error: function (request, error) {
                            console.log(arguments[0]['responseText']);
                            console.log(" Can't do because: " + error);
                        }
                    });

                }
            }

And on server side I put it in session and in a controller for the PDF export, I have taken the image from session and put the image in the HTML, that is converted to PDF.

Hope that helps.

Comose answered 14/12, 2017 at 21:45 Comment(0)
D
0

I have solved this problem when I tried to use Chartjs 1 instead of a new chart js. The reason for this is because laravel snappy uses wkhtmltopdf, which doesn't support css animation, while new chartjs uses css animation. This github issue shows that.

The solution i found is to use google chart instead. It also uses svg, so you can get high resolution charts.

Disappointed answered 3/1, 2018 at 15:28 Comment(0)
S
0

I have implemented the working code for this issue. You can check out the working code here.

NOTE: For generating pdf you must disable the Chart JS animation or add the option javascript-delay=>1000 to the wkhtmltopdf options.

Check this example

<?php
/**
 * Created By: jithinvijayan
 * Date: 12/02/20
 */

use mikehaertl\wkhtmlto\Pdf;

require_once '../vendor/autoload.php';
try {
    $html = '<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.8.0/Chart.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/babel-polyfill/7.0.0/polyfill.min.js"></script>
<style>
    .reportGraph {width:900px;height: 900px;}
</style>
</head>
<body>

<div class="reportGraph">
    <canvas id="canvas"></canvas>
</div>

<script type="text/javascript">
\'use strict\';
(function(setLineDash) {
    CanvasRenderingContext2D.prototype.setLineDash = function() {
        if(!arguments[0].length){
            arguments[0] = [0,0];
        }
        return setLineDash.apply(this, arguments);
    };
})(CanvasRenderingContext2D.prototype.setLineDash);
function drawGraphs() {
    new Chart(
        document.getElementById("canvas"), {
            "responsive": false,
            "type":"pie",
            "data":{
                "labels":["January","February","March","April","May","June","July"],
                "datasets":[
                    {
                        "label":"My First Dataset",
                        "data":[65,59,80,81,56,55,40],
                        backgroundColor: [
                            window.chartColors.red,
                            window.chartColors.orange,
                            window.chartColors.yellow,
                            window.chartColors.green,
                            window.chartColors.blue,
                            window.chartColors.grey,
                            window.chartColors.purple,
                        ],
                        "borderColor":"rgb(75, 192, 192)"
                    }
                ]
            },
            options: {
                animation: {
                    duration: 0, // general animation time
                },
                hover: {
                    animationDuration: 0, // duration of animations when hovering an item
                },
                responsiveAnimationDuration: 0, // animation duration after a resize
            }
        }
    );
}
window.chartColors = {
    red: \'rgb(255, 99, 132)\',
    orange: \'rgb(255, 159, 64)\',
    yellow: \'rgb(255, 205, 86)\',
    green: \'rgb(75, 192, 192)\',
    blue: \'rgb(54, 162, 235)\',
    purple: \'rgb(153, 102, 255)\',
    grey: \'rgb(201, 203, 207)\'
};
window.onload = function() {
    drawGraphs();
};
</script>
</body>
</html>';

    $options = array(
        'page-width' => '210mm',
        'page-height' => '297mm',
        'binary' => "/usr/local/bin/wkhtmltopdf", // For Mac
        

    );


    $pdf = new Pdf($options);
    $pdf->ignoreWarnings = true;
    $pdf->addPage($html);
    $pdf->send('test-chart' . date('d_m_Y') . '.pdf', true);
} catch (\Exception $e) {
    throw $e;
}
Standoff answered 12/2, 2020 at 12:42 Comment(3)
You should try to include a MWE into your answer as outside ressources can change or even be removed.Sosthina
Your working code doesn't seem to contain the javascript-delay setting?Breastfeed
@Breastfeed I've disabled animation. You can check the options to set the javascript-delayStandoff

© 2022 - 2024 — McMap. All rights reserved.