跳到內容

Artisan 開發者指南

Artisan 套件有兩項相互關聯的職責:透過 Chrome 繪製 HTML,並把產生的 PDF 頁面匯入 NextPDF 文件。除錯時,請分清 Chrome、parser(剖析器)與匯入器之間的邊界。

當你要撰寫 renderer 整合、長駐 worker、parser 診斷,或以 nextpdf/artisan 為核心的測試時,請參考本指南。

層級負責方職責不應放在這裡的內容
應用程式應用程式處理 HTML 產生的授權,並選擇 renderer 組態。瀏覽器行程管理。
HTML 政策應用程式與套件在繪製之前拒絕不安全或過大的 HTML。租戶授權或商業決策。
Chrome renderer(渲染器)nextpdf/artisan將 HTML 繪製成由 Chrome 產生的獨立 PDF。通用 PDF 修復或任意 PDF 編輯。
parser/匯入器nextpdf/artisan剖析已繪製的 PDF,並把其中一頁匯入為 form XObject。完整的 PDF 符合性驗證。
核心引擎nextpdf/nextpdf放置匯入的 form 物件,並寫出最終文件。Chrome CDP 生命週期。
階段行為開發者動作
建立組態ChromeRendererConfig 定義執行檔、逾時、CSS、輸入大小與沙箱行為。請使用依環境調整的組態,而不是寫死執行期推測值。
建立 rendererChromeHtmlRenderer 持有 BrowserPool在 worker 內重複使用 renderer,並在 worker 關閉時關閉它。
驗證 HTML安全政策會檢查大小,並用預設 CSS 包裹文件。在進入此階段前先驗證呼叫端的授權。
Chrome 列印CDP 會繪製出一份獨立 PDF。除非有經過審查的政策允許,否則一律封鎖外部資源。
剖析 PDFPdfReader::parse() 會讀取 xref 資料、頁面、物件、資源與修訂版本。除非診斷本身就是目的,否則應把 parser 失敗視為繪製失敗。
匯入頁面PageImporter::import() 會擷取頁面內容、media box、資源與內嵌物件。除非工作流程刻意選擇其他頁面,否則匯入第 0 頁。
路徑用途
app/Pdf/Renderers/*包裝 ChromeHtmlRenderer 的應用程式包裝層。
app/Pdf/Templates/*HTML 範本繪製,以及將 DTO 對映到 view 的處理。
app/Pdf/Policies/*HTML 大小、資源與租戶的繪製政策。
tests/Pdf/Renderer/*使用小型 HTML 測試夾具(fixture)的 renderer 冒煙測試。
tests/Pdf/Parser/*用於匯入 Chrome 輸出的 parser 測試夾具。

請將範本繪製與瀏覽器繪製分離。renderer 應收到最終 HTML 與已知的頁面寬度。

<?php
use NextPDF\Artisan\ChromeHtmlRenderer;
use NextPDF\Artisan\ChromeRendererConfig;
use NextPDF\Artisan\PageImporter;
use NextPDF\Parser\PdfReader;
$renderer = new ChromeHtmlRenderer(new ChromeRendererConfig(
renderTimeout: 30,
maxHtmlSize: 1_000_000,
));
$result = $renderer->render($html, widthPt: 595.28);
$reader = new PdfReader($result->getPdfData());
$reader->parse();
$form = (new PageImporter())->import($reader);

為每個 worker 行程或每個 request 範圍各建立一個 renderer。重複使用 renderer 可避免反覆承擔 Chrome 啟動成本。明確關閉 renderer,可避免 worker 關閉期間發生行程洩漏。

<?php
final class InvoiceChromeRenderer
{
public function __construct(
private readonly ChromeHtmlRenderer $renderer,
) {}
public function renderInvoice(string $html): string
{
return $this->renderer
->render($html, widthPt: 595.28)
->getPdfData();
}
public function close(): void
{
$this->renderer->close();
}
}

當 Chrome 輸出無法匯入時,parser API 會派上用場。診斷應維持唯讀,並避免在成功匯入後變更 parser 狀態。

診斷問題要使用的 API預期訊號
檔案可以剖析嗎?PdfReader::parse()遇到無效的 PDF 結構時會擲回例外。
0 頁存在嗎?PdfReader::getPage(0)會回傳一個 PdfObject
有內容嗎?PdfReader::getPageContentStream($page)非空的內容串流(content stream)。
有資源存在嗎?PdfReader::getPageResources($page)資源字典陣列。
有增量修訂版本嗎?PdfReader::getRevisionCount()計數大於一。
是哪個物件失敗?PdfTokenizer::getOffset() 與 parser 例外的上下文。用來縮減測試夾具的位元組偏移量。
擴充點用途限制
ChromeRendererConfig::fromArray()Framework(框架)組態對映。未知或型別錯誤的選用值會退回預設值。
HtmlSecurityPolicyInterface剖析層的 HTML 政策。不會取代傳輸、行程或授權控管。
LoggerInterface繪製與瀏覽器診斷。預設不要記錄 HTML 內容。
BrowserPool長駐 Chrome 行程的重複使用。必須在 worker 關閉時一併關閉。
PageImporter內嵌已剖析的外部頁面。reader 必須先完成剖析。
parser 類別用於診斷與匯入 Chrome 輸出。並非一般用途的 PDF 修復工具組。
  1. 在最小的繪製測試中重現該 HTML 片段。
  2. 驗證 maxHtmlSize、預設 CSS 與 Chrome 執行檔路徑。
  3. 使用固定的點(point)寬度進行繪製。
  4. PdfReader::parse() 剖析回傳的 PDF 位元組。
  5. 除非工作流程刻意選擇其他頁面,否則匯入第 0 頁。
  6. 為每個能重現失敗的最小 HTML 加入測試夾具。
  7. 在 worker 關閉掛鉤中關閉 renderer。
失敗應在何處處理建議的回應方式
找不到 Chrome 執行檔部署檢查與 renderer 建構路徑。在開始接受繪製流量前,讓就緒檢查失敗。
HTML 過大HTML 政策。在啟動 Chrome 之前就予以拒絕。
瀏覽器逾時renderer 邊界。讓繪製失敗,並記錄範本名稱、大小、寬度與逾時值。
parser 失敗匯入邊界。在政策允許時,儲存一份小型且已淨化的測試夾具以供除錯。
瀏覽器行程洩漏worker 生命週期。在關閉階段將其關閉,並在受控的繪製次數後重新啟動。
考量項目預設值何時應覆寫
繪製逾時30 秒。僅針對已實測、有明確邊界的文件才調高。
HTML 大小上限5,000,000 位元組。對公開的 endpoint 應調低。
沙箱已啟用。僅在容器限制要求且主機已隔離時才停用。
高度heightPt <= 0 時自動計算。對於嚴格的版面契約,請使用固定高度。
外部資源由 renderer 政策封鎖。僅在經過審查的資源政策下才允許。
  • 繪製測試涵蓋具代表性的 HTML 與 CSS。
  • 安全測試涵蓋過大的 HTML,以及被封鎖的資源存取嘗試。
  • 匯入測試會斷言回傳的 form 物件具備內容、media box 與資源。
  • parser 測試涵蓋 xref table、xref stream、object stream 與格式錯誤的測試夾具案例。
  • worker 測試會呼叫 close() 並驗證沒有瀏覽器行程殘留。
  • 效能測試會依範本與內容大小記錄繪製時間。