Aller au contenu

Intégrer des images dans un document

Place une image matricielle à une position absolue avec une largeur explicite. Omets la hauteur : NextPDF la déduit à partir du rapport d’aspect de la source. Cette recette s’appuie sur examples/07-images.php. NextPDF accepte les formats JPEG, PNG, GIF, BMP, WebP et AVIF.

Une image intégrée devient un XObject image ISO 32000-2. Son dictionnaire indique explicitement la largeur, la hauteur et le nombre de bits par composante.

Fenêtre de terminal
composer require nextpdf/core:^3

L’API image() fait partie du cœur. Intégrer un fichier existant ne requiert rien d’autre. Générer une image de test avec GD, comme le fait l’exemple complet, nécessite ext-gd. L’API est stable depuis la version 1.0.0 et fonctionne sur la matrice de backport 8.1–8.4.

image($file, x, y, width, height) charge le fichier via le registre d’images, le décode et le place. Le registre valide le chemin avant de le décoder. Il rejette tout chemin qui contient un octet NUL, ainsi que tout chemin qui prend la forme d’un schéma d’URL (scheme://…), car image() lit uniquement des fichiers locaux, jamais une URL distante. Le registre rejette aussi une width ou une height non positive.

Le placement utilise la matrice de transformation courante pour projeter le carré unité de l’image dans le rectangle cible. ISO 32000-2 §8.8 spécifie l’opérateur cm à l’intérieur d’une paire q/Q pour cela, de sorte que la transformation reste isolée. L’image matricielle décodée devient un XObject image. Son BitsPerComponent est requis et doit valoir 1, 2, 4, 8 ou 16 (§8.9.5).

La surface de l’API est générée automatiquement à partir de la PHPDoc. Cette recette repose sur une seule méthode :

  • image(string $file, ?float $x = null, ?float $y = null, ?float $width = null, ?float $height = null): static — intègre et place une image matricielle locale. Omets height pour préserver le rapport d’aspect de la source. Rejette les chemins en schéma d’URL, les chemins contenant un octet NUL et les dimensions non positives avec une 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');

Voici l’exemple complet, prêt pour le harnais de test. Il génère une image de test déterministe avec GD, ce qui rend la recette autonome. Il respecte NEXTPDF_COOKBOOK_OUTPUT et n’introduit aucune entropie propre à l’exemple.

<?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);
}
  • Pas d’URL distante. Un chemin qui correspond à scheme://… est rejeté avec une PageLayoutException. Télécharge d’abord la ressource dans un fichier local, puis passe ce chemin. Le chargeur ne récupère rien sur le réseau à ta place. C’est une protection délibérée contre les attaques SSRF.
  • Les dimensions non positives lèvent une exception. Une width <= 0 ou une height <= 0 déclenche une PageLayoutException. Pour utiliser la taille naturelle, omets l’argument plutôt que de passer 0.
  • Chemin contenant un octet NUL rejeté. Un chemin qui contient \0 est rejeté. C’est une protection contre l’injection d’octet nul. Valide les noms de fichiers fournis par l’utilisateur.
  • Rapport d’aspect. Passe uniquement width, ou uniquement height, pour mettre à l’échelle proportionnellement. Passer les deux peut déformer l’image.
  • Le même fichier intégré deux fois. Réutiliser un seul chemin met à l’échelle via la matrice de transformation. Le moteur ne redécode pas les mêmes octets. Privilégie la réutilisation du chemin plutôt que la duplication de fichiers.

Le coût de décodage est proportionnel au nombre de pixels de la source ; le budget de 96 Mo autorise donc une photo de taille modérée. Replacer le même chemin coûte peu : un Do supplémentaire et un cm, pas un second décodage. Réduire l’échelle par la largeur ne diminue pas les pixels stockés. Redimensionne au préalable les grandes images source si la taille du document compte.

Les rejets de schéma d’URL et d’octet NUL sont des protections contre le SSRF et l’injection de chemin. image() ne peut pas être détourné pour récupérer http://… ni pour contourner un octet nul. Malgré cela, traite les noms de fichiers fournis par l’utilisateur comme non fiables. Résous-les par rapport à un répertoire de base autorisé avant d’appeler image(). Quand le fichier est téléversé par l’utilisateur, le décodage de l’image s’exécute sur des octets influencés par l’attaquant. Limite la taille de l’entrée et envisage de décoder dans un worker isolé.

ÉnoncéSpécificationClausereference_id
Un dictionnaire d’image spécifie explicitement la largeur, la hauteur et le nombre de bits par composante.ISO 32000-2§8.9.5
BitsPerComponent est requis et vaut 1, 2, 4, 8 ou 16.ISO 32000-2§8.9.5
Une image est placée par l’opérateur cm à l’intérieur de q/Q.ISO 32000-2§8.8

Profil de reproductibilité — structurel. Les octets de l’image sont déterministes pour une source fixe. Chaque document enregistré porte toujours un /ID de trailer et des atomes de date ; le harnais les retire donc et compare la structure normalisée par qpdf. Cette recette décrit comment NextPDF produit la structure. Elle n’affirme pas la conformité ISO 32000-2 comme une revendication générale.

Non applicable. L’intégration d’images matricielles est une capacité du cœur, sans verrou Premium.