Closed CurryPaste closed 3 years ago
Can confirm. Calling html
a second time has no effect.
Yes, but if you call html
again in the callback
function, you can override the last time. That's more interesting 😀 So we finally adopted the window.print
to deal with pdf
Yeah, I thought I could use .addPage() after .html() and call .html() again to insert anoter DOM element into a new page. Turns out I can't. Is there a differrent way to go about doing this?
Interesting. So then I'm stuck and wondering if someone can help: I have a document where I'd like to print html twice. Once at the following coordinates: {x: 15, y: 100}
and the second time slightly lower (depending on the size of a table in between, generated with autotable), for example at {x: 15, y: 150}
. I achieved this previously with the deprecated function fromHtml
How can I achieve this with the latest version of jspdf (v2.3.0) and the html
function?
Another interesting, (maybe related?) issue is the font size of the html - I tried to use inline styling on the div
that wraps the text with a fixed width (as described in #3088 ), and tried forcing the font-size. Regardless of what I enter though, I always get the same font size (nothing smaller).
I've attached an illustration of my current struggle. Here is my code snippet to generate the pdf,
document.html(
`<div style="width: 180px !important; font-size: 14px; font-family: Arial, Helvetica, sans-serif; line-height: 1.2 !important;">${text}</div>`,
{
// eslint-disable-next-line object-shorthand, func-names
callback: function () {
doc.save();
},
x,
y,
}
);
This is a duplicate of #2899, which contains two more examples to reproduce this bug.
I'd be willing to create a pull request with a fix. Any clue where I should look first would be appreciated. It seems html2canvas is executed correctly for subsequent html()
calls, but the result does not end up in the pdf document.
Some workarounds:
html2canvas
directlyhtml-to-image
if your users can use Chrome/Firefox(Safari is not well supported). This library is using SVG and I personally think it's better than html2canvas
.Using html2canvas
directly has the disadvantage that text will be converted to an image, whereas using doc.html()
preserves text as text. And html-to-image
is not an option because of the Safari limitations.
Using
html2canvas
directly has the disadvantage that text will be converted to an image, whereas usingdoc.html()
preserves text as text. Andhtml-to-image
is not an option because of the Safari limitations.
Yes, that's why they are workarounds.
BTW: If server side rendering is activated, HTML element transformed by html-to-image
will be more accurate than html2canvas
does(css shadow, position, ... or any other things Canvas can't do). I'm now using both html-to-image
& html2canvas
with jsPDF
to create complicate worksheets for user. And I do hope the text could be copied. :)
@helsonxiao that is a very interesting workaround suggestion. The goal of my PDF to create not too complicated reports with a few charts and some minimal text. Copying text is not a "requirement".
So this might be worth looking into...
Would you be able to share some examples how you are using html-to-image
with jsPDF
to create a pdf?
@helsonxiao that is a very interesting workaround suggestion. The goal of my PDF to create not too complicated reports with a few charts and some minimal text. Copying text is not a "requirement".
So this might be worth looking into...
Would you be able to share some examples how you are using
html-to-image
withjsPDF
to create a pdf?
Sure! Just turn html into image then call addImage
API.
import html2canvas from 'html2canvas';
import * as htmlToImage from 'html-to-image';
// using svg
image = await htmlToImage.toPng(element, {
// cacheBust: true,
// fontEmbedCss,
});
// or canvas
image = await html2canvas(element, {
// useCORS: true,
});
doc.addImage(
image,
imgX,
imgY,
scaledImgW,
scaledImgH,
undefined,
comporession,
);
Closing this in favor of #2899.
Hey hi, Try with Timeout functions with 5 seconds delay. It works great!
const createPDF = async (index) => {
let pdf = new jsPDF("portrait", "pt", "a4");
let data = await document.querySelector(#reportCardPdf_${index}
);
pdf.html(data).then(() => {
pdf.save(ExamReportCard_${index + 1}.pdf
);
}
);
};
const handlePdfGeneration = () => {
const delay = 2; //please add delay minimum of 2 seconds to avoid white pages in pdf
const limit = state.data.length;
let i = 0;
const limitedInterval = setInterval(() => {
createPDF(i++);
if (i > limit) {
clearInterval(limitedInterval);
}
}, delay * 1000);
}
Didnt work for me :(
I've found a workaround for generating multipage PDF using .html() method,
const doc = new jsPDF("p", "pt", "a4");
let pageHeight = doc.internal.pageSize.getHeight();
doc.html(document.getElementById("Page1"), {
callback: function (pdf) {
pdf.addPage("a4", "l");
pdf.html(document.getElementById("Page2"), {
callback: function (pdf2) {
pdf2.addPage("a4", "l");
pdf2.html(document.getElementById("Page3"), {
callback: function (pdf3) {
pdf3.save("multipage.pdf")
},
x: 0,
y: 2 * pageHeight,
});
},
x: 0,
y: pageHeight,
});
},
x: 0,
y: 0,
});
I've tried your solution and it renders only the first element(Page 1). The rest of the pages (with elements Page2 and Page3) are empty. Can you maybe provide a JSFiddle working example? Thanks.
I've tried your solution and it renders only the first element(Page 1). The rest of the pages (with elements Page2 and Page3) are empty. Can you maybe provide a JSFiddle working example? Thanks.
U can try this: https://codesandbox.io/s/a11y-tabs-forked-rr814c?file=/src/App.js
I found an answer to this question (refrencing)
Just use await
and make sure to return doc
inside the callback
something like this
var doc = new jspdf.jsPDF({
orientation: 'p',
unit: 'pt',
format: 'letter'
});
var field = "<b>html test </b>";
doc.text(10, 10, "test");
//add first html
await doc.html(field, {
callback: function (doc) {
return doc;
},
width: 210,
windowWidth: 210,
html2canvas: {
backgroundColor: 'lightyellow',
width: 210,
height: 150
},
backgroundColor: 'lightblue',
x: 10,
y: 50,
autoPaging: 'text'
});
window.open(doc.output('bloburl'));
now you can call doc.html as many times as you want for that same document
I've found a workaround for generating multipage PDF using .html() method,
const doc = new jsPDF("p", "pt", "a4"); let pageHeight = doc.internal.pageSize.getHeight(); doc.html(document.getElementById("Page1"), { callback: function (pdf) { pdf.addPage("a4", "l"); pdf.html(document.getElementById("Page2"), { callback: function (pdf2) { pdf2.addPage("a4", "l"); pdf2.html(document.getElementById("Page3"), { callback: function (pdf3) { pdf3.save("multipage.pdf") }, x: 0, y: 2 * pageHeight, }); }, x: 0, y: pageHeight, }); }, x: 0, y: 0, });
@prasheel888 Thank you for this. Working as expected.
I've found a workaround for generating multipage PDF using .html() method,
const doc = new jsPDF("p", "pt", "a4"); let pageHeight = doc.internal.pageSize.getHeight(); doc.html(document.getElementById("Page1"), { callback: function (pdf) { pdf.addPage("a4", "l"); pdf.html(document.getElementById("Page2"), { callback: function (pdf2) { pdf2.addPage("a4", "l"); pdf2.html(document.getElementById("Page3"), { callback: function (pdf3) { pdf3.save("multipage.pdf") }, x: 0, y: 2 * pageHeight, }); }, x: 0, y: pageHeight, }); }, x: 0, y: 0, });
Thank You mate!
I've found a workaround for generating multipage PDF using .html() method,
const doc = new jsPDF("p", "pt", "a4"); let pageHeight = doc.internal.pageSize.getHeight(); doc.html(document.getElementById("Page1"), { callback: function (pdf) { pdf.addPage("a4", "l"); pdf.html(document.getElementById("Page2"), { callback: function (pdf2) { pdf2.addPage("a4", "l"); pdf2.html(document.getElementById("Page3"), { callback: function (pdf3) { pdf3.save("multipage.pdf") }, x: 0, y: 2 * pageHeight, }); }, x: 0, y: pageHeight, }); }, x: 0, y: 0, });
saved me Thanks
I've found a workaround for generating multipage PDF using .html() method,
const doc = new jsPDF("p", "pt", "a4"); let pageHeight = doc.internal.pageSize.getHeight(); doc.html(document.getElementById("Page1"), { callback: function (pdf) { pdf.addPage("a4", "l"); pdf.html(document.getElementById("Page2"), { callback: function (pdf2) { pdf2.addPage("a4", "l"); pdf2.html(document.getElementById("Page3"), { callback: function (pdf3) { pdf3.save("multipage.pdf") }, x: 0, y: 2 * pageHeight, }); }, x: 0, y: pageHeight, }); }, x: 0, y: 0, });
const doc = new jsPDF("l", "pt", "a4");
let pageHeight = doc.internal.pageSize.getHeight();
const pages = [
document.getElementById("Page1")!,
document.getElementById("Page2")!,
document.getElementById("Page3")!,
];
htmlPage(doc, pages);
function htmlPage(document: jsPDF, pages: HTMLElement[], index = 0) {
const hasPages = pages.length > 0;
if (!hasPages) return document.save("multipage.pdf");
return document.html(pages[0]!, {
y: index * pageHeight,
callback(pdf) {
if (index > 0 && pages.length > 1) pdf.addPage("a4", "l");
const restPages = pages.slice();
restPages.splice(0, 1);
htmlPage(pdf, restPages, index + 1);
},
});
}
Great solution with the callback logic @prasheel888, here is a recursive one, for arbitrary length documents. This saves me as well as I need portrait and landscape in the same file
const fileName = 'test.pdf'
addPageToPdf(pages);
function addPageToPdf(pages, pageIndex = 0) {
if (pages.length === 0) {
pdf.save(fileName);
return;
}
pdf.html(pages.at(0), {
y: pdf.internal.pageSize.getHeight() * pageIndex,
callback(pdf) {
pdf.addPage();
addPageToPdf(pages.slice(1), ++pageIndex);
}
});
}
@braadworst great solution but it doesn't work if some of the "pages" overflow on a second page. I have an auto-generated table that can span between 1 and 4 pages long depending on user input, and i want my next content to always start on the next page after the end of the table (imagine a page break in word). Is there any way to calculate that?
As I am writing this it dawned on me that I can keep track of how many rows my table has and find the height of the table that way, then do some easy math and stuff to calculate the next page.
Leaving this here in case someone is in a similar situation and might find it helpful.
Edit: Actually found a more reliable solution as my row heights varied so it didnt work out too well. IF YOUR DOCUMENT REQUIRES ONLY ONE PAGE BREAK JUST MAKE THE y value NEGATIVE pageHeight
var page_h = doc.internal.pageSize.getHeight();
doc.html( html_variable_length_content ,
{
callback: function(doc) {
doc.addPage("a4");
doc.html( html_final_page ,
{
callback: function(doc) {
doc.save("test.pdf");
},
y : -page_h
});
},
y: 0
});
I found an easy way to iterate through this and get all of the pages to populate and format. A lot of solutions looked like we were hard coding the pages. This method should allow you to use an array of html pages.
const doc = new jsPDF();
let pageHeight = doc.internal.pageSize.getHeight();
await doc.html(data[0], {
callback: async function (doc) {
// Save the PDF
if(data.length > 0) {
for(let i = 1; i < data.length; i++) {
doc.addPage();
await doc.html(data[i], {
margin: [10, 10, 10, 10],
x: 0,
y: i * pageHeight,
width: 190, //target width in the PDF document
windowWidth: 675 //window width in CSS pixels
});
}
}
doc.save(`${fileName}.pdf`);
},
margin: [10, 10, 10, 10],
x: 0,
y: 0,
width: 190, //target width in the PDF document
windowWidth: 675 //window width in CSS pixels
});
@stev232 - .html is not working for me in Safari. I do not want to use addImage? Any solution?
I want to export a PDF in a function. At the same time, the content inside is generated by using
.html()
many times. If only once.html()
there is no problem at all. But I found that I used it twice, and the content of the second time would not be generated in PDF. So I want to know,.html()
Only once? this is codeBy the way, we've dealt with this problem in other ways, so now we're just a little curious.
At the same time, if I continue to use
html()
in html() callback, I can write the content into PDF, but in the way of covering the previous one.version 2.3.0