Web
Article

Generating PDFs from Web Pages on the Fly with jsPDF

By Massimo Cassandro

Print PDF from a web page

The Portable Document Format has been one the major innovations in the fields of desktop publishing and office automations.

It’s widely used in web publishing too, but unfortunately very often in wrong ways – like using it to replace contents that should have been built with HTML. This causes many problems regarding usability, accessibility, SEO and so on.

However, there are some cases in which PDF files are required: when a document needs to be archived and must be used outside the web (for example an invoice) or when you need a deep control on printing.

It was just the need to control printing that brought me to research a way to easily generate a PDF.

The purpose of this article is not to simply explain how a PDF can be created (there are many easy way to do this), but also to focus on the circumstances where a PDF file can solve a problem, and how a simple tool like jsPDF can help with this.

Dealing with Printing

Anyone who has dealt with CSS printing rules knows how difficult it is to achieve a decent level of cross-browser compatibility (take a look, for example, at the Page-break support table at Can I Use). Therefore, when I need to build something that must be printed, I always try to avoid CSS, and the simplest solution is to use PDF.

I’m not talking here about the simple conversion of HTML to PDF. (I’ve tried several tools of that type, but none of them has fully satisfied me.) My goal is to have complete control over the positioning and size of elements, page breaks and so on.

In the past I’ve often used FPDF, a PHP tool that can easily give you such controls and that can be easily expanded with many plugins.

Unfortunately, the library seems to be abandoned (its last version dates back to 2011) (Update: actually, the latest version appears to be from December 2015), but thanks to some JavaScript libraries, we now have the ability to build PDF files directly in the clients (thus making their generation faster).

When I started my project, some months ago, I searched for a JS library, and finally I found two candidates: jsPDF and pdfmake. pdfmake seems to be well documented and very easy to use, but since it was a beta version, I decided for jsPDF.

PDF Building with jsPDF

The jsPDF documentation is fairly minimal, consisting of a single page along with some demos, and a little more information in the source file (or in its jsDoc pages), so keep in mind that using it for complex projects can be a little hard in the beginning.

Anyway, jsPDF is very easy for basic PDF files generation. Take a look to a simple “Hello World” example:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8">
    <title>Hello world</title>
</head>
<body>
    <h1>Hello world</h1>
    <script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/jspdf/1.0.272/jspdf.debug.js"></script>
    <script type="text/javascript">
        var pdf = new jsPDF();
        pdf.text(30, 30, 'Hello world!');
        pdf.save('hello_world.pdf');
    </script>
</body>
</html>

This HTML page generates a one-page PDF file and saves it on your computer. First you have to link to the jsPDF library (in this case, from cdnjs.com), then a jsPDF instance is created, a line of text is added, and the result is saved as hello_world.pdf.

Note that I’ve used the 1.0.272 version, and that it’s not the latest: at the time of writing this, the most recent version is the 1.1.135, but it has many issues, so I am still using the previous one.

You can see how extremely simple it is to build a basic PDF file (you can find more examples at the jsPDF site).

Let’s try to build something harder.

The Flyer Project

Some months ago, I was asked to build an application for creating some simple flyers. It was part of a larger project that dealt with some services for travel agencies, and the real flyer was partially populated by some JSON data.

The flyer’s main aim was to provide a simple way to display special offers to be exposed in the travel agency shop window.

I’ve rearranged that application for this article, removing all server-side functionality, simplifying the flyer design, getting rid of legacy browser compatibility and building a very simple UI with Bootstrap 3 and jQuery.

The demo works well with Firefox and Chrome, while Explorer (or Edge) doesn’t allow you to display the preview, but only to download the generated PDF.

This is an example of a PDF created with the app (photo credit: Rafael Leão / Unsplash)

The flyer

A functioning demo is available at the end of this article or directly in CodePen. Note that, since CodePen loads the result page in an iframe, the PDF preview – which is loaded in an iframe too – has some issues with Chrome and Safari that prevent the preview from being displayed. (Use Firefox if you can, or try the demo on my personal site).

The Flyer Builder

The user interface allows the user to insert some basic data (a title, an abstract and a price). Optionally, an image can be added, otherwise a grey-boxed Special Offer title is displayed.

Other data (the agency name and its website URL and logo) are embedded in the application code.

User interface

The PDF can be previewed in a iframe (except Explorer or Edge) or directly downloaded.

When the Update preview or the Download buttons are clicked, the PDF is generated using jsPDF and passed to the iframe as a data URI string or saved to disk, as in the above example.

The PDF generation first creates a new instance a jsPDF object with these options: portrait orientation (p), millimeters units (mm), ‘A4’ format.

var pdf = new jsPDF('p', 'mm', 'a4');

Images are added using the addImage function. Note that every object placed in the PDF page must be exactly positioned. You have to take care of the coordinates of each object using the units declared.

// pdf.addImage(base64_source, image format, X, Y, width, height)
pdf.addImage(agency_logo.src, 'PNG', logo_sizes.centered_x, _y, logo_sizes.w, logo_sizes.h);

Images must be Base64 encoded: the agency logo is embedded in the script in this format, while the image loaded by the user is encoded using the readAsDataURL method in the $('#flyer-image').change listener.

The title is added using the textAlign function. Note that this function is not part of the jsPDF core, but, as suggested by the author in his examples, the library can be easily expanded using its API. You can find the textAlign() function at the top of flyer builder script:

pdf.textAlign(flyer_title, {align: "center"}, 0, _y);

This function calculates the X coordinate of the the text string to make it centered, and then calls the native text() method:

pdf.text(text string, X, Y);

To change text properties, you can use the setFontSize(), setFont(), setTextColor() and setFontType() methods.

To set a 20pt Times Bold red string, for example, you need to type this:

pdf.setFontSize(20);
pdf.setFont("times");
pdf.setFontType("bold");
pdf.setTextColor(255, 0, 0);
pdf.text(10,10, 'This is a 20pt Times Bold red string');

The Special offer grey box and the price circle use two similar methods: roundedRect() and circle(). Both of them require top-left coordinates, size values (the width and height in the first case and the radius in the second one):

pdf.roundedRect( X, Y, width, height, radius along X axis, radius along Y axis, style);
pdf.circle( X, Y, radius, style);

The style parameters refers to the fill and stroke properties of the object. Valid styles are: S [default] for stroke, F for fill, and DF (or FD) for fill and stroke.

Fill and stroke properties must be set in advance using setFillColor and setDrawColor, which require a RGB value and setLineWidth that requires the line width value in the unit declared at inception of PDF document.

The complete code is available in the CodePen demo:

See the Pen PDF Flyer generator by SitePoint (@SitePoint) on CodePen.

Conclusion

This basic example shows how it’s possible to build a very basic flyer with jsPDF.

Its use could be easy, but the lack of a complete documentation makes every step really complicated.

I’m still looking around for other solutions, keeping my eye on others like pdfmake. But ultimately, I think the only really definitive solution can be better browser support for printing CSS rules!

Have you used jsPDF or something similar? What was your experience? Let me know in the comments.

Free Guide:

7 Habits of Successful CTOs

"What makes a great CTO?" Engineering skills? Business savvy? An innate tendency to channel a mythical creature (ahem, unicorn)? All of the above? Discover the top traits of the most successful CTOs in this free guide.

  • adam__roberts

    Hey Marcin, good point! I’ve updated that sentence to reflect this, and will ask to the author to take a look to make sure the rest of the article is still valid. Thanks for the comment!

    • https://www.behance.net/massimocassandro Massimo Cassandro

      That’s right, I wrote the article in November. Thanks for the update

  • Ralph Mason

    Thanks for pointing that out, @marcinnabiaek:disqus. Post updated. It was actually true when the author wrote this article.

  • Loganatan

    The article could have been expanded more details like how we can integrate with images and dynamic text date driven.

    • https://www.behance.net/massimocassandro Massimo Cassandro

      the article aims only to give an outline of this technique, anyway, since both images and text are provided thru a form, I think we can define them as “dynamics”, isn’t it?
      Thanks for your comment

      • Loganatan

        yes thats dynamics.. anyway it is ok. thanks for the clarification

  • Mike Thirlwell

    Great article, thanks. In it you mention “the simple conversion of HTML to PDF. (I’ve tried several tools of that type, but none of them has fully satisfied me”; can you point me at one or two of those tools as I’ve been looking for something to do that for a while now?

    • http://yuruiology.com pallymore

      I use wkhtmltopdf – it’s not perfect. but once you figure out the workarounds and then it’ll be great. I use it in production and generated ten thousands of PDF reports with complicated layouts and graphs.

      Personally I don’t really like jsPDF or pdfmake’s approaches. (or Prawn from the Ruby world) I find it harder to maintain than the HTML/CSS/JS way – especially when generate highly dynamic and customizable PDFs.

    • https://www.behance.net/massimocassandro Massimo Cassandro

      Recently my team used html2pdf. In the past I’ve used another couple of php library but I don’t remember their names. Then, we have some JavaScript tools that can do the job, like jspdf itself

    • tikiatua

      Did you have a look at PrinceXML. We tried several solutions ourselves and found this to be the best for our use case. Server license is around 4000$. But in my opinion this is very well invested.

  • jan33w

    Generally Javascript doesn’t write to the disk, except cookies. How is this doing it? And is its disk access secure, not hackable?

    • https://www.behance.net/massimocassandro Massimo Cassandro

      The application can’t write on disk, users must confirm savings each time

  • Gaetano Del Prete

    Nice article!

    • https://www.behance.net/massimocassandro Massimo Cassandro

      Grazie Gaetano!

  • Apaar Bhatnagar

    Great Article…..

  • Saurabh Tiwari

    Hi..I was looking for a JS based HTML to PDF converting tool, when I came across this post. Your post is amazing, nice and easy. But do you have any leads for HTML to PDF conversion using jspdf or any other tool. I have a question posted on SO http://stackoverflow.com/questions/38582567/issues-with-generating-good-quality-pdf-from-javascript-libraries-jspdf-and-htm , kindly reply… Any help is appreciated.

    • https://www.behance.net/massimocassandro Massimo Cassandro

      Hi
      jspdf can convert html to pdf, but I never played with it

  • Saurabh Tiwari

    At around line 350, you have written pdf.text(page_margin, _y, description_lines[i]); where ‘description_lines[i]); refers to your text. I saw the jspdf code and the text method is declared as API.text = function(text, x, y, flags, angle) , which takes ‘text’ as the first argument. I am confused about the parameter mismatch at these two places. Shouldn’t your description_lines[i] be at start. Kindly clarify

    • https://www.behance.net/massimocassandro Massimo Cassandro

      Hi
      I can’t access any computer now, but you can refer to the codepen working example
      bye

  • Saurabh Tiwari

    tried using this… It just stucks at 1/6 :10% and no pdf is generated. After few mins it gives error code 1 and exits

  • Nadeeka Randeniya

    Nice article.. How can I load my pdf to new window(_blank) with this jsPDF, not to download or save.

    • https://www.behance.net/massimocassandro Massimo Cassandro

      mmh,
      I don’t know, but I don’t exclude it is possible…
      Have a try with jsPDF output options

  • https://www.behance.net/massimocassandro Massimo Cassandro

    Hi,
    I think that is not possible to add links with jsPDF

  • http://www.dapperAuteur.com A Brand

    Thanks Massimo. I’m looking for a solution to generate immigration documents. I think this is the solution.

Recommended
Sponsors
Because We Like You
Free Ebooks!

Grab SitePoint's top 10 web dev and design ebooks, completely free!

Get the latest in Front-end, once a week, for free.