Spiria logo.

Create HTML 5 charts export them to PDF for free

September 8, 2015.

During the course of their work, many developers have had to create charts in view of exporting them to a PDF report. Developing these charts was quite onerous due to having to create an image for display in HTML code, then recreating it on the server side, and finally inserting it into a PDF document created on the fly.

Happily, this is a thing of the past. Thanks to HTML 5, it is now possible to create charts using the new Javascript API without having to create an image. All you need to do is use free library content that will convert an HTML page to PDF by interpreting the HTML, Javascript and CSS.

First, select a graphics library 

I personally chose Google charts, which can be found at https://developers.google.com/chart/.

I like Google charts because of the reliability of Google, it's free, it offers a plethora of original charts and I'm a fan of Google's Material Design.
Note that Google uses SVG charts, while other libraries use the canvas. To check browser compatibility, go to the caniuse.com website, which will tell you if the feature you want is compatible with a particular browser. Personally, I test SVG support on the most common browsers.

Following is a 5 step example.

Step 1 : Add a reference to Google Charts

<script type="text/javascript" src="https://www.google.com/jsapi"></script>

Step 2 : Load classes

google.load("visualization", "1", { packages: ["corechart"] });

Step 3 : Implement and define loading for each chart

    google.setOnLoadCallback(drawChatter);
    google.setOnLoadCallback(drawPie);
    google.setOnLoadCallback(drawDonut);
    google.setOnLoadCallback(drawLines);
    google.setOnLoadCallback(drawStacked);

    function drawChatter() {
        var data = google.visualization.arrayToDataTable([
            ['Age', 'Weight'],
            [8, -12],
            [-4, 5.5],
            [11, 14],
            [4, 5],
            [-3, -3.5],
            [6.5, -7]
        ]);

        var options = {
            title: 'Scatter chart',
            hAxis: { title: 'X', minValue: -20, maxValue: 20 },
            vAxis: { title: 'Y', minValue: -20, maxValue: 20 },
            legend: 'none'
        };

        var chart = new google.visualization.ScatterChart(document.getElementById('chartContainer5'));

        chart.draw(data, options);
    }
    
    function drawPie() {

        var data = google.visualization.arrayToDataTable([
            ['Task', 'Hours per Day'],
            ['Work', 11],
            ['Eat', 2],
            ['Commute', 2],
            ['Watch TV', 2],
            ['Sleep', 7]
        ]);

        var options = {
            title: 'Pie chart'
        };

        var chart = new google.visualization.PieChart(document.getElementById('chartContainer4'));

        chart.draw(data, options);
    }

    function drawDonut() {
        var data = google.visualization.arrayToDataTable([
          ['Task', 'Hours per Day'],
          ['Work', 11],
          ['Eat', 2],
          ['Commute', 2],
          ['Watch TV', 2],
          ['Sleep', 7]
        ]);

        var options = {
            title: 'Donut Chart',
            pieHole: 0.4,
        };

        var chart = new google.visualization.PieChart(document.getElementById('chartContainer3'));
        chart.draw(data, options);
    }

    function drawLines() {

        var data = new google.visualization.DataTable();
        data.addColumn('number', 'X');
        data.addColumn('number', 'Y');
        var y = 0;
        var x = 0;
        for (var i = 0; i < 1000; i += 1) {
            y += (Math.random() * 10 - 5);
            x = i - 1000 / 2;
            data.addRows([x,y]);
        }
       
        var options = {
            hAxis: {
                title: 'Y'
            },
            vAxis: {
                title: 'X'
            }
        };

        var chart = new google.visualization.LineChart(document.getElementById('chartContainer2'));

        chart.draw(data, options);
    }

    function drawStacked() {
        var data = new google.visualization.DataTable();
        data.addColumn('timeofday', 'X');
        data.addColumn('number', 'Motivation Level');
        data.addColumn('number', 'Energy Level');
        data.addColumn('number', 'Another');

        data.addRows([
          [{ v: [8, 0, 0], f: '8' }, 1, 1.25, 1.25],
        [{ v: [9, 0, 0], f: '9' }, 2, 1.5, 1.75],
        [{ v: [10, 0, 0], f: '10' }, 3, 2, 2.25],
        [{ v: [11, 0, 0], f: '11' }, 4, 3.25, 1.5],
        [{ v: [12, 0, 0], f: '12' }, 5, 3.25, 1.25],
        [{ v: [13, 0, 0], f: '13' }, 6, 4, 1.25],
        [{ v: [14, 0, 0], f: '14' }, 7, 5, 3.25],
        [{ v: [15, 0, 0], f: '15' }, 8, 6.25, 1.75],
        [{ v: [16, 0, 0], f: '16' }, 9, 8.5, 4.25],
        [{ v: [17, 0, 0], f: '17' }, 10, 11, 2.75],
        ]);

        var options = {
            title: 'Stacked Bar',
            isStacked: true,
            hAxis: {
                title: 'Y',
                viewWindow: {
                    min: [7, 30, 0],
                    max: [17, 30, 0]
                }
            },
            vAxis: {
                title: 'x'
            }
        };

        var chart = new google.visualization.ColumnChart(document.getElementById('chartContainer1'));
        chart.draw(data, options);

 

Step 4 : Create a html placeholder for each chart

<style type="text/css">.myblock { padding: 5px; text-align: center; vertical-align: middle; }</style>

The result is available here : http://chartssample.azurewebsites.net/

Second, download a free NuGet Package here: Select.Pdf

The documentation is available here : http://selectpdf.com/

Server side sample (ASP.NET MVC C#) : 

using System;
using System.Web.Mvc;
using SelectPdf;
using PdfPageOrientation = SelectPdf.PdfPageOrientation;
using PdfPageSize = SelectPdf.PdfPageSize;

namespace WebApplication1.Controllers
{
    public class HomeController : Controller
    {
        public ActionResult Index()
        {
            return View();
        }

        public ActionResult Report()
        {
            SelectPdf.GlobalProperties.HtmlEngineFullPath = HttpContext.ApplicationInstance.Server.MapPath("~/App_Data/Select.Html.dep");
            try
            {
                string pdf_page_size = "A4";
                PdfPageSize pageSize = (PdfPageSize) Enum.Parse(typeof (PdfPageSize),
                    pdf_page_size, true);

                string pdf_orientation = "Portrait";
                PdfPageOrientation pdfOrientation =
                    (PdfPageOrientation) Enum.Parse(typeof (PdfPageOrientation),
                        pdf_orientation, true);

                int webPageWidth = 1600;

                int webPageHeight = 0;

                // instantiate a html to pdf converter object
                HtmlToPdf converter = new HtmlToPdf();

                // set converter options
                converter.Options.PdfPageSize = pageSize;
                converter.Options.PdfPageOrientation = pdfOrientation;
                converter.Options.WebPageWidth = webPageWidth;
                converter.Options.WebPageHeight = webPageHeight;

                // create a new pdf document converting an url
                PdfDocument doc =
                    converter.ConvertUrl(string.Format("http://{0}:{1}/Home/Index", HttpContext.Request.Url.Host,
                        HttpContext.Request.Url.Port));

                // save pdf document
                doc.Save(HttpContext.ApplicationInstance.Response, false, "Sample.pdf");

                // close pdf document
                doc.Close();

                return Content("");
            }
            catch(Exception e)
            {
                return Content(string.Format("{0}


{1}", e.Message, (null!=e.InnerException)?e.InnerException.Message:""));
            }

        }

    }
}

The pdf file produced is available here: sample.pdf

So what do you think? :)