49

I have to generate a PDF report from data entered by a user, which would be saved in an object. So far, I have come across stuff which generates an HTML page and then takes a screenshot and converts it into PDF. I am looking to generate a PDF directly from data stored in the object. Any ideas?

R3tep
  • 12,512
  • 10
  • 48
  • 75
The Bored Theorist
  • 499
  • 1
  • 4
  • 4

8 Answers8

33

You can use jspdf.

working Demo

.html

<div id="pdfTable" #pdfTable>
  <h1>{{name}}</h1>

  <table>
    <tr>
      <th>Company</th>
      <th>Contact</th>
      <th>Country</th>
    </tr>
    <tr>
      <td>Alfreds Futterkiste</td>
      <td>Maria Anders</td>
      <td>Germany</td>
    </tr>
    <tr>
      <td>Centro comercial Moctezuma</td>
      <td>Francisco Chang</td>
      <td>Mexico</td>
   </tr>
    <tr>
      <td>Ernst Handel</td>
      <td>Roland Mendel</td>
      <td>Austria</td>
    </tr>
    <tr>
      <td>Island Trading</td>
      <td>Helen Bennett</td>
      <td>UK</td>
    </tr>
    <tr>
      <td>Laughing Bacchus Winecellars</td>
      <td>Yoshi Tannamuri</td>
      <td>Canada</td>
    </tr>
    <tr>
      <td>Magazzini Alimentari Riuniti</td>
      <td>Giovanni Rovelli</td>
      <td>Italy</td>
    </tr>
  </table>
</div>

<div> <button  (click)="downloadAsPDF()">Export To PDF</button></div>

.ts

  public downloadAsPDF() {
    const doc = new jsPDF();

    const specialElementHandlers = {
      '#editor': function (element, renderer) {
        return true;
      }
    };

    const pdfTable = this.pdfTable.nativeElement;

    doc.fromHTML(pdfTable.innerHTML, 15, 15, {
      width: 190,
      'elementHandlers': specialElementHandlers
    });

    doc.save('tableToPdf.pdf');
  }
Valentino Ru
  • 4,964
  • 12
  • 43
  • 78
DeC
  • 2,226
  • 22
  • 42
  • Any way to covert the `pdf` to `base64` without downloading the form – Sai Manoj Jul 10 '20 at 06:30
  • doc.output('blob") will return base64 – nipun-kavishka Jul 10 '20 at 09:29
  • 1
    But this doesn't allow you to create styling or alignment. It feels like such a poor way to make a pdf... specifying x-y locations and what not – Jitin May 24 '21 at 07:49
  • 1
    Use doc.html instead of doc.fromHTML – sebas2day Oct 01 '21 at 11:01
  • It would be great to explain what this code is doing. For example, what are `specialElementHandlers` and why are they required. They don't seem to exist in the latest version. – mwilson Mar 03 '22 at 16:53
  • Now jspdf 2.5.1 version is avilable. The method fromHTML has been changed to html: `doc.html(cheque, { callback: (doc) => { doc.save('doc.pdf');}, x: 10, y: 10,});` Also You can all documentation from here: https://raw.githack.com/MrRio/jsPDF/master/docs/index.html – Odilbek Utamuratov Mar 28 '23 at 04:13
22

You need to display the contents to be printed within a DIV. After displaying the contents, use the following code which was used and worked for my project

Step 1 :

Run following commands to install npm packages

> npm install jspdf
> npm install html2canvas

Step 2:

Import installed packages in app.components.ts. I haven't imported those packages in constructor()

> import * as jspdf from 'jspdf';
> import html2canvas from 'html2canvas';

Step 3:

Give an id for the HTML div that has to be exported as PDF. Add a button that activates the function too.

<div id="MyDIv" style="margin-left: 45px;" class="main-container">
        
</div>
<div class="icon_image " title="Share As PDF" (click)="exportAsPDF('MyDIv');"><img src="assets/img/pdf.png"></div>

Step 4 :

call the code for generating PDF as follows

    exportAsPDF(divId)
    {
        let data = document.getElementById('divId');  
        html2canvas(data).then(canvas => {
        const contentDataURL = canvas.toDataURL('image/png')  // 'image/jpeg' for lower quality output.
        let pdf = new jspdf('l', 'cm', 'a4'); //Generates PDF in landscape mode
        // let pdf = new jspdf('p', 'cm', 'a4'); Generates PDF in portrait mode
        pdf.addImage(contentDataURL, 'PNG', 0, 0, 29.7, 21.0);  
        pdf.save('Filename.pdf');   
      }); 
    }
Basil
  • 1,664
  • 13
  • 25
  • 3
    This works but, keep in mind that if you don't need high quality exports, you can use `image/jpeg` instead of `PNG`. This will reduce a two pages file from around 25mb down to just 1mb. – Hasher Jan 03 '22 at 19:26
  • 1
    @Hasher, I have edited the code to add it in comments. – Basil Jan 05 '22 at 04:54
  • Is there a way to do this with other libs?! I have a component with many sub components, multiple pages of graphs.. can I just put a bit of code in a parent component to turn the page into a single pdf? Instead of defining individual graphs like this? – ScipioAfricanus Jul 20 '22 at 17:33
  • @ScipioAfricanus, We can go with alternative libraries too. You can define a service and the same can be reused in parent comps. – Basil Jul 22 '22 at 11:07
  • Hey, I got it working with jsPDF and html2canvas. Just had to tool around with it a bit. – ScipioAfricanus Oct 20 '22 at 20:33
  • @ScipioAfricanus, here also we have made use of the same two libraries only. – Basil Oct 27 '22 at 10:22
9

It is a common requirement, but you haven't provided much detail on specific requirements of your PDFs. You can look at:

https://www.npmjs.com/package/angular-pdf-generator

or

https://parall.ax/products/jspdf

as client-side options. There are other options too depending on what you are likely to want to do with the PDF once generated. For example, it might make more sense to send the data back to your server (if you have one) to generate a PDF AND store it in a database etc.

Hope that helps.

Paul Jowett
  • 6,513
  • 2
  • 24
  • 19
  • Is there a documentation or tutorial for angular-pdf-generator, it looks like both GitHub links under npmjs page are not working. – 06serseri Jul 01 '21 at 11:55
7

Data entered by the user can be displayed in the HTML page and can be converted to pdf. Else if you want to bind the object in HTML template use handlebars. Then the HTML template can be converted to PDF in angular 2/4/6/7 using jspdf and html2canvas.The HTML content should be processed as below. This code supports for multiple page pdf creation.

Here data --> htmlcontent

TS :

generatePdf(data) {
     html2canvas(data, { allowTaint: true }).then(canvas => {
      let HTML_Width = canvas.width;
      let HTML_Height = canvas.height;
      let top_left_margin = 15;
      let PDF_Width = HTML_Width + (top_left_margin * 2);
      let PDF_Height = (PDF_Width * 1.5) + (top_left_margin * 2);
      let canvas_image_width = HTML_Width;
      let canvas_image_height = HTML_Height;
      let totalPDFPages = Math.ceil(HTML_Height / PDF_Height) - 1;
      canvas.getContext('2d');
      let imgData = canvas.toDataURL("image/jpeg", 1.0);
      let pdf = new jsPDF('p', 'pt', [PDF_Width, PDF_Height]);
      pdf.addImage(imgData, 'JPG', top_left_margin, top_left_margin, canvas_image_width, canvas_image_height);
      for (let i = 1; i <= totalPDFPages; i++) {
        pdf.addPage([PDF_Width, PDF_Height], 'p');
        pdf.addImage(imgData, 'JPG', top_left_margin, -(PDF_Height * i) + (top_left_margin * 4), canvas_image_width, canvas_image_height);
      }
       pdf.save("HTML-Document.pdf");
    });
  }

HTML:

<div #contentToConvert> Some content here </div>

<button (click)="generatePdf(contentToConvert)">Generate PDF</button>
andyb952
  • 1,931
  • 11
  • 25
Lincy PJ
  • 91
  • 1
  • 5
4

Previous answers didn't work for me. Then I founded this solution. Tested with angular 9.

jspdf version "^2.3.0"

npm install jspdf --save
npm install @types/jspdf --save-dev
  1. Create table in component.html file. <table class="table" id="content" #content></table>

  2. Catch table in component.ts file. @ViewChild('content') content: ElementRef;

savePdf() {
    const doc = new jsPDF();

    const pdfTable = this.content.nativeElement;

    doc.html(pdfTable.innerHTML, {
      callback(rst) {
        rst.save('one.pdf');
      },
      x: 10,
      y: 10
    });

  }

Try this solution.

Udeesha Induwara
  • 605
  • 1
  • 10
  • 20
2

You can print using PDFMAKE plugin. Access it on Git with this link https://www.npmjs.com/package/ng-pdf-make. After the installation is very easy, just import, add these libraries in the component that will print:

import * as pdfMake from "pdfmake/build/pdfmake";

import * as pdfFonts from "pdfmake/build/vfs_fonts";

After that, create a function that will print your document such as:
            
            printDoc(){
            
            pdfMake.vfs = pdfFonts.pdfMake.vfs;
             let dd = {
           
// Here you put the page settings as your footer, for example:
                      footer: function(currentPage, pageCount) {
                          return [
                            {
                              text:
                                currentPage.toString() +
                                " de " +
                                pageCount +
                                "\n" +
                                "Footer Name",
                              alignment: currentPage ? "center" : "center"
                            }
                          ];
                        },
// Here you can enter the page size and orientation:
                pageSize: "A4",
                pageOrientation: "Portrait",
            //in pageOrientation you can put "Portrait" or "landscape"
        
// start the body of your impression:
        content: [
        
                     {
                        table: {
                          widths: ["*"],
                          body: [
                            [
                              {
                                text: [
                                  { text: `${'Text Example'}`, bold: true }
                                ],
                                style: "header",
                                width: "150",
                                alignment: "left",
                                border: [true, true, true, false],
                                margin: [0, 15, 0, 15]
                              }
                            ]
                          ]
                        }
                      },
        ]
        
            }
        
        pdfMake.createPdf(dd).download("Name of Print");
            }
Mirellyssl
  • 21
  • 3
0

Install dependencies:

npm install jspdf
npm install html2canvas

my service

import * as jspdf from 'jspdf';
import html2canvas from 'html2canvas';

...
// make gap from left and right sides. In my case container has margin: 0
// that means that after pdf generation, content has no gaps, looks a bit ugly.
pdfView = false;
constructor(private cdRef: ChangeDetectorRef) {}
...

exportAsPdf() {
  // make gap from left and right sides. In my case container has margin: 0
  // that means that after pdf generation, content has no gaps, looks a bit ugly.
  this.pdfView = true;

  // workaround to wait until pdfView will affect page;
  setTimeout(() => {
    let article = document.getElementById('article');

    if (article) {
      const rect = article.getClientRects();
      const h = rect.item(0)!.height;
      const w = rect.item(0)!.width;

      const orientation = h > w ? 'portrait' : 'landscape';

      html2canvas(article).then((canvas) => {
        const contentDataURL = canvas.toDataURL('image/jpeg');
        let pdf = new jspdf.jsPDF(orientation, 'px', [w, h]);
        pdf.addImage(contentDataURL, 'jpeg', 0, 0, w, h);
        pdf.save('Filename.pdf');

        // back margin to 0 after generation.
        this.pdfView = false;
        this.cdRef.detectChanges();
      });
    }
  }, 0);
}

my html

// tailwind class name, mx-2 === margin (left and right) = 0.5rem;
<div id="article" class="mx-2" [ngClass]="{ 'mx-2': servce.pdfView }"> ... </div>

Show case how it works https://youtu.be/Mwe5nGPhhB8

Yuriy Gyerts
  • 1,464
  • 18
  • 30
-1

The most reliable and easy solution I found with html2pdf.js. I have used it with angular 8. I tried to create stackblitz, but it is having some resolution error. Hence I am pasting the code below:

import * as html2pdf from 'html2pdf.js';

let options = {
        margin: 1,
        filename: 'receipt.pdf',
        html2canvas: { 
          dpi: 192,
          letterRendering: true, 
          allowTaint: true, 
          useCORS: true, 
          logging: false, 
          scrollX: 0,
          scrollY: 0 
        },
        jsPDF: {
          orientation: 'portrait',
          unit: 'cm',
          format: 'a4'
        }
      }; 
      html2pdf().set(options).from(nativeElement).save().finally(() => anyfunc());
kaushik
  • 2,308
  • 6
  • 35
  • 50
  • This works for me. Whats omitted here is the reference to the element, which can be done by setting an `id` on the element and the selecting with `var nativeElement = document.getElementById('name-used-as-id');` – Superman.Lopez Aug 13 '20 at 14:24
  • is the converted pdf's text selectable or searchable? or it is converted to image? – zorlac May 11 '21 at 21:31
  • @zorlac, it is converted to image – kaushik May 12 '21 at 06:46