Search code examples
javascriptpdfjspdf

How do I properly set the image size in jsPDF so that the image isn't blurry?


I'm trying to include an image in a PDF I'm generating with jsPDF, but the image comes out blurry. How can I avoid this and get a sharp image?

This is what the generated PDF looks like

pdf screenshot

I've tried telling jsPDF that the image is 160x60px, but it actually comes out as 285x107 pixels. Also, jsPDF says the page is 446.46 px wide. But if I measure the screenshot it's actually 793 px wide. What's going on here?

This is the code I'm running (jsfiddle link so that you can play around with it if you want)

function pdf() {
  const { jsPDF } = window.jspdf;
  
  const imgData = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAKAAAAA8CAMAAADWtUEnAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAzUExURQB/DtjY2CZF9wAm/6Kr4YKR5wBRiWl87DxX81Fp8ABHpBM2+wAu6QA+ugBoSQBbbQA10ySBQ2kAAAAJcEhZcwAADsMAAA7DAcdvqGQAAANaSURBVGhDzVeBkqswCKymVq3ttf//tY+FkETROzqvM8iMtwKbLcZIcpfzWyd2WnQTo9BNjEI3MQrdxCh0E6PQTYzC85tWelZ0E6PQTYxCNzEK3cQodBOj0E2MwvObVmqwT1dGsYpDgt2K33UjAlP1/0JRGIvfKhi+CQjMGLFX4B0JWPYp3reRLd8iS8OyT9YoGL4VyM9DQjsFLiktfFPjeaavktjwLWaFNg4FQlYwfBOga+EBewWm9GBs4nMaGEl/ZhQ7QvuA9EOkAB8KW/4va3CnwCXNhpd6wa6bdl/RGklBbpo4K7BPChv+ZwXOO094kwmEP2Ddb/NrnNOdsY2PrMA+KZhxJlDQFohH3fKe+Ws5yq8x9TbOE3+cP7Z7+sl32W7ple8ae7/zDdn7IfjiT5ITT769cfgDhda0UoNmBvvt1wd7yBJkH3MBlD7HiYlv9dWTwnb8pVuYKP6UTN4OUDQFLuikb/wgJTT+aBq0Foi7JyP5s1TKcVJAW4KCjqcCmwb9XwWiCcjcYHY0vjeDZLksrFF5AsShIK+fFgBHEf/aDF7TdM3CtC1p/KBA2nE40bdtRRQ43myNXytwSNOSBvHH9GI8LvAyoiP3q7YiCuLX9vTFArHas9/rKzosEPxl3degUA4ZvQz86ivG28n+TLWwHReIesoDMUKh+Fnh7wIPzfTB1ARKsu1cmy6Gb2Et8akCm1ZqcDuD9Hh1Kysz+GSWxJsZxukmXen6UR9/SKHkywxOze/s7SR6Y9AUSGu8+LOeal6omozz9MUq/0eOk0k/Co6TguYvs55qeAvXvD2MmEBBU6Ce22BLacSyhBDvMSc5T+tPbh71MEHW1xmV/ow4K3CeFPL4iiZQ0BY4UOcQfypthtoJV4BTST0PjvXcSF8yI3tDiT9r4ayAPBSEp3xCEyhoC6S6eAZoadVXRY/NhbVbGvVAkxeXFKAKBQkgDgYhK9RxGU2ALtnh1SCoec3UrY6QDvFs6tOvw7jePKAsDZo5sbrVkTUKja6gCdB1XCBWPVl+JTAg/xtUt7RfC7xkhXZ8q7COE57ftNKzopsYhW5iFLqJUegmRqGbGIVuYhSe37TSs6KbGIVuYhS6iVHoJkahmxiFbmIUnt+00rOimxiFbmIIdt0/eH4pjf9qBVQAAAAASUVORK5CYII=";
  const imgWidth = 160;
  const imgHeight = 60;
  const margin = 40;
  const doc = new jsPDF("p", "px", "a4");
  const pageWidth = doc.internal.pageSize.getWidth();
  console.log("pageWidth", pageWidth);
  
  doc.addImage(
    imgData,
    "PNG",
    pageWidth / 2 - imgWidth / 2,
    margin,
    imgWidth,
    imgHeight
  );
  
  doc.save("img.pdf")
}

How can I make the image render with its actual size of 160x60 px so that it's not blurry?

EDIT:

Knowing that the generated pdf is actually 793 pixels wide I can calculate the factor jsPDF is wrong by, by doing pageWidth / 793. If I use this factor to scale the width and height parameters of the image it displays correctly!

  const imgWidth = 160 * pageWidth / 793;
  const imgHeight = 60 * pageWidth / 793;
  
  doc.addImage(
    imgData,
    "PNG",
    pageWidth / 2 - imgWidth / 2,
    margin,
    imgWidth,
    imgHeight
  );

pdf screenshot, correct img size

But I worry that this is going to break on other devices. Like perhaps devices with a different DPI. I want a more robust implementation that doesn't rely on my magic 793 value.


Solution

  • Enabling the px_scaling hotfix makes it work as expected. https://github.com/MrRio/jsPDF/blob/master/HOTFIX_README.md

    See also the discussion in this GitHub issue: https://github.com/MrRio/jsPDF/issues/2927

    HackbrettXXX links a comment there that's also relevant:

    As you can see here, the scaleFactor is set to a fixed value of 1pt = 96/72 px. It does not factor in the device PPI/DPI. This behavior is "correct", since we can argue that PDF is a print medium and CSS sets the pixel ratio for print media to 1px = 1/96in