HTML 管線
Spec: CSS Cascade 5, §6.1 CSS Cascade 5 §6.1 Spec: CSS Display 3, §2 CSS Display 3 §2 Evidence: Code-backed
NextPDF 在你的 PHP 行程內將 HTML 與 CSS 轉譯為 PDF——預設不需要瀏覽器,也不需要子行程。本頁說明轉換所經過的分層階段、CSS 引擎真正涵蓋的範圍,以及在哪些情況下委派給真正的瀏覽器轉譯器才是誠實選擇。
為什麼這很重要
標題為「為什麼這很重要」的區段「HTML 轉 PDF」聽起來像是單一操作。但它其實牽涉一套層疊(cascade)、一套盒模型、一道版面配置流程,以及一道繪製流程。每一項都有明確規格,也各有失效模式。把它們揉成單一程序的引擎很脆弱。只要色彩剖析有一處變更,就可能讓某個盒子位移;而唯一能發現的方法,就是實際轉譯後再檢視。
行程內模型確實有一項優勢:不必安裝瀏覽器、不必操作沙箱,也不必跨越行程邊界封送(marshal)資料。但唯有將轉換拆解得夠乾淨、能各自獨立測試每一項關注點,這項優勢才值得採用。正是這套架構讓「在 PHP 中轉譯 HTML」從僅僅可行變得值得信賴。
簡短版
標題為「簡短版」的區段- HTML/CSS 轉換透過
writeHtml()在行程內執行。產出的是原生 PDF 內容,而非頁面的影像。 - 它是單趟(single-pass)且串流式的。分詞器(tokenizer)產生一份權杖(token)清單。剖析器由左至右消耗它,且不保留完整的 DOM 樹(ADR-001)。硬性上限限制了元素數量與巢狀深度。
- 引擎以明確的分層組織:CSS 剖析與套用器(applicator)、樣式狀態、版面配置與格式化、繪製,以及分頁媒體——並為各層能做什麼訂下嚴格規則(ADR-010)。
- CSS 引擎涵蓋層疊、盒模型,以及常見的版面配置(區塊、行內、表格、浮動等)——涵蓋範圍不小,但仍只是現代瀏覽器所實作功能中一個已界定的子集。
- 當你需要對任意現代 CSS 達到精確的瀏覽器保真度時,NextPDF 可透過一個選用擴充功能委派給無頭瀏覽器轉譯器——這是刻意設計且網路隔離的接縫,並非預設路徑。
NextPDF 如何處理這件事
標題為「NextPDF 如何處理這件事」的區段這項轉換由一連串階段構成,每個階段都消耗前一階段具型別的輸出。
- Tokenize HTML becomes an ordered token list — no retained DOM tree.
- Resolve CSS Parse styles; the cascade and applicators compute typed values.
- Style state A push/pop style stack carries computed values per nesting level.
- Layout Block, inline, table, and float geometry computed; no paint here.
- Paint Borders, backgrounds, text, and decorations emit PDF operators.
- Paged media Page-break and @page rules applied as the cursor crosses page bounds.
有兩條架構規則讓這不只是流程。
各層都有契約。 CSS 文字只會在套用器類別內被讀取。版面配置程式碼負責計算幾何,但不產出任何繪製運算子。繪製程式碼讀取的是不可變的計算樣式快照,絕不會讀取可變的版面配置追蹤狀態。分頁媒體程式碼會觸發斷點,但將頁面裝飾委派給繪製層。這些邊界是強制施行的(ADR-010)。這就是為什麼新增一個 CSS 屬性時,是新增一個套用器,而不是一處同時牽動剖析器、版面配置分派與繪製器的變更。
沒有 DOM。 此管線依決策採單趟、串流式設計(ADR-001):每個巢狀層級至多保留一個樣式狀態,再加上目前作用中的游標,而非每個元素一個物件。少數操作確實需要前瞻(look-ahead),例如表格欄寬計算、:has()、:last-child。這些情況是透過在扁平權杖清單之上建立有界的預掃描索引結構來處理,而不是靠保留一棵樹。元素數量與巢狀深度都設有硬性上限,因此病態的輸入會快速失敗,而不會耗盡記憶體。
CSS 引擎解析的是真正的 CSS 語意,而非徒具其表的仿製品。相互競爭的宣告會依來源、重要性、層、特異性與順序被歸結為每個屬性一個值——也就是真正的層疊。版面配置遵循盒模型。盒子的類型及其所建立的格式化情境,決定了它與其流內(in-flow)同層元素如何配置。引擎的原始碼正是圍繞這些關注點組織(層疊、box/display、flex、float、表格、分片)。這就是為什麼你能對照規格來推論它的行為,而不必靠經驗摸索。
證據怎麼說
標題為「證據怎麼說」的區段本頁為 Evidence: Code-backed 。各階段與規則對應到 core 儲存庫:
- 行程內進入點是
writeHtml(string $html): static,位於src/Core/Concerns/HasTextOutput.php。 - 這套單趟、不保留 DOM,且設有元素與巢狀上限的設計,對應 ADR-001,以及
src/Html/中的 tokenizer/parser/樣式堆疊程式碼。 - 分層的引擎契約——CSS parsing/applicators、樣式狀態、版面配置、繪製、分頁媒體——即 ADR-010,體現在
src/Html/的目錄配置上(例如Cascade/、Css/、Flex/、Float/、Fragmentation/,以及套用器類別)。 - 瀏覽器委派接縫是同一檔案中的
writeHtmlChrome(),其文件說明需要選用的轉譯器擴充功能,外加一個 Chrome/Chromium 執行檔。
這些標準為涵蓋範圍的主張提供了誠實依據。層疊會依來源、重要性、層、特異性、順序,將相互競爭的宣告歸結為每個屬性一個值——依據 Spec: CSS Cascade 5, §6.1 CSS Cascade 5 §6.1 ,而流內配置則遵循盒子與格式化情境規則,依據 Spec: CSS Display 3, §2 CSS Display 3 §2 。同樣重要的是這道邊界:特性查詢(feature query)之所以存在,正是因為並非每個處理器都支援每一項特性,依據 Spec: CSS Conditional 5, §2 CSS Conditional 5 §2 。 NextPDF 的 CSS 引擎是一個已界定、與規格對齊的子集,而坦白說明這一點正是該契約的一部分。
實務範例
標題為「實務範例」的區段行程內轉譯只需要一次呼叫。輸出的是可選取的 PDF 文字,而非點陣化的頁面:
<?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 and CSS</strong> directly into PDF pages,<em>in-process</em>.</p><ul> <li>Headings, paragraphs, bold and italic</li> <li>Lists, tables, inline styles</li></ul>HTML;
$doc->writeHtml($html);$doc->save(__DIR__ . '/html-basic.pdf');如果同一份文件需要任意現代 CSS,且必須達到精確的瀏覽器保真度,這次呼叫就會改為 writeHtmlChrome($html)——同一份文件、不同的轉譯路徑,並且刻意依賴選用的瀏覽器轉譯器。
常見誤解
標題為「常見誤解」的區段一再出現的誤解是:HTML 轉 PDF 引擎「基本上就是個瀏覽器」。它不是,也不宣稱自己是。瀏覽器是整個 Web 平台龐大且持續更新的實作。NextPDF 的行程內引擎是一個與規格對齊、專注於文件版面配置的子集。誠實的心智模型是「一個稱職的列印文件 CSS 引擎」,而非「PHP 裡的 Chrome」。當你真的需要完整的平台時,那正是 writeHtmlChrome() 的用途。它是一條獨立、需主動啟用、有自身運維負擔的路徑,而非無聲的退路。
第二個誤解:以為瀏覽器路徑只不過是「透過網路轉譯頁面」。就設計而言,事實正好相反。委派接縫在轉譯時會無條件封鎖子資源的網路存取——不允許遠端影像、字型、樣式表或框架——因此它無法成為對外請求的攻擊管道。像素保真度,有;開放的對外網路出口,沒有。
限制與邊界
標題為「限制與邊界」的區段本頁說明此管線的運作方式,以及行程內/瀏覽器之間的抉擇。它並非一份 CSS 支援對照表。行程內引擎確切涵蓋哪些屬性、模組與選擇器,是由程式碼及其符合性測試定義,而非由本概觀定義。該涵蓋範圍會持續演進。瀏覽器委派路徑需要一個選用擴充功能,以及一個 Chrome/Chromium 執行檔。該擴充功能的安裝設定、運維特性及其內部結構不在本頁範圍內,並隨該套件一併記載於其文件中。「行程內」描述的是預設的 writeHtml() 路徑。它並非主張每一條轉譯路徑都不使用子行程。這些架構性主張在本頁審閱日期當下是準確的。權威來源是 core 儲存庫中的 src/Html/、ADR-001 與 ADR-010。
行程內 CSS 引擎是 Core 的一項能力。 瀏覽器委派接縫是一個選用擴充功能,這裡僅在能力層級加以呈現:
| Edition | Availability |
|---|---|
| Core | Core 提供行程內 HTML/CSS 引擎(writeHtml)。 |
| Pro | 瀏覽器委派路徑是一個選用的附加擴充功能,與版本層級無關。 |
| Enterprise | 瀏覽器委派路徑是一個選用的附加擴充功能,與版本層級無關。 |
相關文件
標題為「相關文件」的區段- 管線模型——HTML 內容路徑在整體文件流程中的位置。
- 何時不該使用 NextPDF——誠實的邊界,包含瀏覽器路徑或其他工具適合的場景。
- 整合決策指南——針對你的情境,在行程內引擎與轉譯器之間做出選擇。
詞彙表
標題為「詞彙表」的區段- 行程內轉譯 — 在 PHP 行程內將 HTML/CSS 轉換為 PDF,不使用瀏覽器或預設子行程(
writeHtml())。 - 單趟/串流 — 由左至右消耗權杖串流,不保留完整的 DOM 樹(ADR-001)。
- 層疊(Cascade) — CSS 的處理過程,會依來源、重要性、層、特異性與順序,將相互競爭的宣告歸結為每個屬性一個值。
- 格式化情境 — 盒子所建立的版面配置環境,用以決定其流內內容如何配置。
- 引擎分層契約 — 強制施行的規則集(ADR-010),界定剖析、樣式、版面配置、繪製與分頁媒體各層各自能做什麼。
- 瀏覽器委派接縫 — 選用的
writeHtmlChrome()路徑,透過無頭瀏覽器轉譯,並封鎖子資源的網路存取。