跳转到内容

在文档中嵌入图像

以指定宽度将位图图像放置在绝对位置。省略高度时,NextPDF 会根据源图像的宽高比推导高度。本示例对应 examples/07-images.php。NextPDF 支持 JPEG、PNG、GIF、BMP、WebP 与 AVIF。

嵌入的图像会成为 ISO 32000-2 图像 XObject。其字典会明确标示宽度、高度与每个分量的位数。

Terminal window
composer require nextpdf/core:^3

image() API 属于 Core。嵌入已有文件不需要任何额外组件。若要像本示例一样使用 GD 生成测试图像,则需要 ext-gd。该 API 自 1.0.0 起稳定,并适用于 8.1–8.4 的 backport 兼容矩阵。

image($file, x, y, width, height) 会通过图像注册表加载文件、解码并放置图像。注册表会在解码前先验证路径。它会拒绝包含 NUL 字节的路径,以及看起来像 URL scheme(scheme://…)的路径,因为 image() 只读取本地文件,绝不读取远程 URL。注册表也会拒绝非正值的 widthheight

放置时,当前变换矩阵会把图像的单位正方形映射到目标矩形。ISO 32000-2 §8.8 为此指定了 cm 运算符,并将其置于 q/Q 配对内,因此该变换会保持隔离。解码后的位图会成为图像 XObject。它的 BitsPerComponent 为必填,且必须是 1、2、4、8 或 16 之一(§8.9.5)。

API 接口由 PHPDoc 自动生成。本示例只依赖一个方法:

  • image(string $file, ?float $x = null, ?float $y = null, ?float $width = null, ?float $height = null): static — 嵌入并放置一个本地位图图像。省略 height 即可保留源图像的宽高比。以 URL scheme 开头的路径、含 NUL 字节的路径,以及非正值的尺寸,都会被拒绝并抛出 PageLayoutException
<?php
declare(strict_types=1);
require_once __DIR__ . '/vendor/autoload.php';
use NextPDF\Core\Document;
$doc = Document::createStandalone();
$doc->addPage();
// Absolute position; height inferred from the source aspect ratio.
$doc->image(__DIR__ . '/logo.png', x: 15, y: 30, width: 80);
$doc->save(getenv('NEXTPDF_COOKBOOK_OUTPUT') ?: __DIR__ . '/images.pdf');

这是一个完整示例,可直接在测试载具上运行。它会用 GD 生成一张确定性的测试图像,使本示例保持自包含。它会遵循 NEXTPDF_COOKBOOK_OUTPUT,且不会自行固定任何随机种子。

<?php
declare(strict_types=1);
require_once __DIR__ . '/vendor/autoload.php';
use NextPDF\Core\Document;
if (!\extension_loaded('gd')) {
fwrite(STDERR, "ext-gd required for the self-contained test image\n");
exit(1);
}
// Build a deterministic test image (fixed pixels — no entropy).
$imgPath = \tempnam(\sys_get_temp_dir(), 'npf') . '.png';
$img = \imagecreatetruecolor(200, 100);
if ($img === false) {
fwrite(STDERR, "GD imagecreatetruecolor failed\n");
exit(1);
}
$bg = (int) \imagecolorallocate($img, 30, 58, 138);
$fg = (int) \imagecolorallocate($img, 255, 255, 255);
\imagefilledrectangle($img, 0, 0, 199, 99, $bg);
\imagestring($img, 5, 40, 40, 'NextPDF Image', $fg);
\imagepng($img, $imgPath);
\imagedestroy($img);
try {
$doc = Document::createStandalone();
$doc->setTitle('Image Embedding');
$doc->addPage();
$doc->setFont('helvetica', 'B', 18);
$doc->cell(0, 12, 'Image Embedding', newLine: true);
$doc->ln(5);
// Absolute placement; width fixed, height from aspect ratio.
$doc->image($imgPath, x: 15, y: 50, width: 80);
// Same image, narrower — scaled down by the CTM, not re-decoded.
$doc->image($imgPath, x: 15, y: 105, width: 40);
$out = getenv('NEXTPDF_COOKBOOK_OUTPUT') ?: __DIR__ . '/images.pdf';
$doc->save($out);
echo "Created images.pdf\n";
} finally {
@\unlink($imgPath);
}
  • 不支持远程 URL。 匹配 scheme://… 的路径会被拒绝,并抛出 PageLayoutException。请先把素材下载到本地文件,再传入该路径。加载器不会替你通过网络抓取文件。这是刻意设计的 SSRF 防护。
  • 非正值的尺寸会抛出异常。 width <= 0height <= 0 会引发 PageLayoutException。若要使用原始尺寸,请省略相应参数,而不是传入 0
  • 含 NUL 字节的路径会被拒绝。 包含 \0 的路径会被拒绝。这是针对毒空字节(poison null byte)的防护。请验证用户提供的文件名。
  • 宽高比。 只传入 width 或只传入 height,即可等比例缩放。两者都传入则可能使图像变形。
  • 同一个文件嵌入两次。 重复使用同一个路径时,缩放会通过变换矩阵完成。引擎不会重新解码相同的字节。请优先重复使用路径,而不是复制文件。

解码成本与源图像的像素数成正比,因此预算为 96 MB,可容纳一张中等大小的照片。重新放置同一个路径的成本很低:只会多一个 Do 加一个 cm,而不是再次解码。按宽度缩小并不会减少已存储的像素。若在意文档大小,请先重新调整大型源图像的尺寸。

拒绝 URL scheme 与 NUL 字节,是针对 SSRF 和路径注入的防护。攻击者无法诱使 image() 抓取 http://…,也无法让它越过空字节读取。即便如此,仍请将用户提供的文件名视为不可信。在你调用 image() 之前,请先把它们 resolve(解析)到允许的基目录之内。当文件来自用户上传时,图像解码会在攻击者可影响的字节上运行。请限制输入大小,并考虑在隔离的 worker 中进行解码。

陈述规格条款参考 ID
图像字典会明确标示宽度、高度与每个分量的位数。ISO 32000-2§8.9.5
BitsPerComponent 为必填,且必须为 1、2、4、8 或 16。ISO 32000-2§8.9.5
图像由 cm 运算符放置,该运算符位于 q/Q 内。ISO 32000-2§8.8

重现性配置文件 — 结构层级。 对于固定来源,图像字节具有确定性。每份保存的文档仍会带有 trailer 的 /ID 与日期原子,因此测试载具会剥离这些内容,再比对经 qpdf 规范化后的结构。本示例说明 NextPDF 如何生成这套结构。它并未笼统声称完全符合 ISO 32000-2。

不适用。位图图像嵌入是 Core 能力,不受 Premium 限制。