7

I am using jspdf and html2canvas combination to save html page as pdf. A pdf copy of current page is saved the moment you click a button. The problem is, if you zoom in the page, and then click the button, the saved pdf contains incomplete portion of the current page. Most of the part not visible on page due to zooming, gets cut off in the saved pdf page. What is the solution? Below is the js code being invoked upon click of save button-

var pdf = new jsPDF('l', 'pt', 'a4');
var source = $('#someId')[0];
var options = {
   background  : '#eee'
};

pdf.addHTML(source, options, function(){
pdf.save('abcd.pdf');
});

EDIT

Taking idea from Saurabh's approach, I tried quite a similar thing, but without writing code for any extra div element. Before saving to pdf I made the screen size of a fixed width, and after printing I brought back the width back to default normal. It is working fine for, if it fails, we can always fix the height of the screen too, so that it appears fine in generated pdf despite zooming. Below is the code used by me:-

var pdf = new jsPDF('l', 'pt', 'a4');
var source = $('#someId')[0];
var options = {
   background  : '#eee'
};
var width = source.clientWidth;
source.style.width = '1700px';
pdf.addHTML(source, options,                 
function(){
pdf.save('abcd.pdf');
source.style.width = width+'px';
});
shanti
  • 270
  • 1
  • 4
  • 20

2 Answers2

8

Here is how I managed to get the full page pdf while the page is zoomed in using jsPDF's new .html() method. First, I force the page zoom level back to 100% before converting it to pdf. It's important to reset the scale in html2canvas option after that, otherwise it'll returns a blank page.

<script src="https://cdnjs.cloudflare.com/ajax/libs/jspdf/1.5.3/jspdf.debug.js" 
    integrity="sha384-NaWTHo/8YCBYJ59830LTz/P4aQZK1sS0SneOgAvhsIl3zBu8r9RevNg5lHCHAuQ/"
    crossorigin="anonymous"></script>
<script src="https://html2canvas.hertzen.com/dist/html2canvas.min.js"></script>
<!-- html2canvas 1.0.0-alpha.11 or higher version is needed -->
<script>
    function download() {
        // Bring the page zoom level back to 100%
        const scale = window.innerWidth / window.outerWidth;
        if (scale != 1) {
           document.body.style.zoom = scale;            
        }

        let pdf = new jsPDF('p', 'pt', 'a4');
        pdf.html(document.getElementById('idName'), {
            html2canvas: {
                scale: 1 // default is window.devicePixelRatio
            },
            callback: function () {
                // pdf.save('test.pdf');
                window.open(pdf.output('bloburl')); // to debug
            }
        });
    }
</script>

Update: A better way is to adjust the html2canvas.scale according to the scale factor.

function download() {
    let pWidth = pdf.internal.pageSize.width; // 595.28 is the width of a4
    let srcWidth = document.getElementById('idName').scrollWidth;
    let margin = 18; // narrow margin - 1.27 cm (36);
    let scale = (pWidth - margin * 2) / srcWidth;
    let pdf = new jsPDF('p', 'pt', 'a4');
    pdf.html(document.getElementById('idName'), {
        x: margin,
        y: margin,
        html2canvas: {
            scale: scale,
        },
        callback: function () {
            window.open(pdf.output('bloburl'));
        }
    });
}
Weihui Guo
  • 3,669
  • 5
  • 34
  • 56
  • In this example should the two document.getElementById() calls be pointing to the same elem and should the let pdf be declared first in the function? I keep getting "Error: Element is not attached to a Document" – shenn Sep 14 '20 at 13:17
  • @shenn Yeah, it's a typo. It should be the same element. – Weihui Guo Sep 14 '20 at 14:15
  • 1
    typo: "let srcwidth" needs a capital W, otherwise your "better way" worked great for me! – wildhart Feb 23 '21 at 21:06
3

I was going through the same problem, To do this what I did is I made a copy of printing div and while clicking print button I attached div copy to my dom with margin-top:500px

After I got its image then I hide this copy of the div, and set margin-top:0px

I hope this will work for you.

Saurabh Agrawal
  • 7,581
  • 2
  • 27
  • 51
  • 2
    Wha was the logic behind giving top margin? Was your page's upper portion being cut off? – shanti Jan 25 '17 at 12:40
  • 1
    give me a sample fiddle with above mentioned issue. I will give you solution in that. – Saurabh Agrawal Jan 25 '17 at 12:44
  • by default the second image will have style **diplay:none;margin-top:0px ** ie it will be hidden, when you will click print image button then its change its style to **diplay:block;margin-top:500px ** ie it will be visible to bottom of the page. once you get your canvas now replace back its style to **diplay:none;margin-top:0px ** that will work – Saurabh Agrawal Jan 27 '17 at 04:52
  • I will post a sample fiddle demonstrating the issue, on Monday. Right now unable to do so due to some reasons. – shanti Jan 27 '17 at 05:03
  • ok @shanti. I believe this is the best logic to do it. Let me know when you post fiddle I will implement this solution in fiddle. – Saurabh Agrawal Jan 27 '17 at 05:25
  • Hi, please try the code at this link. http://jsfiddle.net/v7xc24re/15 You will notice that if you zoom in on the page, the portion in printed pdf will get cut off. – shanti Jan 30 '17 at 06:59
  • can I solve it logically? will you accepet that? I am going to implement some logic there, then that will work fine as per your requirements, – Saurabh Agrawal Jan 30 '17 at 10:21
  • Yes, as long as it gets implemented using jspdf way, even through some workaround, it would be fine. Just that pdf should get generated in exactly same way that gets generated without zooming. – shanti Jan 30 '17 at 10:23
  • 1
    That works as expected in jsfiddle site, but doesn't work in actual code in my IDE. Any idea what could be the difference? I will anyway, accept this answer after some time since the solution is demonstrable and at least it gives some clue. – shanti Jan 30 '17 at 11:26
  • Thanks @shanti before implementing it directly to your project. first don't hide div container2 just print as it is you want with width 550px, and then check if it displaying properly in html format, at the bottom of your page. – Saurabh Agrawal Jan 30 '17 at 11:29
  • Oh thanks for that comment. I had entirely missed your hidden div logic. Had seen only the code written in script. Let me implement it and see. – shanti Jan 30 '17 at 11:34
  • Keeping another copy of the div seemed a bit odd to me. What I did is, before printing I got the element, and set it's width to be some fixed px, and reset the width to original size after the save command. As of now, it's working, will test for various screen sizes. Accepting your answer now :) – shanti Jan 30 '17 at 13:26
  • Hi @shanti Thanks for accepting my answer, But why didn't you give me +100 reputation for this question? I didn't get +100 reputation for this question, – Saurabh Agrawal Jan 31 '17 at 04:00
  • Done. Actually, the bounty awarding option was not available in the stack exchange app, that I was using all along here to comment. Now, when I logged into the website, I could use that option. Thanks a lot for all your help. I will slightly edit the question to include my approach taken. – shanti Jan 31 '17 at 05:18
  • @SaurabhAgrawal if the content is looped five time means it prints only the current view of the screen – Vignesh Aug 17 '17 at 12:33
  • @Vignesh I will check it once i get some time. My appologies for it – Saurabh Agrawal Aug 17 '17 at 13:20
  • @saurabh Agrawal I created a plunker and posted in above post please check it. – Vignesh Aug 17 '17 at 16:15
  • @Vignesh please post a seperate question for it. :) – Saurabh Agrawal Aug 18 '17 at 04:02
  • @SaurabhAgrawal this question is posted by another person.refer the link https://stackoverflow.com/questions/45713398/download-a-webpage-in-pdf-format-using-angular4 – Vignesh Aug 18 '17 at 04:05
  • @saurabhAgrawal I created a plunker and posted in above post please check it. – Vignesh Aug 18 '17 at 05:12