# easy-invoice/2.4.2/templates/pdf/document.php

Easy Invoice – Invoice Generator, PDF Quotes &amp; Payments, version 2.4.2. 333 lines.

- Page: https://pluginprobe.com/plugins/easy-invoice/2.4.2/code/templates/pdf/document.php
- Raw: https://pluginprobe.com/plugins/easy-invoice/2.4.2/raw/templates/pdf/document.php
- Modified: 2026-09-15T12:31:20+00:00

Line numbers below start at 1. Link to a line or a range by appending a fragment to the
page URL, for example `https://pluginprobe.com/plugins/easy-invoice/2.4.2/code/templates/pdf/document.php#L10-L20`.

```php
<?php
/**
 * Shared invoice/quote layout for server-side PDF rendering.
 *
 * Receives from PdfRenderer::buildHtml(), via invoice.php or quote.php:
 *   $invoice      — Invoice or Quote model (named for the invoice case, which came first)
 *   $formatter    — currency formatter with format()
 *   $ei_pdf_type  — 'invoice' or 'quote'
 *
 * One body for both documents. The plugin already carries ~6,500 lines of
 * near-identical parallel invoice and quote templates, and every fix to one of
 * them is a fix someone forgets to make to the other. The two differ only in
 * four labels and which date they show, so they differ here in four labels and
 * which date they show.
 *
 * Laid out with tables on purpose. dompdf implements CSS 2.1 and ignores
 * `display: flex`, so the flexbox designs in templates/invoice-templates/
 * collapse into stacked rows when rendered here. Tables are the one layout
 * primitive every PDF engine agrees on.
 *
 * Override with the `easy_invoice_pdf_template_path` filter.
 *
 * @package Easy_Invoice
 */

if ( ! defined( 'ABSPATH' ) ) {
    exit;
}

$ei_pdf_type = ( isset( $ei_pdf_type ) && 'quote' === $ei_pdf_type ) ? 'quote' : 'invoice';
$ei_is_quote = 'quote' === $ei_pdf_type;
$ei_is_credit = ! $ei_is_quote && \EasyInvoice\Helpers\TemplateTextHelper::isCreditNote( $invoice );

$text_settings = $ei_is_quote
    ? \EasyInvoice\Helpers\TemplateTextHelper::getQuoteTextSettings()
    : \EasyInvoice\Helpers\TemplateTextHelper::getInvoiceTextSettings();
$company_info  = \EasyInvoice\Helpers\TemplateTextHelper::getCompanyInfo();

$t = static function ( $key, $fallback ) use ( $text_settings ) {
    return ! empty( $text_settings[ $key ] ) ? $text_settings[ $key ] : $fallback;
};

$items    = is_callable( [ $invoice, 'getItems' ] ) ? (array) $invoice->getItems() : [];
$subtotal = is_callable( [ $invoice, 'getSubtotal' ] ) ? (float) $invoice->getSubtotal() : 0.0;
$tax      = is_callable( [ $invoice, 'getTaxAmount' ] ) ? (float) $invoice->getTaxAmount() : 0.0;
$discount = is_callable( [ $invoice, 'getDiscountAmount' ] ) ? (float) $invoice->getDiscountAmount() : 0.0;
$total    = is_callable( [ $invoice, 'getTotal' ] ) ? (float) $invoice->getTotal() : 0.0;
?>
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<style>
    @page { margin: 18mm 15mm; }

    body {
        font-family: "DejaVu Sans", sans-serif;
        font-size: 10pt;
        line-height: 1.45;
        color: #222222;
        margin: 0;
    }

    table { width: 100%; border-collapse: collapse; }
    td, th { vertical-align: top; }

    /* Masthead: company on the left, document meta on the right. A table,
       because this is exactly the two-column arrangement flex would have
       given us on screen and cannot give us here. */
    .masthead td { padding: 0 0 18pt 0; }
    .masthead .company-name { font-size: 15pt; font-weight: bold; color: #111111; }
    .masthead .company-detail { font-size: 8.5pt; color: #555555; }
    .masthead .doc-type {
        font-size: 20pt; font-weight: bold; color: #111111;
        text-align: right; letter-spacing: 1pt;
    }
    .masthead .doc-meta { text-align: right; font-size: 9pt; color: #555555; }
    .masthead .doc-meta strong { color: #222222; }

    .rule { border-bottom: 1px solid #d8d8d8; height: 1px; margin-bottom: 14pt; }

    .parties td { padding-bottom: 16pt; width: 50%; }
    .label {
        font-size: 7.5pt; text-transform: uppercase; letter-spacing: 0.8pt;
        color: #888888; padding-bottom: 3pt;
    }
    .party-name { font-weight: bold; font-size: 10.5pt; }
    .party-detail { font-size: 9pt; color: #555555; }

    /* Line items */
    .items { margin-bottom: 14pt; }
    .items thead th {
        font-size: 7.5pt; text-transform: uppercase; letter-spacing: 0.6pt;
        color: #666666; text-align: left; font-weight: normal;
        border-bottom: 1px solid #cccccc; padding: 0 6pt 6pt 0;
    }
    .items tbody td {
        padding: 7pt 6pt 7pt 0;
        border-bottom: 1px solid #eeeeee;
        font-size: 9.5pt;
    }
    .items .num { text-align: right; padding-right: 0; }
    .items .item-name { font-weight: bold; }
    .items .item-desc { font-size: 8.5pt; color: #666666; }

    /* Totals sit in a right-aligned block; a nested table keeps the
       label/value pairs aligned without floats, which dompdf handles poorly. */
    .totals { width: 46%; margin-left: 54%; }
    .totals td { padding: 4pt 0; font-size: 9.5pt; }
    .totals td.v { text-align: right; }
    .totals tr.grand td {
        border-top: 1.5px solid #222222;
        padding-top: 7pt; font-size: 12pt; font-weight: bold; color: #111111;
    }

    .blocks { margin-top: 20pt; }
    .blocks td { padding-bottom: 12pt; font-size: 8.8pt; color: #555555; }
    .blocks .label { color: #888888; }

    .footer {
        margin-top: 24pt; padding-top: 10pt;
        border-top: 1px solid #e4e4e4;
        font-size: 8pt; color: #888888; text-align: center;
    }
</style>
</head>
<body>

<table class="masthead">
    <tr>
        <td style="width:55%">
            <?php if ( ! empty( $company_info['logo'] ) ) : ?>
                <img src="<?php echo esc_url( $company_info['logo'] ); ?>" style="max-height:52pt; margin-bottom:6pt;" alt="">
                <br>
            <?php endif; ?>
            <?php if ( ! empty( $company_info['name'] ) ) : ?>
                <div class="company-name"><?php echo esc_html( $company_info['name'] ); ?></div>
            <?php endif; ?>
            <?php foreach ( [ 'address', 'email', 'phone', 'website' ] as $field ) : ?>
                <?php if ( ! empty( $company_info[ $field ] ) ) : ?>
                    <div class="company-detail"><?php echo nl2br( esc_html( $company_info[ $field ] ) ); ?></div>
                <?php endif; ?>
            <?php endforeach; ?>
            <?php if ( ! empty( $company_info['tax_number'] ) ) : ?>
                <div class="company-detail"><?php echo esc_html( $company_info['tax_number'] ); ?></div>
            <?php endif; ?>
        </td>
        <td style="width:45%">
            <div class="doc-type"><?php
                echo esc_html( $ei_is_quote
                    ? $t( 'quote_title', __( 'QUOTE', 'easy-invoice' ) )
                    : ( $ei_is_credit ? __( 'CREDIT NOTE', 'easy-invoice' ) : $t( 'invoice_title', __( 'INVOICE', 'easy-invoice' ) ) ) );
            ?></div>
            <div class="doc-meta">
                <?php if ( is_callable( [ $invoice, 'getNumber' ] ) && $invoice->getNumber() ) : ?>
                    <div><?php echo esc_html( $ei_is_quote
                        ? $t( 'quote_number', __( 'Quote No', 'easy-invoice' ) )
                        : ( $ei_is_credit ? __( 'Credit Note No', 'easy-invoice' ) : $t( 'invoice_number', __( 'Invoice No', 'easy-invoice' ) ) ) ); ?>:
                        <strong><?php echo esc_html( $invoice->getNumber() ); ?></strong></div>
                <?php endif; ?>
                <?php if ( is_callable( [ $invoice, 'getIssueDate' ] ) && $invoice->getIssueDate() ) : ?>
                    <div><?php echo esc_html( $ei_is_credit ? __( 'Date', 'easy-invoice' ) : $t( 'invoice_date', __( 'Date', 'easy-invoice' ) ) ); ?>:
                        <strong><?php echo esc_html( $invoice->getIssueDate() ); ?></strong></div>
                <?php endif; ?>
                <?php
                // A quote expires; an invoice falls due. Different field, different word.
                $ei_end_date = $ei_is_quote
                    ? ( is_callable( [ $invoice, 'getExpiryDate' ] ) ? $invoice->getExpiryDate() : '' )
                    : ( ( ! $ei_is_credit && is_callable( [ $invoice, 'getDueDate' ] ) ) ? $invoice->getDueDate() : '' );
                ?>
                <?php if ( $ei_end_date ) : ?>
                    <div><?php echo esc_html( $ei_is_quote
                            ? $t( 'valid_until', __( 'Valid until', 'easy-invoice' ) )
                            : $t( 'due_date', __( 'Due', 'easy-invoice' ) ) ); ?>:
                        <strong><?php echo esc_html( $ei_end_date ); ?></strong></div>
                <?php endif; ?>
            </div>
        </td>
    </tr>
</table>

<div class="rule"></div>

<table class="parties">
    <tr>
        <td>
            <div class="label"><?php echo esc_html( $t( 'to', $ei_is_quote
                ? __( 'Quote for', 'easy-invoice' )
                : __( 'Bill to', 'easy-invoice' ) ) ); ?></div>
            <?php if ( is_callable( [ $invoice, 'getCustomerName' ] ) && $invoice->getCustomerName() ) : ?>
                <div class="party-name"><?php echo esc_html( $invoice->getCustomerName() ); ?></div>
            <?php endif; ?>
            <?php if ( is_callable( [ $invoice, 'getCustomerAddress' ] ) && $invoice->getCustomerAddress() ) : ?>
                <div class="party-detail"><?php echo nl2br( esc_html( $invoice->getCustomerAddress() ) ); ?></div>
            <?php endif; ?>
            <?php if ( is_callable( [ $invoice, 'getCustomerEmail' ] ) && $invoice->getCustomerEmail() ) : ?>
                <div class="party-detail"><?php echo esc_html( $invoice->getCustomerEmail() ); ?></div>
            <?php endif; ?>
        </td>
        <td>
            <?php if ( is_callable( [ $invoice, 'getTitle' ] ) && $invoice->getTitle() ) : ?>
                <div class="label"><?php esc_html_e( 'Reference', 'easy-invoice' ); ?></div>
                <div class="party-detail"><?php echo esc_html( $invoice->getTitle() ); ?></div>
            <?php endif; ?>
        </td>
    </tr>
</table>

<table class="items">
    <thead>
        <tr>
            <th style="width:52%"><?php echo esc_html( $t( 'service', __( 'Description', 'easy-invoice' ) ) ); ?></th>
            <th style="width:12%" class="num"><?php echo esc_html( $t( 'qty', __( 'Qty', 'easy-invoice' ) ) ); ?></th>
            <th style="width:18%" class="num"><?php echo esc_html( $t( 'rate_price', __( 'Rate', 'easy-invoice' ) ) ); ?></th>
            <th style="width:18%" class="num"><?php echo esc_html( $t( 'line_total', $t( 'total', __( 'Amount', 'easy-invoice' ) ) ) ); ?></th>
        </tr>
    </thead>
    <tbody>
    <?php if ( empty( $items ) ) : ?>
        <tr><td colspan="4" style="color:#888888"><?php esc_html_e( 'No line items.', 'easy-invoice' ); ?></td></tr>
    <?php else : ?>
        <?php foreach ( $items as $item ) :
            $name = is_callable( [ $item, 'getName' ] ) ? (string) $item->getName() : '';
            $desc = is_callable( [ $item, 'getDescription' ] ) ? (string) $item->getDescription() : '';
            $qty  = is_callable( [ $item, 'getQuantity' ] ) ? (float) $item->getQuantity() : 0.0;
            $rate = is_callable( [ $item, 'getPrice' ] ) ? (float) $item->getPrice() : 0.0;
            $amt  = is_callable( [ $item, 'getAmount' ] ) ? (float) $item->getAmount() : ( $qty * $rate );
            ?>
            <tr>
                <td>
                    <?php if ( $name !== '' ) : ?><div class="item-name"><?php echo esc_html( $name ); ?></div><?php endif; ?>
                    <?php if ( $desc !== '' ) : ?><div class="item-desc"><?php echo nl2br( esc_html( $desc ) ); ?></div><?php endif; ?>
                </td>
                <td class="num"><?php echo esc_html( rtrim( rtrim( number_format( $qty, 2 ), '0' ), '.' ) ); ?></td>
                <td class="num"><?php echo esc_html( $formatter->format( $rate ) ); ?></td>
                <td class="num"><?php echo esc_html( $formatter->format( $amt ) ); ?></td>
            </tr>
        <?php endforeach; ?>
    <?php endif; ?>
    </tbody>
</table>

<table class="totals">
    <tr>
        <td><?php echo esc_html( $t( 'sub_total', __( 'Subtotal', 'easy-invoice' ) ) ); ?></td>
        <td class="v"><?php echo esc_html( $formatter->format( $subtotal ) ); ?></td>
    </tr>
    <?php if ( $discount > 0 ) : ?>
        <tr>
            <td><?php echo esc_html( $t( 'discount', __( 'Discount', 'easy-invoice' ) ) ); ?></td>
            <td class="v">-<?php echo esc_html( $formatter->format( $discount ) ); ?></td>
        </tr>
    <?php endif; ?>
    <?php if ( $tax > 0 ) : ?>
        <tr>
            <td><?php echo esc_html( $t( 'tax', __( 'Tax', 'easy-invoice' ) ) ); ?></td>
            <td class="v"><?php echo esc_html( $formatter->format( $tax ) ); ?></td>
        </tr>
    <?php endif; ?>
    <?php
    /**
     * Extra total lines — the hook the Additional Tax addon renders into on screen,
     * so a PDF shows the same figures a customer sees in the browser.
     */
    do_action( 'easy_invoice_pdf_totals_after_tax', $invoice );
    ?>
    <tr class="grand">
        <td><?php echo esc_html( $t( 'total', __( 'Total', 'easy-invoice' ) ) ); ?></td>
        <td class="v"><?php echo esc_html( $formatter->format( $total ) ); ?></td>
    </tr>
    <?php
    // Money already received and credit notes issued: the total stays the
    // total, and what is still owed is spelled out underneath it.
    $ei_pdf_id       = ( ! $ei_is_quote && is_callable( [ $invoice, 'getId' ] ) ) ? (int) $invoice->getId() : 0;
    $ei_pdf_paid     = $ei_pdf_id ? \EasyInvoice\Services\InvoiceBalance::paid( $ei_pdf_id ) : 0.0;
    $ei_pdf_credited = $ei_pdf_id ? \EasyInvoice\Services\InvoiceBalance::credited( $ei_pdf_id ) : 0.0;
    if ( $ei_pdf_paid > 0 || $ei_pdf_credited > 0 ) :
        foreach ( \EasyInvoice\Services\CreditNote::forInvoice( $ei_pdf_id ) as $ei_credit_id ) :
            $ei_credit_amount = (float) get_post_meta( $ei_credit_id, '_easy_invoice_total', true );
            if ( $ei_credit_amount <= 0 ) {
                continue;
            }
            ?>
            <tr>
                <td><?php /* translators: %s: credit note number. */ echo esc_html( sprintf( __( 'Credit note %s', 'easy-invoice' ), (string) get_post_meta( $ei_credit_id, '_easy_invoice_number', true ) ) ); ?></td>
                <td class="v"><?php echo esc_html( '-' . $formatter->format( $ei_credit_amount ) ); ?></td>
            </tr>
        <?php endforeach; ?>
        <?php if ( $ei_pdf_paid > 0 ) : ?>
        <tr>
            <td><?php esc_html_e( 'Paid', 'easy-invoice' ); ?></td>
            <td class="v"><?php echo esc_html( '-' . $formatter->format( $ei_pdf_paid ) ); ?></td>
        </tr>
        <?php endif; ?>
        <tr class="grand">
            <td><?php esc_html_e( 'Balance due', 'easy-invoice' ); ?></td>
            <td class="v"><?php echo esc_html( $formatter->format( max( 0, $total - $ei_pdf_paid - $ei_pdf_credited ) ) ); ?></td>
        </tr>
    <?php endif; ?>
</table>

<table class="blocks">
    <?php if ( is_callable( [ $invoice, 'getNotes' ] ) && $invoice->getNotes() ) : ?>
        <tr><td>
            <div class="label"><?php esc_html_e( 'Notes', 'easy-invoice' ); ?></div>
            <?php echo nl2br( esc_html( $invoice->getNotes() ) ); ?>
        </td></tr>
    <?php endif; ?>
    <?php if ( ! $ei_is_credit && is_callable( [ $invoice, 'getTerms' ] ) && $invoice->getTerms() ) : ?>
        <tr><td>
            <div class="label"><?php esc_html_e( 'Terms', 'easy-invoice' ); ?></div>
            <?php echo nl2br( esc_html( $invoice->getTerms() ) ); ?>
        </td></tr>
    <?php endif; ?>
</table>

    <?php
    /**
     * Fires after notes and terms on the PDF, before the footer.
     *
     * @param object $invoice     The document (invoice or quote model).
     * @param string $ei_pdf_type 'invoice' or 'quote'.
     */
    do_action( 'easy_invoice_pdf_after_notes', $invoice, $ei_pdf_type );
    ?>
<?php $ei_pdf_footer = \EasyInvoice\Helpers\TemplateTextHelper::footerText( $invoice, $ei_pdf_type ); ?>
<?php if ( '' !== trim( wp_strip_all_tags( $ei_pdf_footer ) ) ) : ?>
    <div class="footer"><?php echo wp_kses_post( $ei_pdf_footer ); ?></div>
<?php endif; ?>

</body>
</html>

```
