sreeraj nyros
sreeraj nyros

Reputation: 979

How to set image to fit width of the page using jsPDF?

Is there any way to solve this? I tried to set width and height in mm. How can I set it to full-width?

Upvotes: 74

Views: 274769

Answers (19)

Jun Santilla
Jun Santilla

Reputation: 21

I encountered the same issue when working with dom-to-image with jsPDF. I had an image from my html perfectly sized to match my A4 PDF, but upon downloading the PDF, the image is slightly blurry. To address this, I doubled the scale of the image, then fit it to my PDF size before generating.

 const generatePDFfromHTML = async (htmlContentId: string, outputPath: string) => {
    const input = document.getElementById(htmlContentId)
    const scale = 2

    if (!input) {
        console.error(`HTML element with ID ${htmlContentId} not found.`)
        return
    }

    const scaledWidth = input.offsetWidth * scale
    const scaledHeight = input.offsetHeight * scale

    try {
        const dataUrl = await domtoimage.toPng(input, {
            width: scaledWidth,
            height: scaledHeight,
            style: {
                transform: "scale(" + scale + ")",
                transformOrigin: "top left",
            },
        })

        const pdf = new jsPDF()

        const img = new Image()
        img.src = dataUrl

        img.onload = () => {
            const pdfWidth = pdf.internal.pageSize.getWidth()
            const pdfHeight = (img.height * pdfWidth) / img.width
            pdf.addImage(dataUrl, "PNG", 0, 0, pdfWidth, pdfHeight)
            pdf.save(outputPath)
        }
    } catch (error) {
        console.error("Error generating PDF:", error)
    }
}

Upvotes: 0

Badal Saibo
Badal Saibo

Reputation: 3665

The trick is to make the canvas close to the desired size.

const handleDownloadCertificate = async () => {
  const domElement = document.getElementById('certificate');

  if (domElement) {
    try {
      const canvas = await html2canvas(domElement, { scale: 2 });
      const imgData = canvas.toDataURL('image/png');

      const componentWidth = domElement.clientWidth;
      const componentHeight = domElement.clientHeight;

      const ratio = componentHeight / componentWidth;

      const width = 210; // in mm A4
      let height = 297; // in mm A4

      height = ratio * width;

      const pdf = new JSPDF('p', 'mm', [width, height]);

      pdf.addImage(imgData, 'JPEG', 0, 0, width, height);

      const fileName = getCertificateName({ course, name });
      
      pdf.save(`${fileName}.pdf`);
      
    } catch (error) {
      console.error('Error building PDF', error);
    }
  }
};

Upvotes: 1

Sudhakar Reddy
Sudhakar Reddy

Reputation: 348

Change the PDF page width to size of the image as follows

const exportOrgChartPdfFormat = async (response) => {
  try {
    const blob = new Blob([response.data], { // response.data contains blob
      type: "image/png",
    });
    const reader = new FileReader();
    reader.onloadend = () => {
      const base64data = reader.result;
      const img = new Image();
      img.onload = function () {
        const imgWidth = this.width;
        const imgHeight = this.height;
        const pdf = new jsPDF(
          imgWidth > imgHeight ? "l" : "p",
          "pt",
          [imgWidth, imgHeight]
        );
        **pdf.internal.pageSize.width = imgWidth;**
        **pdf.internal.pageSize.height = imgHeight;**
        pdf.addImage(
          base64data,
          "PNG",
          0,
          0,
          imgWidth,
          imgHeight
        );
        pdf.save("export.pdf");
      };

      img.src = base64data;
    };
    reader.readAsDataURL(blob);
  } catch (e) {
    return e;
  }
};

Upvotes: 0

Rafal
Rafal

Reputation: 45

Some answers above didn't cover responsive pages for html2canvas. If you need to resize your image based on changing element dimensions use this code:

html2canvas(input, { scrollY: -window.scrollY }).then((canvas) => {
  const imgData = canvas.toDataURL('image/jpeg');
  const pdf = new jsPDF('p', 'mm', 'a4');
  const imgProps = pdf.getImageProperties(imgData);

  // doc and image dimensions
  const pdfWidth = pdf.internal.pageSize.getWidth();
  const pdfHeight = pdf.internal.pageSize.getHeight();
  const imgWidth = imgProps.width;
  const imgHeight = imgProps.height;

  // claculating right scale to fit the page
  const scale = Math.max(pdfHeight / imgHeight, pdfWidth / imgWidth);
  const finalWidth = imgWidth * scale;
  const finalHeight = imgHeight * scale;
  const leftMargin = (pdfWidth - finalWidth) / 2;
  // add image with alias and compression of your choice
  pdf.addImage( imgData, 'PNG', leftMargin, 0, finalWidth, finalHeight, 'alias', 'FAST');
  pdf.save( 'report-name.pdf' );
});

Upvotes: 3

Philip
Philip

Reputation: 890

If you need to get width 100% of PDF file and auto height you can use getImageProperties property of html2canvas library

html2canvas(input)
      .then((canvas) => {
        const imgData = canvas.toDataURL('image/png');
        const pdf = new jsPDF({
          orientation: 'landscape',
        });
        const imgProps= pdf.getImageProperties(imgData);
        const pdfWidth = pdf.internal.pageSize.getWidth();
        const pdfHeight = (imgProps.height * pdfWidth) / imgProps.width;
        pdf.addImage(imgData, 'PNG', 0, 0, pdfWidth, pdfHeight);
        pdf.save('download.pdf');
      });

Upvotes: 89

EdwardMoses
EdwardMoses

Reputation: 1

convertPhotoImageToPdf(imgPath){
   var doc = new jsPDF(undefined, null, null, true);
   var imgData = 'data:image/jpeg;base64,'+ imgPath;
   doc.addImage(imgData, 'JPEG',10,10,0,0,null,'FAST',0);
   //if want add one page 
   //doc.addPage()
   doc.save('testPdf.pdf')
}

Upvotes: -1

        var width = doc.internal.pageSize.width;    
        var height = doc.internal.pageSize.height;

Upvotes: -2

MoppieMop
MoppieMop

Reputation: 81

There where a lot of solutions with .addImage, I tried many of them, but nothing worked out. So I found the .html() function and after some tries the html2canvas-scale-option. This solution is no general solution, you have to adjust the scale so it will fit your pdf, but this is the only solution, which works for me.

var doc = new jspdf.jsPDF({
    orientation: 'p',
    unit: 'pt',
    format: 'a4'
});

doc.html(document.querySelector('#styledTable'), {
    callback: function (doc) {
        doc.save('file.pdf');
    },
    margin: [60, 60, 60, 60],
    x: 32,
    y: 32,
    html2canvas: {
        scale: 0.3, //this was my solution, you have to adjust to your size
        width: 1000 //for some reason width does nothing
    },
});

Upvotes: 5

Chris
Chris

Reputation: 1139

my solution is to check what ratio (height or width) need to apply depending of the pdf orientation and image size. note: set margin to 0 if no margin is required.

   const imgData = canvas.toDataURL("image/jpeg");

   const pdf = new jsPDF({
       orientation: "portrait", // landscape or portrait
       unit: "mm",
       format: "a4",
   });
   const imgProps = pdf.getImageProperties(imgData);
   const margin = 0.1;

   const pdfWidth = pdf.internal.pageSize.width * (1 - margin);
   const pdfHeight = pdf.internal.pageSize.height * (1 - margin);

   const x = pdf.internal.pageSize.width * (margin / 2);
   const y = pdf.internal.pageSize.height * (margin / 2);

   const widthRatio = pdfWidth / imgProps.width;
   const heightRatio = pdfHeight / imgProps.height;
   const ratio = Math.min(widthRatio, heightRatio);
   
   const w = imgProps.width * ratio;
   const h = imgProps.height * ratio;

   pdf.addImage(imgData, "JPEG", x, y, w, h);

Upvotes: 3

Igor Ryan
Igor Ryan

Reputation: 111

Solution for all screen sizes and dynamic orientation:

import html2canvas from 'html2canvas'
import jsPDF from 'jspdf'

export default async function downloadComponentInPDF(Component: HTMLElement) {
  await html2canvas(Component).then((canvas) => {
    const componentWidth = Component.offsetWidth
    const componentHeight = Component.offsetHeight

    const orientation = componentWidth >= componentHeight ? 'l' : 'p'

    const imgData = canvas.toDataURL('image/png')
    const pdf = new jsPDF({
    orientation,
    unit: 'px'
  })

    pdf.internal.pageSize.width = componentWidth
    pdf.internal.pageSize.height = componentHeight

    pdf.addImage(imgData, 'PNG', 0, 0, componentWidth, componentHeight)
    pdf.save('download.pdf')
  })
}

Upvotes: 11

Priyanshu Chauhan
Priyanshu Chauhan

Reputation: 5535

This is how I've achieved in Reactjs.

Main problem were ratio and scale If you do a quick window.devicePixelRatio, it's default value is 2 which was causing the half image issue.

const printDocument = () => {
  const input = document.getElementById('divToPrint');
  const divHeight = input.clientHeight
  const divWidth = input.clientWidth
  const ratio = divHeight / divWidth;

  html2canvas(input, { scale: '1' }).then((canvas) => {
    const imgData = canvas.toDataURL('image/jpeg');
    const pdfDOC = new JSPDF("l", "mm", "a0"); //  use a4 for smaller page

    const width = pdfDOC.internal.pageSize.getWidth();
    let height = pdfDOC.internal.pageSize.getHeight();
    height = ratio * width;

    pdfDOC.addImage(imgData, 'JPEG', 0, 0, width - 20, height - 10);
    pdfDOC.save('summary.pdf');   //Download the rendered PDF.
  })
}

Upvotes: 3

thephper
thephper

Reputation: 2610

If you want a dynamic sized image to automatically fill the page as much as possible and still keep the image width/height-ratio, you could do as follows:

let width = doc.internal.pageSize.getWidth()
let height = doc.internal.pageSize.getHeight()

let widthRatio = width / canvas.width
let heightRatio = height / canvas.height

let ratio = widthRatio > heightRatio ? heightRatio : widthRatio

doc.addImage(
  canvas.toDataURL('image/jpeg', 1.0),
  'JPEG',
  0,
  0,
  canvas.width * ratio,
  canvas.height * ratio,
)

Upvotes: 9

vohrahul
vohrahul

Reputation: 1191

A better solution is to set the doc width/height using the aspect ratio of your image.

var ExportModule = {
  // Member method to convert pixels to mm.
  pxTomm: function(px) {
    return Math.floor(px / $('#my_mm').height());
  },
  ExportToPDF: function() {
    var myCanvas = document.getElementById("exportToPDF");

    html2canvas(myCanvas, {
      onrendered: function(canvas) {
        var imgData = canvas.toDataURL(
          'image/jpeg', 1.0);
        //Get the original size of canvas/image
        var img_w = canvas.width;
        var img_h = canvas.height;

        //Convert to mm
        var doc_w = ExportModule.pxTomm(img_w);
        var doc_h = ExportModule.pxTomm(img_h);
        //Set doc size
        var doc = new jsPDF('l', 'mm', [doc_w, doc_h]);

        //set image height similar to doc size
        doc.addImage(imgData, 'JPG', 0, 0, doc_w, doc_h);
        var currentTime = new Date();
        doc.save('Dashboard_' + currentTime + '.pdf');

      }
    });
  },
}
<script src="Scripts/html2canvas.js"></script>
<script src="Scripts/jsPDF/jsPDF.js"></script>
<script src="Scripts/jsPDF/plugins/canvas.js"></script>
<script src="Scripts/jsPDF/plugins/addimage.js"></script>
<script src="Scripts/jsPDF/plugins/fileSaver.js"></script>
<div id="my_mm" style="height: 1mm; display: none"></div>

<div id="exportToPDF">
  Your html here.
</div>

<button id="export_btn" onclick="ExportModule.ExportToPDF();">Export</button>

Upvotes: 1

Weihui Guo
Weihui Guo

Reputation: 3997

It's easy to fit the page if you only have one image. The more challenge task is to fit images with various sizes to a pdf file. The key to archive that is to calculate the aspect ratio of the image and relative width/height ratio of the page. The following code is what I used to convert multiple images online to a PDF file. It will rotate the image(s) based on the orientation of the images/page and set proper margin. My project use images with online src. You should be able to modify to suit your needs.

As for image rotation, if you see a blank page after rotation, it could simply be that the image is out of bounds. See this answer for details.

function exportPdf(urls) {
    let pdf = new jsPDF('l', 'mm', 'a4');
    const pageWidth = pdf.internal.pageSize.getWidth();
    const pageHeight = pdf.internal.pageSize.getHeight();
    const pageRatio = pageWidth / pageHeight;

    for (let i = 0; i < urls.length; i++) {
        let img = new Image();
        img.src = urls[i];
        img.onload = function () {
            const imgWidth = this.width;
            const imgHeight = this.height;
            const imgRatio = imgWidth / imgHeight;
            if (i > 0) { pdf.addPage(); }
            pdf.setPage(i + 1);
            if (imgRatio >= 1) {
                const wc = imgWidth / pageWidth;
                if (imgRatio >= pageRatio) {
                    pdf.addImage(img, 'JPEG', 0, (pageHeight - imgHeight / wc) / 2, pageWidth, imgHeight / wc, null, 'NONE');
                }
                else {
                    const pi = pageRatio / imgRatio;
                    pdf.addImage(img, 'JPEG', (pageWidth - pageWidth / pi) / 2, 0, pageWidth / pi, (imgHeight / pi) / wc, null, 'NONE');
                }
            }
            else {
                const wc = imgWidth / pageHeight;
                if (1 / imgRatio > pageRatio) {
                    const ip = (1 / imgRatio) / pageRatio;
                    const margin = (pageHeight - ((imgHeight / ip) / wc)) / 4;
                    pdf.addImage(img, 'JPEG', (pageWidth - (imgHeight / ip) / wc) / 2, -(((imgHeight / ip) / wc) + margin), pageHeight / ip, (imgHeight / ip) / wc, null, 'NONE', -90);
                }
                else {

                    pdf.addImage(img, 'JPEG', (pageWidth - imgHeight / wc) / 2, -(imgHeight / wc), pageHeight, imgHeight / wc, null, 'NONE', -90);
                }
            }
            if (i == urls.length - 1) {
                pdf.save('Photo.pdf');
            }
        }
    }
}

If this is a bit hard to follow, you can also use .addPage([imgWidth, imgHeight]), which is more straightforward. The downside of this method is that the first page is fixed by new jsPDF(). See this answer for details.

Upvotes: 6

Purushoth
Purushoth

Reputation: 2793

You can get the width and height of PDF document like below,

var doc = new jsPDF("p", "mm", "a4");

var width = doc.internal.pageSize.getWidth();
var height = doc.internal.pageSize.getHeight();

Then you can use this width and height for your image to fit the entire PDF document.

var imgData = 'data:image/jpeg;base64,/9j/4AAQSkZJ......';

doc.addImage(imgData, 'JPEG', 0, 0, width, height);

Make sure that your image has the same size (resolution) of the PDF document. Otherwise it will look distorted (stretched).

If you want convert px to mm use this link http://www.endmemo.com/sconvert/millimeterpixel.php

Upvotes: 140

zerzevul
zerzevul

Reputation: 429

My answer deals with a more specific case of what you are asking but I think one could draw some ideas from this to apply more generally. Also, I would post this as a comment to Purushoth's answer (on which mine is based), if only I could.

Ok, so my problem was how to fit a web page into the pdf document, without losing the aspect ratio. I used jsPDF in conjuction with html2canvas and I calculated the ratio from my div's width and height. I applied that same ratio to the pdf document and the page fit perfectly onto the page without any distortion.

var divHeight = $('#div_id').height();
var divWidth = $('#div_id').width();
var ratio = divHeight / divWidth;
html2canvas(document.getElementById("div_id"), {
     height: divHeight,
     width: divWidth,
     onrendered: function(canvas) {
          var image = canvas.toDataURL("image/jpeg");
          var doc = new jsPDF(); // using defaults: orientation=portrait, unit=mm, size=A4
          var width = doc.internal.pageSize.getWidth();    
          var height = doc.internal.pageSize.getHeight();
          height = ratio * width;
          doc.addImage(image, 'JPEG', 0, 0, width-20, height-10);
          doc.save('myPage.pdf'); //Download the rendered PDF.
     }
});

Upvotes: 27

Ravi Guru
Ravi Guru

Reputation: 71

i faced same problem but i solve using this code

html2canvas(body,{
                onrendered:function(canvas){
                    var pdf=new jsPDF("p", "mm", "a4");
                    var width = pdf.internal.pageSize.getWidth();    
                    var height = pdf.internal.pageSize.getHeight();
                    pdf.addImage(canvas, 'JPEG', 0, 0,width,height);
                    pdf.save('test11.pdf');
                }
            }) 

Upvotes: 1

Max
Max

Reputation: 999

The API changed since this commit, using version 1.4.1 it's now

var width = pdf.internal.pageSize.getWidth();
var height = pdf.internal.pageSize.getHeight();

Upvotes: 11

carbon1479
carbon1479

Reputation: 310

I discovered this while experimenting with html2canvas this morning. While this doesn't include provisions for printing multiple pages it does scale the image to page width and reframes the height in ratio to the adjusted width:

html2canvas(document.getElementById('testdiv')).then(function(canvas){
        var wid: number
        var hgt: number
        var img = canvas.toDataURL("image/png", wid = canvas.width, hgt = canvas.height);
        var hratio = hgt/wid
        var doc = new jsPDF('p','pt','a4');
        var width = doc.internal.pageSize.width;    
        var height = width * hratio
        doc.addImage(img,'JPEG',20,20, width, height);
        doc.save('Test.pdf');
    });

Upvotes: 18

Related Questions