0

I am building a webform to PDF utility. Flow is user submits things in form and then submits and a generated PDF opens in new tab.

Problem is sometimes the content of the form can be long and it can get to the 2nd page.

Issue is on the 2nd page the header is not added .. only the content.

My dilemma is how to detect if 2nd page will be required for this particular submission and then insert header dynamically on 2nd page automatically. It will not get more than 2 pages. 90% submissions will be 1 page but only like 10% will get to 2nd page and no more.

Right now, this is how I do it.

I have created a HTML template and I placed place holder variables in {{}} in places mapped to the JSON properties that is retrieved when a form is submitted.

Fill the HTML and render it as PDF using weasyprint or plain simple HTML to PDF conversion using Chrome headless shell.

I am stuck I have tried everything to no avail.....CSS tricks, separating header and body as separate HTML templates ...etc.

Here's the header I am using in my HTML template. and I want it exactly the same on all pages. Body content can be anything.

<header class="header">
  <div class="header-row">
    <div class="header-title">Form 456</div>
    <div class="logo">
      <img src="C:\Users\Public\app\backend\static\mdc_template\uni_logo.png" alt="Logo" width="50px"
        style="margin-top: -10px;" />
    </div>
  </div>

  <section class="info-header">
    <div class="info-block provider-info">
      <div class="info-line">
        <span class="info-label">Provider:</span><span class="data-value">{{provider_name}}</span>
      </div>
      <div class="info-line">
        <span class="info-label">2nd Provider:</span><span class="data-value">{{2nd_provider}}</span>
      </div>
    </div>
    <div class="info-block student-info student-info-offset">
      <div class="info-line">
        <span class="info-label">Date:</span><span class="data-value">{{date}}</span>
      </div>
      <div class="info-line">
        <span class="info-label">Location:</span><span class="data-value">{{location_name}}</span>
      </div>
      <div class="info-line">
        <span class="info-label">Name:</span><span class="data-value">{{student_name}}</span>
      </div>
      <div class="info-line">
        <span class="info-label">Date of Birth:</span><span class="data-value">{{d_o_b}}</span>
      </div>
      <div class="info-line">
        <span class="info-label">Guardian:</span><span class="data-value">{{guardian_id}}</span>
      </div>
      <div class="info-line">
        <span class="info-label">Course:</span><span class="data-value">{{course_name}}</span>
      </div>
    </div>
  </section>
</header>

1 Answer 1

0
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Form 456 PDF</title>
    <style>
        /* ========================================
           CSS Paged Media 設定 (關鍵部分)
           ========================================
        */
        @page {
            size: A4;
            /* 1. 預留頂部空間給 Header */
            /* 假設您的 Header 高度約為 150px (40mm左右),我們留 50mm 確保不重疊 */
            margin-top: 50mm; 
            margin-bottom: 20mm;
            margin-left: 20mm;
            margin-right: 20mm;

            /* 2. 定義頁首區域 */
            @top-center {
                /* 使用我們定義的 header-content 元素填充這裡 */
                content: element(header-content);
                width: 100%; /* 確保寬度填滿 */
            }
        }

        /* 定義頁尾 (選用:顯示頁碼) */
        @page {
            @bottom-right {
                content: "Page " counter(page) " of " counter(pages);
                font-size: 10pt;
            }
        }

        /* ========================================
           Header 樣式
           ========================================
        */
        header.header {
            /* 3. 將 Header 定義為 Running Element */
            position: running(header-content);
            
            /* 基本樣式調整,確保在 Margin Box 內顯示正常 */
            width: 100%; 
            font-family: Arial, sans-serif;
            border-bottom: 2px solid #000; /* 分隔線 */
            padding-bottom: 10px;
            /* 注意:top/left 在這裡無效,因為它已經移出文檔流 */
        }

        .header-row {
            display: flex;
            justify-content: space-between;
            align-items: center;
            margin-bottom: 15px;
        }

        .header-title {
            font-size: 24px;
            font-weight: bold;
        }

        .logo img {
            /* 確保圖片顯示 */
            display: block;
        }

        .info-header {
            display: flex;
            justify-content: space-between;
            font-size: 12px;
            width: 100%;
        }

        .info-block {
            width: 48%;
        }

        .info-line {
            margin-bottom: 4px;
            border-bottom: 1px dotted #ccc; /* 增加可讀性 */
            padding-bottom: 2px;
            display: flex;
            justify-content: space-between;
        }

        .info-label {
            font-weight: bold;
            color: #555;
        }

        .data-value {
            font-weight: normal;
        }

        /* ========================================
           Body 內容樣式
           ========================================
        */
        body {
            font-family: 'Times New Roman', Times, serif;
            font-size: 14px;
            line-height: 1.5;
            margin: 0; /* @page 已經處理了邊距,這裡設為 0 */
        }

        .content-section {
            margin-bottom: 20px;
        }
        
        h2 {
            border-bottom: 1px solid #333;
            padding-bottom: 5px;
        }
    </style>
</head>
<body>

    <!-- 
      這個 header 會被 CSS 抓取並放到每一頁的頂部。
      在 HTML 結構中,它放在哪裡並不重要,通常放在 body 的最前面。
    -->
    <header class="header">
        <div class="header-row">
            <div class="header-title">Form 456</div>
            <div class="logo">
                <!-- 
                   注意:WeasyPrint 對於本地路徑 C:\Users... 支援度可能不佳,
                   建議轉換成 base64 或使用 file:///C:/Users/... 格式 
                -->
                <img src="file:///C:/Users/Public/app/backend/static/mdc_template/uni_logo.png" alt="Logo" width="50px" />
            </div>
        </div>

        <section class="info-header">
            <div class="info-block provider-info">
                <div class="info-line">
                    <span class="info-label">Provider:</span><span class="data-value">{{provider_name}}</span>
                </div>
                <div class="info-line">
                    <span class="info-label">2nd Provider:</span><span class="data-value">{{second_provider}}</span>
                </div>
            </div>
            <div class="info-block student-info">
                <div class="info-line">
                    <span class="info-label">Date:</span><span class="data-value">{{date}}</span>
                </div>
                <div class="info-line">
                    <span class="info-label">Location:</span><span class="data-value">{{location_name}}</span>
                </div>
                <div class="info-line">
                    <span class="info-label">Name:</span><span class="data-value">{{student_name}}</span>
                </div>
                <div class="info-line">
                    <span class="info-label">Date of Birth:</span><span class="data-value">{{d_o_b}}</span>
                </div>
                <div class="info-line">
                    <span class="info-label">Guardian:</span><span class="data-value">{{guardian_id}}</span>
                </div>
                <div class="info-line">
                    <span class="info-label">Course:</span><span class="data-value">{{course_name}}</span>
                </div>
            </div>
        </section>
    </header>

    <!-- 這裡開始是主要內容 -->
    <main>
        <div class="content-section">
            <h2>Report Details</h2>
            <p>
                <!-- 模擬長內容,確保會換頁 -->
                Here starts the main body content. This content will flow naturally. 
                Because we used `position: running(header-content)`, the header above 
                is removed from the normal flow here and moved to the page margin.
            </p>
            <p>
                {{long_content_placeholder}}
            </p>
            
            <!-- 插入大量換行符模擬第二頁 -->
            <br><br><br><br><br><br><br><br><br><br>
            <br><br><br><br><br><br><br><br><br><br>
            <br><br><br><br><br><br><br><br><br><br>
            
            <h3>Second Page Content</h3>
            <p>
                If you see the header above this text on the second page, the solution worked!
            </p>
        </div>
    </main>

</body>
</html>
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Form 456 Export</title>
    <style>
        /* BASIC RESET & TYPOGRAPHY 
        */
        body {
            font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
            margin: 0;
            padding: 0;
            color: #333;
            background-color: white;
        }

        /* THE MAGIC SAUCE: 
           We structure the page as one giant table.
           The <thead> holds your header.
           The <tbody> holds your content.
           Browsers automatically repeat <thead> on new pages.
        */
        .print-layout-table {
            width: 100%;
            border-collapse: collapse;
        }

        .print-layout-table thead th {
            /* This ensures the header styling doesn't break */
            font-weight: normal; 
            text-align: left;
            border: none;
            padding: 0;
        }

        /* YOUR ORIGINAL HEADER CSS 
           (Slightly optimized for the table layout)
        */
        .header {
            border-bottom: 2px solid #333;
            margin-bottom: 20px;
            padding-bottom: 10px;
            /* Ensure header background is white so it doesn't blend if content scrolls oddly */
            background: white; 
        }

        .header-row {
            display: flex;
            justify-content: space-between;
            align-items: center;
            margin-bottom: 15px;
        }

        .header-title {
            font-size: 24px;
            font-weight: bold;
        }

        .info-header {
            display: flex;
            justify-content: space-between;
            flex-wrap: wrap;
            gap: 20px;
        }

        .info-block {
            flex: 1;
        }

        .info-line {
            margin-bottom: 5px;
            font-size: 14px;
        }

        .info-label {
            font-weight: bold;
            margin-right: 5px;
            color: #555;
        }

        .data-value {
            color: #000;
        }

        /* CONTENT STYLING 
        */
        .content-placeholder {
            padding-top: 20px;
            line-height: 1.6;
        }
        
        /* Just for visual debugging of the mock content */
        .mock-paragraph {
            margin-bottom: 15px;
            text-align: justify;
        }

        /* PRINT SPECIFIC FIXES 
        */
        @media print {
            /* Ensure the table header group repeats */
            thead { 
                display: table-header-group; 
            }
            
            /* Prevent weird breaks inside rows if possible */
            tr {
                page-break-inside: avoid;
            }

            body {
                -webkit-print-color-adjust: exact;
            }
        }
    </style>
</head>
<body>

    <!-- 
      WRAPPER TABLE 
      We wrap the entire document in a table. 
      The <thead> is your header. 
      The <tbody> is your form content.
    -->
    <table class="print-layout-table">
        
        <!-- HEADER SECTION (Will repeat on every page) -->
        <thead>
            <tr>
                <th>
                    <header class="header">
                        <div class="header-row">
                            <div class="header-title">Form 456</div>
                            <div class="logo">
                                <!-- NOTE: Updated image to a placeholder for this demo since local path won't work -->
                                <img src="https://placehold.co/100x50?text=Logo" alt="Logo" width="50px" style="margin-top: -10px;" />
                            </div>
                        </div>

                        <section class="info-header">
                            <div class="info-block provider-info">
                                <div class="info-line">
                                    <span class="info-label">Provider:</span><span class="data-value">{{provider_name}}</span>
                                </div>
                                <div class="info-line">
                                    <span class="info-label">2nd Provider:</span><span class="data-value">{{2nd_provider}}</span>
                                </div>
                            </div>
                            <div class="info-block student-info student-info-offset">
                                <div class="info-line">
                                    <span class="info-label">Date:</span><span class="data-value">{{date}}</span>
                                </div>
                                <div class="info-line">
                                    <span class="info-label">Location:</span><span class="data-value">{{location_name}}</span>
                                </div>
                                <div class="info-line">
                                    <span class="info-label">Name:</span><span class="data-value">{{student_name}}</span>
                                </div>
                                <div class="info-line">
                                    <span class="info-label">Date of Birth:</span><span class="data-value">{{d_o_b}}</span>
                                </div>
                                <div class="info-line">
                                    <span class="info-label">Guardian:</span><span class="data-value">{{guardian_id}}</span>
                                </div>
                                <div class="info-line">
                                    <span class="info-label">Course:</span><span class="data-value">{{course_name}}</span>
                                </div>
                            </div>
                        </section>
                    </header>
                </th>
            </tr>
        </thead>

        <!-- BODY SECTION (Your actual form answers/long content goes here) -->
        <tbody>
            <tr>
                <td>
                    <div class="content-placeholder">
                        <h3>Submission Details</h3>
                        <p><em>(Scroll down or Print Preview to see the header repeat on Page 2)</em></p>
                        
                        <!-- Generates dummy text to force a page break for demonstration -->
                        <script>
                            for(let i = 0; i < 30; i++) {
                                document.write('<p class="mock-paragraph"><strong>Item ' + (i+1) + ':</strong> Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur.</p>');
                            }
                        </script>

                        <div style="border: 1px solid red; padding: 10px; margin-top: 20px;">
                            <strong>End of Form Content</strong>
                        </div>
                    </div>
                </td>
            </tr>
        </tbody>
    </table>

</body>
</html>
New contributor
kenshinsan is a new contributor to this site. Take care in asking for clarification, commenting, and answering. Check out our Code of Conduct.
Sign up to request clarification or add additional context in comments.

1 Comment

Your answer could be improved with additional supporting information. Please edit to add further details, such as citations or documentation, so that others can confirm that your answer is correct. You can find more information on how to write good answers in the help center.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.