html2pdf.js
html2pdf.js copied to clipboard
html2pdf failed with too many pages
Hi @eKoopmans,
Firstly, I want to thank you for writing this great file. It saves me a lot of time dealing with the quality of content and layout. Recently, I was playing around with this file and vue js and found an issue:
https://jsfiddle.net/rover1234/woa5ucyx/
In two tests in the link, I created a simple for loop which shows 5 texts on the screen, and it is fine to output to a pdf file with 5 pages which show these texts since I apply your page break. However, in the second test, it failed to show the content of more than 20 texts which are all blank pages even though the page break still works fine.
I am not sure about this case. Is it because the picture generated from canvas is too large? and how could I find a way to get away from it (like creates multiple elements to generate different pdf content and combine everything into one file)?
Thanks
It seems to me that everytime I increase the number of the height of the margin, I could get more pdf pages with the text. On the other hand, when I decrease the height, there are blank pages with the same amount of contexts I generated. Increasing the height is not a solution for me since if there are more pages generated, it will still fail at some point.
https://jsfiddle.net/rover1234/woa5ucyx/
Hi @CSENoni, sorry I didn't respond sooner. Yes, I see the problem. I've reproduced it here using only html2canvas, which html2pdf relies on.
The problem is in fact deeper than that - it's a problem directly in how canvases work. Canvases have a maximum height/width, and once you exceed that the canvas becomes unusable! That's what's happening here.
I wasn't aware of that problem, so thank you for bringing it up. It's a serious limitation and I don't have any immediate solution. As you mentioned, changing the margins and page size will change how many pages you can get (also changing the resolution/scale), but that's just because it changes the size of the underlying canvas.
Thanks for your answer. My workaround : changed html2canvas dpi option from 192 to 96
Hi @eKoopmans
I don't know well html2pdf underlying technologies, but will it be possible to use a second canvas once the limitation is reached on the first one and then merge canvas or PDF?
The cool workaround of @MaNuGitH works fine, but it just to postpone the problem, principally if the content of your PDF is dynamic.
Hope it can help :)
BR
I ran this problem as well and @MaNuGitH's workaround didn't work for me. I wrote a function that might be helpful for someone. Lucky for me the new branch new-api
exposes more functions and breaks the process of turning html into a pdf into several steps. I notice that I can output my html as an image created from being drawn onto a canvas which is pretty much the last step before turning it into a PDF. So, I decided to create my own instance of jsPDF
and only pass in a page at a time to html2pdf
. Then I add the image created by html2pdf
to my instance of jsPDF
. I hope this is helpful.
import html2pdf from 'html2pdf.js';
import jsPDF from 'jspdf';
const exportHTMLToPDF = async (pages, outputType='blob') => {
const opt = {
margin: [0,0],
filename: 'myfile.pdf',
image: { type: 'jpeg', quality: 0.98 },
html2canvas: { dpi: 192, letterRendering: true },
jsPDF: { unit: 'in', format: 'letter', orientation: 'portrait' }
};
const doc = new jsPDF(opt.jsPDF);
const pageSize = jsPDF.getPageSize(opt.jsPDF);
for(let i = 0; i < pages.length; i++){
const page = pages[i];
const pageImage = await html2pdf().from(page).set(opt).outputImg();
if(i != 0) {
doc.addPage();
}
doc.addImage(pageImage.src, 'jpeg', opt.margin[0], opt.margin[1], pageSize.width, pageSize.height);
}
// This can be whatever output you want. I prefer blob.
const pdf = doc.output(outputType);
return pdf;
}
You can also write it using recursion if you can't use async-await.
Hi @GGross5213 ,
I am still learning JS and I am running into the same problem and I was wondering how can I implement your workaround in my case.
Currently, I have a dynamic content and I am using @eKoopmans plugin to generate a PDF report for it.
So this is my current function:
$("#download").click(function(){
filename = this.value + '.pdf'
var element = document.getElementById('report');
html2pdf(element, {
margin: 0.25,
filename: filename,
image: { type: 'jpeg', quality: 0.98 },
html2canvas: { dpi: 96, letterRendering: true },
jsPDF: { unit: 'in', format: 'letter', orientation: 'portrait' }
});
});
and I would like to know where should I put my "element" variable into your function in order to have it working?
I would much appreciate if you could give me hand with it.
@nevergiveup777 Does your element have the html2pdf page-break classes in it?
The way I did it was by breaking my element up into the pages that I wanted, and then passing only one page at a time into html2pdf. Then I convert that page into an image using html2pdf and add it to an instance of jsPDF that I control (what html2pdf does underneath the hood). I would also recommend using the new-api
branch of html2pdf has it exposes an outputImg
function. I guess you could implement it using the callback branch, but it will be much easier with the new-api
. I hope this helps.
@GGross5213 in your example I got error jsPDF.getPageSize is not a function. and pages arg is the html element that we are going to export, right? I have very large tables and images that i needed to convert into pdf but as you know if goes blank.
@waqar-imtiaz The pages arg is a list containing strings of HTML. In my case each element in the list is a single page that is ended with the </div><div class="html2pdf__page-break"></div>
. Can you post the code where you are calling jsPDF.getPageSize()
? Also what format are your images in? I know I had problems with images showing up in the PDF. If they were coming from an external url such as AWS S3, they wouldn't render. I think it has something to do with CORS. I couldn't figure out how to allow CORS/set up a proxy. So, I just converted them into base64 data URI's and included those.
@GGross5213 My HTML is as follow:
<!-- pdf-export div starts from here -->
<div id="pdf-export" >
<div class="cover-page">
<h1>True Monitor Report</h1>
<h2>{{monitorName}}</h2>
<ul class="monitor-details">
<li>PDF Created Date:<span class="pull-right"> {{currentDate}}</span></li>
<li>Last Run:<span class="pull-right">{{getDateTime(monitorDetails.lastRun)}}</span></li>
<li>Search Sources:
<span class="pull-right">
<i *ngIf="monitorDetails.facebookSource">Facebook,</i>
<i *ngIf="monitorDetails.twitterSource"> Twitter,</i>
<i *ngIf="monitorDetails.googleSource"> Google,</i>
<i *ngIf="monitorDetails.youtubeSource"> YouTube,</i>
<i *ngIf="monitorDetails.instagramSource"> Instagram,</i>
<i *ngIf="monitorDetails.documentSource"> Documents</i>
</span>
</li>
<li>Search Terms:<span class="pull-right"><i *ngFor="let term of monitorDetails.searchTerms">{{term}},</i></span></li>
<li>Result Counts:<span class="pull-right">{{totalMonitorResultsCount.counts}}</span></li>
</ul>
</div>
<div class="html2pdf__page-break"></div>
<div id="images" class="clearfix" style="clear: both; width: 100%">
<h2>{{monitorName}}</h2>
<!-- images come here and I add page breaks after six images -->
</div>
<table>
<!--here comes the table, data for this table comes from the data base and it can be
thousands of rows, so I can not add page breaks here in the table (or maybe there is someway around).
so when I generate pdf using html2pdf pdf goes blank even if the data is just about 400 rows only, I tried
to use fromhtml method of jspdf but I could not make the desired layout of images, if you can help me
that would be great. thanks. :) -->
</table>
</div>
and js code is: ` var pages = document.getElementById('pdf-export') const exportHTMLToPDF = async (pages, outputType='blob') => { console.log('checking now the pdf'); const opt = { margin: [0,0], filename: 'myfile.pdf', image: { type: 'jpeg', quality: 0.98 }, html2canvas: { dpi: 192, letterRendering: true }, jsPDF: { unit: 'mm', format: 'a4', orientation: 'landscape' } }; const doc = new jsPDF(opt.jsPDF); const pageSize = jsPDF.getPageSize(opt.jsPDF); for(let i = 0; i < pages.length; i++){ const page = pages[i]; const pageImage = await html2pdf().from(page).set(opt).outputImg(); if(i != 0) { doc.addPage(); } doc.addImage(pageImage.src, 'jpeg', opt.margin[0], opt.margin[1], pageSize.width, pageSize.height); } // This can be whatever output you want. I prefer blob. const pdf = doc.output(outputType); return pdf; }
exportHTMLToPDF(element, 'outputPdf')`
PS: I think my knowledge of js is very limited, sorry in advance if this is wrong in any way.
Hi @GGross5213,
How do you create the array of pages that you pass to the exportHTMLToPDF function?
Thanks
Hi @GGross5213,
I tried using html2pdf().from(element).toPdf().get('pdf').then(pdf => { $scope.pages = pdf.internal.pages; });
but the result is another blank pdf... I tried toContainer, toCanvas, toImg, but none of those produce an array of pages. Do I have to send the html for each page in an array somehow?
@waqar-imtiaz sorry for the delay. Life has been crazy lately. It looks like you don't need to loop through that html element. You should just be able to pass it into the html2pdf function as is. If you do have too many rows in the table that it is causing the Canvas height limit to be reached then maybe you need to periodically split up the table with </div><div class="html2pdf__page-break"></div>
. If you add the page breaks then create a list of html strings where each element in the list is a page ending with the html2pdf page break. Also the output type needs to be one of save
, blob
, arraybuffer
, bloburi
, bloburl
, datauristring
, dataurlstring
, dataurlnewwindow
, datauri
, or dataurl
. This comes from jsPDF's documentation: https://rawgit.com/MrRio/jsPDF/master/docs/jspdf.js.html#line992
Let me know if you have anymore questions. I hope this helps.
@audra415 sorry for the delay. Life has been crazy. So the the array of pages that I pass into theexportHTMLToPDF
funciton is a mixture of html strings that I hard coded which I add data to on run time and html elements that I selected using jquery/javascript selectors.
What are you trying to do with the pdf after you generate it? It looks like you are using angular and I am not too familiar with that. It also looks like you should using outputImg
or outputPdf
instead of toPdf
or toImg
.
Let me know if that helps.
@GGross5213,
Ah ha, array of strings for each page... Got it. I'm building a report that will have different lengths depending on the data used to generate it. So I'll have to break up the pages, use toPdf, then stitch them together in one pdf.
@eKoopmans Sounds great. I will say that I think you want to use outputImg
then add the img to your own instance of jsPDF. I think that is the easiest way to stitch the pages together if your total page size exceeds the canvas height limit. (I believe that is how html2pdf.js generates the pdf underneath the hood)
Hi,
Reading through the comments I see it mentions canvas height limit.
If the problem is with the canvas height limit can the canvas height limit be increased?
If this is the case do you know how can this be done.
@GGross5213 I am generating bulk invoice on client side, even if give page-break. More than 15 a4 pages generates blank pages. Also i followed your earlier answer of attaching single single page but after generating like 200 to 250 pages, cpu utilisation is so high that system gets hang. Can you put more light for such type of need.
Hi,
If you have a high demand that is too large for your server you can try
https://phantomjscloud.com
I have been looking at this for some of my pdf conversions. I am still evaluating this for my needs but have managed to use it if it is text only. Their support is currently looking at the issue I have with images. The reason this may solve your problem is that it is fast and takes the load off your server. Also free for up to 500 pages per day.
On Sun, Oct 28, 2018 at 5:25 AM sohilfynd [email protected] wrote:
@GGross5213 https://github.com/GGross5213 I am generating bulk invoice on client side, even if give page-break. More than 15 a4 pages generates blank pages. Also i followed your earlier answer of attaching single single page but after generating like 200 to 250 pages, cpu utilisation is so high that system gets hang. Can you put more light for such type of need.
— You are receiving this because you commented. Reply to this email directly, view it on GitHub https://github.com/eKoopmans/html2pdf/issues/19#issuecomment-433677645, or mute the thread https://github.com/notifications/unsubscribe-auth/Ao-tIarshognmIwEnwyvXLDgrkyiGkNUks5upT-0gaJpZM4OTsI7 .
--
For How To products visit http://howto.a2zidx.com http://howto.a2zidx.com
Click here or visit http://www.art-seekers.com for Art and Photography http://www.art-seekers.com
Click here or visit http://www.a2zidx.com/?1 for website traffic http://www.a2zidx.com/?1
Click here or visit http://www.myromanticstories.com/ for romantic stories http://www.myromanticstories.com/
Hi @eKoopmans ,
I have very large tables and images that i needed to convert into pdf.
lots of table row with different height of column content in it.
may be attachment can explain my issue
Hi @CSENoni, sorry I didn't respond sooner. Yes, I see the problem. I've reproduced it here using only html2canvas, which html2pdf relies on.
The problem is in fact deeper than that - it's a problem directly in how canvases work. Canvases have a maximum height/width, and once you exceed that the canvas becomes unusable! That's what's happening here.
I wasn't aware of that problem, so thank you for bringing it up. It's a serious limitation and I don't have any immediate solution. As you mentioned, changing the margins and page size will change how many pages you can get (also changing the resolution/scale), but that's just because it changes the size of the underlying canvas.
@eKoopmans Could it be possible to generate one canvas per page break and them add all to the PDF?
@eKoopmans first of all thank you for your library ... we are completely blocked for two days: the "limitation of the canvas" is driving us crazy .... we have tried many solutions but none seems to solve the problem in a reliable way (one is too slow, another produces a "not nice" layout", and so on)
Do you have in your plan to address this problem in the next future? It would be really really really appreciated :)
@albertmat try pdfmake library. Its quite efficient and quick plus the pdf it produces is very small size.
@sohilfynd really thanks... I will try it...
I didn't read anything yet.. there is some utility, "on the shelve", for converting an existing HTML template to PDF?
Is this issue fixed? It is generating blank pages when number of pages increase more than 15 or 16.
@charutiwari04 no news on that :(! Since the problem is not the library itself but "the way" it uses for generating the pdf (see [https://stackoverflow.com/a/11585939/4080966]) i solved generating more than one pdf and, then, merging them server side. For sure it's not the cleaner possible solution... but it works :)
Hi all! First off, huge thanks to @GGross5213 for being all over the comments here, I really appreciate your help.
Second, the central issue here is that HTML canvases are just broken, and have a maximum size that I can't fix. I've investigated creating a "fake" canvas that stitches together multiple canvases, and it might be worth the effort to try to create such a thing, but I haven't had the chance to yet and I don't think it's a realistic goal.
However, there's a recent development that may fix this issue as well as a few other big problems. jsPDF has a built-in canvas "engine", which means we could draw straight into jsPDF! This would also mean vector graphics (highlightable text, small files, all that jazz). It's very promising and it's high on my to-do list for this project.
That said, I have very little time to maintain this project, so I can't make any promises. For now, this approach from @GGross5213 is your best bet (splitting your page into smaller chunks and individually adding them into the PDF). You could cut out a bit of the work with something like this:
// Assuming "pages" is an array of HTML elements or strings that are separate pages:
var worker = html2pdf().from(pages[0]).toPdf();
pages.slice(1).forEach(function (page) {
worker = worker.get('pdf').then(function (pdf) {
pdf.addPage();
}).from(page).toContainer().toCanvas().toPdf();
});
worker = worker.save();
Notice you don't even need any async/await, the code above will build a "then" chain that should bring you right through to the end. Good luck!
Edit: Modified code snippet to remove the "extra" page at the end per @ScottStevenson's comment.
Thanks for the solution @eKoopmans. Just wanted to point out that this will leave a blank page at the end so I used this:
for(let i=0; i<pages.length; i++) {
worker = worker.set(opt).from(pages[i]).toContainer().toCanvas().toPdf().get('pdf').then((pdf) =>
{
if (i < pages.length - 1) { // Bump cursor ahead to new page until on last page
pdf.addPage();
}
});
}
The only issue with this is that it can make it take a very long time for the promise to resolve when split up like this, freezing the UI. We added a progress indicator but would like to allow the user to cancel the process so they can get UI control back. Is there any way to cancel the Promise?
Hi @ScottStevenson, the cancelling is a cool idea! There's nothing supported now but I could see incorporating a cancel feature into the .then()
implementation. Could you open a separate issue with that suggestion? Thanks.