串流排版引擎¶
NextPDF Core 提供固定座標排版——開發者需手動指定每個元素的確切位置。串流排版引擎(StreamingLayoutEngine)引入了流式排版模型:內容自動向下流動,頁面空間耗盡時自動插入分頁,並應用高品質的排版規則。
這是生成長篇報告、法律文件、技術手冊等可變長度文件的正確選擇。
核心概念¶
graph TD
Content["內容串流\n(文字/圖片/表格)"] --> Engine["StreamingLayoutEngine"]
Engine --> PageBreaker["SmartPageBreaker\n智慧分頁決策"]
PageBreaker --> OrphanWidow["OrphanWidowController\n孤行/寡行控制"]
OrphanWidow --> ColumnLayout["ColumnLayoutManager\n多欄排版"]
ColumnLayout --> Pages["PDF 頁面\n(自動生成)"]
Engine --> FloatManager["FloatManager\n圖片浮動"]
FloatManager --> Pages 快速開始¶
use NextPDF\Pro\FlowLayout\StreamingLayoutEngine;
use NextPDF\Pro\FlowLayout\LayoutOptions;
use NextPDF\Pro\FlowLayout\Content\Paragraph;
use NextPDF\Pro\FlowLayout\Content\Heading;
use NextPDF\Pro\FlowLayout\Content\FlowTable;
use NextPDF\Pro\FlowLayout\Content\PageBreak;
$engine = new StreamingLayoutEngine(
LayoutOptions::create(
pageWidth: 595.28, // A4 寬度(pt)
pageHeight: 841.89, // A4 高度(pt)
marginTop: 72.0, // 25.4mm
marginBottom: 72.0,
marginLeft: 72.0,
marginRight: 72.0,
)
);
// 加入標題
$engine->add(Heading::create('年度財務報告', level: 1));
$engine->add(Heading::create('摘要', level: 2));
// 加入段落(自動換行與分頁)
$engine->add(Paragraph::create(
'本報告涵蓋 2025 年度全年財務數據,包括收入、支出、利潤與現金流量分析。'
. '所有數據均依據國際財務報告準則(IFRS)編製,並經由獨立會計師事務所審計確認。'
));
// 顯式分頁
$engine->add(PageBreak::create());
// 加入大型表格(自動跨頁,含標題列重複)
$engine->add(
FlowTable::create(headers: ['季度', '收入', '支出', '淨利', '毛利率'])
->addRow(['Q1 2025', 'NT$12,500,000', 'NT$8,200,000', 'NT$4,300,000', '34.4%'])
->addRow(['Q2 2025', 'NT$14,100,000', 'NT$9,100,000', 'NT$5,000,000', '35.5%'])
->addRow(['Q3 2025', 'NT$13,800,000', 'NT$8,800,000', 'NT$5,000,000', '36.2%'])
->addRow(['Q4 2025', 'NT$16,200,000', 'NT$10,100,000', 'NT$6,100,000', '37.7%'])
->withRepeatHeaderOnNewPage(true)
->withAlternatingRowColor('#F8FAFC', '#FFFFFF')
);
// 生成 PDF
$pdf = $engine->render();
file_put_contents('/output/annual-report.pdf', $pdf);
SmartPageBreaker — 智慧分頁¶
SmartPageBreaker 在插入分頁前評估多個排版品質指標,避免產生視覺上不佳的分頁點:
use NextPDF\Pro\FlowLayout\PageBreak\SmartPageBreaker;
use NextPDF\Pro\FlowLayout\PageBreak\PageBreakOptions;
$pageBreaker = new SmartPageBreaker(
PageBreakOptions::create()
->avoidBreakingAfterHeading(true) // 標題後不分頁(至少跟隨一行)
->avoidBreakingInsideTable(false) // 允許表格內分頁(長表格必要)
->avoidBreakingInsideList(true) // 清單項目不拆分
->preferBreakBeforeHeading(true) // 傾向在標題前分頁
->minimumLinesBeforeBreak(3) // 分頁前至少保留 3 行
->minimumLinesAfterBreak(3) // 分頁後至少保留 3 行
);
$engine = new StreamingLayoutEngine(
options: LayoutOptions::create(/* ... */),
pageBreaker: $pageBreaker,
);
強制與禁止分頁¶
use NextPDF\Pro\FlowLayout\Content\Paragraph;
use NextPDF\Pro\FlowLayout\Content\KeepTogether;
// 強制在此前分頁
$engine->add(Paragraph::create('新章節開始', breakBefore: true));
// 強制在此後分頁
$engine->add(Paragraph::create('章節結束', breakAfter: true));
// 將多個元素保持在同一頁(若空間不足則整體移至下頁)
$engine->add(
KeepTogether::create([
Heading::create('重要圖表', level: 3),
Image::create('/charts/q4-revenue.png', width: 400),
Caption::create('圖 3.1:Q4 2025 收入分布'),
])
);
OrphanWidowController — 孤行/寡行控制¶
排版學中,孤行(orphan)是段落第一行遺留在上一頁底部,寡行(widow)是段落最後一行落在下一頁頂部。兩者都是需要避免的排版瑕疵:
use NextPDF\Pro\FlowLayout\Typography\OrphanWidowController;
use NextPDF\Pro\FlowLayout\Typography\OrphanWidowOptions;
$owController = new OrphanWidowController(
OrphanWidowOptions::create(
minimumOrphanLines: 2, // 段落第一行後至少保留 2 行在同頁
minimumWidowLines: 2, // 段落最後 2 行必須在同頁
strategy: 'push-forward', // 'push-forward'(整段移至下頁)| 'pull-back'(從下頁拉回行)
)
);
$engine = new StreamingLayoutEngine(
options: $layoutOptions,
orphanWidowController: $owController,
);
行間距與段落間距¶
use NextPDF\Pro\FlowLayout\Content\Paragraph;
use NextPDF\Pro\FlowLayout\Style\ParagraphStyle;
$style = ParagraphStyle::create(
fontSize: 10.5,
lineHeight: 1.6, // 行高倍數
spaceBefore: 6.0, // 段落前間距(pt)
spaceAfter: 6.0, // 段落後間距(pt)
firstLineIndent: 14.0, // 首行縮排
alignment: 'justify', // 'left' | 'right' | 'center' | 'justify'
hyphenation: true, // 啟用斷字(需設定語言)
language: 'zh-Hant',
);
$engine->add(Paragraph::create('段落文字...', style: $style));
多欄版面¶
use NextPDF\Pro\FlowLayout\ColumnLayout\ColumnDefinition;
$engine->setColumnLayout(
columns: 2,
gutter: 18.0, // 欄間距(pt)
columnRule: true, // 是否顯示欄線
balanceLastPage: true // 最後一頁是否平衡兩欄
);
// 跨欄元素(如大型圖片或標題)
$engine->add(
Heading::create('全版標題', level: 1)
->spanAllColumns(true)
);
$engine->add(Paragraph::create('第一欄文字...'));
$engine->add(Paragraph::create('繼續流入第一欄...'));
// 強制跳至下一欄
$engine->add(ColumnBreak::create());
$engine->add(Paragraph::create('第二欄文字...'));
圖片浮動¶
use NextPDF\Pro\FlowLayout\Content\FloatImage;
use NextPDF\Pro\FlowLayout\Float\FloatSide;
$engine->add(
FloatImage::create(
path: '/images/company-logo.png',
width: 150.0,
side: FloatSide::Right,
margin: 12.0, // 圖文間距
captionText: '圖 1.1:公司 Logo',
)
);
// 圖片右浮動,後續段落自動環繞
$engine->add(Paragraph::create('這段文字會自動環繞右側的圖片排版...'));
頁眉頁尾範本¶
use NextPDF\Pro\FlowLayout\Template\HeaderTemplate;
use NextPDF\Pro\FlowLayout\Template\FooterTemplate;
$engine->setHeader(
HeaderTemplate::create()
->leftText('機密文件')
->centerText('年度財務報告 2025')
->rightImage('/assets/logo.png', height: 20.0)
->borderBottom(0.5, '#D97706')
->firstPageDifferent(true) // 封面頁不顯示頁眉
);
$engine->setFooter(
FooterTemplate::create()
->leftText('NextPDF Pro 生成')
->centerText('頁次:{page} / {total}') // 自動總頁數
->rightText(date('Y-m-d'))
->borderTop(0.5, '#E5E7EB')
);
自動目錄(TOC)¶
use NextPDF\Pro\FlowLayout\Toc\TableOfContents;
use NextPDF\Pro\FlowLayout\Toc\TocOptions;
$toc = new TableOfContents(
TocOptions::create(
title: '目錄',
maxLevel: 3, // 包含至 H3
dotLeader: true, // .......... 點線
pageNumberAlignment: 'right',
indentPerLevel: 15.0,
)
);
// 在文件開頭插入 TOC 佔位符
$engine->add($toc->placeholder());
// 加入正文內容(標題會自動注冊至 TOC)
$engine->add(Heading::create('第一章:執行摘要', level: 1));
// ...
// render() 後 TOC 自動填入正確頁碼
$pdf = $engine->render();
效能與大型文件¶
| 文件規模 | 記憶體使用 | 渲染時間 |
|---|---|---|
| 50 頁(1 萬字) | ~32 MB | < 1 秒 |
| 200 頁(5 萬字) | ~85 MB | 2–4 秒 |
| 1,000 頁(25 萬字) | ~320 MB | 15–30 秒 |
| 5,000 頁以上 | 建議串流模式 | 依章節分批 |
// 超大型文件:使用串流章節模式,控制記憶體峰值
$engine = StreamingLayoutEngine::createChunked(
options: $layoutOptions,
chunkSize: 100, // 每 100 頁輸出一次,釋放記憶體
outputPath: '/tmp/report-chunks/',
);
// 最後合併所有章節 PDF
$merger = new PdfMerger();
$finalPdf = $merger->mergeDirectory('/tmp/report-chunks/');
相關資源¶
- 圖表引擎 — 在串流排版中嵌入動態圖表
- StreamingLayoutEngine API 參考
- SmartPageBreaker API 參考
- 案例研究:金融報表自動化