將 HTML 繪製為 PDF 頁面
重點速覽
標題為「重點速覽」的區段這則 recipe(範例)會使用 writeHtml(),把一段 HTML 與 CSS 片段轉成 PDF 頁面內容。你把 markup(標記語言)交給它,它就會繪製成排好版的頁面。完整且可直接執行的程式碼版本在 examples/08-html-basic.php。你可以依照下面的步驟操作,也可以直接複製這個範例。
NextPDF 會一趟讀完你的 HTML,並把結果直接串流寫進頁面。這是一條單趟串流(streaming)流水線。你不必先理解這套模型也能使用這則範例,但它值得記住,因為本頁後面的幾條規則都受它影響。
composer require nextpdf/core:^3這個指令會安裝 nextpdf/core 套件。本頁範例以 PHP 8.4 執行,支援的執行環境是 >=8.4 <9.0。
概念總覽
標題為「概念總覽」的區段writeHtml() 接收一段 HTML 字串,從目前游標位置開始,將內容繪製到目前頁面。以下逐步說明內部流程。首先,引擎只掃描你的 HTML 一次,將其拆成一串 token(HtmlTokenizer)。接著,它會由左到右走訪這份清單(HtmlParser)。對每個元素,它會把對應的 PDF 繪圖指令(也就是內容串流運算子)寫進緩衝區。兩次呼叫之間,引擎從不會在記憶體中建立或保留你的元素樹狀結構。這是刻意的設計,也就是單趟串流模型,記錄於 ADR-001。
每個受支援的區塊元素都會變成版面框,每段文字則會變成顯示文字(text-show)運算子。來自行內 style 屬性與 <style> 區塊的樣式,會透過 CSS 串接(cascade)resolve(解析);也就是在多條規則同時適用時,決定哪個樣式勝出的那套標準規則。文字換行、對齊與間距遵循 CSS Text 模型;該模型規範原始文字如何成為排版後、已斷行的文字(W3C CSS Text Level 3)。
如果你不選字型,內文會使用一套預設字體。那套預設是一個標準 Type 1 字型,也就是 ISO 32000-2 所列 14 個標準字型之一。只有在你註冊並選用自己的字型,或某個一致性設定檔要求 NextPDF 嵌入替代字型時,這套預設才會改變。
先釐清一項期望:NextPDF 支援的是 HTML 與 CSS 的一個子集,而不是兩者的全部。這則範例涵蓋的正是該受支援子集,並不主張完整支援 HTML 或 CSS。每個模組已驗證的確切狀態,請見 CSS 支援矩陣一節。
API 介面
標題為「API 介面」的區段這個方法的簽章是 writeHtml(string $html): static。它宣告於 NextPDF\Contracts\PdfDocumentInterface 介面,並實作於 NextPDF\Core\Concerns\HasTextOutput。它會繪製到目前頁面;若還沒有任何頁面,它會替你建立一個。這個方法的完整 PHPDoc 表格由原始碼產生。
程式碼範例 — 快速上手
標題為「程式碼範例 — 快速上手」的區段<?php
declare(strict_types=1);
require_once __DIR__ . '/vendor/autoload.php';
use NextPDF\Core\Document;
$doc = Document::createStandalone();$doc->setTitle('HTML Basic');$doc->addPage();
$doc->writeHtml('<h1>HTML Rendering in NextPDF</h1><p>Rendered with <strong>writeHtml()</strong>.</p>');
$doc->save(__DIR__ . '/out.pdf');程式碼範例 — 正式環境
標題為「程式碼範例 — 正式環境」的區段這是完整、自成一體的範例,也是測試載具(harness)實際執行的版本。它對應 examples/08-html-basic.php。它不會寫死輸出路徑,而是寫到載具提供的位置,讓可重現性載具能把腳本跑兩次並比對結果。
<?php
declare(strict_types=1);
require_once __DIR__ . '/vendor/autoload.php';
use NextPDF\Core\Document;
$doc = Document::createStandalone();$doc->setTitle('HTML Basic');$doc->addPage();
$html = <<<'HTML'<h1 style="color: #1E3A8A;">HTML Rendering in NextPDF</h1>
<p>NextPDF renders <strong>HTML content</strong> directly into PDF pages.This is the recommended approach for <em>mixed formatting</em>.</p>
<h2>Supported elements</h2>
<ul> <li>Headings (h1-h6)</li> <li>Paragraphs with <strong>bold</strong> and <em>italic</em></li> <li>Ordered and unordered lists</li> <li>Tables with borders and alignment</li> <li>Inline styles (color, font-size, margin)</li></ul>
<h2>Ordered list</h2>
<ol> <li>Create a Document instance</li> <li>Add pages and content</li> <li>Call save() or output()</li></ol>HTML;
$doc->writeHtml($html);
// The harness sets NEXTPDF_COOKBOOK_OUTPUT and runs this script twice.// Honour it: do not hard-code a path, do not echo the PDF to STDOUT.$out = getenv('NEXTPDF_COOKBOOK_OUTPUT');$doc->save($out !== false ? $out : __DIR__ . '/render-html-to-pdf.pdf');
echo "Wrote render-html-to-pdf.pdf\n";預期的 STDOUT:
Wrote render-html-to-pdf.pdf邊界情況與陷阱
標題為「邊界情況與陷阱」的區段- 游標交接。
writeHtml()會將游標推進到已繪製內容的結尾。接在後面的cell()或第二次writeHtml()會從那裡接續,而不是從頁面頂端開始。 - 還沒有頁面。 若不存在任何頁面,
writeHtml()會在繪製前先加一頁。當你需要先設定特定頁面尺寸時,請明確呼叫addPage()。 - 元素數與巢狀深度上限。 這個串流引擎設有
50,000個元素與 100 層巢狀的上限(ADR-001)。超過上限的文件會被拒絕,而不是遭到無聲截斷。 - 不支援的標記語言。 落在受支援子集之外的元素與屬性會被忽略或退回(fall back);它們不會拋出例外。依賴某個屬性之前,請先對照 CSS 支援矩陣確認涵蓋範圍。
- 外部資源。 遠端影像與樣式表受外部資源政策管理;預設政策不會擷取任意遠端 URL。
由於分詞(tokenization)與繪製會對你的輸入做單趟處理,成本會隨 token 數量線性成長,也就是 O(n)。這則範例的預設預算是 wall_ms: 1500, peak_mb: 96。由於引擎以串流方式輸出、且不在記憶體中保留 DOM,尖峰記憶體會跟著內容串流緩衝區與作用中的樣式堆疊大小走,而不是跟著整份文件大小走。
CSS 支援矩陣摘錄(僅列 Verified 的列)
標題為「CSS 支援矩陣摘錄(僅列 Verified 的列)」的區段這裡只列出評為 Verified、且出現在經真實性稽核的 CSS 支援矩陣中的列。「Verified」代表有 src/Html/ 實作、一套實質的專屬 fixture 測試組,且能在結構設定檔下決定性地通過。
| W3C 模組 | 層級 | 狀態 | 佐證 |
|---|---|---|---|
CSS 彈性盒版面(css_flexbox_1) | 1 | 已驗證(Verified) | src/Html/Flex/、tests/Unit/Html/Flex/ |
CSS 格線版面(css_grid_1) | 1 | 已驗證(Verified) | src/Html/Grid/、WPT 語料 |
CSS Cascading and Inheritance 串接與繼承(css_cascade_3) | 3 | 已驗證(Verified) | src/Html/Cascade/、tests/Unit/Html/Cascade/ |
CSS 表格(css_tables_3) | 3 | 已驗證(Verified) | src/Html/Table/、表格 fixture 與黃金 PDF |
CSS 字型(css_fonts_4) | 4 | 已驗證(Verified) | src/Html/FontFace/、tests/Unit/Html/FontFace/ |
像 text-align、text-indent 與 color 這類屬性,在矩陣中評為「Claimed」(已實作,但沒有專屬模組 fixture),所以這裡刻意不列為 Verified。
單趟串流的限制(ADR-001)
標題為「單趟串流的限制(ADR-001)」的區段這個 HTML 引擎不保留任何 DOM。它的狀態是一個純量游標,再加上一個 push/pop 樣式堆疊;只含空白的文字節點會在分詞階段被丟棄。一個後果是:較晚出現的元素無法重新設定較早元素的樣式,而需要完整樹狀脈絡的選擇器(例如複雜的 :has() 情況)會依 ADR-006 受到限制。規劃版面時,請只依賴文件順序。
分層契約(ADR-010)
標題為「分層契約(ADR-010)」的區段剖析、版面與繪製是彼此分離的層。剖析器不會發出原始繪製運算子,版面分派也不會剖析 CSS;越過這些邊界,正是 ADR-010 禁止的耦合債。對範例作者而言,這代表公開的進入點是 writeHtml(),不要直接操作剖析器內部。
大型文件的記憶體預算
標題為「大型文件的記憶體預算」的區段依 ADR-020,以容器為範圍的排版脈絡(flex、表格)可能會建立一棵短暫的子樹,但每個脈絡受限於 5,000 個節點、20 層深度,並在所有作用中脈絡間共享 50 MB 的作用記憶體上限與 10 層巢狀限制。在那些脈絡之外,串流模型不保留任何樹。讓個別表格與 flex 容器維持在節點上限之內,記憶體用量才會可預期。
安全性須知
標題為「安全性須知」的區段請將 HTML 輸入視為不可信任。NextPDF 不執行任何指令稿,預設的外部資源政策也不會擷取任意遠端 URL,所以引擎本身採取保守做法。即便如此,凡是由使用者輸入組成的 HTML,在繪製之前都要先驗證或清理。元素與巢狀上限也會保護你:它們限制了一份惡意或格式錯誤的文件能要求多少工作量。
一致性
標題為「一致性」的區段| 陳述 | 規範 | 條款 | 參考 ID |
|---|---|---|---|
| CSS Text 規範原始文字如何轉換成排版後、已斷行的文字。 | W3C CSS Text Level 3(文字模組第 3 級) | 條款 css_text_3#x1.x2.p4 | |
| 預設內文字體解析為一個標準 Type 1 字型。 | ISO 32000-2 | 條款 iso32000_2_sec9#x1.x29 |
這則範例示範 NextPDF 如何繪製受支援的 HTML 與 CSS 子集。它並不主張完整支援 HTML 或 CSS;每個模組已驗證的狀態列在 CSS 支援矩陣中。
商業脈絡
標題為「商業脈絡」的區段不適用。