0

I have a simple report page, and I am using Laravel 7 to build it.

enter image description here

I want to trigger auto-download a PDF with that view.

What would be the most lightweights I should look into? I did a quick Google, and I saw so many options.

I decided to try this and did all the steps, below is my final code

Final Codes

Note this line : $pdf = PDF::loadView('layouts.be.baby.report', get_defined_vars());

public function downloadReportPDF($id) {


    $code = Request::get('code'); 
    $baby = Baby::where('adminCode',$code)->where('id',strtolower($id))
    ->orWhere('readOnlyCode',$code)->where('id',strtolower($id))
    ->first();
    
    if($baby){


        $inputs    = Request::all();
        $interval  = 'week';

        if(array_key_exists('interval', $inputs)){
            $interval  = $inputs['interval'];
        }

        switch ($interval) {
            case 'day':
            $q = BabyLog::where('created_at', '>', now()->today());
            break;
            case 'week':
            $q = BabyLog::where('created_at', '>', now()->subWeek());
            break;
            case 'month':
            $q = BabyLog::where('created_at', '>', now()->subMonth());
            break;
            case 'year':
            $q = BabyLog::where('created_at', '>', now()->subYear());
            break;
            default:
            $q = BabyLog::orderBy('created_at', 'desc');
            break;
        }


        $logs = $q->where('babyId',$baby->id)->orderBy('created_at', 'desc')->get()->groupBy(function ($log) {
            return $log->created_at->format('m/d/y');
        });


        // dd($logs);

        $graphData = [];

        foreach($logs as $date => $logsOnThatDay){


            $pee      = 0;
            $poop     = 0;
            $feed     = 0;
            $medicine = 0;
            $sleep    = 0;


            foreach($logsOnThatDay as $logOnThatDay){

                // if(strtotime($logOnThatDay->created_at) < strtotime(date('Y-m-d H:i:s'))){

                if($logOnThatDay->type == 'pee'){
                    $pee++;
                }

                if($logOnThatDay->type == 'poop'){
                    $poop++;
                }

                if($logOnThatDay->type == 'feed'){
                    $feed++;
                }

                if($logOnThatDay->type == 'medicine'){
                    $medicine++;
                }

                if($logOnThatDay->type == 'sleep'){
                    $sleep++;
                }


                $graphData[$date]['pee']      = $pee;
                $graphData[$date]['poop']     = $poop;
                $graphData[$date]['feed']     = $feed;
                $graphData[$date]['medicine'] = $medicine;
                $graphData[$date]['sleep']    = $sleep;

            }
        }


        array_pop($graphData);

        //PDF 
        $pdf = PDF::loadView('layouts.be.baby.report', get_defined_vars());

        return $pdf->download('file.pdf');
    }


}

It is working, when I visit the route :

Route::get('/baby/{id}/report/download','BabyController@downloadReportPDF');

But the styles seems very messed up.

  • Images are missing, and so on.

enter image description here

How do I improve on that ?


Updated

I've updated my images links to remote url now... they loaded now.

enter image description here

Somehow styles still messed up.

I even tried inline style for my padding, and still rendering wrong... :(

report.blade.php

<!DOCTYPE html>
<html>
<head>

    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta name="author" content="Bunlong Heng">
    <meta name="csrf-token" value="{{ csrf_token() }}">

    <title>Report</title>

    <link id="favicon" rel="shortcut icon" href="{{$baby->babyProfilePath}}?q={{microtime()}}" type="image/x-icon" />
    <link rel="shortcut icon" href="{{$baby->babyProfilePath}}?q={{microtime()}}" type="image/favicon.ico" />
    <link rel="apple-touch-icon" href="{{$baby->babyProfilePath}}?q={{microtime()}}" sizes="152x152">


    <link  href="https://maxcdn.bootstrapcdn.com/bootstrap/3.4.1/css/bootstrap.min.css" rel="stylesheet">
    <script src="https://www.chartjs.org/dist/2.8.0/Chart.min.js"></script>
    <script src="https://www.chartjs.org/samples/latest/utils.js"></script>
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>


    <style type="text/css">

        body {
            background-color: white; 
            color: black;

        }

        h5 {
            font-weight: normal;
            color: #86868b;
        }

    
        .text-poop {
            border: #ffb54c 1px solid !important;  
            color: #ffb54c; 
        }

        .text-pee { 
            border: #46b8da 1px solid !important;  
            color: #46b8da;
        }

        .text-medicine { 
            border: #ffdf0a 1px solid !important;  
            color: #ffdf0a;
        }

        .text-sleep { 
            border: #ca88ff 1px solid !important;  
            color:  #ca88ff;
        }

        .text-feed { 
            border: black 1px solid !important;  
            color:  black;

        }



    </style>


</head>


<body>

    <div class="container ">
        <div class="row ">

            <div class="col-sm-2 "></div>
            <div class="col-sm-8">


                <h1>{{ $baby->babyName }}</h1>

                <hr>

                <h5> Interval : {{ $interval }}ly. </h5>
                <h5> Today : {{ date('D F j, Y, g:i a') }}</h5>
                <h5> Parents : {{ $baby->name }} ({{ $baby->email }})</h5>
                <h5> URL : {{ env('APP_URL') }}/baby/{{ $baby->id }}/report?code={{ $baby->readOnlyCode }}</h5>

                <hr>

                <canvas id="canvas"></canvas>

                <hr>

                <table class="table skill-table">
                    <thead class="thin-border-bottom">
                        <th>date</th>
                        <th>Ago</th>
                        <th><img src="https://i.imgur.com/Xhg4Iwi.png" /> </th>
                        <th><img src="https://i.imgur.com/peU9Bas.png" /> </th>
                        <th><img src="https://i.imgur.com/Y3rrj9T.png" /> </th>
                        <th><img src="https://i.imgur.com/zQrE1o5.png" /> </th>
                        <th><img src="https://i.imgur.com/yCk62aM.png" /> </th>
                    </thead>

                    <tbody>


                        @foreach ($graphData as $date => $graph)

                        <tr>

                            <td >
                                {{$date}}
                            </td>
                            <td>{{ DateHelper::ago($date) }} ago.</td>

                            <td >
                                <a style="padding: 5px; " class="text-poop">{{$graph['poop'] ?? '' }}</a>
                            </td>

                            <td >
                                <a style="padding: 5px; " class="text-pee">{{$graph['pee'] ?? '' }}</a>
                            </td>

                            <td >
                                <a style="padding: 5px; " class="text-feed">{{$graph['feed'] ?? '' }}</a>
                            </td>

                            <td >
                                <a style="padding: 5px; " class="text-medicine">{{$graph['medicine'] ?? '' }}</a>
                            </td>

                            <td >
                                <a style="padding: 5px; " class="text-sleep">{{$graph['sleep'] ?? '' }}</a>
                            </td>

                        </tr>

                        @endforeach

                    </div>
                </div>
            </div>


            <script type="text/javascript">


                function hexToRgb(hex, opacity=1) {

                    var h=hex.replace('#', '');
                    h =  h.match(new RegExp('(.{'+h.length/3+'})', 'g'));

                    for(var i=0; i<h.length; i++)
                        h[i] = parseInt(h[i].length==1? h[i]+h[i]:h[i], 16);

                    if (typeof opacity != 'undefined')  h.push(opacity);

                    return 'rgba('+h.join(',')+')';

                }



                var url_string    = window.location.href;
                var url           = new URL(url_string);




                var data  = {};
                if(url.searchParams.get("interval") != null){
                    data.interval = url.searchParams.get("interval");
                }

                // console.log(data.interval);  

                $.ajax({
                    method: 'POST',
                    url: '/baby/{{$id}}/graphsData',
                    crossDomain: true,
                    contentType: false,
                    headers: {
                        'X-CSRF-TOKEN': $('meta[name="csrf-token"]').attr('value'),
                        "Accept": "application/json",
                        "Content-Type": "application/x-www-form-urlencoded",
                        "Cache-Control": "no-cache"
                    },
                    data: data,
                    success: function(response){
                        // console.log(response);


                        keys = [];
                        
                        peeData      = [];
                        poopData     = [];
                        feedData     = [];
                        medicineData = [];
                        sleepData    = [];

                        $.each(response, function(key,val) {

                            keys.push(key);

                            peeData.push(val.pee);
                            poopData.push(val.poop);
                            feedData.push(val.feed);
                            medicineData.push(val.medicine);
                            sleepData.push(val.sleep);


                        });



                        // console.log(peeData, poopData, feedData, medicineData, sleepData);


                        var originalLineDraw = Chart.controllers.line.prototype.draw;
                        Chart.helpers.extend(Chart.controllers.line.prototype, {
                            draw: function() {
                                originalLineDraw.apply(this, arguments);

                                var chart = this.chart;
                                var ctx = chart.chart.ctx;

                                var index = chart.config.data.lineAtIndex;
                                if (index) {
                                    var xaxis = chart.scales['x-axis-0'];
                                    var yaxis = chart.scales['y-axis-0'];

                                    ctx.save();
                                    ctx.beginPath();
                                    ctx.moveTo(xaxis.getPixelForValue(undefined, index), yaxis.top);
                                    ctx.strokeStyle = '#ff0000';
                                    ctx.textAlign = '#ff0000';
                                    ctx.fillText('NOW',0,0);
                                    ctx.lineTo(xaxis.getPixelForValue(undefined, index), yaxis.bottom);
                                    ctx.stroke();
                                    ctx.restore();
                                }
                            }
                        });

                        var options = {  weekday: 'long', year: 'numeric', month: 'long', day: 'numeric', hour: '2-digit', minute: '2-digit', second: '2-digit', hour12: false };
                        var fullDayText = new Date().toLocaleTimeString('en-us', options);


                        var chartColors = [hexToRgb('#fff',1), hexToRgb("ccc",1)];

                        let canvas = document.getElementById('canvas').getContext('2d');
                        canvas.height = 2000;


                        var renderChart = function() {

                            new Chart(canvas, {
                                type:'bar', 
                                data:{
                                    labels: keys,
                                    datasets:[
                                    {
                                        label: 'poop',
                                        data: poopData,
                                        backgroundColor: hexToRgb('#ffb54c',.5),
                                        borderWidth:1,
                                        borderColor: hexToRgb('#ffb54c',1),
                                        hoverBorderWidth:3,
                                    },
                                    {
                                        label: 'pee',
                                        data: peeData,
                                        backgroundColor: hexToRgb('#46b8da',.5),
                                        borderWidth:1,
                                        borderColor: hexToRgb('#46b8da',1),
                                        hoverBorderWidth:3,
                                    },
                                    {
                                        label: 'feed',
                                        data: feedData,
                                        backgroundColor: hexToRgb('#fff',.5),
                                        borderWidth:1,
                                        borderColor: hexToRgb('#ccc',1),
                                        hoverBorderWidth:3,
                                    },
                                    {
                                        label: 'medecine',
                                        data: medicineData,
                                        backgroundColor: hexToRgb('#ffdf0a',.5),
                                        borderWidth:1,
                                        borderColor: hexToRgb('#ffdf0a',1),
                                        hoverBorderWidth:3,
                                    },
                                    {
                                        label: 'sleep',
                                        data: sleepData,
                                        backgroundColor: hexToRgb('#ca88ff',.5),
                                        borderWidth:1,
                                        borderColor: hexToRgb('#ca88ff',1),
                                        hoverBorderWidth:3,
                                    }
                                    ],
                                    lineAtIndex: new Date().getHours()
                                },
                                options:{
                                    legend:{
                                        display:true,
                                        position:'right',
                                        labels:{
                                            fontColor:'#000'
                                        }
                                    }
                                }


                            });

                        };

                        renderChart();
                        setTimeout(renderChart(), 1000);



                    },
                    error: function(jqXHR, textStatus, errorThrown) {
                        console.log(JSON.stringify(jqXHR));
                        console.log("AJAX error: " + textStatus + ' : ' + errorThrown);
                    }
                });


            </script>




        </body>
        </html>
code-8
  • 54,650
  • 106
  • 352
  • 604
  • You can use JavaScript to print that – Rifat Bin Reza Mar 16 '21 at 03:23
  • Oh cool, will it auto download for users – code-8 Mar 16 '21 at 03:25
  • You can use https://github.com/MrRio/jsPDF it's comparatively easy to use I believe – Rifat Bin Reza Mar 16 '21 at 03:31
  • https://stackoverflow.com/questions/18191893/generate-pdf-from-html-in-div-using-javascript see here – Rifat Bin Reza Mar 16 '21 at 03:33
  • Please don't close this, I want to it Laravel friendly, I will update my post show more... that I tried. – code-8 Mar 16 '21 at 03:36
  • FYI, I haven't flagged your question. Why don't you build the pdf in a html page using js and css sending the data from laravel backend? Is that something you might consider? – Rifat Bin Reza Mar 16 '21 at 03:40
  • @cyber8200 I think u should re-check css, js in link, script tag. I think the problem in path of these files. try Internal css to determine the problem – Lee Mar 16 '21 at 03:46
  • @LengKeng : Can you help me please ? I updated my posted with my entire blade.view – code-8 Mar 16 '21 at 03:58
  • @LengKeng : Please see that I tried move the padding style into inline and still not taking any effect. – code-8 Mar 16 '21 at 03:59
  • @cyber8200 From my experience, if you want to generate pdf from server then try. https://github.com/barryvdh/laravel-snappy This will let you run the js before creating the pd file. with charts, chartjs will not work when creating pdf server on the server side. Try highchart or google chart instead – Lee Mar 16 '21 at 04:04
  • pass data into view instead of using ajax call – Lee Mar 16 '21 at 04:10

1 Answers1

1

If you want use trigger print you can do it by using window.print, it's will show print dialog that contain print as pdf. example code

<input type="button" value="Print" onClick="window.print()">
this will show in print preview

For print another page you can use iframe. you can hide it using CSS too. example:

<button onclick='window.frames["printf"].print();'>print</button>
<iframe id="printf" name="printf" src="test.html" height="1%" width="100%" style="visibility: hidden"></iframe>

I tried print page contain chartjs and works 100%, you can test it out

If you want to trigger download as PDF, you should use package. JS solution is jsPDF and PHP/laravel solution is laravel-dompdf . Both packages will not print exactly as browser render especially if you are using CSS3 or Image source from local. You need to custom the CSS and asset that compatible for both packages.

If you are using laravel dompdf you need to change below code:

<img src="/media/image.png" />

to

<img src="{{ public_path('/media/image.png') }}">

you can using asset() but you need activate remote asset in laravel dompdf too.

You can check DomPDF CSS Compatibility here.

Muhammad Dyas Yaskur
  • 6,914
  • 10
  • 48
  • 73
  • I just change my images to use remote url, they loaded now, but somehow styles still messed up and chart is not displaying.... – code-8 Mar 16 '21 at 03:53
  • @cyber8200 check style/CSS compatibility here: https://github.com/dompdf/dompdf/wiki/CSSCompatibility , for the chart how did you make it? if made by JS or HTML5, I dont think dompdf support it. – Muhammad Dyas Yaskur Mar 16 '21 at 03:57
  • I like this suggestion that you shared `onClick="window.print()">` how can I do that if the page that I want to print is not on that page.. – code-8 Mar 16 '21 at 04:00
  • Do you mean print from another URL page? Maybe you can use iframe: https://stackoverflow.com/questions/9616426/javascript-print-iframe-contents-only/9616706 – Muhammad Dyas Yaskur Mar 16 '21 at 04:04
  • 1
    @cyber8200 check my example using hidden iframe https://yaskur.com/iframeprint.html , it works using html5 or javascript too. that url contain chartjs and works 100% – Muhammad Dyas Yaskur Mar 16 '21 at 04:26
  • Can u share a working example ? Ur link show empty white page – code-8 Mar 16 '21 at 04:38
  • it has small print button on the top left corner – Muhammad Dyas Yaskur Mar 16 '21 at 05:41