So you've got an Ionic app and you need to create PDFs from within the app. You don't want to offload to a server for PDF generation/download because the PDF is user specific and unique each time it is generated, so why not just generated it client side? For one, that was near impossible a number of years ago. Two, you've tried before and it just never worked right. Well, I managed to get PDF generation in an Ionic app and wanted to share my experience so you can do the same.
What we are going to do is capture a portion of the HTML, turn it into a PDF, and trigger the download for the user.
The first step is to add the required libraries:
Cordova Plugins:
"cordova-plugin-file-transfer"
"cordova-plugin-file-opener2"
index.html (install via bower or point to CDN):
<script src="lib/jspdf/dist/jspdf.min.js"></script>
<script src="lib/html2canvas/dist/html2canvas.js"></script>
Next, we create the PDF generation function:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
| $scope.generatePDF = function (method) {
//set the margins for the PDF
var margins = {
top: 40,
left: 40
};
// function to create a hidden clone of the target element,
// allowing manipulation specific for the PDF output without changing the actual target element.
var hiddenClone = function (element) {
document.documentElement.style.overflow = 'visible';
document.body.style.overflow = 'visible';
// Create clone of element
var clone = element.cloneNode(true);
// Position element relatively within the
// body but still out of the viewport
var style = clone.style;
style.position = 'relative';
style.top = window.innerHeight + 'px';
style.left = 0;
style.width = '535px';
style.zIndex = '9999';
// Append clone to body and return the clone
document.body.appendChild(clone);
return clone;
}
// now create the clone of your target element and send it to html2canvas
var html2Pdf = hiddenClone(document.getElementById('pdfTarget'));
html2canvas(html2Pdf, {
onrendered: function (canvas) {
// once the canvas is rendered, create new jsPDF object and working variables
var doc = new jsPDF('p', 'pt', 'letter');
var dataUrl = canvas.toDataURL('image/jpeg');
doc.addImage(dataUrl, margins.top, margins.left, canvas.width, canvas.height);
var dataUrlPdf = doc.output();
var dataUrlPdfBase64 = 'data:application/pdf;base64,' + btoa(dataUrlPdf);
// once we've got the canvas, we can remove the clone element and reset styles
document.body.removeChild(html2Pdf);
document.documentElement.style.overflow = 'hidden';
document.body.style.overflow = 'hidden';
var filename = 'PDF-' + $filter('date')(new Date(), 'yyyy-M-d') + '.pdf';
// Save location
var targetPath = cordova.file.externalRootDirectory + filename;
// use the cordova file transfer plugin to send the file to the cordova file opener plugin,
// allowing user to pick download method (only seems to work on Android)
$cordovaFileTransfer.download(dataUrlPdfBase64, targetPath, {}, true).then(function (fileEntry) {
console.log('Success');
console.log(fileEntry);
fileEntry.file(function (file) {
cordova.plugins.fileOpener2.open(
file.localURL,
file.type, {
error: function (e) {
console.log('Error status: ' + e.status + ' - Error message: ' + e.message);
},
success: function () {
console.log('file opened successfully');
}
}
);
});
}, function (error) {
console.log('Error');
console.log(error);
}, function (progress) {
// PROGRESS HANDLING GOES HERE
});
}
});
};
|
Now you can run the function and try it out...
Hopefully you'll have successful client side PDF generation from HTML. I will say these plugins/libraries are touchy, especially when used together but this formula worked for me. Best of luck and feel free to share your implementations and feedback!