Creating customisable & beautiful PDFs using jsPDF API , AEM and Angular
Hi All,
I recently came across a requirement wherein I had to generate statements of account in PDF format. In this article, you will be able to learn one way in which you can do the same using two amazing technologies namely: AEM and Angular.
So what are we waiting for! Let’s get started.
What are the options available? 🤔
There are two major APIs that assist in creating PDF from Angular:
I will walk you through the second one since I had to create PDFs from html tables with header and footer and jsPDF-autoTable provides quite a convenient way to achieve the same. I hope you will explore the other one on your own.
How do we get started? 🔰
As mentioned in the official github link, you can get the library into your project using any of the following:
npm install jspdf jspdf-autotable
- Download jspdf and jspdf-autotable from github
- Use a CDN, for example: https://unpkg.com/jspdf and https://unpkg.com/jspdf-autotable
What next? ⏭️
Now for this to work in AEM, you can either:
- Keep the dependency in your Angular package.json and ensure that it gets built as part of your Angular project which then gets copied into an AEM clientlibrary folder for the application. (Recommended since you no longer have to keep on updating the raw files and it will be taken care of automatically.)
- Or you can copy the raw files for jspdf.min.js and jspdf.plugin.autotable.js from the same github link and place those into an AEM clientlibrary folder. Use this folder as a dependency wherever PDF creation is needed as shown below:
Including the pdf library in other clientlibrary folder:
There you go! The necessary library files will now be available to your scripts wherever you include the training.components.statement clientlibrary call.
The important part 📑
Note: For the code you can simply explore examples.js or the demo link provided in the github link for the same. I will be explaining you the basic differences of the various approaches.
To initialize a new pdf object you need to do the following:
var doc = new jsPDF();
Now let us explore on basic use cases for the PDF generation:
Plain text in PDF:
doc.text(‘Text to be printed in PDF’, start-x-axis-position-from-left, start-y-axis-position-from-top);
e.g.
var doc = new jsPDF();doc.text(“This is basic text”, 14, 15);
PDF from a html table:
Give the html table an id or a class so as to identify the same.
<table id=”my-table”>…….</table>
Now in jsPDF:
Just initialize an autoTable object with html attribute set to the value of the identifier.
e.g.
var doc = new jsPDF();doc.autoTable({html:”#my-table”}); //as simple as that!
Text relative to an already printed autoTable:
Suppose you have created an autotable already and you want a text below the table. You can do that using:
e.g.
var doc = new jsPDF();doc.autoTable({html:”#my-table”});let finalY = doc.previousAutoTable.finalY; //this gives you the value of the end-y-axis-position of the previous autotable.doc.text(“Text to be shown relative to the table”, 12, finalY + 10);
Print an Image in the PDF:
This is a bit complex and not straightforward as adding a text. I was trying to add an SVG image and having a hard time getting it to work. Thanks to a teammate of mine I got to know that SVGs are not supported and you have to first convert them to either JPEG or PNG using an online tool like https://image.online-convert.com.
Next you need to convert the JPEG/PNG image into a datauri object using an online tool like the one provided by dopiaza or sveinbjorn.
Once you have the datauri, save it in a var and use addImage function of jsPDF as shown below:
var doc = new jsPDF();var img = “data:image/jpg;base64,jtSEjBYHkTNc5E………………………”;/* addImage explained below:
param 1 -> image in code format
param 2 -> type of the image. SVG not supported. needs to be either PNG or JPEG.
param 3 -> X axis margin from left
param 4 -> Y axis margin from top
param 5 -> width of the image
param 6 -> height of the image
*/doc.addImage(img, ‘JPEG’, 150, 10, 40, 20); //specify the image format in the function. Can be one among JPEG/PNG.
Define Header and Footer in PDF:
The header and footer are set using the didDrawPage function of jsPDF which is used to detect if a complete page has been drawn or not. We have already seen above how to use html attribute to render the PDF body. In this section we will see how to use head and body attributes of autoTable.
Note that head and body define the head and body of the table and not the header or footer of the page.
Which means any custom styling that you write will be applied to the head or body of the table and not the page header or footer.
e.g.
// toPdf function to print the pdfBody which is an array of jsonobjects holding the table data into pdf.
ctrl.toPdf = function(pdfBody) {
var doc = new jsPDF();
var totalPagesExp = "{total_pages_count_string}"; //placeholder for total number of pages
doc.autoTable({
styles: {
cellPadding: 0.5,
fontSize: 12
},
//startY: 30, /* if start position is fixed from top */
tableLineColor: [0, 0, 0], //choose RGB
tableLineWidth: 0.5, //table border width
head: headRows(), //define head rows
body: bodyRows(pdfBody.length, pdfBody),
/*first param is the number of rows in total and second param is the actual content. This is the actual body of the pdf between the header and footer*/
bodyStyles: {
margin: 40,
fontSize: 10,
lineWidth: 0.2,
lineColor: [0, 0, 0]
},
/*whatever you write in didDrawPage comes in every page. Header or footer is determined from startY position in the functions.*/
didDrawPage: function(data) {
// Header
doc.setFontSize(12);
var fileTitle = "Test document";
var img = "data:image/jpg;base64,jtSEjBYHkTNc5E………………………"; //use addImage as explained earlier.
doc.text(fileTitle, 14, 35);
doc.addImage(img, 'JPEG', 157, 10, 40, 20);
// Footer
var pageSize = doc.internal.pageSize;
//jsPDF 1.4+ uses getHeight, <1.4 uses .height
var pageHeight = pageSize.height ? pageSize.height : pageSize.getHeight();
// jsPDF 1.4+ uses getWidth, <1.4 uses .width
var pageWidth = pageSize.width ? pageSize.width : pageSize.getWidth();
doc.autoTable({
html: '#footer_text_pdf',
startY: pageHeight - 50,
styles: {
halign: 'center',
cellPadding: 0.2
}
});
var str = "Page " + doc.internal.getNumberOfPages()
// Total page number plugin only available in jspdf v1.0+
if (typeof doc.putTotalPages === 'function') {
str = str + " of " + totalPagesExp;
}
doc.setFontSize(10);
doc.text(str, data.settings.margin.left, pageHeight - 10);
},
margin: {
bottom: 60, //this decides how big your footer area will be
top: 40 //this decides how big your header area will be.
}
});
// Total page number plugin only available in jspdf v1.0+
if (typeof doc.putTotalPages === 'function') {
doc.putTotalPages(totalPagesExp);
}
fileName = 'Statement.pdf'
doc.save(fileName); //this downloads a copy of the pdf in your local instance.
};
Let’s look at the headRows and bodyRows function:
/*define key for identifying column and value to display in PDF table head. (Table head and not page header) */
function headRows() {
return [{
id: 'ID',
name: 'Name',
email: 'Email',
city: 'City',
expenses: 'Sum'
}];
}/*define key for identifying column and value to display in PDF table footer. (Table footer and not page footer)*/function footRows() {
return [{
id: 'ID',
name: 'Name',
email: 'Email',
city: 'City',
expenses: 'Sum'
}];
}/* bodyRows returns the pdfData in the form of jsonArray which is then parsed by the autoTable function */function bodyRows(rowCount, pdfBody) {
rowCount = rowCount || 10;
let body = [];
for (var j = 0; j < rowCount; j++) {
body.push({
id: j+1,
name: pdfBody[j].name,
email: pdfBody[j].email,
city: pdfBody[j].city,
expenses: pdfBody[j].expenses,
});
}
return body;
}
You can apply styling and change font as shown below:
examples.custom = function() {
var doc = new jsPDF();
doc.setFontSize(12);
doc.setFontStyle('arial');doc.autoTable({
head: head,
body: body,
startY: 15, /* apply styling to table body */
bodyStyles: {
valign: 'top'
}, /* apply global styling */
styles: {
cellWidth: 'wrap',
rowPageBreak: 'auto',
halign: 'justify'
}, /* apply styling specific to table columns */
columnStyles: {
text: {
cellWidth: 'auto'
}
}
});return doc;
};
There are three out of the box themes for the PDF:
examples.themes = function() {
var doc = new jsPDF();
doc.setFontSize(12);
doc.setFontStyle('bold'); doc.text('Theme "striped"', 14, 16); //default
doc.autoTable({
head: headRows(),
body: bodyRows(5),
startY: 20
}); doc.text('Theme "grid"', 14, doc.autoTable.previous.finalY + 10);
doc.autoTable({
head: headRows(),
body: bodyRows(5),
startY: doc.autoTable.previous.finalY + 14,
theme: 'grid'
}); doc.text('Theme "plain"', 14, doc.autoTable.previous.finalY + 10);
doc.autoTable({
head: headRows(),
body: bodyRows(5),
startY: doc.autoTable.previous.finalY + 14,
theme: 'plain'
});return doc;
};
There is a lot more that you can do with the jsPDF-autoTable plugin and this is only a kick-starter that I have provided.
Go ahead and make your own beautiful PDFs! 🏃
But before you leave, please shower your love by 👏 for this article in case it made a difference. For any ❓ or suggestions, please use the comments section. If not me, someone else might be able to help you out.