契约 / 文档
文档域包含用于构建 PDF 的契约:负责内容的 PdfDocumentInterface、用于在 worker 中安全创建文档的 DocumentFactoryInterface、字体和图像注册表契约,以及三个输出与布局枚举。它们自 1.0.0 或 1.7.0 起均为 stable。
composer require nextpdf/core:^3概念概述
标题为“概念概述”的章节PdfDocumentInterface 是主要的 API 接口面。它定义页面管理、字体选择、单元格与多单元格文本布局、HTML 渲染、图像嵌入以及最终输出。每个方法都返回 static,因此调用可以链式串联。Document::createStandalone() 返回一个满足该接口的具体实例。在你自己的服务中,请以该接口作为类型提示。这样,引擎内部实现就能保持可替换。
文档创建有两条路径。在传统 PHP-FPM 请求中,createStandalone() 会构建一个带私有注册表的自包含文档。长时间运行的 worker 则使用另一条路径,包括 RoadRunner、Swoole 和 Laravel Octane。在这类环境中,DocumentFactoryInterface::create() 返回一个全新的、用完即弃的 Document。该文档从进程生命周期级别的注册表读取数据,但绝不会修改它们。该工厂持有 FontRegistryInterface 和 ImageRegistryInterface 单例。每个文档都会获得自己的渲染上下文和写入器。这就形成了故障隔离:一个文档无法破坏另一个文档所依赖的共享状态。
注册表契约正是 worker 能保持高速的原因。FontRegistryInterface 只解析一次字体文件,并在整个进程生命周期内缓存解析后的元数据。它可以在预热后锁定,使生产流量无法再修改它。ImageRegistryInterface 在有界的最近最少使用策略下缓存解码后的图像二进制数据。即使二进制数据被逐出,图像元数据仍保持驻留。二者都公开 memoryUsage(),用于容量规划。ImageRegistryInterface 继承自 ResettableService。该契约会逐出缓存数据,但不会破坏结构性元数据。worker 可以在内存承压时丢弃图像缓存,并继续提供服务。
三个枚举补齐了该域。OutputDestination 用于选择内联显示、强制下载、写入文件系统或返回原始字符串。Orientation 用于选择纵向或横向。Alignment 用于选择文本的左对齐、居中、右对齐或两端对齐。每个枚举项的值都沿用旧版 TCPDF 代码。因此,compat-tcpdf 桥接能够干净地完成映射。这些枚举的向后兼容承诺是只增不减:不会移除任何枚举项,新的枚举项可能在次要版本中加入。
API 接口面
标题为“API 接口面”的章节| 类型 | 种类 | 关键成员 | 稳定性 | 起始版本 |
|---|---|---|---|---|
PdfDocumentInterface | 接口 | addPage()、setMargins()、setFont()、cell()、multiCell()、writeHtml()、image()、output()、save() | stable | 1.0.0 |
DocumentFactoryInterface | 接口 | create(?Config): Document | stable | 1.7.0 |
ResettableService | 接口 | reset(): void | stable | 1.7.0 |
FontRegistryInterface | 接口 | register()、get()、warmup()、lock()、isLocked()、registerFromBinary()、memoryUsage() | stable | 1.7.0 |
ImageRegistryInterface | 接口 | load()、loadFromString()、getMetadata()、memoryUsage() (继承自 ResettableService) | stable | 2.0.0 |
OutputDestination | 枚举(字符串) | Inline、Download、File、String | stable | 1.0.0 |
Orientation | 枚举(字符串) | Portrait、Landscape | stable | 1.0.0 |
Alignment | 枚举(字符串) | Left、Center、Right、Justify | stable | 1.0.0 |
FontRegistryInterface 和 ImageRegistryInterface 在字体排印页面中有完整说明;本页则介绍它们在创建生命周期中的作用。
代码示例 — 快速上手
标题为“代码示例 — 快速上手”的章节<?php
declare(strict_types=1);
require_once __DIR__ . '/../vendor/autoload.php';
use NextPDF\Core\Document;
$doc = Document::createStandalone();$doc->setTitle('Hello World');$doc->addPage();$doc->setFont('helvetica', '', 24);$doc->cell(0, 15, 'Hello, NextPDF!', newLine: true);$doc->setFont('helvetica', '', 12);$doc->cell(0, 10, 'This is a minimal PDF generated with NextPDF.', newLine: true);$doc->save(__DIR__ . '/output/01-hello-world.pdf');代码示例 — 生产环境
标题为“代码示例 — 生产环境”的章节<?php
declare(strict_types=1);
require_once __DIR__ . '/../vendor/autoload.php';
use NextPDF\Core\PdfFactory;use NextPDF\ValueObjects\{Margin, PageSize};
$factory = PdfFactory::new() ->withPageSize(PageSize::A4()) ->withMargins(new Margin(15.0, 15.0, 15.0, 15.0)) ->withCompress(true) ->withLang('en');
// The same configured factory creates independent documents.$doc = $factory->create();$doc->setTitle('PdfFactory Example');$doc->setAuthor('NextPDF');$doc->addPage();$doc->setFont('helvetica', '', 16);$doc->cell(0, 12, 'Created via PdfFactory', newLine: true);
$doc2 = $factory->create();$doc2->addPage();$doc2->setFont('helvetica', '', 12);$doc2->cell(0, 10, 'Second document from the same factory.');
$doc->save(__DIR__ . '/output/02-pdf-factory.pdf');PdfFactory 是不可变构建器;每个 with*() 都返回一个新实例。它在底层组合了一个 DocumentFactoryInterface,因此概述中的 worker 注册表模型无需额外装配即可适用。
边界情况与注意事项
标题为“边界情况与注意事项”的章节createStandalone()会构建私有注册表。在 worker 循环中,这会在每次请求时重新解析每种字体。应改用带共享注册表的DocumentFactoryInterface。- 从设计上讲,
Document是用完即弃的对象。在多个逻辑文档之间复用同一个实例会导致状态泄漏。应为每个文档调用一次create(),并交给垃圾回收处理。 FontRegistryInterface::lock()会使register()、addFontDirectory()和warmup()抛出LogicException。应在预热之后锁定,绝不要在处理请求期间锁定。OutputDestination::File会写入服务器文件系统并返回原始字节。save()是显式的文件路径方式。不要对同一个文档混用这两种方式。cell()的边框参数为了兼容 TCPDF 而接受bool|string;空字符串与false并不相同。请传入与你意图一致且类型明确的值。
字体和图像注册表使文档域成为受内存上限约束的系统,而不是逐请求重复处理的系统。首次请求中的字体解析占主要开销。在 worker 示例中,三个文档合计的 performance_budget 为 1500 ms 实际耗时和 64 MB 峰值内存。这一预算几乎全部用于首次字体解析。预热之后,每个文档由这些契约引入的工作量为 O(1)——一次注册表查找和一次上下文分配。在任一注册表上调用 memoryUsage() 都会返回一个 MemoryReport,用于实时容量规划。ResettableService::reset() 在持续负载下限制峰值内存。
安全说明
标题为“安全说明”的章节文档契约不涉及任何密码学层面,但存在两项运维风险。第一,image() 接受路径或 URL。对于不可信输入,应通过 ExternalResourcePolicyInterface(参见安全策略页面)约束远程获取,而不要直接传入用户可控的 URL。第二,writeHtml() 是 HTML 管道的入口点。不可信的标记在渲染之前必须先经过 HtmlSecurityPolicyInterface。文档层本身不做净化处理。这是安全策略域的职责;由于该策略是契约,你无需分叉即可提供更严格的实现。
合规性
标题为“合规性”的章节文档契约按 ISO 32000-2 的定义实现 PDF 2.0 文档结构。输出、页面和字体处理按 ISO 32000-2 §7 生成间接对象和交叉引用流。内容按引擎层契约 (ADR-010) 通过写入器层发出。除结构合规性外,本页不提出任何条款级声明。PDF/A 和 PDF/UA 合规性在提取和无障碍页面中说明,这些页面包含规范性表格。