How to create a PDF file in Ionic apps using PouchDB
In Short:
You will learn how to create a PDF file based on data from PouchDB and print the file with cordova-plugin-printer.
Pre-Condition:
For creating a PouchDB instance and inserting data, check out the following tutorial:
How it works
Step 1
Create an ionic angular app or use an existing one. For creating a new one you can use the "tabs" template.
ionic start myApp tabs
Step 2
Install the following dependencies
npm install pdfmake --save
ionic cordova plugin add cordova-plugin-file
npm install @ionic-native/file
ionic cordova plugin add cordova-plugin-printer
npm install @ionic-native/printer
Step 3
Import the dependencies installed before and create a global pdfMake object
import {Printer} from '@ionic-native/printer/ngx';
import {File} from '@ionic-native/file/ngx';
@NgModule({
...
providers: [
File,
Printer,...
]
})
import pdfMake from 'pdfmake/build/pdfmake';
import pdfFonts from 'pdfmake/build/vfs_fonts';
import { File } from '@ionic-native/file/ngx';
import {CouchApiService} from '../providers/couch-api.service';
pdfMake.vfs = pdfFonts.pdfMake.vfs;
...
export class OurPage {
pdfObj = null;
constructor(private couchApiService: CouchApiService,
private file: File,
private printer: Printer) {
}
}
Step 4
Create a method for a PDF layout. For displaying the stored data of our PouchDB a HTML table is used.
The stored data will be added to the template in an additional method (next step).
private createTemplate() {
const docDefinition = {
content: [
{text: 'Our Title', style: 'header'},
{text: new Date().toLocaleString(), alignment: 'right'},
{text: 'Our Subtitle ', style: 'subheader'},
{text: '', style: 'story', margin: [0, 20, 0, 20]},
{
table: {
// headers are repeated if the table spans over multiple pages
// you can declare how many rows should be treated as headers e.g. 1
headerRows: 1,
widths: [150, '*', '*', '*', 150],
body: [
[{text: 'Date', style: 'tableHeader'}, {text: 'Table Name 2', style: 'tableHeader'}, {text: 'Table Name 3', style: 'tableHeader'}, {text: 'Table Name 4', style: 'tableHeader'}, {text: 'Table Name 5', style: 'tableHeader'}]
]
},
layout: {
// we will specify a layout for our table
fillColor: (rowIndex, node, columnIndex) => {
return (rowIndex % 2 === 0) ? '#CCCCCC' : null;
},
hLineWidth: (i, node) => {
return (i === 0 || i === node.table.body.length) ? 2 : 1;
},
vLineWidth: (i, node) => {
return (i === 0 || i === node.table.widths.length) ? 2 : 1;
},
}
},
{text: '©AppIT Online - ' + new Date().getFullYear(), style: 'story', margin: [0, 0, 0, 20]},
],
styles: {
//additional styles
header: {
fontSize: 18,
bold: true,
},
subheader: {
fontSize: 14,
bold: true,
margin: [0, 15, 0, 0]
},
story: {
italic: true,
alignment: 'center',
width: '50%',
},
tableHeader: {
bold: true,
fontSize: 13,
color: 'black'
}
}
};
return docDefinition;
}
Step 5
After creating the PDF template the PouchDB content needs to be added to the HTML table.
private generatePDF(docDefinition: any) {
this.couchApiService.read('our_database_name')
.then((entries: any) => {
for (const entry of entries) {
docDefinition.content[4].table.body.push(
[new Date(entry.time).toLocaleString(),
entry.column2,
entry.column3,
entry.column4,
entry.column5 || ''
]);
}
this.pdfObj = pdfMake.createPdf(docDefinition);
this.pdfObj.getBuffer((buffer) => {
const blob = new Blob([buffer],
{type: 'application/pdf'});
//after creating the pdf we will write on the file storage
this.getStoragePath()
.then((url) => {
this.file.writeFile(url,
'appit-filename.pdf', blob, {replace: true})
.then(() => {
//after that we will print the pdf
this.print();
}).catch((error) => {
console.log(error);
});
})
.catch((error) => {
console.log(error);
});
});
})
.catch((error) => {
console.log(error);
});
}
Step 6
Then a method for printing the PDF is needed. Therefore the "cordova-plugin-printer" is used.
private async print() {
this.printer.isAvailable()
.then(() => {
this.getStoragePath().then( (url) => {
const options: PrintOptions = {
name: 'Name',
duplex: true
};
this.printer.print(
url + '/' + 'appit-filename.pdf',
options
);
})
.catch((error) => {
console.log(error);
});
})
.catch((error) => {
console.log(error);
});
}
Step 7
In the steps before the "getStoragePath" method was used for receiving the file path, where the file will be persisted.
private getStoragePath() {
const file = this.file;
return this.file.resolveDirectoryUrl(this.file.dataDirectory)
.then( (directoryEntry) => {
return file.getDirectory(
directoryEntry,
'our_root_folder',
{
create: true,
exclusive: false
})
.then( () => {
return directoryEntry.nativeURL + 'our_root_folder/';
});
});
}
Step 8
Finally lets put it all together
//UI button
<button class="btn btn-primary my-4" type="button" (click)="exportPDF()">
Print PDF
</button>
// our-page.ts
async exportPDF() {
const docDefinition = this.createTemplate();
this.generatePDF(docDefinition);
}
Step 9 (Optional)
The CouchAPI Service is explained in this tutorial:
https://blog.appit-online.de/background-geolocation-tracking-with-ionic-4/
The "read" method is defined as follows
read(name) {
const db = new PouchDB(name + '.db',
{
auto_compaction: true,
adapter: 'cordova-sqlite',
iosDatabaseLocation: 'Library',
androidDatabaseImplementation: 2
}
);
return new Promise((resolve, reject) => {
db.allDocs({
include_docs: true
}).then((result) => {
this.data = [];
result.rows.map((row) => {
if (row.doc._id) {
this.data.push(row.doc);
}
});
resolve(this.data);
}).catch((error) => {
console.log(error);
reject(error);
});
});
}