跳到內容

在文件中嵌入影像

以指定寬度將點陣影像放在絕對位置。省略高度時,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 限制。