120

Is it possible to save HTML page as PDF using JavaScript or jquery?

In Detail:

I generated one HTML Page which contains a table . It has one button 'save as PDF'. If user clicks that button then that HTML page has to convert as PDF file.

Is it possible using JavaScript or jquery?

ram
  • 1,209
  • 2
  • 9
  • 3
  • 1
    possible answer here: http://stackoverflow.com/questions/12108806/how-to-convert-total-html-page-to-pdf-using-javascript-or-jquery – colin Apr 04 '13 at 21:54

13 Answers13

38

This might be a late answer but this is the best around: https://github.com/eKoopmans/html2pdf

Pure javascript implementation. Allows you to specify just a single element by ID and convert it.

Billcountry
  • 585
  • 5
  • 6
  • 20
    No answer is late :) . Thanks – Kalyan Chavali Jun 09 '20 at 11:49
  • 1
    This is what I used, although it did have difficulty putting the page breaks in the right spot. I had to add extra divs with ` ` inside, and add X padding-top (X = page number) to the element i was page-breaking before, since each page's content would start higher and higher up. Not sure if it was a bootstrap conflict or what. Here's the code I added, in a function to run right before calling html2pdf: `elements = document.getElementsByClassName("newBox"); for (var i = 0; i < elements.length; i++) {elements[i].style.paddingTop = elements[i].style.paddingTop = i + "px";};` – gamingexpert13 Jul 16 '21 at 18:22
  • But html2pdf breaks up my tabular UI elements (built using CSS grid) at weird places. – Ron16 Dec 15 '21 at 12:33
  • 2
    html2pdf is super easy to use! But it renders all content into an image and places that image into a PDF. So no selectable text. – ironmouse Apr 04 '22 at 15:56
  • html2pdf has few limitations that one should consider. 1. it doesn't load iFrames 2. it usually doesn't render CSS correctly (as mentioned by @Ron16) 3. it doesn't load images hosted on 3rd party domains 4. As mentioned by @ironmouse - it renders all content into an image and places that image into a PDF – Suraj Ingle Aug 17 '23 at 07:56
32

Yes, Use jspdf To create a pdf file.

You can then turn it into a data URI and inject a download link into the DOM

You will however need to write the HTML to pdf conversion yourself.

Just use printer friendly versions of your page and let the user choose how he wants to print the page.

Edit: Apparently it has minimal support

So the answer is write your own PDF writer or get a existing PDF writer to do it for you (on the server).

AJPerez
  • 3,435
  • 10
  • 61
  • 91
Raynos
  • 166,823
  • 56
  • 351
  • 396
  • 1
    jspdf look interesting, but the demos do not work in Firefox 5.0 nor IE! – Tim Büthe Aug 01 '11 at 09:58
  • 1
    jspdf has minimal support for features, it doesnt even support graphics. But unless the style system and rendering stuff is there - it's basically just useful for small notes. Otherwise you will have to write a whole HTML renderer in JS as well (or cheat and use canvas with drawElement, which is just supported in firefox i seem to remember). Javascript is really not up for this line of coding if you ask me. Perhaps call an external webservice is the easiest way. – Jon Lennart Aasenden Aug 01 '11 at 10:00
  • JavaScript (as a language) is entirely up to it. JavaScript running with the restrictions of browser security models and APIs less so. – Quentin Aug 01 '11 at 10:01
  • 4
    Im sure someone could build a house from matches or toothpicks, but at the end of the day - a fully blown pdf compiler with support for stitching, graphics, font embedding, styles and fully functional lookup tables is out of the question. Have a look at the source for jspdf - it supports only the most simple tags and no lookup dictionaries. A fully blown pdf compiler is hard to do in C++ or Delphi even, a pure JS implementation would be suicidal. There are companies who work for years selling only their pdf compilers (see gnostice for instance). This is not a "one liner". – Jon Lennart Aasenden Aug 01 '11 at 10:05
  • @JonLennartAasenden Yes it does have minimal support. You can still write a PDF writer in js if you want to. It's not an easy task however. A pure JS implementation is just as suicidal as C++ or Delphi. Don't pretend JS is a second-class citizen. – Raynos Aug 01 '11 at 11:04
18

Ya its very easy to do with javascript. Hope this code is useful to you.

You'll need the JSpdf library.

<div id="content">
     <h3>Hello, this is a H3 tag</h3>

    <p>a pararaph</p>
</div>
<div id="editor"></div>
<button id="cmd">Generate PDF</button>

<script>
    var doc = new jsPDF();
    var specialElementHandlers = {
        '#editor': function (element, renderer) {
            return true;
        }
    };

    $('#cmd').click(function () {
        doc.fromHTML($('#content').html(), 15, 15, {
            'width': 170,
                'elementHandlers': specialElementHandlers
        });
        doc.save('sample-file.pdf');
    });

    // This code is collected but useful, click below to jsfiddle link.
</script>

jsfiddle link here

Blackjoker
  • 593
  • 5
  • 5
9

I used jsPDF and dom-to-image library to export HTML to PDF.

I post here as reference to whom concern.

$('#downloadPDF').click(function () {
    domtoimage.toPng(document.getElementById('content2'))
      .then(function (blob) {
          var pdf = new jsPDF('l', 'pt', [$('#content2').width(), $('#content2').height()]);
          pdf.addImage(blob, 'PNG', 0, 0, $('#content2').width(), $('#content2').height());
          pdf.save("test.pdf");
      });
});

Demo: https://jsfiddle.net/viethien/md03wb21/27/

Hien Nguyen
  • 24,551
  • 7
  • 52
  • 62
  • 1
    If i goto responsive mode and download the pdf, pdf page doesnt come properly, Is there a way to achieve the same result as desktop mode when i download using responsive mode. – Nancy Jun 11 '20 at 05:47
  • @Nancy you can change the width and height of content before taking the screen shot. – Hamid Shoja May 31 '22 at 07:24
7

You can use Phantomjs. Download here and use the following example to test the html->pdf conversion feature https://github.com/ariya/phantomjs/blob/master/examples/rasterize.js

Example code:

phantomjs.exe examples/rasterize.js http://www.w3.org/Style/CSS/Test/CSS3/Selectors/current/xhtml/index.html sample.pdf
singh1469
  • 2,024
  • 22
  • 22
  • Can you use it with javascript instead of a command line utility? – Dynamic Mar 22 '17 at 09:53
  • @Dynamic no, this is a headless browser which you can control through javascript. You can tell it to print a web page to PDF via javascript but the implementation is not javascript. However, used it for this exact scenario by wrapping it in an app that consumes a queue of pages to print, and i use javascript to add entries to the queue. Similarly you could wrap it in a service. To control how things get printed though you're under the same limitation as chrome print (e.g. css and js to create a print friendly view) – Shane May 29 '17 at 03:48
6

Here is how I would do it, its an idea not bulletproof design, you need to modify it

  • The user clicks the save as PDF button
  • The server is sent a call using ajax
  • The server responds with a URL for PDF generated using HTML, I have used Apache FOP very succssfully
  • The js handling the ajax response does a location.href to point the URL send by JS and as soon as that URL loads, it sends the file using content disposition header as attachment forcing user to download the file.
Kumar
  • 5,038
  • 7
  • 39
  • 51
3

It is much easier and reliable to convert html to pdf server side. We are using Google Puppeteer. It is well maintained with wrappers for any server side language of your choosing. Puppeteer uses headless Chrome to generate screenshots and/or PDF files. It will save you a LOT of headache especially if you need to generate complex PDF files with tables, images, graphs, multiple pages and so

https://developers.google.com/web/tools/puppeteer/

Matej Janovčík
  • 1,242
  • 12
  • 13
1

There is another very obvious way to convert HTML to PDf using JavaScript: use an online API for that. This will work fine if you don't need to do the conversion when the user is offline.

PdfMage is one option that has a nice API and offers free accounts. I'm sure you can find many alternatives (for example, here)

For PdfMage API you'd have something like this:

 $.ajax({
    url: "https://pdfmage.org/pdf-api/v1/process",
    type: "POST",
    crossDomain: true,
    data: { Html:"<html><body>Hi there!</body></html>" },
    dataType: "json",
    headers: {
        "X-Api-Key": "your-key-here" // not very secure, but a valid option for non-public domains/intranet
    },
    success: function (response) {
        window.location = response.Data.DownloadUrl;
    },
    error: function (xhr, status) {
        alert("error");
    }
});
Andre Borges
  • 1,360
  • 14
  • 37
1

I know this is an old question, but depending on the readers use case an easy way is to call window.print() and tell the user to choose the save as PDF option. In CSS you can use media queries to hide or show content specifically for printing so you can control what is shown on the PDF. For example I use these .no-print and .only-print for this purpose.

.only-print {
   display: none
}
@media print {
  .no-print {
    display: none
  }
  .only-print {
    display: block
  }
}

In my use case I hide all the navigation and buttons stuff, I also hide some collapsed blocks and instead show all the uncollapsed blocks.

Bob
  • 614
  • 1
  • 7
  • 19
0

Yes. For example you can use the solution by https://grabz.it.

It's got a Javascript API which can be used in different ways to grab and manipulate the screenshot. In order to use it in your app you will need to first get an app key and secret and download the free Javascript SDK.

So, let's see a simple example for using it:

//first include the grabzit.min.js library in the web page
<script src="grabzit.min.js"></script>
//include the code below to add the screenshot to the body tag    
<script>
//use secret key to sign in. replace the url.
GrabzIt("Sign in to view your Application Key").ConvertURL("http://www.google.com").Create();
</script>

Then simply wait a short while and the image will automatically appear at the bottom of the page, without you needing to reload the page.

That's the simplest one. For more examples with image manipulation, attaching screenshots to elements and etc check the documentation.

Johnny
  • 14,397
  • 15
  • 77
  • 118
0

$('#cmd2').click(function() {
   var options = {
  //'width': 800,
   };
   var pdf = new jsPDF('p', 'pt', 'a4');
   pdf.addHTML($("#content2"), -1, 220, options, function() {
     pdf.save('admit_card.pdf');
   });
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/html2canvas/0.4.1/html2canvas.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jspdf/1.3.5/jspdf.min.js"></script>

<div id="content2" style="background: #fff;border-bottom: 1px solid #ffffff;">
                     <div class="tokenDet" style="padding: 15px;border: 1px solid #000;width: 80%;margin: 0 auto;position: relative;overflow: hidden;">
                         <div class="title" style="text-align: center;border-bottom: 1px solid #000;margin-bottom: 15px;">
                             <h2>Entrance Exam Hall Ticket</h2>
                            </div>
                            <div class="parentdiv" style="display: inline-block;width: 100%;position: relative;">
                             <div class="innerdiv" style="width: 80%;float: left;">
                              <div class="restDet">
                                        <div class="div">
                                            <div class="label" style="width: 30%;float: left;">
                                                <strong>Name</strong>
                                            </div>
                                            <div class="data" style="width: 70%;display: inline-block;">
                                                <span>Santanu Patra</span>
                                            </div>
                                            <div class="label" style="width: 30%;float: left;">
                                                <strong>D.O.B.</strong>
                                            </div>
                                            <div class="data" style="width: 70%;display: inline-block;">
                                                <span>17th April, 1995</span>
                                            </div>
                                            <div class="label" style="width: 30%;float: left;">
                                                <strong>Address</strong>
                                            </div>
                                            <div class="data" style="width: 70%;display: inline-block;">
                                                <span>P.S. Srijan Corporate Park, Saltlake, Sector 5, Kolkata-91</span>
                                            </div>
                                            <div class="label" style="width: 30%;float: left;">
                                                <strong>Contact Number</strong>
                                            </div>
                                            <div class="data" style="width: 70%;display: inline-block;">
                                                <span>9874563210</span>
                                            </div>
                                            <div class="label" style="width: 30%;float: left;">
                                                <strong>Email Id</strong>
                                            </div>
                                            <div class="data" style="width: 70%;display: inline-block;">
                                                <span>santanu@macallied.com</span>
                                            </div>
                                            <div class="label" style="width: 30%;float: left;">
                                                <strong>Parent(s) Name</strong>
                                            </div>
                                            <div class="data" style="width: 70%;display: inline-block;">
                                                <span>S. Patra</span><br /><span>7896541230</span>
                                            </div>
                                            <div class="label" style="width: 30%;float: left;">
                                                <strong>Exam Center</strong>
                                            </div>
                                            <div class="data" style="width: 70%;display: inline-block;">
                                                <span>Institute of Engineering & Management</span>
                                            </div>
                                            <div class="label" style="width: 30%;float: left;">
                                                <strong>Hall Number</strong>
                                            </div>
                                            <div class="data" style="width: 70%;display: inline-block;">
                                                <span>COM-32</span>
                                            </div>
                                        </div>
                                    </div>
                             </div>
                                <div class="sideDiv" style="width: 20%;float: left;">
                                 <div class="atts" style="float: left;width: 100%;">
                                     <div class="photo" style="width: 115px;height: 150px;float: right;">
                                         <img src="images/candidateImg.gif" style="width: 100%;"/>
                                        </div>
                                        <div class="sign" style="position: absolute;bottom: 0;right: 0;border-top: 1px dashed #000;left: 80%;text-align: right;">
                                         <small>Self Attested</small>
                                        </div>
                                    </div>
                                </div>
                            </div>
                        </div>
                    </div>
                    <button class="btn btn-info" id="cmd2">Download Token</button>
Santanu
  • 146
  • 1
  • 6
  • The above code doesn't work in any browser I have tested. Nothing happens when you click "Download Token" button and no errors are logged. Can you please check. – Almeister9 Nov 15 '18 at 11:28
0

Had similar issue, could not use jspdf since my legacy code contained multiple tables with several colspan inside. In Jspdf thead > th's must match tbody > tr > td

I ended up using html2pdf package which worked for me

on your file add the library

<script src="https://cdnjs.cloudflare.com/ajax/libs/html2pdf.js/0.8.1/html2pdf.bundle.min.js" integrity="sha512-vDKWohFHe2vkVWXHp3tKvIxxXg0pJxeid5eo+UjdjME3DBFBn2F8yWOE0XmiFcFbXxrEOR1JriWEno5Ckpn15A==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>

Get the content you want to save as pdf

var pdf_content = document.getElementById("pdf_body");

Add option or configuration to your file

var options = {
      margin:       1,
      filename:     'isolates.pdf',
      image:        { type: 'jpeg', quality: 0.98 },
      html2canvas:  { scale: 2 },
      jsPDF:        { unit: 'in', format: 'letter', orientation: 'portrait' }
    };

Save the file

html2pdf(pdf_content, options);
Wainaina Nik
  • 163
  • 1
  • 8
0

In short: no. The first problem is access to the filesystem, which in most browsers is set to no by default due to security reasons. Modern browsers sometimes support minimalistic storage in the form of a database, or you can ask the user to enable file access.

If you have access to the filesystem then saving as HTML is not that hard (see the file object in the JS documentation) - but PDF is not so easy. PDF is a quite advanced file-format that really is ill suited for Javascript. It requires you to write information in datatypes not directly supported by Javascript, such as words and quads. You also need to pre-define a dictionary lookup system that must be saved to the file. Im sure someone could make it work, but the effort and time involved would be better spent learning C++ or Delphi.

HTML export however should be possible if the user gives you non restricted access

Jon Lennart Aasenden
  • 3,920
  • 2
  • 29
  • 44
  • 2
    Why are C++ and Delphi _inheritantly_ better at creating a PDF writer? – Raynos Aug 01 '11 at 11:06
  • 2
    Because those languages were buildt to make advanced software. Javascript was not. Javascript was never finished, this is why the prototype system is hanging out. The author had planned to add a HL to it with real classes and more datatypes - but he didnt have time. So it was published "as is". It does not support pointers, raw memory allocation is painful, it does not support some of the native datatypes you find in other languages, it does not support packed structures (struct in C, Record in Pascal) ... the list is endless. I love JS, but it is a browser toy, not a real language. – Jon Lennart Aasenden Aug 10 '11 at 02:55
  • There are some Java based writers out there, and if you look at the size of the source-code, it should be fairly self evident that it would be even longer under Javascript - but the crucial point is: the IO formating and the lookup tables. Im sure someone could do it - but it would be extremely slow and basically, a waste of time. And how will you embed font data? You cant even get the file from the OS, let alone convert it (which is a whole library in itself) and embed it. Why build a house of matches when you can just, build a normal house? – Jon Lennart Aasenden Aug 10 '11 at 03:05
  • 2
    "but it is a browser toy, not a real language." That's like saying Scheme is not a real language. "it should be fairly self evident that it would be even longer under Javascrip", no. Java is severely more verbose then JavaScript. It should be about 2/3 shorter then the Java version. "but it would be extremely slow and basically," By extremely slow you mean 3/4x slower then C++ ? Can we please stop treating js as a second class citizen. Thanks. – Raynos Aug 10 '11 at 07:42
  • Also. If you are going to write code that interfaces with systems made in C++ - then it's fairly logical that you have to match the required data structures produced by these languages (the PDF file structure being one example). And this is where JS get's into trubble. Try bit-shifting 64bits ... you cant. Because bit shifting under JS is restricted to 32bits no matter what number you initially assigned a variable. I love JS, but it's not a real language. Sorry. And btw.. this has nothing to do with you or me personally. It doesnt make you better or worse than me. – Jon Lennart Aasenden Aug 12 '11 at 06:43
  • Last fall i had to deliver an iPhone app. Since it had to interface directly with our custom server - it meant i couldnt use Javascript. Well, I sat down and learnt C# and monotouch for that particular task. My next project however will be pure javascript. This is not a case of "A versus B", but rather "what language is best suited for a particular task". I have seen some amazing stuff done with JS - and i really love coding javascript. It all depends on the project what utility you should use. – Jon Lennart Aasenden Aug 12 '11 at 06:54
  • I agree with you there, JavaScript has limitations in accessing binary data (which are being fixed) and it's an absolute nightmare to interface with external things in JS. Your just demonizing it too much ;) – Raynos Aug 12 '11 at 09:16
  • 12
    Regardless of your personal opinion, Jon, could you stop making the assertion that "Javascript is not a real language," because that's BS. Javascript is an extremely powerful language that's optimized for different purposes than a low-level compiled language like C or C++. There are things you can do in JS that you cannot do in C or C++, but does that mean C and C++ aren't "real" languages? No, just that they're meant for different things. JS is just as real a programming language as any other turing-complete language. – Isochronous Jul 08 '13 at 13:48