Edupala

Comprehensive Full Stack Development Tutorial: Learn Ionic, Angular, React, React Native, and Node.js with JavaScript

How to implement Angular pdf generator with PdfMaker in Angular 11|12?

angular pdf generator

In this tutorial, we’ll learn how to use the Angular Pdf generator using the PdfMake library. The PdfMake library allows us to export our data to PDF in both client and server-side using a pure javascript library. We’ll discuss how to integrate PdfMake in our angular project. What we are learning from the Angular pdf generator.

  • How to convert our data into PDF
  • Conversion of an image to Base64, so we can include an image in our PDF.
  • Create an Angular table

Prerequisites:
To create and run an Angular project, We need node js and Angular CLI already install in our system, if not then check how to install Node js and angular in Ubuntu. To install the Angular CLI, open a terminal and run the following command:

npm install -g @angular/cli

Setting up and configure a project for Angular pdf generator

Once we have our Angular CLI installed, let’s create an Angular project and install our PdfMake library. The PdfMake is easy and flexible, allowing us to control the style of our PDF document.

ng new angular-pdf
npm i pdfmake --save

We’ll demonstrate Here’s a quick screenshot of what we’ll be building.

angular pdf generator
Angular pdf generator

We need data to create an Angular table and export it in PDF format. In a real application, we are retrieving our data source from API remote server, for the Angular pdf generator we have some dummy data. We will retrieve data using the HTTP client module in the same way if the data source is remote. Let’s add our dummy data, in the assets folder create a data folder and students.json.

[
    {
        "name": "Will Smith",
        "gender": "Male",
        "country": "USA"
    },
    {
        "name": "Jackline Joy",
        "gender": "Female",
        "country": "Sri Lanak"
    },
    {
        "name": "Alu Arjun",
        "gender": "Male",
        "country": "India"
    },
    {
        "name": "Kavitha Kumar",
        "gender": "Female",
        "country": "India"
    },
    {
        "name": "John Snow",
        "gender": "Male",
        "country": "United Kingdom"
    },
    {
        "name": "Priya kanana",
        "gender": "Female",
        "country": "India"
    },
    {
        "name": "Shri Devi",
        "gender": "Female",
        "country": "Sri Lanka"
    },
    {
        "name": "Richard Roy",
        "gender": "Male",
        "country": "France"
    },
    {
        "name": "Sonu Nigam",
        "gender": "Male",
        "country": "India"
    },
    {
        "name": "Priya Dutt",
        "gender": "Female",
        "country": "USA"
    },
    {
        "name": "Will Smith",
        "gender": "Male",
        "country": "USA"
    },
    {
        "name": "Jackline Joy",
        "gender": "Female",
        "country": "Sri Lanak"
    },
    {
        "name": "Alu Arjun",
        "gender": "Male",
        "country": "India"
    },
    {
        "name": "Kavitha Kumar",
        "gender": "Female",
        "country": "India"
    },
    {
        "name": "John Snow",
        "gender": "Male",
        "country": "United Kingdom"
    },
    {
        "name": "Priya kanana",
        "gender": "Female",
        "country": "India"
    },
    {
        "name": "Shri Devi",
        "gender": "Female",
        "country": "Sri Lanka"
    },
    {
        "name": "Richard Roy",
        "gender": "Male",
        "country": "France"
    },
    {
        "name": "Sonu Nigam",
        "gender": "Male",
        "country": "India"
    },
    {
        "name": "Priya Dutt",
        "gender": "Female",
        "country": "USA"
    },
    {
        "name": "Will Smith",
        "gender": "Male",
        "country": "USA"
    },
    {
        "name": "Jackline Joy",
        "gender": "Female",
        "country": "Sri Lanak"
    },
    {
        "name": "Alu Arjun",
        "gender": "Male",
        "country": "India"
    },
    {
        "name": "Kavitha Kumar",
        "gender": "Female",
        "country": "India"
    },
    {
        "name": "John Snow",
        "gender": "Male",
        "country": "United Kingdom"
    },
    {
        "name": "Priya kanana",
        "gender": "Female",
        "country": "India"
    },
    {
        "name": "Shri Devi",
        "gender": "Female",
        "country": "Sri Lanka"
    },
    {
        "name": "Richard Roy",
        "gender": "Male",
        "country": "France"
    },
    {
        "name": "Sonu Nigam",
        "gender": "Male",
        "country": "India"
    },
    {
        "name": "Priya Dutt",
        "gender": "Female",
        "country": "USA"
    },
    {
        "name": "Will Smith",
        "gender": "Male",
        "country": "USA"
    },
    {
        "name": "Jackline Joy",
        "gender": "Female",
        "country": "Sri Lanak"
    },
    {
        "name": "Alu Arjun",
        "gender": "Male",
        "country": "India"
    },
    {
        "name": "Kavitha Kumar",
        "gender": "Female",
        "country": "India"
    },
    {
        "name": "John Snow",
        "gender": "Male",
        "country": "United Kingdom"
    },
    {
        "name": "Priya kanana",
        "gender": "Female",
        "country": "India"
    },
    {
        "name": "Shri Devi",
        "gender": "Female",
        "country": "Sri Lanka"
    },
    {
        "name": "Richard Roy",
        "gender": "Male",
        "country": "France"
    },
    {
        "name": "Sonu Nigam",
        "gender": "Male",
        "country": "India"
    },
    {
        "name": "Priya Dutt",
        "gender": "Female",
        "country": "USA"
    },
    {
        "name": "Will Smith",
        "gender": "Male",
        "country": "USA"
    },
    {
        "name": "Jackline Joy",
        "gender": "Female",
        "country": "Sri Lanak"
    },
    {
        "name": "Alu Arjun",
        "gender": "Male",
        "country": "India"
    },
    {
        "name": "Kavitha Kumar",
        "gender": "Female",
        "country": "India"
    },
    {
        "name": "John Snow",
        "gender": "Male",
        "country": "United Kingdom"
    },
    {
        "name": "Priya kanana",
        "gender": "Female",
        "country": "India"
    },
    {
        "name": "Shri Devi",
        "gender": "Female",
        "country": "Sri Lanka"
    },
    {
        "name": "Richard Roy",
        "gender": "Male",
        "country": "France"
    },
    {
        "name": "Sonu Nigam",
        "gender": "Male",
        "country": "India"
    },
    {
        "name": "Priya Dutt",
        "gender": "Female",
        "country": "USA"
    }
]

We are also adding an image in our PDF document, let’s create folder images in asset and add your image. In our case, we add some dummy color logos in src/assets/images/logo.png

Angular pdf generator example

We need to have models for our student data, let’s create a folder name called model in the app/models and add a file called student.ts file. Edit app/models/student.ts file.

export interface IStudent {
    name: string;
    gender: string;
    country: string;
}

We have to import the HTTClient module for the HTTP GET method to retrieve our student data for the Angular pdf generator example. Edit app.module.ts file.

import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
import { HttpClientModule } from '@angular/common/http';

@NgModule({
  declarations: [
    AppComponent
  ],
  imports: [
    BrowserModule,
    AppRoutingModule,
    HttpClientModule
  ],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule { }

Example of Angular pdf generator

Once we have installed and configured our project and we need to create two services, one for angular pdf generator and one for canvas service to convert images to base64.

ng generate services/pdf
ng generate services/canvas

The Pdf service allows us to export pdf from our data, in the PDF document we have three sections: header, footer, and body. In the header we have a logo and text, the header can include the company name and address. In Pdf footer have the page number of the PDF document and the PDF body contain actually data.

import { Injectable } from '@angular/core';
import pdfMake from 'pdfmake/build/pdfmake';
import pdfFonts from 'pdfmake/build/vfs_fonts';
pdfMake.vfs = pdfFonts.pdfMake.vfs;
import { CanvasService } from './canvas.service';

import { IStudent } from '../models/student';

@Injectable({
  providedIn: 'root'
})
export class PdfService {

  constructor(private canvas: CanvasService) { }

  /** Method to set PDF Title */
  setTitle(logo: any, title: string, page: any, pageSize: string): any {
    // let companyDetail = '';
    let margin;
    let fontSize;

    if (pageSize === 'A6') {
      margin = [15, 10, 0, 10];
      fontSize = 8;
    } else {
      margin = [15, 10, 0, 10];
      fontSize = 10;
    }

    return [
      {
        table: {
          widths: ['35%', '65%'],
          body: [
            [{
              image: logo,
              width: 60,
              height: 30,
            }],
            [{ text: title, fontSize, margin: [100, -20, 0, 0] }],
          ],
        },
        layout: 'noBorders',
      },

    ];
  }

  /**  Method for creating PDF table header */
  createTableHeader(header: string[]): any {
    const pageHeader = { fila_0: {} };
    header.forEach((attribute, i) => {
      pageHeader.fila_0['col_' + (+i + 1)] = { text: attribute, style: 'tableHeader', margin: [0, 8, 0, 0] };
    });
    return pageHeader;
  }

  /** Method for creating PDF table content */
  createBody(headers: object, records: object[]): any {
    const body = [];
    // tslint:disable-next-line:forin
    for (const key in headers) {
      const row = [];
      // tslint:disable-next-line: forin
      for (const headerKey in headers[key]) {
        row.push(headers[key][headerKey]);
      }
      body.push(row);
    }

    records.forEach((record) => {
      const row = [];
      // tslint:disable-next-line: forin
      for (const key in record) {
        row.push(record[key]);
      }
      body.push(row);
    });
    return body;
  }

  /** Define PDF document definition */
  // tslint:disable-next-line: max-line-length
  getDocDefinition(title: string, logo: any, header: string[],
    record: object[], orientation: string, columns: string[], pageSize?: string): any {

    const page = {
      header: title,
      body: { header, record },
    };
    const body = this.createBody(this.createTableHeader(page.body.header), page.body.record);

    let pageMargin;
    let headerFont;
    let contentFont;
    if (pageSize === 'A6') {
      pageMargin = [10, 110, 10, 55];
      headerFont = 10;
      contentFont = 7;
    } else {
      headerFont = 16;
      contentFont = 10;
      pageMargin = [10, 110, 10, 55];
    }
    const docDefinition = {
      pageOrientation: orientation,
      pageSize,
      pageMargins: pageMargin,
      header: this.setTitle(logo, title, page, pageSize),
      footer: (currentPage, pageCount) => {
        return { text: 'Page ' + currentPage.toString() + ' of ' + pageCount, alignment: 'center', margin: [0, 30, 0, 0] };
      },

      content: [
        {
          margin: [10, -40, 10, 10],
          style: 'tableContent',
          table: {
            widths: columns,
            headerRows: 1,
            body,
          },
        },
      ],
      styles: {
        header: {
          fontSize: headerFont,
          bold: true,
        },
        tableHeader: {
          bold: true,
        },
        tableContent: {
          fontSize: contentFont,
        },
      },
    };
    return docDefinition;
  }

  generatePdf(docDefinition): void {
    const pdfObject = pdfMake.createPdf(docDefinition);
    // On a browser simply use download!
    pdfObject.download();
  }

  getPdfData(data: IStudent[]): void {
    const columns = ['40%', '15%', '45%'];
    const header = ['Name', 'Gender', 'Counter'];


    const title = 'Student Information';
    // tslint:disable-next-line: max-line-length
    this.canvas.getBase64Image('./assets/images/logo.png')
      .then(base64Img => {
        const logo = base64Img;
        this.generatePdf(this.getDocDefinition(title, logo, header, this.mapData(data), 'portrait', columns, 'A4'));
      });

  }

  // tslint:disable-next-line: typedef
  mapData(data: IStudent[]): any {
    return data.map((item) => {
      return {
        Name: item.name,
        Gender: item.gender,
        Country: item.country
      };
    });
  }
}

To display an image in PDF make, we have canvas service to convert image to base64 which we have shown in the pdf document. Edit canvas.service.ts file. 

import { Injectable } from '@angular/core';

@Injectable({
  providedIn: 'root'
})
export class CanvasService {

  constructor() { }

  // Convert image url in assets to b64
  getBase64Image(url: string): Promise<any> {
    return new Promise((resolve) => {
      const image = new Image();
      const outputFormat = 'image/png';
      image.crossOrigin = 'Anonymous';
      image.onload = () => {
        // tslint:disable-next-line: no-angle-bracket-type-assertion
        let canvas = <HTMLCanvasElement>document.createElement('CANVAS');
        // let canvas = document.createElement('CANVAS') as HTMLCanvasElement;
        const context = canvas.getContext('2d');
        let dataURL;
        canvas.height = image.height;
        canvas.width = image.width;
        context.drawImage(image, 0, 0);
        dataURL = canvas.toDataURL(outputFormat);
        canvas = null;
        resolve(dataURL);
      };
      image.src = url;
    });
  }
}

Edit our component to retrieve and create a Table
Let’s first retrieve our students’ JSON data and display the data in table formats. Edit app.component.ts file.

import { HttpClient } from '@angular/common/http';
import { Component, OnInit } from '@angular/core';
import { IStudent } from './models/student';
import { PdfService } from './services/pdf.service';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.scss']
})
export class AppComponent implements OnInit {
  title = 'pdf';
  students: IStudent[];

  constructor(private http: HttpClient, private pdf: PdfService) { }

  ngOnInit(): void {
    this.http.get('./assets/data/students.json').subscribe((response: IStudent[]) => {
      this.students = response;
    });
  }

  exportToPDF(): void {
    this.pdf.getPdfData(this.students);
  }
}

In the app-component template, we have a button called export to PDF. When we click on this button, it will be called our pdf service to convert our data to PDF documents and download the file to the client-side. In the app-component template, we have a button called export to PDF. When we click on this button, it will be called our pdf service to convert our data to PDF documents and download the file to the client-side. 

<router-outlet></router-outlet>
<div style="margin:0 30px;">
  <h2 style="text-align: center;">Angular datatable export to PDF</h2>
  <button style="float: right; margin-bottom: 10px;" (click)="exportToPDF()">Export to PDF</button>
  <table>
    <tr>
      <th>S.no</th>
      <th>Name</th>
      <th>Gender</th>
      <th>Country</th>
    </tr>
    <tr *ngFor="let student of students; index as i">
      <td>{{ i }}</td>
      <td>{{ student.name }}</td>
      <td>{{ student.gender }}</td>
      <td>{{ student.country }}</td>
    </tr>
  </table>
</div>

Edit app.component.scss file to apply style for our table.

table {
    font-family: arial, sans-serif;
    border-collapse: collapse;
    width: 100%;
  }
  
  th {
    background-color: red;
    border: 1px solid red;
  }
  td {
    border: 1px solid #dddddd;
  }
  td, th {
    text-align: left;
    padding: 8px;
  }
  
  tr:nth-child(even) {
    background-color: #dddddd;
  }

Style our Pdf document
PdfMake library is flexible and easy to use, we can use paragraphs, columns, lists, tables, canvas, etc and declare your own styles, and use custom fonts. The PdfMake has an online page, that allows us to play online with their PDF example.

styles: {
	header: {
		fontSize: 18,
		bold: true,
		alignment: 'justify' //left|right
		margin: [0, 190, 0, 80] //left, top, right, bottom
	},
	subheader: {
		fontSize: 15,
		bold: true 
	},
	quote: {
		italics: true
	},
	small: {
		fontSize: 8
	}
}

Conclusion
In this tutorial, we have completed our angular Pdf export demo in Angular 10 using the PdfMake library. Check our demo code for Angular Pdf export in GitHub for source code.

Related posts

How to implement Angular pdf generator with PdfMaker in Angular 11|12?

Leave a Reply

Your email address will not be published. Required fields are marked *

Scroll to top