跳转到内容
getnextpdf.com

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,并在关闭时将其关闭。
验证 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() 并验证没有浏览器进程残留。
  • 性能测试会按模板与内容大小记录渲染时间。