Create Multiple Page PDF with Top and Bottom Margins using jsPDF addImage

  sonic0002        2025-03-06 01:34:53       215        0          English  简体中文  繁体中文  ภาษาไทย  Tiếng Việt 

I’ve been working on a browser extension that saves web pages as PDFs in reader mode using jsPDF + html2canvas, providing a clean and distraction-free way to view them offline. Everything was going smoothly until I noticed a UI issue—there were no top and bottom margins at the page break area.

original.jpg

When converting an HTML page to a PDF using html2canvas and jsPDF, the process works by first rendering the entire webpage as a canvas. html2canvas captures everything visible in the browser and converts it into an image. This image is then added to a PDF using jsPDF.addImage().

However, if the webpage is longer than a single page in the PDF, jsPDF doesn’t automatically handle page breaks intelligently. Instead, it simply places the full image onto the first page, and if the image is too tall, it will get cut off. To support multi-page PDFs, we have to manually slice the image and position each segment correctly on separate pages.

By default, jsPDF does not leave any margins at page breaks—each new page simply picks up exactly where the previous one left off. This can create a visually awkward experience, especially if text is abruptly split between pages. To fix this, we need to manually calculate and add margins. This involves adjusting the positioning of the image for each page and using doc.rect() to mask the areas where we want padding.

The original code without margins would be like:

const imgHeight = fullHeight * scale; // height of the HTML content image
const pageHeight = 297; // A4 height in mm

let heightLeft = imgHeight;
let position = 0;
let currentPage = 1;

// if we have more than one page, we need to add padding to the top and bottom
if(imgHeight > pageHeight){
    // Additional pages
    while (heightLeft > 0) {
        doc.addImage(
            jpegDataUrl,
            'JPEG',
            0,                  // x position
            position,           // y position
            pdfWidth,           // width
            imgHeight,          // height: ensure last page fits correctly
            "",
            "FAST"
        );

        currentPage++;
        position -= pageHeight;  // Move the starting position down
        heightLeft -= pageHeight;
    
        if(heightLeft > 0){
            doc.addPage();
        }
    }
} else {
    // Single page
    ...
}

The new code which supports adding top and bottom margins

const imgHeight = fullHeight * scale; // height of the HTML content image
const pageHeight = 297; // A4 height in mm
const topPadding = 10;  
const bottomPadding = 10;
const contentHeight = pageHeight - topPadding - bottomPadding;

let heightLeft = imgHeight;
let position = topPadding;
let currentPage = 1;

// if we have more than one page, we need to add padding to the top and bottom
if(imgHeight > contentHeight){
    // Additional pages
    while (heightLeft > 0) {
        doc.addImage(
            jpegDataUrl,
            'JPEG',
            0,                  // x position
            position,           // y position
            pdfWidth,           // width
            imgHeight,          // height: ensure last page fits correctly
            "",
            "FAST"
        );

        doc.setFillColor(255, 255, 255);
        doc.rect(0, 0, pdfWidth, topPadding, 'F'); // padding top
        doc.rect(0, pageHeight - bottomPadding, pdfWidth, bottomPadding, 'F'); // padding bottom 

        currentPage++;
        position -= contentHeight;  // Move the starting position down
        heightLeft -= contentHeight;
    
        if(heightLeft > 0){
            doc.addPage();
        }
    }
} else {
    // Single page
    ...
}

The key code here is the two lines of code which mask the overflowed content due to the introduction of top and bottom padding.

doc.setFillColor(255, 255, 255);
doc.rect(0, 0, pdfWidth, topPadding, 'F'); // padding top
doc.rect(0, pageHeight - bottomPadding, pdfWidth, bottomPadding, 'F'); // padding bottom 

Example Calculation

Let’s break it down with numbers(the unit here can be mm, or others):

  • Assume the original webpage height is 180.
  • The page height is set to 100, with top and bottom margins of 10 each.
  • This means the actual content height(contentHeight) per page is 100 - 10 - 10 = 80.

First Page:

  • The position starts at 10.
  • The first 0-90 of the original webpage content is placed on the first page (since addImage() is called).
  • doc.rect() then masks the top 10 and bottom 10, leaving the 0-80 region of the original webpage visible.

Second Page:

  • The new position is (previous position - contentHeight) = (10 - 80) = -70.
  • This means the content from 70 to 170 of the original webpage is rendered on the second page.
  • Again, doc.rect() masks the 70-80(original webpage content) and 160-170 region, leaving 80-160 of the original content visible.

Since the padding creates a blank area at the start and end of each page, it ensures a clean and readable format without abrupt cuts at page breaks.

new.jpg

Hope this helps anyone facing the same issue! 

JSPDF  MULTIPLE PAGE  MARGIN  BOTTOM MARGIN  ADDIMAGE 

           

  RELATED


  0 COMMENT


No comment for this article.



  RANDOM FUN

Just before deploying to product server