图形:path(路径)、shading(渐变着色)与 transform(坐标转换)基础组件
重点速览
标题为“重点速览”的章节Graphics 模块将绘图意图转换为 PDF 图形运算符。它涵盖 path(路径)、线条样式、色彩空间、坐标转换、shading(渐变着色)、pattern(图样)、半色调与图像加载。
composer require nextpdf/core:^3概念总览
标题为“概念总览”的章节Graphics 是向量与位图绘图层。它生成的运算符序列由 ContentStream 与 Writer 模块序列化为 PDF。content stream(内容流)将页面内容编码为一系列有序的图形运算符——ISO 32000-2 §8。本模块负责输出这些运算符,本身不写出完整文档。
DrawingEngine 是主要操作接口。它是一个链式、有状态的 builder(构建器)。每个 setter 都返回 self,累积一次图形状态变更或一个 path-painting 运算符,并附加到内部缓冲区;你可以用 getStream() 读取该缓冲区。这个引擎直接映射 PDF 图形状态——线宽、线条样式、描边与填色、alpha 与混色模式、斜接限制、soft mask(柔性遮罩)、裁切、叠印、平坦度、平滑度、渲染意图、黑色生成与底色移除,分别对应明确记录的运算符。与色彩相关的 setter 接受一个 Color 值对象,或一个明确的 ColorSpace,因此设备色彩空间与 CIE 色彩空间共用同一种调用形式。
引擎周边还有三组相关组件。第一组是图像输入。ImageLoader 将文件或内存中的字节块解码为一个 ImageLoadResult。ImageRegistry 使用一份 MemoryReport 对已解码图像去重并跟踪内存占用,让大型文档保持在内存预算内。第二组是向量导入。SvgParser 与 EpsParser 将外部向量格式转换为同一份运算符流,并对外提供 getBoundingBox() 供布局使用。第三组是设备色彩保真度:shading(渐变着色,ShadingManager,包含 Type2/Type3 与 mesh 系列)、pattern(图样,PatternFill)、半色调(Type1/Type5/Type6/Type10/Type16)、转换函数,以及基于 ICC 的色彩空间。
TransformEngine 是专门负责坐标转换的轻量级配套组件。它用 startTransform() 与 stopTransform() 包裹一段转换;这两者会输出 q 与 Q 这组 save/restore 配对。它提供命名仿射辅助方法——scale、translate、rotate、skew、mirrorH、mirrorV——每个方法都可接受一个可选枢轴点。转换矩阵将内部坐标空间映射到目标坐标空间。这与 ISO 32000-2 应用于 shading 定义域的模型相同——见 §8.7.4。
色彩管理遵循 ADR-012:ICCBased 与基于 CIE 的色彩空间会输出明确的 cs/CS 内容流运算符,而不是依赖设备色彩 fallback。ICC profile 会封装为一个 ICCBased 流,并依 ISO 32000-2 §8.6.5.5 带有正确的成分数量。
API 接口
标题为“API 接口”的章节| 类别 | 主要方法 | 角色 |
|---|---|---|
DrawingEngine | getStream(), reset(), setLineWidth(), setLineStyle(), setDrawColor(), setFillColor(), setAlpha(), setSoftMask(), clip(), setOverprint(), setRenderingIntent(), line(), rect(), circle(), ellipse(), polygon(), linearGradient() | 有状态的 path 与图形状态运算符构建器 |
TransformEngine | startTransform(), stopTransform(), scale(), translate(), rotate(), skew(), mirrorH(), mirrorV(), getStream() | 仿射坐标转换 |
ImageLoader | load(string $filePath), loadFromString(string $data, string $mimeType) | 将图像解码为 ImageLoadResult |
ImageRegistry | load(), loadFromString(), getMetadata(), memoryUsage(), reset() | 带去重功能和内存报告的图像缓存 |
SvgParser | parse(), parseFile() | 将 SVG 转换为运算符流 |
EpsParser | parse(), parseFile(), getBoundingBox() | 将 EPS 转换为运算符流 |
ShadingManager | shading 注册与字典输出 | 轴向、径向与 mesh shading |
Halftone(抽象类) | halftoneType(), toDict(), hasStream(), getStream() | Type 1/5/6/10/16 半色调网屏 |
运行 composer docs:generate-api-php -- --module=Graphics 即可获取完整的 PHPDoc 表格。
代码示例 —— 快速上手
标题为“代码示例 —— 快速上手”的章节来源:examples/06-colors-and-drawing.php。
<?php
declare(strict_types=1);
require_once __DIR__ . '/../vendor/autoload.php';
use NextPDF\Graphics\Color;use NextPDF\Graphics\DrawingEngine;use NextPDF\Graphics\LineStyle;
$engine = new DrawingEngine();
$engine ->setLineWidth(1.5) ->setDrawColor(Color::rgb(0, 51, 102)) ->setFillColor(Color::rgb(230, 240, 250)) ->rect(20.0, 20.0, 160.0, 80.0) ->line(20.0, 110.0, 180.0, 110.0, new LineStyle(dash: [3.0, 2.0]));
$contentStreamBytes = $engine->getStream();代码示例 —— 正式环境
标题为“代码示例 —— 正式环境”的章节这段代码将一个带内存报告的图像 registry 与一段 transform 包裹组合在一起。其结构对应 examples/07-images.php 与 examples/21-transforms.php 所用的结构。
<?php
declare(strict_types=1);
require_once __DIR__ . '/../vendor/autoload.php';
use NextPDF\Graphics\DrawingEngine;use NextPDF\Graphics\ImageRegistry;use NextPDF\Graphics\TransformEngine;
$registry = new ImageRegistry();$image = $registry->load('/srv/assets/logo.png');
$report = $registry->memoryUsage();if ($report->bytes > 32 * 1024 * 1024) { // Decoded image cache exceeded the budget — reset before the next page. $registry->reset();}
$transform = new TransformEngine();$transform ->startTransform() ->translate(40.0, 700.0) ->scale(0.5, 0.5) ->stopTransform();
$engine = new DrawingEngine();$engine->reset();$page = $transform->getStream() . $engine->getStream();边界情况与注意事项
标题为“边界情况与注意事项”的章节DrawingEngine是有状态的。各个独立页面之间应调用reset(),否则前一个图形状态会泄漏到下一份流中。TransformEngine需要成对调用startTransform()/stopTransform()。包裹不对称会留下一个悬空的q,并在下游 Writer 中破坏 save/restore 栈。setSoftMask()、setOverprint()、setBlackGeneration()与setUnderColorRemoval()会写出扩展图形状态标记。在拒绝该功能的 profile 下,这些方法不会生效。依赖视觉结果前,请先检查 profile 门控条件。ImageRegistry会按内容去重。两条字节内容相同的路径会共享同一个对象。不要假设每一次load()调用都会对应一张 PDF 图像。EpsParser::getBoundingBox()返回的是解析得到的边界框,而不是页面框。如果 EPS 超出目标矩形,请自行应用裁剪。- 黑点补偿是建议性的,并且基于标记。它本身不会转换像素。
验证与错误处理
标题为“验证与错误处理”的章节有两项生成端变更属于破坏性变更。两者都将过去静默发生的损坏,改为在调用处明确失败。
输入验证现在会抛出异常(迁移注意事项)。 绘图输入在抵达运算符流之前会先经过验证,并以 InvalidArgumentException 拒绝格式错误的值。过去传入 NaN、Infinity 或超出范围值的调用方,会静默生成损坏的运算符;同样的输入现在会抛出异常。受验证的约束如下:
- 色彩 alpha 必须是有限值,且落在
[0, 1]范围内。 - CTM 操作数、模板尺寸、渐变顶点坐标与 mesh 补丁坐标都必须是有限值——不能包含
NaN或Infinity。 - 渐变补丁的边缘标志必须是
{0, 1, 2, 3}其中之一。 - Type 2/3/4 函数参数与半色调参数都会进行边界检查。
- 着色剂名称会被转义。
- OCG(可选内容)图层名称不能为空。
升级前,请审核那些从上游数据计算坐标或 alpha 的调用处:过去能通过的值,现在会变成硬性错误。
ICCBased 的 /N 默认为 fail-closed。 纯 PDF 输出会拒绝 /N 成分数量落在 {1, 3, 4} 之外的 ICCBased 色彩空间,并将声明的 /N 与内嵌 profile 以及 /Alternate 空间相互核对。这遵循 ISO 32000-2 §8.6.5.5 针对 ICCBased 流的规则;该流带有 /N 以及一个 /Alternate 空间。N 通道 ICC profile(例如一个 N = 6 的六色 profile)只有在 PDF/A 或 PDF/X profile 启用、并通过 IccConformancePolicy::ProfileGated 选择加入时才会保留。这是针对成分数量的结构性门控,并不表示已经通过 PDF/A 或 PDF/X 认证。
运算符输出量与绘图调用次数成线性关系——对缓冲区进行 O(n) 次追加,不会重排。图像解码成本主要取决于 codec 与像素数量,而不是 registry。registry 以内容哈希去重,是大型文档最主要的优化手段:重复使用的素材只需付出一次解码与一个 PDF 对象的成本。本模块参考工作负载的 performance_budget 为 1500 ms 墙钟时间与 64 MB 峰值内存。使用 ImageRegistry::memoryUsage() 观察已解码图像的内存占用,并用 reset() 在各个页面组之间释放这部分内存。
安全性注意事项
标题为“安全性注意事项”的章节SvgParser 与 EpsParser 会处理不受信任的向量输入。请将两者都视为可能解析恶意数据的解析器。在调用 parse() 之前,请先强制应用输入大小上限。当来源由用户提供时,请在受限的 worker 中执行提取。EPS 是一种 PostScript 方言。解析器只转换一个受限子集,并不会执行通用解释器,但你仍应限制输入大小与解析时间。图像加载器会解码第三方 codec。请保持执行环境中的图像扩展为最新版本,并为解码后的尺寸设置上限。关于信任边界与 worker 隔离指南,请参阅 /modules/core/security/ 一节中的引擎威胁模型。
符合性
标题为“符合性”的章节本模块输出的 PDF 图形运算符结构与 ISO 32000-2 §8 一致,ICCBased 色彩空间字典依据 §8.6.5.5,shading 字典的 Domain、Function、Matrix 与 BBox 则依据 §8.7.4。这些都是实现层面的事实:运算符与字典的形状由 src/Graphics/ 生成,并由 tests/Unit/Graphics/ 加上 tests/Golden/PdfWriter/PdfWriterShadingGoldenBaselineSmokeTest 与 PdfWriterExtGStateGoldenSmokeTest 基线加以验证。它们并不构成端到端的 PDF 2.0 或 PDF/X 符合性声明。整个文档层级的符合性,是由 /modules/core/conformance/ 一节所述的 oracle 与 golden 测试套件另行验证的。ICC OutputIntent 的 profile 行为由 ADR-011 与 ADR-012 决定,而不只由本模块决定。