Repeat Letter Head in Custom Print Formate

Issue Summary

I’m trying to create a custom print format where the letter head (logo and company info) repeats on every page of a multi-page PDF. The footer is repeating correctly, but the letter head only appears on the first page.

Expected Behavior

The letter head should repeat on every page, similar to the example shown in the attached screenshot where both pages have the complete header with logo and company information.

Current Behavior

  • Letter head only appears on the first page

  • Subsequent pages are missing the header section

Setup

ERPNext Version: erpnext: 15.91.2 Print Format Type: Custom (Jinja/HTML)

Code

Print Format Code

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">

<style>
@page {
    size: A4;
    margin: 15mm 10mm 20mm 10mm;

    @bottom-right {
        content: "Page " counter(page) " of " counter(pages);
        font-size: 9pt;
        font-weight: bold;
    }
}

body {
    font-family: Arial, Helvetica, sans-serif;
    font-size: 10pt;
    margin: 0;
}

.print-table {
    width: 100%;
    border-collapse: collapse;
}

.print-table thead {
    display: table-header-group;
}

.items-table {
    width: 100%;
    border-collapse: collapse;
    table-layout: fixed;
}

.items-table th,
.items-table td {
    border: 1px solid #000;
    padding: 6px;
    font-size: 9pt;
}

.items-table th {
    background: #000;
    color: #fff;
    font-weight: bold;
    -webkit-print-color-adjust: exact;
    print-color-adjust: exact;
}

.items-table tr {
    page-break-inside: avoid;
}

.bottom-spacer {
    height: 60mm;
}

.totals-wrapper {
    page-break-inside: avoid;
}

.totals-table {
    width: 300px;
    margin-left: auto;
    border-collapse: collapse;
    border: 2px solid #000;
}

.totals-table td {
    padding: 8px 10px;
}

.total-label {
    font-weight: bold;
}

.total-value {
    text-align: right;
    font-weight: 600;
}

.grand-total-row {
    background: #333;
    color: #fff;
    font-weight: bold;
}

.footer-note {
    font-size: 9pt;
    margin-top: 10px;
    padding-top: 6px;
    border-top: 1px solid #000;
}
</style>
</head>

<body>

<table class="print-table">

    <!-- =====================
         HEADER (REPEATS)
    ===================== -->
    <thead>

        <!-- LETTER HEAD -->
        <tr>
            <td colspan="8">
                {{ letter_head }}
            </td>
        </tr>

        <!-- COLUMN HEADERS (NO NESTED TABLE, NO THEAD) -->
        <tr>
            <th width="6%">QTY</th>
            <th width="12%">CODE</th>
            <th width="26%">DESCRIPTION</th>
            <th width="10%">SIZE</th>
            <th width="12%">FINISH</th>
            <th width="12%">PRICE</th>
            <th width="10%">DISC</th>
            <th width="12%">TOTAL</th>
        </tr>

    </thead>

    <!-- =====================
         BODY
    ===================== -->
    <tbody>
        {% for item in doc.items %}
        <tr>
            <td>{{ item.qty }}</td>
            <td>{{ item.item_code }}</td>
            <td>{{ item.item_name }}</td>
            <td>{{ item.custom_size or "" }}</td>
            <td>{{ item.custom_finish_color or "" }}</td>
            <td>{{ frappe.utils.fmt_money(item.rate, currency=doc.currency) }}</td>
            <td>
                {% if item.discount_percentage %}
                    {{ "%.0f"|format(item.discount_percentage) }}%
                {% endif %}
            </td>
            <td>{{ frappe.utils.fmt_money(item.amount, currency=doc.currency) }}</td>
        </tr>
        {% endfor %}

        <tr>
            <td colspan="8">
                <div class="bottom-spacer"></div>

                <div class="totals-wrapper">
                    <table class="totals-table">
                        <tr>
                            <td class="total-label">SUB TOTAL</td>
                            <td class="total-value">
                                {{ frappe.utils.fmt_money(doc.net_total, currency=doc.currency) }}
                            </td>
                        </tr>

                        {% for tax in doc.taxes %}
                        <tr>
                            <td class="total-label">{{ tax.description }}</td>
                            <td class="total-value">
                                {{ frappe.utils.fmt_money(tax.tax_amount, currency=doc.currency) }}
                            </td>
                        </tr>
                        {% endfor %}

                        <tr class="grand-total-row">
                            <td class="total-label">TOTAL</td>
                            <td class="total-value">
                                {{ frappe.utils.fmt_money(doc.grand_total, currency=doc.currency) }}
                            </td>
                        </tr>
                    </table>

                    <div class="footer-note">
                        <strong>NOTE:</strong> Please inform us of any price or qty discrepancies immediately
                    </div>
                </div>
            </td>
        </tr>
    </tbody>

</table>

</body>
</html>

Letter Head Code

<style>
.print-header {
    display: flex;
    justify-content: space-between;
    align-items: flex-start;
    border-bottom: 1.5px solid #000;
    padding-bottom: 8px;
    margin-bottom: 10px;
}

.header-left {
    display: flex;
    gap: 12px;
}

.company-logo {
    max-height: 60px;
    max-width: 120px;
}

.company-name {
    font-size: 16px;
    font-weight: bold;
    text-transform: uppercase;
}

.company-address {
    font-size: 10px;
    color: #444;
}

.company-vat {
    font-size: 11px;
    margin-top: 3px;
}

.header-right {
    text-align: right;
    font-size: 10px;
}

.doc-title {
    background: #000;
    color: #fff;
    text-align: center;
    padding: 6px;
    font-size: 11pt;
    font-weight: bold;
    margin: 6px 0 10px;
    -webkit-print-color-adjust: exact;
    print-color-adjust: exact;
}

.info-table {
    width: 100%;
    border-collapse: collapse;
    font-size: 9pt;
}

.info-table td {
    border: 1px solid #000;
    padding: 6px;
}
</style>

{% set company = frappe.get_doc("Company", doc.company) %}

<div class="print-header">

    <div class="header-left">
        {% if company.company_logo %}
            <img src="{{ company.company_logo }}" class="company-logo">
        {% endif %}

        <div>
            <div class="company-name">{{ company.company_name }}</div>
            <div class="company-address">
                {{ (doc.address_display or "").replace("<br>", ", ") }}
            </div>
            {% if company.tax_id %}
            <div class="company-vat">
                <strong>VAT NO:</strong> {{ company.tax_id }}
            </div>
            {% endif %}
        </div>
    </div>

    <div class="header-right">
        {% if company.phone_no %}<div><strong>Phone:</strong> {{ company.phone_no }}</div>{% endif %}
        {% if company.email %}<div><strong>Email:</strong> {{ company.email }}</div>{% endif %}
        {% if company.website %}<div><strong>Web:</strong> {{ company.website }}</div>{% endif %}
    </div>

</div>

<div class="doc-title">SALES ACKNOWLEDGMENT</div>

<table class="info-table">
<tbody><tr>
    <td width="33%"><strong>Billing Address</strong><br>{{ doc.address_display or "" }}</td>
    <td width="33%"><strong>Delivery Address</strong><br>{{ doc.shipping_address or "" }}</td>
    <td width="34%">
        <strong>Order No:</strong> {{ doc.name }}<br>
        <strong>Date:</strong> {{ doc.transaction_date }}<br>
        <strong>PO No:</strong> {{ doc.po_no or "" }}
    </td>
</tr>
</tbody>
</table>

Question

How can I make the letter head repeat on every page of the PDF? I’ve tried placing it in the thead section with display: table-header-group, but it’s not working. Is there a specific way to handle letter head repetition in Frappe/ERPNext custom print formats?

Any help would be greatly appreciated!

Hello @Harsh_Uvtech , Please refer “Print Setting”.
Refer → Print Settings