Html2canvas for each div export to pdf separately - javascript

I have Page, It has 6 div with same class name "exportpdf", I am converting those div into pdf using jspdf and html2canvas
var elementTobePrinted = angular.element(attrs.selector),
iframeBody = elementTobePrinted.contents().find('div.exportpdf');
In html2canvas.....
html2canvas(elementTobePrinted, {
onrendered: function (canvas) {
var doc = new jsPDF();
for(var i=1;i<elementTobePrinted.length;i++){
doc.addImage(canvas.toDataURL("image/jpeg"), 'jpeg', 15, 40, 180, 160);
doc.addImage(canvas.toDataURL("image/jpeg"),'JPEG', 0, 0, 215, 40)
doc.addPage();
}
doc.save(attrs.fileName);
}
I converted page to canvas.its create same div contents for whole pdf. I need each div contents into same pdf with different pages.
Can anyone help me?
The problem is with html2canvas:
doc.addImage(canvas.toDataURL("image/jpeg"), 'jpeg', 15, 40,180, 160);
Here I need to pass elementTobePrinted list to addImage.

On angular 2(now 6) framework.
u can use the logic as below,
On click execute generateAllPdf() function,
gather all 6 id's from my html collection,
iterate through each id and call html2canvas function,
as html2canvas runs in background to process images, i m using
await on function,
after the html2canvas completes its process, i ll save the
document,
If suppose i wont use await i ll end-up in downloading an empty
page.
below is my code logic,
// As All Functions in js are asynchronus, to use await i am using async here
async generateAllPdf() {
const doc = new jsPDF('p', 'mm', 'a4');
const options = {
pagesplit: true
};
const ids = document.querySelectorAll('[id]');
const length = ids.length;
for (let i = 0; i < length; i++) {
const chart = document.getElementById(ids[i].id);
// excute this function then exit loop
await html2canvas(chart, { scale: 1 }).then(function (canvas) {
doc.addImage(canvas.toDataURL('image/png'), 'JPEG', 10, 50, 200, 150);
if (i < (length - 1)) {
doc.addPage();
}
});
}
// download the pdf with all charts
doc.save('All_charts_' + Date.now() + '.pdf');
}

I think the issue here is that elementTobePrintedis not what you think it is.
When you run the code:
var elementTobePrinted = angular.element(attrs.selector)
This will return you a list of every element that matches the conditions, so you said you have 6 of these elements ("It has 6 divs").
Have you tried replacing:
html2canvas(elementTobePrinted, {
onrendered: function (canvas) {
var doc = new jsPDF();
for(var i=1;i<elementTobePrinted.length;i++) {
doc.addImage(canvas.toDataURL("image/jpeg"), 'jpeg', 15, 40, 180, 160);
doc.addImage(canvas.toDataURL("image/jpeg"),'JPEG', 0, 0, 215, 40)
doc.addPage();
}
doc.save(attrs.fileName);
}
With...
for(var i=0; i<elementTobePrinted.length; i++){
html2canvas(elementTobePrinted[i], {
onrendered: function (canvas) {
var doc = new jsPDF();
doc.addImage(canvas.toDataURL("image/jpeg"), 'jpeg', 15, 40, 180, 160);
doc.addImage(canvas.toDataURL("image/jpeg"),'JPEG', 0, 0, 215, 40)
doc.addPage();
doc.save(attrs.fileName);
}
}
The reason I suggest this is that html2Canvas wants a SINGLE element as its first parameter and your example above passes a list of elements (I think, assuming angular.element(attrs.selector) finds all 6 divs you are trying to print).

Related

jspdf-autotable styles not working in live website

I am trying to set a custom font inside styles{} inside doc.autoTable({ }). This is my code:
let doc = new jsPDF();
doc.autoTable({
html: '#news_pos',
startX: 10,
startY: 15,
theme: 'grid',
bodyStyles: {lineColor: [0, 0, 0]},
styles: {
font: "SakalBharati", //<------ This is the font
fontSize: 10,
cellWidth: 'auto',
halign: 'center',
fillColor: [225, 197, 238]
},
})
doc.save("file.pdf");
The above code is working perfectly when I run it in localhost. But now, when I've uploaded it into my live website, strangely, it's not working. The code below works if I apply the font to a standard text (in both localhost and live website) like:
doc.setFont("SakalBharati","normal");
doc.text(10,15,"पूसीरे के");
/*This code works perfectly in both local and live server. Only
when I apply the font in jspdf-autotable (in live server),
it doesn't work*/
Localhost output:
Live website output:
Why is this happening? How do I fix this?
EDIT: I even tried converting the text to an image and appending it to the cell in my HTML table:
var tCtx = document.getElementById('textCanvas').getContext('2d'),
imageElem = document.getElementById('image');
tCtx.canvas.width = tCtx.measureText("पूसीरे के रेसुब ने नाबालिगों").width;
tCtx.fillText("पूसीरे के रेसुब ने नाबालिगों", 0, 10);
imageElem.src = tCtx.canvas.toDataURL();
htmltable_cell.appendChild(imageElem);
And then appending this image into my table cell in autotable:
doc.autoTable({
html: '#news_pos',
startX: 10,
startY: 15,
theme: 'grid',
didDrawCell: function(hookData) {
for(var j=1; j<document.getElementById("news_pos").rows.length; j++){
for(var k=0; k<document.getElementById("news_pos").rows[j].cells.length; k++){
if(document.getElementById("news_pos").rows[j].cells.item(k).innerHTML.includes("img")){
if(hookData.column.index == k && hookData.row.index == j){
var td = hookData.cell.raw;
var img = td.getElementsByTagName('img')[0];
var dim = hookData.cell.height - hookData.cell.padding('vertical');
doc.addImage(img.src, hookData.cell.x+4, hookData.cell.y+1, 80,dim+2);
}
}
}
}
},
bodyStyles: {lineColor: [0, 0, 0]}
})
This too gives the same output as the screenshots shared above

html2pdf.js produces a blank document on iOS

I am using eKoopman's html2pdf.js library https://github.com/eKoopmans/html2pdf.js to produce a list of recommendation for a user at the end of an assessment. These are dynamically generate based on the responses.
It works wonderfully on desktop. However when I try to output the PDF on iOS the document is blank except for the headers and footers that I add in a separate function.
Its like it completely ignores the element.
Below is the code I trigger when the user clicks a button.
var element = $('#recommendationsdisplay').html();
var opt = {
margin: [.75,0,.75,0],
filename: 'Click_Start_' + Math.floor(Date.now() / 10000) + '.pdf',
enableLinks: true,
image: {
type: 'jpeg',
quality: 1
},
html2canvas: {
scale: 2,
dpi: 300,
letterRendering: true
},
jsPDF: {
unit: 'in',
format: 'letter',
orientation: 'portrait'
},
pagebreak:{
mode: ['avoid-all', 'css', 'legacy'],
avoid: 'div.recgrid-item'
}
};
html2pdf().from(element, 'string').set(opt).toPdf().get('pdf').then(function (pdfObject) {
/* some image related encoding */
var headerTitle = "Recommendations";
var footerCR = "© 2020";
// Header and Footer
for (var i = 1; i < pdf_pages.length; i++) {
pdfObject.setPage(i);
pdfObject.setFontSize(14);
pdfObject.setTextColor('#0090DA');
pdfObject.addImage(headerData, 'PNG', 0, 0, 8.5, .5);
pdfObject.setFontSize(10);
pdfObject.setTextColor('#777777');
pdfObject.text(footerCR, 4, 10.5);
pdfObject.text(' ' + i, 7.375, 10.5);
pdfObject.addImage(logoData, 'PNG', .75, 10.25, 1, .325);
}
}).save();
EDIT: It seems like the issue is with the canvas size limitation. The element is rendered properly in the PDF if its height is NOT above a certain threshold (fewer items chosen in the assessment). My document is only a few pages long (<7)though and I have seen other users report being able to create PDFs with dozens of pages so I am not sure what the issue is.
pagebreak mode = avoid-all causing the problem. Take away 'avoid-all' .
I have resolved this issue by splitting the whole document into pages and then passing pages one by one to the html2pdf library to generate and bind all pages into a single pdf file.
Example code.
var opt = {
margin: [5, 10, 0.25, 10],
pagebreak: {mode: 'css', after: '.page2el'},
image: {type: 'jpeg', quality: 1},
filename: 'testfile.pdf',
html2canvas: {dpi: 75, scale: 2, letterRendering: true},
jsPDF: {unit: 'pt', format: 'letter', orientation: 'p'},
};
var count = 1;
let doc = html2pdf().set(opt).from(document.getElementById('page2el')).toPdf();
jQuery("#cs_pdf").find("div").each(function (e) {
// Filtering document each page with starting id with page2el
if (jQuery(this).attr('id') && jQuery(this).attr('id').indexOf('page2el') != -1) {
if (count != 1) {
doc = doc.get('pdf').then((pdf) => {
pdf.addPage();
var totalPages = jQuery("#cs_pdf").find(".page2el").length + 1;
// Adding footer text and page number for each PDF page
for (let i = 1; i <= totalPages; i++) {
pdf.setPage(i);
pdf.setFontSize(10);
pdf.setTextColor(60);
if (i !== 1) {
pdf.text('Page ' + i + ' of ' + totalPages, pdf.internal.pageSize.getWidth() - 100, pdf.internal.pageSize.getHeight() - 25);
}
if (i === 1) {
pdf.text("Confidential", pdf.internal.pageSize.getWidth() - 340, pdf.internal.pageSize.getHeight() - 35);
} else {
pdf.text(consultant_company, pdf.internal.pageSize.getWidth() - 530, pdf.internal.pageSize.getHeight() - 25);
}
}
}).from(document.getElementById(jQuery(this).attr('id'))).toContainer().toCanvas().toPdf();
}
count++;
}
// On Jquery each loop completion executing save function on doc object to compile and download PDF file
}).promise().done(function () {
doc.save();
});

Adding Images with JSPDF

Am using JSPDF to generate PDF in javascript, when I add Images and I generate the PDF file, the PDF file does not display the image until I generate the PDF a second time. Here is my code below
var doc = new jsPDF('p', 'pt', 'a4');
var res = doc.autoTableHtmlToJson(document.getElementById("data-table-committee"));
var header = function (data) {
doc.setFontSize(18);
doc.setTextColor(40);
doc.setFontStyle('normal');
doc.text("Committee Count List", data.settings.margin.left, 80);
console.log('adding image');
doc.addImage(headerImgData, 'PNG', data.settings.margin.left, 20, 150, 30);
console.log('adding image done');
};
var options = {
beforePageContent: header,
margin: {
top: 50
},
startY: doc.autoTableEndPosY() + 20
};
doc.autoTable(res.columns, res.data, options);
doc.save("Committee Count List.pdf");
What am I doing wrong and How can I get my image to be displayed the first time I generate the PDF
You can also create pdf using pdfkit. Here is the code that generates pdf with an image.
var PDFDocument = require ('pdfkit');
var fs = require('fs');
doc = new PDFDocument
doc.pipe(fs.createWriteStream('output.pdf'))
doc.image('E:/sii/nodejs/uploads/public/uploads/test.jpg', {
fit: [250, 300],
align: 'center',
valign: 'center'
});
doc.end()

uncaught reference error:. jspdf is not defined

I have used jsPDF for PDF conversion of tables. In bower_components I have 3 files Filesaver.js, jspdf.js, and jspdf.plugin.table.js files. While my page loads the files are also loading. It is showing this error at the end of jspdf.plugin.table.js file pointing (jsPDF.API). In my controller I have the function like this.
$scope.exportPdf = function(sampletable, a, b) {
var addr = $scope.address;
var name = a + ' ' + b;
var address = addr;
var tbldata = [],
fontSize = 8,
height = 1,
doc;
doc = new jsPDF('p', 'pt', 'a4', true);
doc.setFont("times", "normal");
doc.setFontSize(fontSize);
doc.text(280, 50,'Hi');
doc.text(220,80,address);
tbldata = [];
tbldata = doc.tableToJson('sampletable');
height = doc.drawTable(tbldata, {
xstart: 10,
ystart: 10,
tablestart: 100,
marginleft: 10,
xOffset: 15,
yOffset: 20
});
doc.save("mypdf.pdf");
}
Please help me.
Add the dependency files into index file, you should refer the dependency.

Javascript for loop, wait for function callback

I have a for loop that calls a function inside of itself. The function has a callback, however the for loop does not wait for it to finish and continues working.
The for loop actually finishes so quickly that the next action, serving a document that was supposed to be populated inside the for loop, is done before the first function call is completed.
Here is the content of the call I'm actually using, where images is an array holding external URLs to images:
// New jsPDF Document
var doc = new jsPDF();
doc.setFontSize(12);
// For each image we add the image to the document as an image
for (var index = 0; index < images.length; index++) {
// We only add pages after the first one
if (index !== 0) {
doc.addPage();
}
// This puts the URL of the active element at the top of the document
doc.text(35, 25, images[index].path);
// Call to our function, this is the 'skipped' portion
convertImgToDataURLviaCanvas(images[index].path, function(base64Img) {
console.log('An image was processed');
doc.addImage(base64Img, 15, 40, 180, 180);
});
}
doc.save('demo.pdf');
console.log('Document served!');
We get the image URLs from our array, and add everything. The convertImgToDataURLviaCanvas function is here:
// Gets an URL and gives you a Base 64 Data URL
function convertImgToDataURLviaCanvas(url, callback, outputFormat){
var img = new Image();
img.crossOrigin = 'Anonymous';
img.onload = function() {
var canvas = document.createElement('CANVAS');
var ctx = canvas.getContext('2d');
var dataURL;
canvas.height = this.height;
canvas.width = this.width;
ctx.drawImage(this, 0, 0);
dataURL = canvas.toDataURL(outputFormat);
callback(dataURL);
canvas = null;
};
img.src = url;
}
In the former examples even the line doc.text(35, 25, images[index].path); does properly write the URLs to the top of the page. Since that is contained in the array and works along the iterator. However, the for loop is completely done before the first image has been added to our document!
With the console.log you would see: 'Document served!' before the first 'An image was processed'. The goal would be for it to be reversed, with every 'An image was processed' being outputted before Document served! appeared.
How can I achieve this functionality?
To guarantee the order of images, using promises is simple
var promises = images.map(function(image, index) {
return new Promise(function(resolve) {
convertImgToDataURLviaCanvas(image.path, function(base64Img) {
resolve(base64Img);
});
});
}
Promise.all(promises).then(function(results) {
results.forEach(function(base64Img, index) {
if (index !== 0) {
doc.addPage();
}
doc.text(35, 25, images[index].path);
console.log('An image was processed');
doc.addImage(base64Img, 15, 40, 180, 180);
});
doc.save('demo.pdf');
console.log('Document served!');
});
For completeness (unchecked though) - the non promise way to guarantee image order and have the addPage/text in the correct place
var base64 = new Array(images.length); // base64 images held here
images.forEach(function(image, index) {
// Call to our function, this is the 'skipped' portion
convertImgToDataURLviaCanvas(image.path, function(base64Img) {
console.log('An image was processed');
// We only add pages after the first one
base64[index] = base64Img;
done++;
if (done == images.length) {
base64.forEach(function(img64, indx) {
if (indx !== 0) {
doc.addPage();
}
// This puts the URL of the active element at the top of the document
doc.text(35, 25, images[indx].path);
doc.addImage(img64, 15, 40, 180, 180);
}
doc.save('demo.pdf');
console.log('Document served!');
}
});
}
One way to do it is as follows
var done = 0; // added this to keep counf of finished images
for (var index = 0; index < images.length; index++) {
// We only add pages after the first one
if (index !== 0) {
doc.addPage();
}
// This puts the URL of the active element at the top of the document
doc.text(35, 25, images[index].path);
// Call to our function, this is the 'skipped' portion
convertImgToDataURLviaCanvas(images[index].path, function(base64Img) {
console.log('An image was processed');
doc.addImage(base64Img, 15, 40, 180, 180);
// added this code, checks number of finished images and finalises the doc when done == images.length
done++;
if (done == images.length) {
// move the doc.save here
doc.save('demo.pdf');
console.log('Document served!');
}
// end of new code
});
}
Using promises, it's easy as ...
// For each image we add the image to the document as an image
var promises = images.map(function(image, index) {
// We only add pages after the first one
if (index !== 0) {
doc.addPage();
}
// This puts the URL of the active element at the top of the document
doc.text(35, 25, image.path);
// Call to our function, this is the 'skipped' portion
// this returns a Promise that is resolved in the callback
return new Promise(function(resolve) {
convertImgToDataURLviaCanvas(image.path, function(base64Img) {
console.log('An image was processed');
doc.addImage(base64Img, 15, 40, 180, 180);
resolve(index); // doesn't really matter what is resolved
});
});
}
Promise.all(promises).then(function(results) {
doc.save('demo.pdf');
console.log('Document served!');
});

Categories

Resources