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

English 简体中文 繁体中文 ภาษาไทย Tiếng Việt
Summary

When generating multi-page PDFs from HTML content using html2canvas and jsPDF, a common challenge is the absence of automatic top and bottom margins at page breaks, leading to visually awkward content splits. The process typically involves rendering the entire webpage as a single canvas image, then manually slicing and positioning segments onto individual PDF pages with jsPDF.addImage(). To implement consistent margins, developers must adjust the addImage y-position for each page, accounting for desired topPadding and bottomPadding. The key technique involves using doc.setFillColor(255, 255, 255) and doc.rect() to mask the content that would otherwise overflow into the margin areas, effectively creating clean, readable padding at the top and bottom of each page. This ensures a professional document appearance without abrupt content truncation.

我一直在開發一個 瀏覽器擴充功能,它使用 jsPDF + html2canvas 以閱讀模式將網頁儲存為 PDF,提供一種乾淨且無干擾的離線檢視方式。一切進展順利,直到我注意到一個 UI 問題——在分頁區域沒有頂部和底部邊距。

original.jpg

當使用 html2canvasjsPDF 將 HTML 頁面轉換為 PDF 時,該過程首先將整個網頁呈現為畫布。html2canvas 捕獲瀏覽器中所有可見的內容,並將其轉換為圖像。然後使用 jsPDF.addImage() 將此圖像新增到 PDF 中。

但是,如果網頁長度超過 PDF 中的單個頁面,jsPDF 不會自動智慧地處理分頁。相反,它只是將完整圖像放置在第一頁上,如果圖像太高,它將被截斷。為了支援多頁 PDF,我們必須手動切割圖像,並將每個片段正確地放置在單獨的頁面上。

預設情況下,jsPDF 不會在分頁處留下任何邊距——每個新頁面只是從前一個頁面結束的地方開始。這可能會產生視覺上尷尬的體驗,尤其是在文字在頁面之間突然分割的情況下。為了修正這個問題,我們需要手動計算並新增邊距。這涉及調整每個頁面的圖像位置,並使用 doc.rect() 來遮罩我們想要填充的區域。

沒有邊距的原始程式碼如下:

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
    ...
}

支援新增頂部和底部邊距的新程式碼

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
    ...
}

這裡的關鍵程式碼是兩行程式碼,它們遮罩了由於引入頂部和底部填充而導致的溢位內容。

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

範例計算

讓我們用數字來分解它(這裡的單位可以是 mm 或其他):

  • 假設原始網頁高度為 180。
  • 頁面高度設定為 100,頂部和底部邊距各為 10。
  • 這意味著每頁的實際內容高度 (contentHeight) 為 100 - 10 - 10 = 80。

第一頁:

  • 位置從 10 開始。
  • 原始網頁內容的前 0-90 放置在第一頁上(因為呼叫了 addImage())。
  • 然後 doc.rect() 遮罩頂部 10 和底部 10,使原始網頁的 0-80 區域可見。

第二頁:

  • 新位置是(先前位置 - contentHeight)= (10 - 80) = -70。
  • 這意味著原始網頁的 70 到 170 的內容呈現在第二頁上。
  • 同樣,doc.rect() 遮罩 70-80(原始網頁內容)和 160-170 區域,使原始內容的 80-160 可見。

由於填充在每個頁面的開始和結束處建立了一個空白區域,因此它可以確保乾淨且可讀的格式,而不會在分頁處突然截斷。

new.jpg

希望這能幫助遇到同樣問題的人!

JSPDF MULTIPLE PAGE MARGIN BOTTOM MARGIN ADDIMAGE

  RELATED

  COMMENT

1
Ruben
Sep 5, 2026 at 12:02 pm
Устали от восторженных отзывов, которые заканчиваются ничем? Я предлагаю другой взгляд: делюсь только проверенными советами, рабочими схемами и эффективными алгоритмами для планирования маршрутов. Основа моего подхода заключается в следующем: путешествия — это не магия, а математика. Билеты, жильё, страховка и маршрут — всё это поддаётся разумному расчёту. Я помогу вам находить действительно стоящие варианты, чтобы отдых был ярким, а не разорительным. Здесь отсутствуют пустые обещания — только рабочий план, который сделает любую поездку проще и выгоднее. Туры https://t.me/tripkarta