342

I have the following html code:

<!DOCTYPE html>
<html>
    <body>
        <p>don't print this to pdf</p>
        <div id="pdf">
            <p><font size="3" color="red">print this to pdf</font></p>
        </div>
    </body>
</html>

All I want to do is to print to pdf whatever is found in the div with an id of "pdf". This must be done using JavaScript. The "pdf" document should then be automatically downloaded with a filename of "foobar.pdf"

I've been using jspdf to do this, but the only function it has is "text" which accepts only string values. I want to submit HTML to jspdf, not text.

Mosh Feu
  • 28,354
  • 16
  • 88
  • 135
John Crawford
  • 9,656
  • 9
  • 31
  • 42
  • 1
    As mentioned above I do *not* want to use the "text" function. I want to give it HTML. Your link only deals with plain text and not html – John Crawford Aug 12 '13 at 16:26
  • 5
    jsPDF **does** have a `fromHTML` function; see the "HTML Renderer" example at: http://mrrio.github.io/jsPDF/ – mg1075 May 06 '14 at 21:51

18 Answers18

317

jsPDF is able to use plugins. In order to enable it to print HTML, you have to include certain plugins and therefore have to do the following:

  1. Go to https://github.com/MrRio/jsPDF and download the latest Version.
  2. Include the following Scripts in your project:
    • jspdf.js
    • jspdf.plugin.from_html.js
    • jspdf.plugin.split_text_to_size.js
    • jspdf.plugin.standard_fonts_metrics.js

If you want to ignore certain elements, you have to mark them with an ID, which you can then ignore in a special element handler of jsPDF. Therefore your HTML should look like this:

<!DOCTYPE html>
<html>
  <body>
    <p id="ignorePDF">don't print this to pdf</p>
    <div>
      <p><font size="3" color="red">print this to pdf</font></p>
    </div>
  </body>
</html>

Then you use the following JavaScript code to open the created PDF in a PopUp:

var doc = new jsPDF();          
var elementHandler = {
  '#ignorePDF': function (element, renderer) {
    return true;
  }
};
var source = window.document.getElementsByTagName("body")[0];
doc.fromHTML(
    source,
    15,
    15,
    {
      'width': 180,'elementHandlers': elementHandler
    });

doc.output("dataurlnewwindow");

For me this created a nice and tidy PDF that only included the line 'print this to pdf'.

Please note that the special element handlers only deal with IDs in the current version, which is also stated in a GitHub Issue. It states:

Because the matching is done against every element in the node tree, my desire was to make it as fast as possible. In that case, it meant "Only element IDs are matched" The element IDs are still done in jQuery style "#id", but it does not mean that all jQuery selectors are supported.

Therefore replacing '#ignorePDF' with class selectors like '.ignorePDF' did not work for me. Instead you will have to add the same handler for each and every element, which you want to ignore like:

var elementHandler = {
  '#ignoreElement': function (element, renderer) {
    return true;
  },
  '#anotherIdToBeIgnored': function (element, renderer) {
    return true;
  }
};

From the examples it is also stated that it is possible to select tags like 'a' or 'li'. That might be a little bit to unrestrictive for the most usecases though:

We support special element handlers. Register them with jQuery-style ID selector for either ID or node name. ("#iAmID", "div", "span" etc.) There is no support for any other type of selectors (class, of compound) at this time.

One very important thing to add is that you lose all your style information (CSS). Luckily jsPDF is able to nicely format h1, h2, h3 etc., which was enough for my purposes. Additionally it will only print text within text nodes, which means that it will not print the values of textareas and the like. Example:

<body>
  <ul>
    <!-- This is printed as the element contains a textnode -->        
    <li>Print me!</li>
  </ul>
  <div>
    <!-- This is not printed because jsPDF doesn't deal with the value attribute -->
    <input type="textarea" value="Please print me, too!">
  </div>
</body>
Mosh Feu
  • 28,354
  • 16
  • 88
  • 135
snrlx
  • 4,987
  • 2
  • 27
  • 34
  • 4
    I'm going to guess the element handler can be a class? That would be much more semantically in line with HTML5 standards. An ID not only carries too much speificity weight in CSS, it also has to be unique. – Imperative Jan 03 '15 at 07:28
  • No, as far as I know, classes are no valid selectors at the moment as stated in their examples: "we support special element handlers. Register them with jQuery-style ID selector for either ID or node name. ("#iAmID", "div", "span" etc.) There is no support for any other type of selectors (class, of compound) at this time.". Still, it is possible to use tag names if that is in line with your use case and doesn't hide other important elements on your page. – snrlx Jan 06 '15 at 09:02
  • On their page https://parall.ax/products/jspdf they state that they support IE6+ via a shim. Haven't tried it myself though. – snrlx Mar 06 '15 at 10:41
  • @snrlx can u tel me how can i pass css classes with html tags ? – Erum Apr 15 '15 at 11:48
  • Unfortunately that is not possible yet as far as I know. – snrlx Apr 15 '15 at 16:55
  • 2
    @snrlx I get blank pdf and this error: renderer.pdf.sHashCode is not a function – Lisa Solomon Mar 23 '16 at 03:39
  • Please tell me how to implement download link or button, is it on doc.output("i need to add specific url here ?) or doc.save ? or doc.fromHtml ? – Mohamad Al Asmar Jul 27 '16 at 08:12
  • Would this also work with highcharts and other svgs? – user3115056 Aug 03 '16 at 21:24
  • I need no retrieve the css information also, do you know another library? @snrlx – gabrielAnzaldo Nov 04 '16 at 19:56
  • Hey gabodev, unfortunately not. For my purposes, jsPDF was always enough. – snrlx Nov 06 '16 at 14:59
  • I generated a pdf by using this js library, but it's getting blurred pdf. Actually, i have done this by getting HTML page content and convert it to DataUrl and generate PDF. Do you know how to increase quality of this image or pdf ? – dush88c Jan 22 '17 at 05:51
  • 7
    "If you want to ignore certain elements, you have to mark them with an ID" - a wonderful library, ruined by that inverted reqiurement. The OP wanted to print a single `
    `, which could be one of hundreds - so he has to mark all the *unwanted* DOM elements??
    – Mawg says reinstate Monica Jan 24 '17 at 19:13
  • Include also jspdf.plugin.cell.js for rendering table(s). – Slappy Feb 22 '17 at 08:20
  • thanks @snrlx . Did you play with it using HTML including IMGs? I am having an issue: my IMG has a width of 100px and generated much much bigger in the PDF... [here](https://stackoverflow.com/questions/44266210/jspdf-i-cant-generate-the-pdf-with-the-defined-img-dimensions-width) – Micky May 30 '17 at 16:05
  • @Micky: sorry but I never played with images so I can't assist you with that :/ – snrlx Jun 07 '17 at 11:28
  • i am using version 1.5.3 (from npm), the function fromHTML already exists in this library however I am getting the following error, 'addImage does not support files of type 'UNKNOWN', please ensure that a plugin for 'UNKNOWN' support is added' does anyone know the plugin needed and can provide a link, thanks. – steff_bdh Mar 13 '19 at 18:50
  • @MawgsaysreinstateMonica We can select div(s) using tag and select a specific one using it's index. Whats wrong with that? – Sumit Badsara Dec 31 '19 at 16:47
  • Yes, we can. But the OP wants to ***ignore*** a certain div. So, if he has 1,000 divs, he woudl have to slect 999 of them by name in order to ingore that one. – Mawg says reinstate Monica Dec 31 '19 at 21:52
  • I have a big HTML, very complicated but all static having bootstrap classes and tabled. Used this method. Getting following error- `Cannot read property 'name' of undefined` – Srk95 Jan 06 '20 at 09:07
  • 2
    This plugin might be good, but it's poorly documented. Most of the examples I see in stackoverflow don't even work. – Don Dilanga Jun 01 '20 at 14:06
  • Some table attributes like 'rowspan' and 'colspan' are not properly rendered in the printed document in my case. Is there any solution to this? – John Jul 18 '20 at 11:54
  • 17
    If I'm not wrong ````doc.fromHTML```` is no longer supported – Shardul Birje Feb 14 '21 at 18:56
  • can someone tell how to add html css/styles in the print doc ? – Hem M Apr 26 '21 at 10:46
  • 4
    `doc.fromHTML()` has been deprecated and replaced with `doc.html()` – ajsaule Nov 07 '22 at 03:02
88

This is the simple solution. This works for me. You can use the javascript print concept and simple save this as pdf.

<html xmlns="http://www.w3.org/1999/xhtml">
<head>
    <title></title>
    <script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.8.3/jquery.min.js"></script>
    <script type="text/javascript">
        $("#btnPrint").live("click", function () {
            var divContents = $("#dvContainer").html();
            var printWindow = window.open('', '', 'height=400,width=800');
            printWindow.document.write('<html><head><title>DIV Contents</title>');
            printWindow.document.write('</head><body >');
            printWindow.document.write(divContents);
            printWindow.document.write('</body></html>');
            printWindow.document.close();
            printWindow.print();
        });
    </script>
</head>
<body>
    <form id="form1">
    <div id="dvContainer">
        This content needs to be printed.
    </div>
    <input type="button" value="Print Div Contents" id="btnPrint" />
    </form>
</body>
</html>
AGuyCalledGerald
  • 7,882
  • 17
  • 73
  • 120
vaibhav kulkarni
  • 1,733
  • 14
  • 20
  • 1
    lighter solution, but this dissmiss css styling :s – Jon Jul 18 '16 at 12:47
  • 44
    "and simple save this as pdf" - I missed that part. How do you do it? – Mawg says reinstate Monica Jan 24 '17 at 19:16
  • 12
    This worked for me, for solving the problem of CSS styling, I created another css file called printPDF.css and added using link tag just like this in above example: printWindow.document.write('DIV Contents'); printWindow.document.write(''); printWindow.document.write(''); – Prime_Coder Feb 16 '17 at 06:43
  • 3
    Some comments: 1) You don't need an especific stylesheet for printing. In your current stylesheet, do something like: @media print { .pageBreak { page-break-before: always; } .labelPdf { font-weight: bold; font-size: 20px; } .noPrintPdf { display: none; } } and use these classes as your needs. 2) .live("click", ...) doesn't work to me, so I use .on("click", ...) instead. – Davidson Lima Sep 13 '17 at 14:19
  • 3
    @DavidsonLima this code creates new window which will not see old window css. That's why it's "ignoring" css, it's not ignoring, just in new window it's not loaded. Just load it in head and it will be rendering. – Lukas Liesis May 06 '18 at 13:40
  • 2
    Just in case any one is trying to make this happen with Angular, please put your CSS file in the assets folder. – rgantla Mar 14 '19 at 16:34
  • This is not working in Firefox browser. Mostly Chrome browser shows print preview as pdf. – Arun K May 03 '19 at 05:47
  • I am using the same solution. However, this is not "actual" html to pdf solution. The user can select a printer not PDF when the printer page popup. Is there any parameter I can enforce to print to PDF only? – Alex Poon May 08 '19 at 16:49
  • @Alex Poon yes you can save this as PDF – vaibhav kulkarni Nov 21 '19 at 13:10
  • it does not take input values. – Nessi Dec 16 '19 at 05:49
  • 1
    Is there any way we can do it through programming like with out opening print dialog we click on save button – Muneem Habib Apr 30 '21 at 12:29
  • Anyone wondering about the live() jQuery function -- it is deprecated, use on() : https://www.w3schools.com/jquery/event_live.asp – Design.Garden Mar 07 '22 at 00:41
  • Good idea, but do not work in mobile devices :( – Arthur Sep 26 '22 at 14:01
29
  • No depenencies, pure JS
  • To add CSS or images - do not use relative URLs, use full URLs http://...domain.../path.css or so. It creates separate HTML document and it has no context of main thing.
  • you can also embed images as base64

This served me for years now:

export default function printDiv({divId, title}) {
  let mywindow = window.open('', 'PRINT', 'height=650,width=900,top=100,left=150');

  mywindow.document.write(`<html><head><title>${title}</title>`);
  mywindow.document.write('</head><body >');
  mywindow.document.write(document.getElementById(divId).innerHTML);
  mywindow.document.write('</body></html>');

  mywindow.document.close(); // necessary for IE >= 10
  mywindow.focus(); // necessary for IE >= 10*/

  mywindow.print();
  mywindow.close();

  return true;
}

Of course this will open print dialog and user will have to know she/he can select print to pdf option, to get pdf. There may be printer pre-selected and if user confirms may get this document actually printed. To avoid such situation and to provide PDF without any extras, you need to make PDF file. Probably on the server side. You could have tiny html page with invoice only and convert it to PDF file with headless chrome. It's super easy with puppeteer. No need to install/config chrome, just install npm package puppeteer (managed by chrome team) and run it. Keep in mind this will actually launch real chrome just w/o GUI, so you need to have some RAM & CPU for this. Most servers will be fine with low enough traffic. Here is code sample but this must run on the BACKEND. Nodejs. Also it's slow call, it's resources intensive call. You should run it NOT on api call but e.g. after invoice was created - create pdf for it and store, if pdf was not generated yet, just show message to try again in couple minutes.

const puppeteer = require('puppeteer');
(async () => {
  const browser = await puppeteer.launch();
  const page = await browser.newPage();
  await page.goto('https://your-domain.com/path-to-invoice', {
    waitUntil: 'networkidle2',
  });
  await page.pdf({ path: 'invoice-file-path.pdf', format: 'a4' });

  await browser.close();
})();

Learn more here: https://pptr.dev/

Lukas Liesis
  • 24,652
  • 10
  • 111
  • 109
  • 3
    The problem with this is the pdf will not have any css effects in it. – Shadab Faiz May 07 '18 at 09:38
  • 2
    @ShadabFaiz It will but it may not be the same as main window. You can still add custom css tho this html too. – Lukas Liesis May 07 '18 at 19:26
  • Yeah. Iagree with you can add css in the window but how print the result in file pdf? – Mirko Cianfarani May 29 '18 at 16:25
  • 2
    Does not render images, though. – Jan Pi Sep 26 '18 at 10:56
  • @JanPi can't test it now but try adding an image as a base64 string, before it double check those paths to images are full paths, not relative. – Lukas Liesis Sep 27 '18 at 08:40
  • not even able to add external style using css file, most styles are not working, but approach is good. – Pardeep Jain Sep 09 '19 at 07:01
  • @PardeepJain it's HTML document you can anything to it like in normal HTML doc. Just make sure paths to CSS are full paths not relative while it's completely separated from the main dom tree. – Lukas Liesis Sep 09 '19 at 11:03
  • 3
    I love this! Some tweaks here and there and it looks good. And one small thing don't remove the extra spacing at `` it needs this :P – phrogg Feb 18 '20 at 10:17
  • @LukasLiesis it's opening content in preview mode. what if i wants to download file without any prompt? – pathe.kiran Aug 31 '21 at 10:54
  • @pathe.kiran then you need to make that file available. If you mean pdf, then you need to create pdf and serve pdf. No matter how you will call print function, it will always show print preview, unless there is some special software/config on user side which you don't control. Updated my answer with puppeteer sample for that task. – Lukas Liesis Aug 31 '21 at 20:06
  • @LukasLiesis, I tried your example with dynamic php form, but I only get the html structure of the document, but not the values. Do you know how to get user inputs together with the html part? – Даяна Димитрова Dec 14 '21 at 07:13
  • 1
    @ДаянаДимитрова it will print HTML from URL, if your values are in html, those will be printed. If you use PHP, you will need to render on server side. This code will literally open browser, navigate to url and print. You can read puppeteer API docs, it is possible to fill up the form or execute any JS on page after load, so maybe that's what you are looking for. But probably for your context it will be easier to just build dedicated endpoint with PHP. For 1st example it will take HTML out of page, so you could render hidden div with values from inputs inside HTML and use that div for print – Lukas Liesis Dec 14 '21 at 12:54
  • Instead of `mywindow.close();` which for me closes the printing dialog too, I would recommend `mywindow.addEventListener("afterprint", () => mywindow.close());` – tsgrgo Aug 01 '23 at 06:25
21

if you need to downloadable pdf of a specific page just add button like this

<h4 onclick="window.print();"> Print </h4>

use window.print() to print your all page not just a div

Wael Mahmoud
  • 376
  • 2
  • 9
18

You can use autoPrint() and set output to 'dataurlnewwindow' like this:

function printPDF() {
    var printDoc = new jsPDF();
    printDoc.fromHTML($('#pdf').get(0), 10, 10, {'width': 180});
    printDoc.autoPrint();
    printDoc.output("dataurlnewwindow"); // this opens a new popup,  after this the PDF opens the print window view but there are browser inconsistencies with how this is handled
}
cnotethegr8
  • 7,342
  • 8
  • 68
  • 104
Marco
  • 293
  • 3
  • 13
  • 1
    I'm curious, has this ever worked for anyone other than OP ? From reading the code, I seem to understand that it would only work with elements that have an ID. It's probably a bit more complicated than that, anyhow I have no idea how to make this work. – Michael Apr 25 '14 at 15:31
  • 19
    From what I observed, very ironically, fromHTML only works if the element sent as a parameter doesn't contain any HTML: only plain text supported. Kinda kills the purpose of the whole thing imo. – Michael Apr 25 '14 at 15:40
  • Worked perfectly for me. The element you want to pass in is not neccessarily required to have an ID. That's just the way that the replicant found the node he wanted to pass in. Additionally this solution also works without 'printDoc.autoPrint()'. If you want to leave this specific line in the code, then you are required to include the autoPrint-Plugin. – snrlx Jul 18 '14 at 11:52
  • 2
    `.fromHTML()` has been deprecated and replaced with `.html()` – ajsaule Nov 07 '22 at 02:56
18

As mentioned, you should use jsPDF and html2canvas. I've also found a function inside issues of jsPDF which splits automatically your pdf into multiple pages (sources)

function makePDF() {

    var quotes = document.getElementById('container-fluid');

    html2canvas(quotes, {
        onrendered: function(canvas) {

        //! MAKE YOUR PDF
        var pdf = new jsPDF('p', 'pt', 'letter');

        for (var i = 0; i <= quotes.clientHeight/980; i++) {
            //! This is all just html2canvas stuff
            var srcImg  = canvas;
            var sX      = 0;
            var sY      = 980*i; // start 980 pixels down for every new page
            var sWidth  = 900;
            var sHeight = 980;
            var dX      = 0;
            var dY      = 0;
            var dWidth  = 900;
            var dHeight = 980;

            window.onePageCanvas = document.createElement("canvas");
            onePageCanvas.setAttribute('width', 900);
            onePageCanvas.setAttribute('height', 980);
            var ctx = onePageCanvas.getContext('2d');
            // details on this usage of this function: 
            // https://developer.mozilla.org/en-US/docs/Web/API/Canvas_API/Tutorial/Using_images#Slicing
            ctx.drawImage(srcImg,sX,sY,sWidth,sHeight,dX,dY,dWidth,dHeight);

            // document.body.appendChild(canvas);
            var canvasDataURL = onePageCanvas.toDataURL("image/png", 1.0);

            var width         = onePageCanvas.width;
            var height        = onePageCanvas.clientHeight;

            //! If we're on anything other than the first page,
            // add another page
            if (i > 0) {
                pdf.addPage(612, 791); //8.5" x 11" in pts (in*72)
            }
            //! now we declare that we're working on that page
            pdf.setPage(i+1);
            //! now we add content to that page!
            pdf.addImage(canvasDataURL, 'PNG', 20, 40, (width*.62), (height*.62));

        }
        //! after the for loop is finished running, we save the pdf.
        pdf.save('test.pdf');
    }
  });
}
BaptWaels
  • 1,646
  • 2
  • 14
  • 17
18

i use jspdf and html2canvas for css rendering and i export content of specific div as this is my code

$(document).ready(function () {
    let btn=$('#c-oreder-preview');
    btn.text('download');
    btn.on('click',()=> {

        $('#c-invoice').modal('show');
        setTimeout(function () {
            html2canvas(document.querySelector("#c-print")).then(canvas => {
                //$("#previewBeforeDownload").html(canvas);
                var imgData = canvas.toDataURL("image/jpeg",1);
                var pdf = new jsPDF("p", "mm", "a4");
                var pageWidth = pdf.internal.pageSize.getWidth();
                var pageHeight = pdf.internal.pageSize.getHeight();
                var imageWidth = canvas.width;
                var imageHeight = canvas.height;

                var ratio = imageWidth/imageHeight >= pageWidth/pageHeight ? pageWidth/imageWidth : pageHeight/imageHeight;
                //pdf = new jsPDF(this.state.orientation, undefined, format);
                pdf.addImage(imgData, 'JPEG', 0, 0, imageWidth * ratio, imageHeight * ratio);
                pdf.save("invoice.pdf");
                //$("#previewBeforeDownload").hide();
                $('#c-invoice').modal('hide');
            });
        },500);

        });
});
Reegan Miranda
  • 2,879
  • 6
  • 43
  • 55
Ghazaleh Javaheri
  • 1,829
  • 19
  • 25
  • 6
    this working but it converting the content to image – Samad Aug 17 '19 at 08:13
  • 3
    Also, how to set page break so that the content/image will print on new page if it doesn't fit on current page? – SHEKHAR SHETE Mar 05 '20 at 05:53
  • for contents converted to image, name each div in a sequence,
    and so, and when converting them to PDF, refer to them via array.... something like this: html2canvas($("#div_pdf"+i)[0]).then(function (canvas) { variable i can be the result of a class in common: var clases = document.querySelectorAll(".export_pdf"); var len = clases.length;
    – Pablo Contreras Mar 12 '21 at 06:37
  • Merci Ghazaleh jan, you saved me some time :) – Mohsen Sep 21 '22 at 15:29
12

One way is to use window.print() function. Which does not require any library

Pros

1.No external library require.

2.We can print only selected parts of body also.

3.No css conflicts and js issues.

4.Core html/js functionality

---Simply add below code

CSS to

@media print {
        body * {
            visibility: hidden; // part to hide at the time of print
            -webkit-print-color-adjust: exact !important; // not necessary use         
               if colors not visible
        }

        #printBtn {
            visibility: hidden !important; // To hide 
        }

        #page-wrapper * {
            visibility: visible; // Print only required part
            text-align: left;
            -webkit-print-color-adjust: exact !important;
        }
    }

JS code - Call bewlow function on btn click

$scope.printWindow = function () {
  window.print()
}

Note: Use !important in every css object

Example -

.legend  {
  background: #9DD2E2 !important;
}
Rohit Parte
  • 3,365
  • 26
  • 26
  • 2
    There are problems with Print function of the browsers. Users usually have default options selected for the Print view (margins, page size and more). Therefore, it is very difficult to produce required PDF with required styling without training user, which is way more difficult and approx impossible... – Rahmat Ali Sep 27 '19 at 10:59
7

2022 Answer:

To generate PDF from HTML Element and prompt to save file:

import { jsPDF } from "jsPDF"

function generatePDF() {
  const doc = new jsPDF({ unit: 'pt' }) // create jsPDF object
  const pdfElement = document.getElementById('pdf') // HTML element to be converted to PDF

  doc.html(pdfElement, {
    callback: (pdf) => {
      pdf.save('MyPdfFile.pdf')
    },
    margin: 32, // optional: page margin
    // optional: other HTMLOptions
  })
}
<button onclick="generatePDF()">Save PDF</button>

...

To preview PDF without printing:

doc.html(pdfElement, {
  callback: (pdf) => {
    const myPdfData = pdf.output('datauristring')
  }
})
<embed type="application/pdf" src={myPdfData} />

...
For more HTMLOptions:
https://github.com/parallax/jsPDF/blob/master/types/index.d.ts

Noa
  • 207
  • 4
  • 4
6

Use pdfMake.js and this Gist.

(I found the Gist here along with a link to the package html-to-pdfmake, which I end up not using for now.)

After npm install pdfmake and saving the Gist in htmlToPdf.js I use it like this:

const pdfMakeX = require('pdfmake/build/pdfmake.js');
const pdfFontsX = require('pdfmake-unicode/dist/pdfmake-unicode.js');
pdfMakeX.vfs = pdfFontsX.pdfMake.vfs;
import * as pdfMake from 'pdfmake/build/pdfmake';
import htmlToPdf from './htmlToPdf.js';

var docDef = htmlToPdf(`<b>Sample</b>`);
pdfMake.createPdf({content:docDef}).download('sample.pdf');

Remarks:

  • My use case is to create the relevant html from a markdown document (with markdown-it) and subsequently generating the pdf, and uploading its binary content (which I can get with pdfMake's getBuffer() function), all from the browser. The generated pdf turns out to be nicer for this kind of html than with other solutions I have tried.
  • I am dissatisfied with the results I got from jsPDF.fromHTML() suggested in the accepted answer, as that solution gets easily confused by special characters in my HTML that apparently are interpreted as a sort of markup and totally mess up the resulting PDF.
  • Using canvas based solutions (like the deprecated jsPDF.from_html() function, not to be confused with the one from the accepted answer) is not an option for me since I want the text in the generated PDF to be pasteable, whereas canvas based solutions generate bitmap based PDFs.
  • Direct markdown to pdf converters like md-to-pdf are server side only and would not work for me.
  • Using the printing functionality of the browser would not work for me as I do not want to display the generated PDF but upload its binary content.
ilmiacs
  • 2,566
  • 15
  • 20
  • If I read the code correctly, this does not support CSS border styles (e.g. on tables), correct? – ninjagecko Oct 26 '19 at 04:16
  • Off topic, I use pdfmake to create pdf with the content not from html. My question is : how to provide our own specific file name instead of random file name resulted when using its method : pdfMake.createPdf(docDefinition).open() ? – Lex Soft Jul 22 '20 at 15:31
  • Now on-topic, the gist you mentioned does not exist. What is the problem with html-to-pdfmake which you said you ended up not using it ? I saw in the github it was still maintained recently. – Lex Soft Jul 22 '20 at 15:50
3

I was able to get jsPDF to print dynamically created tables from a div.

$(document).ready(function() {

        $("#pdfDiv").click(function() {

    var pdf = new jsPDF('p','pt','letter');
    var specialElementHandlers = {
    '#rentalListCan': function (element, renderer) {
        return true;
        }
    };

    pdf.addHTML($('#rentalListCan').first(), function() {
        pdf.save("caravan.pdf");
    });
    });
});

Works great with Chrome and Firefox... formatting is all blown up in IE.

I also included these:

<script src="js/jspdf.js"></script>
    <script src="js/jspdf.plugin.from_html.js"></script>
    <script src="js/jspdf.plugin.addhtml.js"></script>
    <script src="//mrrio.github.io/jsPDF/dist/jspdf.debug.js"></script>
    <script src="http://html2canvas.hertzen.com/build/html2canvas.js"></script>
    <script type="text/javascript" src="./libs/FileSaver.js/FileSaver.js"></script>
    <script type="text/javascript" src="./libs/Blob.js/Blob.js"></script>
    <script type="text/javascript" src="./libs/deflate.js"></script>
    <script type="text/javascript" src="./libs/adler32cs.js/adler32cs.js"></script>

    <script type="text/javascript" src="js/jspdf.plugin.addimage.js"></script>
    <script type="text/javascript" src="js/jspdf.plugin.sillysvgrenderer.js"></script>
    <script type="text/javascript" src="js/jspdf.plugin.split_text_to_size.js"></script>
    <script type="text/javascript" src="js/jspdf.plugin.standard_fonts_metrics.js"></script>
Wasskinny
  • 77
  • 1
  • 6
1

If you want to export a table, you can take a look at this export sample provided by the Shield UI Grid widget.

It is done by extending the configuration like this:

...
exportOptions: {
    proxy: "/filesaver/save",
    pdf: {
        fileName: "shieldui-export",
        author: "John Smith",
        dataSource: {
            data: gridData
        },
        readDataSource: true,
        header: {
            cells: [
                { field: "id", title: "ID", width: 50 },
                { field: "name", title: "Person Name", width: 100 },
                { field: "company", title: "Company Name", width: 100 },
                { field: "email", title: "Email Address" }
            ]
        }
    }
}
...
Vladimir Georgiev
  • 1,949
  • 23
  • 26
1

This example works great.

<button onclick="genPDF()">Generate PDF</button>

<script src="https://cdnjs.cloudflare.com/ajax/libs/jspdf/1.5.3/jspdf.min.js"></script>
<script>
    function genPDF() {
        var doc = new jsPDF();
        doc.text(20, 20, 'Hello world!');
        doc.text(20, 30, 'This is client-side Javascript, pumping out a PDF.');
        doc.addPage();
        doc.text(20, 20, 'Do you like that?');
    
        doc.save('Test.pdf');
    }
</script>
Ribaz
  • 476
  • 3
  • 10
  • Though this answer introduces a js to pdf library, The question is to create pdf from the contents of an HTML element and not manually creating the pdf's structure with code. – 123survesh Oct 01 '21 at 14:03
1

Dissatisfied with the rendering of html2canvas and lack of modern CSS3/JS and print specific CSS support of pdfMake's outdated version of WebKit...

Here's a theoretical solution, it's headless and can render pages faithfully, supports page breaks, margins, different page sizes and can be automated. You can even render WebGl to PDF.

printToPDF command you'd run on chrome_devtools protocol:

printToPDF({
  printBackground: false,
  pageWidth: 8.5,
  pageHeight: 11,
  transferMode: "ReturnAsStream" // or ReturnAsBase64
})

Ray Foss
  • 3,649
  • 3
  • 30
  • 31
1

The following method works fine for my case.

Hide additional parts for a page like the following example

@media print{
    body{
        -webkit-print-color-adjust: exact; // if you want to enable graphics
        color-adjust: exact !important; // if you want to enable graphics
        print-color-adjust: exact !important; // if you want to enable graphics
       
        * {
            visibility: hidden;
            margin:0;
            padding:0
        }
        .print_area, .print_area *{
            visibility: visible;
        }
        .print_area{
            margin: 0;
            align: center;
        }
        .pageBreak { 
           page-break-before : always; // If you want to skip next page
           page-break-inside: avoid;  // If you want to skip next page
         }
      }
    @page {
        size: A4; margin:0mm; // set page layout
        background-color: #fff;
    }
}

Use the javascript print function to print execution.

<button onclick="window.print()">Print</button>
Robin Hossain
  • 711
  • 9
  • 12
0

To capture div as PDF you can use https://grabz.it solution. It's got a JavaScript API which is easy and flexible and will allow you to capture the contents of a single HTML element such as a div or a span

In order to implement it you will need to first get an app key and secret and download the (free) SDK.

And now an example.

Let's say you have the HTML:

<div id="features">
    <h4>Acme Camera</h4>
    <label>Price</label>$399<br />
    <label>Rating</label>4.5 out of 5
</div>
<p>Cras ut velit sed purus porttitor aliquam. Nulla tristique magna ac libero tempor, ac vestibulum felisvulput ate. Nam ut velit eget
risus porttitor tristique at ac diam. Sed nisi risus, rutrum a metus suscipit, euismod tristique nulla. Etiam venenatis rutrum risus at
blandit. In hac habitasse platea dictumst. Suspendisse potenti. Phasellus eget vehicula felis.</p>

To capture what is under the features id you will need to:

//add the sdk
<script type="text/javascript" src="grabzit.min.js"></script>
<script type="text/javascript">
//login with your key and secret. 
GrabzIt("KEY", "SECRET").ConvertURL("http://www.example.com/my-page.html",
{"target": "#features", "format": "pdf"}).Create();
</script>

Please note the target: #feature. #feature is you CSS selector, like in the previous example. Now, when the page is loaded an image screenshot will now be created in the same location as the script tag, which will contain all of the contents of the features div and nothing else.

The are other configuration and customization you can do to the div-screenshot mechanism, please check them out here

Johnny
  • 14,397
  • 15
  • 77
  • 118
-1

any one try this

    (function () {  
        var  
         form = $('.form'),  
         cache_width = form.width(),  
         a4 = [595.28, 841.89]; // for a4 size paper width and height  

        $('#create_pdf').on('click', function () {  
            $('body').scrollTop(0);  
            createPDF();  
        });  
        //create pdf  
        function createPDF() {  
            getCanvas().then(function (canvas) {  
                var  
                 img = canvas.toDataURL("image/png"),  
                 doc = new jsPDF({  
                     unit: 'px',  
                     format: 'a4'  
                 });  
                doc.addImage(img, 'JPEG', 20, 20);  
                doc.save('Bhavdip-html-to-pdf.pdf');  
                form.width(cache_width);  
            });  
        }  

        // create canvas object  
        function getCanvas() {  
            form.width((a4[0] * 1.33333) - 80).css('max-width', 'none');  
            return html2canvas(form, {  
                imageTimeout: 2000,  
                removeContainer: true  
            });  
        }  

    }());  
General Grievance
  • 4,555
  • 31
  • 31
  • 45
abd aziz
  • 65
  • 5
-1

Quick, dirty, gets the job done.

** No clickable text on the PDF **

  1. Screenshots the div using html2canvas. Returns a canvas element.
  2. Puts the canvas element on a pdf using jsPDF
const handleDownloadPDF = () => {
  const domElement = document.getElementById('print');
  if (domElement) {
    html2canvas(domElement).then((canvas) => {
      const imgData = canvas.toDataURL('image/png');
      const pdf = new JSPDF();
      pdf.addImage(imgData, 'JPEG', 0, 0, pdf.internal.pageSize.getWidth(), pdf.internal.pageSize.getHeight());
      pdf.save(`${new Date().toISOString()}.pdf`);
    });
  }
};
  1. You can always skip the PDF part if not needed and directly download the canvas as a png or jpg. This won't require jsPDF library
const handleDownloadPng = () => {
  const domElement = document.getElementById('print');

  html2canvas(domElement).then(canvas => {
    // Convert canvas to a PNG data URL
    const pngDataUrl = canvas.toDataURL("image/png");

    // Create a download link
    const downloadLink = document.createElement("a");
    downloadLink.href = pngDataUrl;
    downloadLink.download = "div-image.png";
    downloadLink.textContent = "Download PNG";

    // Append the link and trigger a click
    document.body.appendChild(downloadLink);
    downloadLink.click();
    document.body.removeChild(downloadLink);
  });
}
Badal Saibo
  • 2,499
  • 11
  • 23