Ir al contenido

Insertar imágenes en un documento

Se coloca una imagen rasterizada en una posición absoluta con un ancho explícito. Al omitir la altura, NextPDF la deriva de la relación de aspecto de la fuente. Esta recipe se basa en examples/07-images.php. NextPDF acepta JPEG, PNG, GIF, BMP, WebP y AVIF.

Una imagen insertada se convierte en un XObject de imagen según ISO 32000-2. Su diccionario indica de forma explícita el ancho, la altura y los bits por componente.

Ventana de terminal
composer require nextpdf/core:^3

La API image() pertenece a Core. Insertar un archivo existente no requiere componentes adicionales. Para generar una imagen de prueba con GD, como en el ejemplo de respaldo, se necesita ext-gd. La API es estable desde la 1.0.0 y se ejecuta sobre la matriz de backports 8.1-8.4.

image($file, x, y, width, height) carga el archivo a través del registro de imágenes, lo decodifica y lo coloca. El registro valida la ruta antes de decodificarla. Rechaza toda ruta que contenga un byte NUL y toda ruta que parezca un esquema de URL (scheme://…), porque image() solo lee archivos locales, nunca una URL remota. El registro también rechaza un width o height no positivo.

La colocación usa la matriz de transformación actual para mapear el cuadrado unitario de la imagen dentro del rectángulo de destino. Para ello, ISO 32000-2 §8.8 especifica el operador cm dentro de un par q/Q, de modo que la transformación queda aislada. La imagen rasterizada decodificada se convierte en un XObject de imagen. Su BitsPerComponent es obligatorio y debe ser uno de 1, 2, 4, 8 o 16 (§8.9.5).

La superficie de la API se genera automáticamente a partir del PHPDoc. Esta recipe depende de un único método:

  • image(string $file, ?float $x = null, ?float $y = null, ?float $width = null, ?float $height = null): static — inserta y coloca una imagen rasterizada local. Omitir height preserva la relación de aspecto de la fuente. Rechaza las rutas con esquema de URL, las rutas con byte NUL y las dimensiones no positivas con una 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');

Este es el ejemplo completo, listo para el harness. Genera una imagen de prueba determinista con GD, de modo que la recipe se mantiene autocontenida. Respeta NEXTPDF_COOKBOOK_OUTPUT y no fija ninguna entropía propia.

<?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);
}
  • Sin URL remotas. Una ruta que coincide con scheme://… se rechaza con una PageLayoutException. Primero hay que descargar el recurso a un archivo local y luego pasar esa ruta. El cargador no realiza descargas por red. Esta es una protección deliberada contra SSRF.
  • Las dimensiones no positivas lanzan una excepción. Un width <= 0 o height <= 0 genera una PageLayoutException. Para usar el tamaño natural, se debe omitir el argumento en lugar de pasar 0.
  • Ruta con byte NUL rechazada. Una ruta que contiene \0 se rechaza. Esta es una protección contra el envenenamiento por byte nulo. Se deben validar los nombres de archivo proporcionados por el usuario.
  • Relación de aspecto. Pasar solo width, o solo height, permite escalar proporcionalmente. Pasar ambos puede distorsionar la imagen.
  • El mismo archivo insertado dos veces. Reutilizar una ruta escala la imagen a través de la matriz de transformación. El motor no vuelve a decodificar bytes idénticos. Es preferible reutilizar la ruta antes que duplicar archivos.

El costo de decodificación es proporcional al número de píxeles de la fuente, por lo que el presupuesto es de 96 MB para admitir una foto moderada. Volver a colocar la misma ruta tiene un costo bajo: un Do adicional más un cm, no una segunda decodificación. Reducir la escala por el ancho no reduce los píxeles almacenados. Conviene redimensionar de antemano las imágenes de origen grandes si el tamaño del documento importa.

Los rechazos de esquema de URL y de byte NUL son protecciones contra SSRF e inyección de rutas. image() no puede ser engañado para que descargue http://… ni para que cruce más allá de un byte nulo. Aun así, los nombres de archivo proporcionados por el usuario deben tratarse como no confiables. Se deben resolver contra un directorio base permitido antes de llamar a image(). Cuando el archivo lo sube el usuario, la decodificación de la imagen se ejecuta sobre bytes influidos por un atacante. Se debe limitar el tamaño de la entrada y considerar la decodificación en un worker aislado.

DeclaraciónEspecificaciónCláusulareference_id
Un diccionario de imagen especifica de forma explícita el ancho, la altura y los bits por componente.ISO 32000-2§8.9.5
BitsPerComponent es obligatorio y es 1, 2, 4, 8 o 16.ISO 32000-2§8.9.5
Una imagen se coloca mediante el operador cm dentro de q/Q.ISO 32000-2§8.8

Perfil de reproducibilidad — estructural. Los bytes de la imagen son deterministas para una fuente fija. Cada documento guardado sigue incluyendo un /ID en el tráiler y átomos de fecha, así que el harness los elimina y compara la estructura normalizada por qpdf. Esta recipe describe cómo NextPDF produce la estructura. No afirma la conformidad con ISO 32000-2 como una declaración general.

No aplica. La inserción de imágenes rasterizadas es una capacidad de Core sin restricciones de nivel Premium.