跳转到内容

NextPDF Artisan 概览

NextPDF Artisan 是 NextPDF 的 Chrome 桥接。它通过 Chrome DevTools Protocol 将 HTML 片段发送到 headless Chrome 进程,捕获 printToPDF 的输出,再将结果作为 Form XObject 嵌入目标文档。嵌入后的文本仍可选取、可搜索。

Artisan 软件包(nextpdf/artisan)为开源的 NextPDF 引擎扩展了一个将布局委派给 Chrome 的 renderer(渲染器)。NextPDF 原生 HTML 管线已覆盖相当广的 CSS 子集。Artisan 桥接面向需要 Chrome 级别布局能力的文档——CSS flexbox 与 grid、从 data URI 加载的自定义网页字体,以及复杂选择器——同时仍生成矢量文本,而非栅格化的屏幕截图。

这个桥接由一组小而职责单一的组件串成管线。ChromeHtmlRenderer 负责统筹一次绘制。ChromeSecurityPolicy 验证输入,并将其包进一份受严格限制的 HTML 文档。BrowserPool 管理 Chrome 进程的生命周期。ViewportCalculator 将 PDF 点数映射到 CSS 像素。NextPDF\Parser 读取器解析 Chrome 输出,再由 PageImporter 将其转换成 Form XObject。每个组件都是 final,采用构造函数注入,并以 PHPStan level 10 完整标注类型。

这个桥接依赖外部组件。它需要 chrome-php/chrome 库(^1.15),以及一个 PHP 进程可访问的 Chrome 或 Chromium 可执行文件。两者都不会随软件包附带。当该库不存在时,这个桥接会抛出 ChromeNotAvailableException,而不会无声降级——详见 /integrations/artisan/troubleshooting/ 页面上的 /integrations/artisan/failure-modes/ 一节。

在输入通过 ChromeSecurityPolicy::validate() 之前,绘制绝不会抵达 Chrome;而且 Chrome 收到的文档一律由严格的 Content-Security-Policy 和一道纵深防御的 CDP 网络封锁包裹。由于这个桥接可能处理不受信任的 HTML,它的传输与隔离设计在 /integrations/artisan/security-and-operations/ 页面上有明确记载,而不是在此摘要中一笔带过。

这是该软件包发布状态下观察到的行为,已对照 src/Artisan/tests/Unit/Artisan/ 测试套件验证。它并不声称与交互式 Chrome 浏览器逐像素一致:动画会以最终帧捕获,布局不依赖 JavaScript,而且只会导入第一个 Chrome 页面。

HTML fragment

ChromeSecurityPolicy::validate()

ChromeSecurityPolicy::wrapHtml()

CSP + reset CSS

BrowserPool

headless Chrome

CDP: Network.setBlockedURLs '*'

Page.setDocumentContent

Chrome printToPDF

NextPDF\\Parser\\PdfReader

PageImporter → Form XObject

Embedded in target PDF

(text selectable)

Diagram
组件职责来源
ChromeHtmlRenderer协调单次绘制;返回 ChromeRenderResultsrc/Artisan/ChromeHtmlRenderer.php
ChromeRendererConfig不可变的配置值对象src/Artisan/ChromeRendererConfig.php
ChromeSecurityPolicy输入验证 + 安全的 HTML 封套src/Artisan/ChromeSecurityPolicy.php
BrowserPoolChrome 进程的生命周期与重启策略src/Artisan/BrowserPool.php
ViewportCalculator72 pt/inch ↔ 96 px/inch 换算src/Artisan/ViewportCalculator.php
ChromeRenderResult带类型的绘制输出(ChromeRenderResultInterfacesrc/Artisan/ChromeRenderResult.php
PageImporter已解析的 Chrome 页面 → ImportedFormXObjectsrc/Artisan/PageImporter.php
EInvoiceServiceFactory供 Premium 电子发票合约使用的无容器工厂src/Artisan/EInvoiceServiceFactory.php
  • **版本沿革。**发布到 Composer 的构件标记为 v0.1.0。源代码 docblock 带有 @since 1.7.0(Chrome 桥接)与 @since 1.1.0(电子发票工厂),两者都继承自更名前的 nextpdf/core 版本线;软件包更名为 nextpdf/artisan 的时间点,对应 2.0.0 的 CHANGELOG 条目。请将 Composer 标签视为权威的安装版本,并将 @since 标记视为引擎版本的 provenance(来源信息)。
  • 仅限第一页。 PageImporter::import() 默认使用页面 Index(索引)0。溢出到第二个 Chrome 页面的内容会被裁剪,除非明确指定高度——此点在 /integrations/artisan/production-usage/ 页面说明。
  • 没有 DI 容器。 Artisan 不使用容器。EInvoiceServiceFactory 为没有服务容器的环境提供一致的实例化接口;参见 /integrations/artisan/boot-and-discovery/.

每次绘制都要付出一次 Chrome 加载页面与 printToPDF 的成本。BrowserPool 会让 Chrome 进程在多次绘制之间保持存活,并在每 100 次绘制后重启一次,以限制内存增长。主导 Big-O 的是 Chrome 对输入的布局成本,而非桥接本身。关于本页参考流程的实测预算,请见 frontmatter 中的 performance_budget 与 /integrations/artisan/production-usage/ 页面。

这个桥接会在 Chrome 内绘制可能不受信任的 HTML。Chrome 接触输入之前,输入已先经过大小与内容验证。包裹后的文档带有 default-src 'none'。一道 CDP 级别的封锁会阻止所有子资源请求。完整的传输与隔离模型——包括 Chrome sandbox 标志的明确限制——记载于 /integrations/artisan/security-and-operations/ 页面。请勿把这一节视为完整的安全态势。

开源桥接将 HTML 绘制成 PDF。Premium 各层级会在绘制好的文档之上,再叠加合规的电子发票嵌入(Pro)与验证(Enterprise)。当这些层级未安装时,EInvoiceServiceFactory 会返回 null,因此开源路径在没有它们的情况下仍完全可用。

  • /integrations/artisan/install/——安装
  • /integrations/artisan/configuration/——配置
  • /integrations/artisan/quickstart/——快速上手
  • /integrations/artisan/chrome-renderer-setup/——Chrome 渲染器设置
  • /integrations/artisan/security-and-operations/——安全与运维
  • /integrations/artisan/production-usage/——生产环境使用