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

当使用 html2canvas 和 jsPDF 将 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 

示例计算

让我们用数字来分解它(这里的单位可以是毫米或其他):

  • 假设原始网页高度为 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

  COMMENTS

0

No comment for this article.