跳轉到

發票 PDF 範例

需求套件nextpdf/core 難度:入門

完整程式碼

<?php

declare(strict_types=1);

use NextPDF\Core\Document;
use NextPDF\Core\Content\TextRenderer;
use NextPDF\Core\Graphics\DrawingEngine;
use NextPDF\Core\ValueObjects\PageSize;
use NextPDF\Core\ValueObjects\Margin;
use NextPDF\Core\ValueObjects\Position;
use NextPDF\Core\ValueObjects\Dimension;
use NextPDF\Core\ValueObjects\Color;

// ─── 文件初始化 ───────────────────────────────────────────────
$document = Document::createStandalone(
    pageSize: PageSize::A4,
    margin:   Margin::symmetric(vertical: 20.0, horizontal: 25.0),
);

$page    = $document->pages()->add();
$text    = $document->text();
$drawing = $document->drawing();

// ─── 頁首:公司資訊 ──────────────────────────────────────────
// Logo(假設已準備好圖片)
$logo = $document->images()->embed('/assets/company-logo.png');
$drawing->image(image: $logo, x: 25.0, y: 20.0, width: 45.0);

// 公司名稱與聯絡資訊
$text->write(
    text:     'NextPDF 科技有限公司',
    position: Position::at(x: 80.0, y: 25.0),
    fontSize: 14.0,
    fontName: 'NotoSansCJKtc-Bold',
    color:    Color::fromHex('#1E3A8A'),
);

$text->write(
    text:     "台北市信義區信義路五段 7 號\nTel: (02) 1234-5678  |  Email: billing@nextpdf.dev",
    position: Position::at(x: 80.0, y: 33.0),
    fontSize: 9.0,
    fontName: 'NotoSansCJKtc',
    color:    Color::fromHex('#6B7280'),
    lineHeight: 1.5,
);

// 分隔線
$drawing->line(
    x1:    25.0, y1: 55.0,
    x2:   185.0, y2: 55.0,
    color: Color::fromHex('#E5E7EB'),
    width: 0.5,
);

// ─── 發票標題與基本資訊 ──────────────────────────────────────
$text->write(
    text:     '發  票',
    position: Position::at(x: 25.0, y: 65.0),
    fontSize: 22.0,
    fontName: 'NotoSansCJKtc-Bold',
    color:    Color::fromHex('#111827'),
);

// 發票號碼、日期(右側對齊)
$invoiceData = [
    ['label' => '發票號碼', 'value' => 'INV-2024-001'],
    ['label' => '開立日期', 'value' => '2024-03-15'],
    ['label' => '付款期限', 'value' => '2024-04-14'],
];

$y = 65.0;
foreach ($invoiceData as $row) {
    $text->write(
        text:     $row['label'] . ':',
        position: Position::at(x: 130.0, y: $y),
        fontSize: 9.0,
        fontName: 'NotoSansCJKtc',
        color:    Color::fromHex('#6B7280'),
    );
    $text->write(
        text:     $row['value'],
        position: Position::at(x: 155.0, y: $y),
        fontSize: 9.0,
        fontName: 'NotoSansCJKtc-Bold',
        color:    Color::fromHex('#111827'),
    );
    $y += 8.0;
}

// ─── 買方資訊 ────────────────────────────────────────────────
$drawing->rectangle(
    x: 25.0, y: 90.0, width: 75.0, height: 35.0,
    fill:         Color::fromHex('#F9FAFB'),
    cornerRadius: 2.0,
);

$text->write(
    text:     '帳單收件人',
    position: Position::at(x: 28.0, y: 93.0),
    fontSize: 8.0,
    fontName: 'NotoSansCJKtc',
    color:    Color::fromHex('#6B7280'),
);

$text->write(
    text:     "王小明\n台北市大安區復興南路一段 100 號\n(02) 9876-5432",
    position: Position::at(x: 28.0, y: 100.0),
    fontSize: 10.0,
    fontName: 'NotoSansCJKtc',
    color:    Color::fromHex('#111827'),
    lineHeight: 1.6,
);

// ─── 商品明細表格 ────────────────────────────────────────────
$tableTop = 140.0;

// 表格標頭背景
$drawing->rectangle(
    x: 25.0, y: $tableTop, width: 160.0, height: 8.0,
    fill: Color::fromHex('#1E3A8A'),
);

// 表格標頭文字
$headers = [
    ['text' => '品名', 'x' => 27.0],
    ['text' => '數量', 'x' => 115.0],
    ['text' => '單價', 'x' => 135.0],
    ['text' => '小計', 'x' => 160.0],
];

foreach ($headers as $header) {
    $text->write(
        text:     $header['text'],
        position: Position::at(x: $header['x'], y: $tableTop + 1.5),
        fontSize: 9.0,
        fontName: 'NotoSansCJKtc-Bold',
        color:    Color::fromHex('#FFFFFF'),
    );
}

// 商品資料
$items = [
    ['name' => 'NextPDF Pro 年度授權', 'qty' => 1,  'price' => 9_900,  'total' => 9_900],
    ['name' => 'NextPDF Enterprise 月費', 'qty' => 3, 'price' => 4_500,  'total' => 13_500],
    ['name' => '技術支援服務(小時)',    'qty' => 5,  'price' => 2_000,  'total' => 10_000],
    ['name' => '字型授權包(CJK)',       'qty' => 1,  'price' => 3_000,  'total' => 3_000],
];

$rowY = $tableTop + 8.0;
foreach ($items as $i => $item) {
    $bgColor = ($i % 2 === 0)
        ? Color::fromHex('#FFFFFF')
        : Color::fromHex('#F9FAFB');

    $drawing->rectangle(
        x: 25.0, y: $rowY, width: 160.0, height: 9.0,
        fill: $bgColor,
    );

    $text->write(
        text:     $item['name'],
        position: Position::at(x: 27.0, y: $rowY + 2.0),
        fontSize: 9.0,
        fontName: 'NotoSansCJKtc',
        color:    Color::fromHex('#111827'),
    );
    $text->write(
        text:     (string) $item['qty'],
        position: Position::at(x: 120.0, y: $rowY + 2.0),
        fontSize: 9.0,
        fontName: 'NotoSansCJKtc',
        color:    Color::fromHex('#374151'),
        alignment: 'right',
        maxWidth:   12.0,
    );
    $text->write(
        text:     'NT$ ' . number_format($item['price']),
        position: Position::at(x: 130.0, y: $rowY + 2.0),
        fontSize: 9.0,
        fontName: 'NotoSansCJKtc',
        color:    Color::fromHex('#374151'),
        alignment: 'right',
        maxWidth:   25.0,
    );
    $text->write(
        text:     'NT$ ' . number_format($item['total']),
        position: Position::at(x: 155.0, y: $rowY + 2.0),
        fontSize: 9.0,
        fontName: 'NotoSansCJKtc-Bold',
        color:    Color::fromHex('#111827'),
        alignment: 'right',
        maxWidth:   30.0,
    );

    $rowY += 9.0;
}

// ─── 合計欄 ──────────────────────────────────────────────────
$subtotal = 36_400;
$tax      = (int) ($subtotal * 0.05);
$total    = $subtotal + $tax;

$summaryY = $rowY + 5.0;
$summaryRows = [
    ['label' => '稅前合計', 'value' => 'NT$ ' . number_format($subtotal), 'bold' => false],
    ['label' => '營業稅(5%)', 'value' => 'NT$ ' . number_format($tax),  'bold' => false],
    ['label' => '應付總計',    'value' => 'NT$ ' . number_format($total),  'bold' => true],
];

foreach ($summaryRows as $row) {
    if ($row['bold']) {
        $drawing->line(
            x1: 130.0, y1: $summaryY - 1.0,
            x2: 185.0, y2: $summaryY - 1.0,
            color: Color::fromHex('#1E3A8A'),
            width: 0.5,
        );
    }
    $text->write(
        text:      $row['label'],
        position:  Position::at(x: 130.0, y: $summaryY),
        fontSize:  $row['bold'] ? 10.0 : 9.0,
        fontName:  $row['bold'] ? 'NotoSansCJKtc-Bold' : 'NotoSansCJKtc',
        color:     Color::fromHex($row['bold'] ? '#1E3A8A' : '#6B7280'),
    );
    $text->write(
        text:      $row['value'],
        position:  Position::at(x: 155.0, y: $summaryY),
        fontSize:  $row['bold'] ? 11.0 : 9.0,
        fontName:  $row['bold'] ? 'NotoSansCJKtc-Bold' : 'NotoSansCJKtc',
        color:     Color::fromHex($row['bold'] ? '#1E3A8A' : '#374151'),
        alignment: 'right',
        maxWidth:  30.0,
    );
    $summaryY += $row['bold'] ? 9.0 : 7.0;
}

// ─── 備注與付款資訊 ──────────────────────────────────────────
$text->write(
    text:     '付款說明',
    position: Position::at(x: 25.0, y: $summaryY + 5.0),
    fontSize: 9.0,
    fontName: 'NotoSansCJKtc-Bold',
    color:    Color::fromHex('#374151'),
);

$text->write(
    text:     "銀行:台灣銀行  帳號:012-3456789-01\n戶名:NextPDF 科技有限公司\n請於付款期限前完成匯款,並來信告知。",
    position: Position::at(x: 25.0, y: $summaryY + 13.0),
    fontSize: 8.5,
    fontName: 'NotoSansCJKtc',
    color:    Color::fromHex('#6B7280'),
    lineHeight: 1.6,
);

// ─── 輸出 ────────────────────────────────────────────────────
$document->save('/output/invoice-INV-2024-001.pdf');

程式碼說明

變化延伸

加上數位簽章

動態資料來源

延伸閱讀