Перейти к содержимому

Встраивание изображений в документ

Разместите растровое изображение в абсолютной позиции и явно задайте ширину. Не указывайте высоту, и NextPDF вычислит её по соотношению сторон исходного изображения. Этот рецепт основан на examples/07-images.php. Можно использовать файлы JPEG, PNG, GIF, BMP, WebP и AVIF.

NextPDF встраивает изображение как объект XObject изображения по стандарту ISO 32000-2. Словарь изображения явно задаёт ширину, высоту и число бит на компонент.

Окно терминала
composer require nextpdf/core:^3

image() входит в API Core. Для встраивания существующего файла больше ничего не требуется. Пример, включённый в рецепт, формирует тестовое изображение с помощью GD, поэтому ему требуется ext-gd. Этот API стабилен с 1.0.0 и поддерживается в матрице бэкпортов 8.1–8.4.

image($file, x, y, width, height) загружает файл через реестр изображений, декодирует его и размещает. Перед декодированием реестр проверяет путь. Он отклоняет любой путь, который содержит байт NUL или имеет вид схемы URL (Uniform Resource Locator) (scheme://…). image() читает только локальные файлы и никогда не читает удалённый URL. Реестр также отклоняет неположительное значение width или height.

При размещении используется текущая матрица преобразования, чтобы отобразить единичный квадрат изображения в целевой прямоугольник. 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, пути с байтом 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 <= 0 или height <= 0 вызывает PageLayoutException. Чтобы использовать естественный размер, опустите аргумент, а не передавайте 0.
  • Путь с байтом NUL отклоняется. Путь, содержащий \0, отклоняется. Это защищает от атак на путь через нулевой байт (poison null byte). Проверяйте имена файлов, предоставленные пользователем.
  • Соотношение сторон. Передавайте только width или только height, чтобы масштабировать пропорционально. Если передать оба значения, изображение может исказиться.
  • Один и тот же файл встраивается дважды. Повторно используйте один и тот же путь, чтобы масштабировать через матрицу преобразования. Движок не декодирует повторно идентичные байты. Предпочитайте повторное использование пути, а не дублирование файлов.

Стоимость декодирования пропорциональна числу пикселей исходного изображения, поэтому бюджет составляет 96 МБ: этого достаточно для фотографии умеренного размера. Повторное размещение того же пути обходится дёшево: один дополнительный Do плюс cm, а не повторное декодирование. Уменьшение масштаба по ширине не сокращает число хранимых пикселей. Если важен размер документа, заранее уменьшайте размер крупных исходных изображений.

Отклонение схемы URL и байта NUL защищает от SSRF и внедрения в путь. Нельзя обманом заставить image() выполнить выборку http://… или принять путь с байтом NUL. Тем не менее относитесь к именам файлов, предоставленным пользователем, как к недоверенным. Разрешайте их относительно допустимого базового каталога, прежде чем вызывать image(). Когда файл загружен пользователем, декодирование изображения выполняется над байтами, на которые может влиять злоумышленник. Ограничивайте размер входных данных и рассмотрите декодирование в изолированном рабочем процессе.

УтверждениеСпецификацияПунктreference_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

Профиль воспроизводимости — структурный. При фиксированном источнике байты изображения детерминированы. Каждый сохранённый документ по-прежнему содержит /ID в трейлере и атомы дат, поэтому тестовый стенд удаляет их и сравнивает структуру после нормализации qpdf. Этот рецепт описывает, как NextPDF формирует структуру. Он не делает общего заявления о соответствии ISO 32000-2.

Неприменимо. Встраивание растровых изображений — функция Core, не ограниченная Premium.