html2pdf.js produces a blank document on iOS - javascript

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();
});

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

How to download pdf in nuxt project?

<div style="height: 0;position: fixed; left: 50%; top: 0; transform: translateX(-50%);
z-index: -1;overflow: hidden">
<ticket-pdf v-if="pdfData" :pdfData="pdfData" :downloadBtn="downloadBtn" #download="downloadBtn = $event" /> </div>
In my nuxt project, i need to download pdf file in my main componet .When user clicked download button it will be downloaded directlty without previewing pdf component. So i hide my ticket-pdf component inside my main component . This way works in localhost but when i published my project to test area it does not work. It download only 1 empty page. and i face with this mistake.
Failed to load resource: the server responded with a status of 500 ()
If i open my pdf file in a other page and i can preview and download it also. But i need to doownload it without routing anywhere and directly in my main component
printPDF() {
const html2canvasOptions = {
allowTaint: true,
removeContainer: true,
imageTimeout: 15000,
logging: true,
useCORS: true,
scrollX: 0,
scrollY: 0,
scale: 2,
};
// Initialize the PDF.
let pdf = new jsPDF('p', 'in', [8.5, 11], false);
let totalPage = 0
//i have many pages and countof pages changes . It depends. so firstly i calculate page count and create pdf.
let pageCount = document.querySelectorAll('[id^=contentPrint]').length
for (let i = 0; i < pageCount; i++) {
const contentPrint = document.getElementById('contentPrint-' + i);
html2canvas(contentPrint, html2canvasOptions)
.then((canvas) => {
const image = {type: 'jpeg', quality: 1};
const margin = [0.5, 0.5];
let imgWidth = 8.5;
let pageHeight = 11;
let innerPageWidth = imgWidth - margin[0] * 2;
let innerPageHeight = pageHeight - margin[1] * 2;
// Calculate the number of pages.
let pxFullHeight = canvas.height;
let pxPageHeight = Math.floor(canvas.width * (pageHeight / imgWidth));
let nPages = Math.ceil(pxFullHeight / pxPageHeight);
totalPage += nPages
// Define pageHeight separately so it can be trimmed on the final page.
pageHeight = innerPageHeight;
// Create a one-page canvas to split up the full image.
let pageCanvas = document.createElement('canvas');
let pageCtx = pageCanvas.getContext('2d');
pageCanvas.width = canvas.width;
pageCanvas.height = pxPageHeight;
for (let page = 0; page <= nPages; page++) {
// Trim the final page to reduce file size.
if (page === nPages - 1 && pxFullHeight % pxPageHeight !== 0) {
pageCanvas.height = pxFullHeight % pxPageHeight;
pageHeight = (pageCanvas.height * innerPageWidth) / pageCanvas.width;
}
// Display the page.
let w = pageCanvas.width;
let h = pageCanvas.height;
pageCtx.fillStyle = 'white';
pageCtx.fillRect(0, 0, w, h);
pageCtx.drawImage(canvas, 0, page * pxPageHeight, w, h, 0, 0, w, h);
// Add the page to the PDF.
if (page > 0) pdf.addPage();
let imgData = pageCanvas.toDataURL('image/' + image.type, image.quality);
pdf.addImage(imgData, image.type, margin[1], margin[0], innerPageWidth, pageHeight);
}
});
}
setTimeout(() => {
// pdf.deletePage(totalPage + 1)
pdf.save("e-ticket" + '.pdf')
}, 3000)
setTimeout(() => {
this.$emit("download", this.download);
}, 4000)
},
This is how i create pdf area. When user clicked download button i create pdf and download it thanks to my props.
printPDF() {
window.scrollTo(0, 0);
// Initialize the PDF.
let pdf = new jsPDF('p', 'in', [8.5, 11], false);
let totalPage = 0
let counter = 0
let myArray = []
let pageCount = document.querySelectorAll('[id^=contentPrint]').length
for (let i = 0; i < pageCount; i++) {
myArray.push('contentPrint-' + i)
}
for (let i = 0; i < pageCount; i++) {
const contentPrint = document.getElementById('contentPrint-' + i);
html2canvas(contentPrint, {
scale: 2, useCORS: true, allowTaint: true, scrollY: 0
}).then((canvas) => {
.................
........
.....
counter = counter + 1;
callBack();
})
}
const callBack = () => {
if (myArray.length === counter) {
pdf.save("filename" + '.pdf')
this.$emit("download", this.download);
}
}
},
the problem was about my settimeout function . ın there ı would call pdf.save before pdf created and sure every browser works in different speed. so if we use callback function after all process done , then we can obtain our pdf rpint
You could maybe try to simply put
<a href="/your_file.csv" download>
by putting your_file.csv into the static directory.
Not sure if it can be enough in your case, sorry if it's not.

How to set header and footer using html2pdf - jspdf javascript plugin?

I want to convert HTML contents to a pdf file and store it to the server so, I used jspdf - html2pdf function for convert HTML contents to pdf file.
so i am trying a javascript code in angular project like that,
var element = this.pdfTable.nativeElement
var opt = {
margin: 0.5,
filename: 'ct-scan.pdf',
enableLinks: false,
pagebreak: { mode: 'avoid-all' },
image: { type: 'jpeg', quality: 0.98 },
html2canvas: { scale: 2 },
jsPDF: { unit: 'in', format: 'a4', orientation: 'portrait' }
};
html2pdf().from(element).set(opt).toPdf().get('pdf').then(function (pdf) {
var totalPages = pdf.internal.getNumberOfPages();
for (var i = 1; i <= totalPages; i++) {
pdf.setPage(i);
pdf.setFontSize(10);
pdf.setTextColor(150);
pdf.text('Page ' + i + ' of ' + totalPages, pdf.internal.pageSize.getWidth() - 100,
pdf.internal.pageSize.getHeight() - 30);
}
}).save()
it works fine., but the header and footer are not set in the result. so anybody can help me to solve this issue? thanks in advance.
Since your unit is using inch jsPDF: { unit: 'in' ... , minus the width of 100 and the height of 30 will become negative number. The pdf won't show the text probably.
Solution: You can print the current width and height by console.log; or just divided by 2 to make it go center, then you can adjust the text to your desired position.
var element = this.pdfTable.nativeElement;
var opt = {
margin: 0.5,
filename: 'ct-scan.pdf',
enableLinks: false,
pagebreak: { mode: 'avoid-all' },
image: { type: 'jpeg', quality: 0.98 },
html2canvas: { scale: 2 },
jsPDF: { unit: 'in', format: 'a4', orientation: 'portrait' }
};
html2pdf().from(element).set(opt).toPdf().get('pdf').then(function (pdf) {
var totalPages = pdf.internal.getNumberOfPages();
//print current pdf width & height to console
console.log("getHeight:" + pdf.internal.pageSize.getHeight());
console.log("getWidth:" + pdf.internal.pageSize.getWidth());
for (var i = 1; i <= totalPages; i++) {
pdf.setPage(i);
pdf.setFontSize(10);
pdf.setTextColor(150);
//divided by 2 to go center
pdf.text('Page ' + i + ' of ' + totalPages, pdf.internal.pageSize.getWidth()/2,
pdf.internal.pageSize.getHeight()/ 2);
}
}).save();

html2pdf: downloading pdf texts are overlapping and break the page

Currently, I am using the html2pdf library for downloading PDF files, it is working fine for me, but the problem is that after a few downloads the texts are overlapping and the complete page is broken. Using a pagebreak class I can restrict the page break issue but the overlapping issue and the broken page issue is still there. tried codes are
<div class="export-pdf" id="export-pdf">
<div class="fullWidth page-min-height">
Planned Procedure
</div>
</div>
var element = document.getElementById('export-pdf');
var opt = {
margin: [10, 0, 10, 0],
pageNumber: true,
pagebreak: {
mode: 'css',
avoid: '.breakPage',
before: '.beforeClass'
},
filename: test.pdf,
};
html2pdf().set(opt).from(element).save()
texts are overlapped I am expecting 'Planned Procedure'...if we have more data
complete pdf text become overlap after a few downloads,
Try This function
function genTablePDF(heightSize){
html2canvas(document.getElementById("HTMLtoPDF"),{
onrendered: function (canvas){
var img = canvas.toDataURL("image/png");
var doc = new jsPDF();
doc.addImage(img,'JPEG',5,8,200, +heightSize);
doc.setPage(2);
doc.internal.getNumberOfPages();
var dateName = new Date();
doc.save('PDF ( ' + dateName + ' ) ' +'.pdf'); // file name pdf
}
});
}
in last parameter the variable is for height of the page
doc.addImage(img,'JPEG',5,8,200, +heightSize);
You can pass it through function heightSize OR you can use
heightSize = document.getElementById('pageSize').value
where document.getElementById('pageSize').value is giving the numbers of rows the the page table have or you can use your business logic like:
var heightSize = document.getElementById('pageSize').value;
if( +heightSize === 7 ){
heightSize = 120;
}
if( +heightSize === 14 ){
heightSize = 200;
}
if( +heightSize === 21 ){
heightSize = 260;
}
if( +heightSize === 28 || +heightSize === 35 || +heightSize === 42 || +heightSize === 50 ){
heightSize = 285;
}
doc.addImage(img,'JPEG',5,8,200, +heightSize);
I hope it is helpful for you :)
var element = document.getElementById('inner');
var opt = {
margin: 30,
filename: this.auth_user,
image: {type: 'jpeg',quality: 0.98},
html2canvas: {
scale: 2,
bottom: 20
},
pagebreak: { mode: ['css'], padding : 200 },
jsPDF: {
unit: 'mm',
orientation: 'portrait'
}
};
html2pdf().set(opt).from(element).then(function() {
}).save();
Had also problems with overlapping text with safari browser. This fix helped: GitHub. "remove linebreaks from tags"

Html2canvas for each div export to pdf separately

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).

Categories

Resources