Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

New html-method #1176

Closed
eKoopmans opened this issue Apr 14, 2017 · 68 comments
Closed

New html-method #1176

eKoopmans opened this issue Apr 14, 2017 · 68 comments

Comments

@eKoopmans
Copy link
Contributor

Existing jsPDF plugins

Hi, I've been working on a new html2pdf package that uses html2canvas + jsPDF to convert HTML content to PDF. I know there are already three existing jsPDF plugins for HTML: addHTML, fromHTML, and html2pdf (same name). I don't want to step on any toes - from what I can tell:

  • fromHTML is the oldest plugin and renders the HTML directly to PDF (which is great), but its support for complex HTML/CSS is lacking.
  • addHTML is newer (but now deprecated) and uses html2canvas/rasterizeHTML to create a canvas, then puts the image onto the PDF page. Current state is described at How to get forward production with html2pdf / addHtml #944.
  • html2pdf: I haven't found much info (apart from the demo), but it looks like it renders directly to PDF like fromHTML, which again is great but runs the risk of poor HTML/CSS support.

New html2pdf package

My html2pdf package takes the same approach as addHTML - I convert to a canvas with html2canvas, split that image up into pages, and attach each image onto its own PDF page. I believe it has some advantages over the jsPDF plugins:

  • Easier to use - creating a PDF from an HTML element/page/string is one line: html2pdf(element);
  • Fits content to the page - it copies the HTML first into an (invisible) container div with the PDF page width, so that content can resize appropriately (also working on a 'shrink-to-page' option)
  • Supports page-breaks - a feature that comes up a lot, i.e. Add automatic page breaks in HTML and text functions #190
  • Is external - it keeps the logic separate and can respond more readily to updates in both repos (including html2canvas internally in jsPDF causes some problems)
  • Is external (disadvantage) - it can't be used inline with other jsPDF commands the way the current plugins are - it's a standalone package that creates and closes the PDF.

Open issues

That said, I've found 48 open issues on jsPDF that I think html2pdf could resolve, but I don't want to start pushing html2pdf if it conflicts with jsPDF's internal implementations. @MrRio @Flamenco I'd appreciate your feedback!

@Flamenco
Copy link
Contributor

My first reaction would be to:

  1. List the end user's usage needs and issues.
  2. Write an API
  3. Let each vendor implement the API
  4. Let the user choose the implementation they want.

This way each implementation will have a standardized usage and documentation.

The main issues I have seen are related to Pagination, Image Quality, CORS, Scaling, Tables, SVG integration, and Font handling.

@eKoopmans
Copy link
Contributor Author

Thanks @Flamenco, I appreciate the feedback. I think I could rework my package to have an API that could be used within jsPDF, in the same manner as addHTML.

And thanks for the list of common issues! Here's my status on those:

  • Pagination: pages break automatically, with margins, and you can add custom page-breaks with the html2pdf__page-break class
  • Image Quality: I have an open pull request on html2canvas to add custom resolution, which is accessible through html2pdf
  • Scaling: good point, right now it just uses the HTML initial sizes
  • Tables, SVG, Fonts: relies on how well html2canvas does each, which is mostly "not bad"
  • CORS: again depends on html2canvas, which doesn't support CORS (root problem is with canvases themselves). Don't know any way around that one

@Flamenco
Copy link
Contributor

The CORS issue could be solved with a proxy handler that gets the image from a server-side request. I'm sure countless hours have been wasted in users fighting that issue...

There are DATA URI issues that also affect image quality. Also, the user might want to scale image resolution, or suppress them.

For pagination, I am thinking more about the API. E.G. How does the user declare IF and WHERE they want breaks, not HOW the implementation chooses to do so. Many issues are because the user scrapes a site that does not have elements with CSS indicators, or long tables that will not fit.

@Flamenco
Copy link
Contributor

Once a nice API is ready, it will be trivial to wrap the existing implementations in them. They could reuses an abstract pagination and image logic as well.

@eKoopmans
Copy link
Contributor Author

Hah I may have just the thing for the CORS issue, I'll have to tinker around with it when I have the chance. I'll think about your suggestions for pagination and image quality. Thanks again!

@Flamenco
Copy link
Contributor

A simple image callback that is registered will suffice, but it means the render call will need to be async.

@raghbendra2015
Copy link

raghbendra2015 commented Jun 28, 2017

Is it fixed now and all features are available in jsPDF or still have some issues .?

Because I am facing these three still:

Pagination: pages break automatically, with margins, and you can add custom page-breaks with the html2pdf__page-break class

Image Quality: I have an open pull request on html2canvas to add custom resolution, which is accessible through html2pdf

Tables, SVG, Fonts: relies on how well html2canvas does each, which is mostly "not bad"

I am using below code:

$(function() {
    $('#download_as_pdf').click(function() {
        var pdf = new jsPDF('lanscape');
        var options = { pagesplit: true,'background': '#fff' };
        pdf.addHTML($('#customer_report_section'), options, function() {
            pdf.save("<?php echo $username . ".pdf"; ?>");
        });
    });
});

@eKoopmans
Copy link
Contributor Author

Hi @raghbendra2015, I haven't worked any of these changes into jsPDF yet, but I will soon! I was describing fixes I've made in my separate package, html2pdf, but @Flamenco suggested changing that package so it could be incorporated directly into jsPDF. And I'm almost ready to do that!

@raghbendra2015
Copy link

@eKoopmans, thanks for your reply I am using below code which is updated:

    $('#download_as_pdf').click(function() {;
        var pdf = new jsPDF('p', 'pt', 'a4'); // basic create pdf
        pdf.internal.scaleFactor = 4; // play with this value

        pdf.addHTML(document.getElementById('customer_report_section'), {pagesplit: true, retina: true, background: '#fff'}, function () { // addHtml with automatic pageSplit
            //var out = pdf.save('dataurlnewwindow');
            // output format of your pdf -> there are a lot blob, base64....
            pdf.save("<?php echo $username . ".pdf"; ?>");
        });
    });

But Facing the same issue regarding the format so is there any other way to handle this or I should go for other options, please suggest if we can fix it.
AB Mauri (3).pdf

@eKoopmans
Copy link
Contributor Author

For now I would recommend trying html2pdf, and make sure to use the versions of the dependencies included in the vendor directory there. You can create a PDF from your component like so:

html2pdf(document.getElementById('customer_report_section'));

@rahulbussa
Copy link

@eKoopmans Thanks for great plugin , but i am not able to generate pdf for large html data.Any help

@eKoopmans
Copy link
Contributor Author

Hi @rahulbussa, html2pdf currently relies on html2canvas to generate an image from the HTML. Take a look at the issues there, I know there have been other concerns with slow performance/etc. Good luck!

@marc-y-marc
Copy link

@eKoopmans

Dear sir,
It is again proven that not all hero's wear a cape these days (or do you? ;-).

My god, you have done an amazing job in simplifying this whole struggle to generate PDF's from HTML via javascript. I have had so many issues untill i came across this post.

Thank you so much mister!

@eKoopmans
Copy link
Contributor Author

You're welcome, citizen! Hah no capes, but I'm always happy to help. Take it easy.

@gpatki
Copy link

gpatki commented Sep 6, 2017

Hello!

I have a query regarding stylesheet support.
I have css linked in the html that I would be passing for conversion.
Will the generated pdf pick these up?
Please do point to any link with example for the same if any.
Currently my pdf is generating without the styles.

Thanks in advance!

@keyurshubham2014
Copy link

Hello. I want to use this in reactJs. can you suggest me examples for that and how to import html2pdf in react?

@pavitrakumar78
Copy link

pavitrakumar78 commented Oct 10, 2017

@eKoopmans hey! Nice work! Both your implementation and the original html2pdf are basically :
DOM -> html2canvas -> canvas -> add as an Image in jsPDF - right?... So these are not zoomable pdfs, correct?

@eKoopmans
Copy link
Contributor Author

@pavitrakumar78, yes it converts to an image first. In a perfect world we'd be able to go straight from HTML to PDF (which apparently is/was in the works in jsPDF), but I think HTML's just too complex to do that realistically.

I did make a modification to html2canvas to add a 'DPI' feature, so you can increase the resolution of the images at least.

@eKoopmans
Copy link
Contributor Author

@gpatki really sorry I never got back to you! Yes the styles should work, can you send me an example of it not working? It would be best if you opened an issue on the html2pdf page - it could be a bug/problem with the package!

@keyurshubham2014 also sorry I never responded! html2pdf should play nicely with React - just in your HTML you need to include the <script> tags mentioned in the readme, and on whatever event you want to create the PDF (e.g. a button click), you call html2pdf() giving whatever DOM element you want to print.

@moises-morales
Copy link

@eKoopmans Hey!

I have been using your JS Package, and it's very useful, but i have a question,
how can i add header and footer?
I've seen, i could add them with jsPDF with the method fromHTML, like the next example:

https://plnkr.co/edit/trrLAn6I9o2OwSkRFBZK?p=preview

I hope that i can explained me.

Thanks.

@Uzlopak
Copy link
Collaborator

Uzlopak commented Sep 6, 2018

Hi @eKoopmans

i completely refactored the context2d plugin. It is not "perfect" but it is now mainly resulting in the same behaviour like a canvas element. So maybe we can modify your plugin to use context2d?

See #1931

(I will merge it asap when i know why even though all tests run perfectly I get an IE11 error)

@Uzlopak
Copy link
Collaborator

Uzlopak commented Sep 6, 2018

TBH we should deprecate addHTML and fromHTML and focus on your plugin. I really dont think that we will ever "support" addHTML and fromHTML

@Uzlopak
Copy link
Collaborator

Uzlopak commented Sep 6, 2018

@eKoopmans

Can we call the method maybe html?

@Uzlopak
Copy link
Collaborator

Uzlopak commented Sep 8, 2018

I created a html-plugin based on your html2pdf code

https://github.com/MrRio/jsPDF/blob/master/plugins/html.js

@Flamenco
Copy link
Contributor

Flamenco commented Sep 8, 2018

@arasabbasi Is the context2d pageWrap code working? I can't remember if it was experimental or not.

@Uzlopak
Copy link
Collaborator

Uzlopak commented Sep 8, 2018

Yes. Had to figure out what the problem actually was. First of all html2canvas is just taking the visible area of the html code. So you would have to give the correct parameters so that it will convert all html-elements without hiding them by clipping them. And second we had in canvas.js the setter for width and height effecting the pageWrapY and pageWrapX properties of context2d. html2pdf seems to call at one point the setters for width and height of canvas resulting in messing up with the pageWrapY and pageWrapX and thus resulting in no pageBreak at all. See:

image

Next Step would be to find a solution for "overflowing" elements. Because pageWrap works, but not perfect. I thought today about possible solutions... alot were hacky and about cutting elements in parts and so. Then I realized it is much simpler to solve:

For example, we have a rectangle which overflows over two pages on the Y-Axis. We dont need to make hefty calculations. We just put the rectangle on the correct position in the first page and put a duplicate of the rectangle on the second page with y - heightOfVisibleAreaOnPage1. We could do this with all even more complex elements.

I didnt delete addhtml.js and fromhtml.js. Maybe fromhtml is still as a lightweight html to pdf parser useful.. i dont know yet.

Another problem is, that the IE support is horrible as usual. And to be honest... I hate to optimize for IE. And the cherry on top is, that you can not just simply open the example files because IE can not show dataurl based pdfs. pain in the ass.

@Flamenco
Copy link
Contributor

Flamenco commented Sep 8, 2018

We made a decision 5 years ago to not support IE at all. We told all our clients to install Firefox or Chrome. The few that complained actually thanked us in the end for weaning them off IE. Obviously no complaints from the developers either.

Hey, your refactoring looks great. I have a unpublished fork of context2d that is now going to be a PITA to merge in though...

BTW I finally got around to publishing one of the sites that context2d was written for: https://www.quickchords.org/collections/. Rendering to c2d/canvas instead of PDF for preview is at least 10x faster. Fast enough for near real time rendering.

@eKoopmans
Copy link
Contributor Author

Hi @arasabbasi, thanks for your updates! A few questions:

  1. jsPDF's context2d is news to me! Is the idea that it should behave exactly like a canvas' context?
  2. Does it draw vectors into the PDF, or images? E.g. if I do ctx.fillText('Hello', 0, 0), will the text be selectable in the PDF?
  3. If it's vectors, awesome!

In terms of porting html2pdf into a jsPDF plugin, that was always the goal, so thank you for taking the initiative! It wasn't really possible until my big API upgrade with v0.9.0.

That said I think it may be a simpler process of including html2pdf as a dependency in package.json, and writing a simple wrapper script that maps some of the API onto jsPDF, but I'm not too sure of the standard procedure for plugins here.

Thanks again!

@Uzlopak
Copy link
Collaborator

Uzlopak commented Sep 11, 2018

Hi @eKoopmans,

  1. Originally @Flamenco wrote it. It is mimicking context2d interface. So yes. It makes the jsPDF behave like a canvas context.
  2. vectors and text.
    Your ctx.fillText will result in a call to context2d.fillText which then calls jsPDF.API.text

I put html.js as a plugin but html2canvas as dependency into package.json ;).

@Uzlopak
Copy link
Collaborator

Uzlopak commented Dec 4, 2018

@eKoopmans
@Flamenco

I refactored context2d (again, lol). Now the context2d has a property "autoPaging". If set to true, the pdf becomes so to speak a huge canvas.

Check it out.

@eKoopmans
Copy link
Contributor Author

Hey @arasabbasi, thanks again for your work on the .html() plugin! I'm in the process of merging the jsPDF canvas functionality directly into html2pdf.js, but I'm not getting the results I'd expect. Could you have a look at this fiddle:
https://jsfiddle.net/eKoopmans/egm94jqh/

Notice that the PDF has unusual spacing on the text - it seems like there's some scaling gone wrong. This behaviour happens using the .html() plugin as well as using html2canvas directly with the jsPDF canvas.

I'm excited to get this working!

@try-it-atleast-onces
Copy link

@arasabbasi I also have the issue with text, but for me the text is merging with one another. For Example if I have 10 words in a like, first and last word of the words are merging at few places.

I am using the .html() method to print the PDF. Have you faced the issues like this, Your help is much appreciated.

@bekab95
Copy link

bekab95 commented Feb 18, 2019

@eKoopmans thanks very much !! I have spent days to get correct pdf without your plugin

@SorenV
Copy link

SorenV commented Feb 19, 2019

@eKoopmans Thank you for your work on this! Looking at your fiddle it seems the jsPDF constructor default unit of 'mm' is what is causing the weird spacing.

var doc = new jsPDF('p', 'pt', 'letter');

I'm still working through familiarization with the docs, but it currently isn't clear to me how the unit value affects the document elements. For instance, 'pt' looks right, but 'px' and 'mm' both do not. You know more about the internals so I thought I would bring this to your attention. Thanks again!

@umbe1987
Copy link

umbe1987 commented Feb 21, 2019

First of all, thanks for this amazing project!
Is it possible to resize the HTML div passed to .html() and not having it to fit the entire PDF page?
My goal would be to add a table elem beside an image. Was looking in the options, but could not find anything related to (e.g.) width, or height.

@Uzlopak
Copy link
Collaborator

Uzlopak commented Feb 21, 2019

@eKoopmans
I am right now reorganizing my life and have not much capacity to do this until the beginning of next month. But I am aware of your request.

@eKoopmans
Copy link
Contributor Author

Hi @arasabbasi, understood - I know how that goes. And @SorenV thanks, that was a helpful direction to look!

I can tell now what the issue is (same thing that was reported by @Peppe87 before me). Units are getting applied incorrectly. I have made a new issue #2294 to follow up.

@JordWyatt
Copy link

JordWyatt commented Feb 26, 2019

Has anybody else experienced issues with the callback option of the new html method not being executed when using Bluebird? From playing around with local examples using native promises results in the callback executing as expected, but when using Bluebird it fails silently.

This may be coincidental but it is the only change I am making which causes the CB to fail.

Edit:

I've narrowed this down as an issue with using Bluebird promises.

I modified jspdf.debug.js to take a 'promise' argument:

 jsPDFAPI.html = function (src, options) {
      ...
      Promise = options.promise || Promise
      ...
    };

And then passed in an alternative implementation (es6 Promise in this case) into the options object

 doc.html(anchor, {
    promise: es6Promise.Promise,
    callback: function(pdf) {
      pdf.save("test.pdf");
    }
  });

Passing in bluebird's Promise object results in the callback not being called.

@Chedvaka
Copy link

Chedvaka commented May 5, 2019

Hi,
I am using the library and I have one problem:
when I trying to print in Asian language I get something like this vî ŒÇŠ ˜¨ –ª {I }� •USÍe?
{V šØ N-^¦ Nãx¼[Æ^¦ f� S×e; dÊv„
Even though I added the font to the jspdf.

@Uzlopak
Copy link
Collaborator

Uzlopak commented May 8, 2019

@Chedvaka
Supply a working test case...

@pedrovsp
Copy link

pedrovsp commented Jun 3, 2019

Is there any work towards setting margins to the pdf? All the solutions I've seen so far consists in inserting an image with margins into the pdf, which I don't think is the optimal approach since the file size increases significantly.

@vinodsamy
Copy link

How to convert gibberish pdf raw data to original pdf file:
%PDF-1.3
%ºß¬à
3 0 obj
<</Type /Page
/Parent 1 0 R
/Resources 2 0 R
/MediaBox [0 0 595.28 841.89]
/Contents 4 0 R

endobj
4 0 obj
<<
/Length 10

stream
0.20 w

@mateuszrusiecki
Copy link

mateuszrusiecki commented Jun 25, 2019

@pedrovsp Did you try to use fromHTML and set margins there? For example:

var htmlString =" some html ";
var doc = new jsPDF('landscape','pt');
doc.fromHTML(htmlString,10,10,{}, {}, {
    top: 10,
    bottom: 10
});
...

@haseebuddin
Copy link

haseebuddin commented Oct 22, 2019

can i get help on this url while generating pdf it didnt show much positive outcome on pdf while using html2canvas https://testing.resumepagepro.com/, can i get some help for this . thanks if you want you can test it out by clicking the "Download as PDF" button

kittsville added a commit to kittsville/deedpoll.lgbt that referenced this issue Dec 1, 2019
`fromHTML` is deprecated so this solution is only a stop-gap. Will need to use `html` as described in parallax/jsPDF#1176
#3
@saugatUNO
Copy link

Why is the font changing to the Times new roman when using html2pdf. I am using in Vue.js with vuetify, where the font is set to "Robot" , then sans-serif??

@Staremang
Copy link

@saugatUNO I had a similar problem.
You have to to integrate a custom font (use fontconverter):

const font = '...'; // Halvar-Breitschrift-Bold-Web.ttf as base64
doc.addFileToVFS('Halvar-Breitschrift-Bold-Web.ttf', font);
doc.addFont('Halvar-Breitschrift-Bold-Web.ttf', 'halvar breitschrift', 'bold');

It's also important to make fontName in lower case (halvar breitschrift).
Then these styles will work:

.text {
  font-family: 'Halvar Breitschrift', sans-serif;
  font-weight: bold;
}

@saugatUNO
Copy link

@Staremang Thank you for your reply.
But I do not know how to integrate the custom font in my method. (I used fontconverter which gave .js file of the font)
my function is
{

var element = div
        var opt = {
            margin: 0,
            filename: filename,
            image: { type: 'jpeg', quality: 0.98 },
            html2canvas: {
                scale: 1.98,
                useCORS: true,
                optimized: false,
                allowTaint: false
            },
            jsPDF: { unit: 'in', format: 'letter', orientation: 'landscape' }
        }

        html2pdf().from(element).set(opt).save()

}

@Zankhna95
Copy link

Zankhna95 commented May 18, 2020

Hello!

I have a query regarding stylesheet support.
I have css linked in the html that I would be passing for conversion.
Will the generated pdf pick these up?
Please do point to any link with example for the same if any.
Currently my pdf is generating without the styles.

Thanks in advance!

I am also facing same issue. Is there any way to link css/scss file to this plugin? @eKoopmans

@github-actions
Copy link

This issue is stale because it has been open 90 days with no activity. It will be closed soon. Please comment/reopen if this issue is still relevant.

@austinjl
Copy link

austinjl commented Sep 10, 2020

I've yet to find a working solution for setting page margins. This is what I see in the documentation but it is not working for me

var pdf = new jsPDF('p', 'pt', 'letter');
pdf.html(document.querySelector('#render_me'), {
callback: async function(pdf) {
pdf.save('sample-file.pdf');
},
margin: [25, 50, 25, 50]
});

http://raw.githack.com/MrRio/jsPDF/master/docs/module-html.html#~html

@HackbrettXXX
Copy link
Collaborator

#2924

@HackbrettXXX HackbrettXXX unpinned this issue Sep 24, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests