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.

ฉันได้พัฒนา ส่วนขยายเบราว์เซอร์ ที่บันทึกหน้าเว็บเป็นไฟล์ PDF ในโหมดอ่านโดยใช้ jsPDF + html2canvas ซึ่งเป็นวิธีที่สะอาดและปราศจากสิ่งรบกวนในการดูแบบออฟไลน์ ทุกอย่างเป็นไปอย่างราบรื่นจนกระทั่งฉันสังเกตเห็นปัญหา UI อย่างหนึ่ง นั่นคือไม่มีระยะขอบด้านบนและด้านล่างในบริเวณที่แบ่งหน้า

original.jpg

เมื่อแปลงหน้า HTML เป็น PDF โดยใช้ html2canvas และ jsPDF กระบวนการจะทำงานโดยการเรนเดอร์หน้าเว็บทั้งหมดเป็นผืนผ้าใบก่อน html2canvas จะจับภาพทุกสิ่งที่มองเห็นได้ในเบราว์เซอร์และแปลงเป็นรูปภาพ จากนั้นรูปภาพนี้จะถูกเพิ่มลงใน PDF โดยใช้ jsPDF.addImage().

อย่างไรก็ตาม หากหน้าเว็บยาวกว่าหนึ่งหน้าใน 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

  COMMENT

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