Adding Images with JSPDF - javascript

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

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

Concat Buffers or alternative method

I want to resize image screenshot of webshot module without touching disk, doing all in memory.
Here is what I have
var webshot = require('webshot');
var fs = require('fs');
var sharp = require('sharp');
alldata = new Buffer(1024*1024);
var options= {
windowSize: {
width: 1024
, height: 768
},
zoomFactor:0.25,
renderDelay:500,
quality:50,
phantomConfig: {'ignore-ssl-errors': 'true'}
};
var file = fs.createWriteStream('google.png', {encoding: 'binary'});
var renderStream = webshot('google.com', options);
var completed = false;
renderStream.on('data', function(data) {
console.log(Type(data));
alldata.write(data);
});
renderStream.on('end', function(data) {
completed=true;
});
require('deasync').loopWhile(function(){return !completed;});
Since the data will be delivered in chunks, I need to combine Buffers and at the end convert the image to another size using Sharp:
var resizeTransform = sharp(thebuffer).resize(320, 270).max();
But I just can't concat buffers and not sure how to do it directly with Sharp without concatenating buffers. Any ideas how to do it properly?
You can use pipe to resize your image.
var webshot = require('webshot');
var fs = require('fs');
var sharp = require('sharp');
var options = {
windowSize: {
width: 1024,
height: 768
},
zoomFactor: 0.25,
renderDelay: 500,
quality: 50,
phantomConfig: { 'ignore-ssl-errors': 'true' }
};
var file = fs.createWriteStream('google.png', { encoding: 'binary' });
var renderStream = webshot('google.com', options);
const resizeStream = sharp().resize(320, 270).png();
//pipe your stream, get the webshot, resize it, then save to png
renderStream.pipe(resizeStream).pipe(file);
//since res is a writable stream, you can pipe the stream down to it just like the file stream.
//renderStream.pipe(resizeStream).pipe(res);

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.

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