跳到內容

HTML:HTML+CSS 轉 PDF 繪製子系統

HTML 子系統會透過單趟向前掃描,把 HTML+CSS 轉換成 PDF 內容串流。它是整個引擎中規模最大、風險最高的子系統(src/Html/ 底下有 324 個檔案)。

Terminal window
composer require nextpdf/core:^3

HTML 子系統是一個單趟串流式 HTML+CSS 轉 PDF renderer。公開介面只有一個方法:Document::writeHtml()。在內部,HtmlParser 會在單趟向前掃描中對輸入做 tokenize(標記化)、resolve(解析)樣式、計算版面配置,並輸出 PDF 運算子——過程中不保留任何文件樹。

先釐清適用範圍。這個子系統並不是保留式文件 renderer。它不持有元素圖(element graph),不會對已寫出的內容重新進行版面配置,也不允許在剖析開始後變更輸入。它只在固定的規格版本上實作一份精選的 CSS 子集,並受兩份架構決策管控。ADR-001 固定了單趟串流模型及其上限。ADR-010 固定了四層契約(CSS 剖析、樣式狀態、版面配置、繪製),以及分頁媒體與量測的附屬層。

HtmlParser 在模組清單中被列為 critical(關鍵)風險等級。有五個檔案帶有已記載的危險區註記:HtmlParser 協調器(串流式 tokenizer,超過 1000 行程式碼)、HtmlStyleState(含 100 多個 CSS 屬性欄位,採堆疊繼承模型)、HtmlBlockHandler(區塊派送,與樣式狀態耦合)、FlexLayoutEngine(完整的 flex 量測與版面配置),以及 TableParser(跨分頁處理 colspan/rowspan 的分頁邏輯)。在這裡做變更時,請視為 plan-mode(規劃模式)的工作。

本頁是進入點。細節頁面如下:pipeline 說明各階段順序、css-resolver 說明階層與優先序、layer-contracts-adr010 說明各層邊界,以及 streaming-constraints-adr001 說明不保留文件樹的模型及其上限。

writeHtml() 能算繪由右至左(RTL)內容。請在 body、表格或任何元素上設定 CSS direction: rtl 屬性。引擎會透過字型排印層的雙向引擎,以 Unicode 雙向演算法(UAX #9)解析視覺順序——BidiEngine 的細節請見 Typography。混合的拉丁文、阿拉伯文與數字內容會正確排序,而阿拉伯文後面的數字會保持其數位由左至右。

阿拉伯文也會採用情境字形塑形:引擎會為每個字母選用起始、中間、結尾或孤立形式,並套用 Lam-Alef 連字。塑形需要一個字元對映涵蓋 Arabic Presentation Forms-B 區塊的已註冊字型;僅含拉丁字元的字體,包括 standard-14 字型,無法繪製阿拉伯文。在表格中,每個儲存格會各自重排與塑形,並在 direction: rtl 之下對齊到起始(右側)邊緣。RTL 適用於阿拉伯文、希伯來文、波斯文與烏爾都文;希伯來文會被重排,但不會被塑形。

請以 CSS direction 屬性設定方向——HTML dir 屬性不會對映到它。非表格區塊與行內文字的水平對齊,以及 text-align: justify,皆尚未套用。如需可執行的阿拉伯文發票範例與目前限制的完整清單,請見 Render right-to-left Arabic HTML

符號位置角色
Document::writeHtml(string $html): staticsrc/Core/Concerns/HasTextOutput.php公開進入點。在目前游標位置繪製 HTML。
Document::createStandalone(): selfsrc/Core/Document.php建立一份獨立(standalone)文件。
HtmlParser::parse(string $html): HtmlRenderResultsrc/Html/HtmlParser.php內部協調器。
HtmlRenderResultsrc/Html/HtmlRenderResult.php不可變的結果:串流、結束游標、已使用的字型。
DefaultHtmlSecurityPolicysrc/Html/DefaultHtmlSecurityPolicy.php預設的 tag/attribute/CSS/URL 政策。
HtmlSecurityPolicyInterfacesrc/Contracts/HtmlSecurityPolicyInterface.php供自訂政策使用的政策契約。

取自 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();
$doc->writeHtml('<h1 style="color:#1E3A8A;">HTML Rendering</h1><p>Direct to PDF.</p>');
$doc->save(__DIR__ . '/output/08-html-basic.pdf');

以下是一份帶有內嵌樣式區塊的表格報表,以 examples/09-html-table.php 為範本。

<?php
declare(strict_types=1);
require_once __DIR__ . '/../vendor/autoload.php';
use NextPDF\Core\Document;
use NextPDF\Exception\HtmlParsingException;
function renderInventory(string $rowsHtml, string $out): void
{
$doc = Document::createStandalone();
$doc->setTitle('Inventory');
$doc->addPage();
$html = '<style>table { width: 100%; } '
. 'th { background-color: #1E3A8A; color: #FFFFFF; }</style>'
. '<table border="1" cellpadding="5">' . $rowsHtml . '</table>';
try {
$doc->writeHtml($html);
} catch (HtmlParsingException $e) {
// Input cap, element cap (50,000), or nesting cap (100). Do not retry.
throw $e;
}
$doc->save($out);
}
  • 精選的 CSS 子集。 支援程度依模組而定,且版本已固定。在依賴某個屬性之前,先查閱 CSS 支援對照表
  • 硬性上限會拋出例外。 10 MB 輸入、50,000 個元素、100 層巢狀——任何一項超出都會拋出 HtmlParsingException。參見 串流限制
  • 不會重新進行版面配置。 輸出依文件順序一次寫出;較晚出現的樣式無法改變先前的輸出。
  • :has() 由開關控管,需開啟 css.has 實驗性功能。
  • 關鍵風險子系統。 有五個危險區檔案。在 src/Html/ 底下做變更時請使用 plan mode(規劃模式)。

renderer 不保留任何文件樹,並以單趟向前掃描執行。元素、巢狀與輸入的上限都是硬性限制。完整細節與 worker 安全契約請見 串流限制(ADR-001)

CSS 剖析、樣式狀態、版面配置與繪製切分為四層,彼此以單向契約相連,並另有分頁媒體與量測的附屬層。完整細節請見 各層契約(ADR-010)

樣式狀態與游標的記憶體用量是 O(巢狀深度),而非 O(元素數量)。每頁的 performance_budgetpeak_mb: 6450,000 個元素的上限是硬性上限;較大的輸入請拆成多次 writeHtml() 呼叫。細節請見 串流限制

走訪複雜度為 O(token 數量)。表格欄寬計算會額外增加一次有界的逐表列掃描。選用 :has() 時,預先掃描會額外增加一趟有界的 token 清單掃描。HTML 繪製管線的效能基準測試會強制執行 5% 的回歸門檻(已合併的工作,PR #564)。每頁的 performance_budgetwall_ms: 1500peak_mb: 64)是營運上的上限。

DefaultHtmlSecurityPolicy 會強制執行一份標籤、屬性、CSS 屬性與 URL scheme 的允許清單,外加 10 MB 的輸入上限與 100 層的巢狀上限——且獨立於 parser 之外。CSS 屬性允許清單就是安全性上限。執行階段的支援表則是另一個獨立的能力上限。若要提供更嚴格的政策,請實作 HtmlSecurityPolicyInterface。外部資源的擷取則由 DefaultExternalResourcePolicy 另行管控。

href 與圖片 src 值中,URL 允許清單也會拒絕以反斜線開頭(\…)與 UNC(\\host\share)路徑;這與既有對協定相對(//)URL 的拒絕,以及僅允許 http(s) 或相對路徑的允許清單規則並行。反斜線會在檢查之前正規化為正斜線,因此 Windows 絕對路徑的本機檔案引入或 SMB 共享擷取——兩者皆不帶 URI scheme——都無法落入「無 scheme,因此視為相對路徑」的分支。

CSS 支援對照表節錄(僅列已驗證的列)

標題為「CSS 支援對照表節錄(僅列已驗證的列)」的區段

本頁不重複列出各屬性的支援狀態。CSS 支援對照表 是各 W3C 模組已驗證狀態的唯一權威來源,其中也標示哪些模組屬於 Verified(已驗證)、哪些屬於 Claimed(聲稱)。

這個子系統在固定的規格版本上實作一份精選的 CSS 子集。階層的行為規格對映已連同條號與 chunk 識別碼記載於 css-resolver 一節。各模組的符合性狀態列於 CSS 支援對照表

企業版能力。 Premium 以完全相同的單趟管線擴大 CSS 涵蓋範圍(進階列印與額外模組)。各版本之間的架構、上限與各層契約都相同。請見 CSS 支援對照表