Aller au contenu

Contrats / Document

Le domaine document regroupe les contrats qui te permettent de construire tes PDF : PdfDocumentInterface pour le contenu, DocumentFactoryInterface pour une création sûre côté worker, les contrats de registre de fonts et d’images, ainsi que les trois enums de livraison et de mise en page. Tous sont stable depuis la 1.0.0 ou la 1.7.0.

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

PdfDocumentInterface est la surface d’API principale. Elle définit la gestion des pages, la sélection des fonts, la mise en page du texte en cellule et en multicellule, le rendu HTML, l’intégration d’images et la sortie finale. Chaque méthode renvoie static, ce qui permet d’enchaîner les appels. Document::createStandalone() renvoie une instance concrète qui satisfait l’interface. Utilise l’interface comme type dans tes propres services. L’implémentation interne du moteur reste ainsi interchangeable.

La création d’un document suit deux chemins. Dans une requête PHP-FPM classique, createStandalone() construit un document autonome doté de registres privés. Dans un worker à longue durée de vie, l’autre chemin s’applique. Cela inclut RoadRunner, Swoole et Laravel Octane. Dans ce cas, DocumentFactoryInterface::create() renvoie un Document neuf et jetable. Le document lit depuis des registres liés à la durée de vie du processus, mais ne les modifie jamais. La fabrique détient les singletons FontRegistryInterface et ImageRegistryInterface. Chaque document obtient son propre contexte de rendu et son propre writer. C’est un mécanisme de confinement des défaillances. Un document ne peut pas corrompre l’état partagé dont dépend un autre document.

Les contrats de registre expliquent pourquoi un worker reste rapide. FontRegistryInterface analyse un fichier de font une seule fois. Il met en cache les métadonnées analysées pour toute la durée de vie du processus. Il peut être verrouillé après la phase de préchauffage, si bien que le trafic de production ne peut plus le modifier. ImageRegistryInterface met en cache les données binaires d’image décodées selon une politique bornée de type least-recently-used. Les métadonnées d’image restent résidentes même après l’éviction du binaire. Les deux exposent memoryUsage() pour la planification de capacité. ImageRegistryInterface étend ResettableService. Ce contrat évince les données mises en cache sans détruire les métadonnées structurelles. Un worker peut libérer les caches d’images sous pression mémoire et continuer à répondre.

Trois enums complètent le domaine. OutputDestination sélectionne l’affichage en ligne, le téléchargement forcé, l’écriture sur le système de fichiers ou le renvoi d’une chaîne brute. Orientation sélectionne le mode portrait ou paysage. Alignment sélectionne le texte aligné à gauche, centré, aligné à droite ou justifié. Chaque enum associe ses cas au code TCPDF historique comme valeur d’enum. Le pont compat-tcpdf établit donc une correspondance nette. La promesse de rétrocompatibilité de ces enums est additive. Aucun cas n’est supprimé. De nouveaux cas peuvent être ajoutés dans une version mineure.

TypeNatureMembres clésStabilitéDepuis
PdfDocumentInterfaceinterfaceaddPage(), setMargins(), setFont(), cell(), multiCell(), writeHtml(), image(), output(), save()stable1.0.0
DocumentFactoryInterfaceinterfacecreate(?Config): Documentstable1.7.0
ResettableServiceinterfacereset(): voidstable1.7.0
FontRegistryInterfaceinterfaceregister(), get(), warmup(), lock(), isLocked(), registerFromBinary(), memoryUsage()stable1.7.0
ImageRegistryInterfaceinterfaceload(), loadFromString(), getMetadata(), memoryUsage() (étend ResettableService)stable2.0.0
OutputDestinationenum (string)Inline, Download, File, Stringstable1.0.0
Orientationenum (string)Portrait, Landscapestable1.0.0
Alignmentenum (string)Left, Center, Right, Justifystable1.0.0

FontRegistryInterface et ImageRegistryInterface sont documentés en détail sur la page typographie ; la page document couvre leur rôle dans le cycle de vie de la création.

examples/01-hello-world.php
<?php
declare(strict_types=1);
require_once __DIR__ . '/../vendor/autoload.php';
use NextPDF\Core\Document;
$doc = Document::createStandalone();
$doc->setTitle('Hello World');
$doc->addPage();
$doc->setFont('helvetica', '', 24);
$doc->cell(0, 15, 'Hello, NextPDF!', newLine: true);
$doc->setFont('helvetica', '', 12);
$doc->cell(0, 10, 'This is a minimal PDF generated with NextPDF.', newLine: true);
$doc->save(__DIR__ . '/output/01-hello-world.pdf');
examples/02-pdf-factory.php
<?php
declare(strict_types=1);
require_once __DIR__ . '/../vendor/autoload.php';
use NextPDF\Core\PdfFactory;
use NextPDF\ValueObjects\{Margin, PageSize};
$factory = PdfFactory::new()
->withPageSize(PageSize::A4())
->withMargins(new Margin(15.0, 15.0, 15.0, 15.0))
->withCompress(true)
->withLang('en');
// The same configured factory creates independent documents.
$doc = $factory->create();
$doc->setTitle('PdfFactory Example');
$doc->setAuthor('NextPDF');
$doc->addPage();
$doc->setFont('helvetica', '', 16);
$doc->cell(0, 12, 'Created via PdfFactory', newLine: true);
$doc2 = $factory->create();
$doc2->addPage();
$doc2->setFont('helvetica', '', 12);
$doc2->cell(0, 10, 'Second document from the same factory.');
$doc->save(__DIR__ . '/output/02-pdf-factory.pdf');

PdfFactory est le builder immuable ; chaque with*() renvoie une nouvelle instance. Il compose un DocumentFactoryInterface sous-jacent, donc le modèle de registres pour worker présenté dans la vue d’ensemble s’applique sans câblage supplémentaire.

  • createStandalone() construit des registres privés. Dans une boucle de worker, cela réanalyse chaque font à chaque requête. Utilise plutôt DocumentFactoryInterface avec des registres partagés.
  • Un Document est jetable par conception. Réutiliser la même instance d’un document logique à l’autre provoque une fuite d’état. Appelle create() pour chaque document et laisse le ramasse-miettes le récupérer.
  • FontRegistryInterface::lock() force register(), addFontDirectory() et warmup() à lever une LogicException. Verrouille après le préchauffage, jamais pendant le traitement d’une requête.
  • OutputDestination::File écrit sur le système de fichiers du serveur et renvoie les octets bruts. save() est la voie explicite pour écrire dans un fichier. Ne mélange pas les deux pour un même document.
  • cell() accepte bool|string pour l’argument de bordure, par compatibilité TCPDF ; une chaîne vide n’est pas la même chose que false. Passe la valeur typée qui exprime ton intention.

Les registres de fonts et d’images font du domaine document un système borné en mémoire plutôt qu’un système à reconstruire par requête. Le coût est dominé par l’analyse des fonts à la première requête. Le performance_budget est de 1500 ms en temps réel et 64 Mo en pic sur trois documents dans l’exemple worker. La quasi-totalité de ce budget correspond à la première analyse de font. Après le préchauffage, le travail attribuable au contrat par document est en O(1) — une recherche dans le registre et une allocation de contexte. memoryUsage() sur l’un ou l’autre registre renvoie un MemoryReport pour une planification de capacité en direct. ResettableService::reset() borne le pic de mémoire sous charge soutenue.

Les contrats document ne portent aucune surface cryptographique, mais deux risques opérationnels s’appliquent. D’abord, image() accepte un chemin ou une URL. Lorsque l’entrée n’est pas fiable, restreins la récupération distante via ExternalResourcePolicyInterface (voir la page security-policy) plutôt que de passer directement des URL contrôlées par l’utilisateur. Ensuite, writeHtml() est le point d’entrée du pipeline HTML. Le markup non fiable doit passer par un HtmlSecurityPolicyInterface avant le rendu. La couche document elle-même ne nettoie pas l’entrée. C’est le rôle du domaine security-policy, et c’est un contrat ; tu peux donc fournir une politique plus stricte sans forker.

Les contrats document implémentent la structure de document PDF 2.0 telle que définie dans ISO 32000-2. La gestion de la sortie, des pages et des fonts produit des objets indirects et un flux de références croisées conformément à ISO 32000-2 §7. Le contenu est émis par la couche writer conformément au contrat de la couche moteur (ADR-010). Aucune revendication au niveau d’une clause n’est affirmée sur cette page au-delà de la conformité structurelle. Les conformités PDF/A et PDF/UA sont documentées sur les pages extraction et accessibilité, qui portent les tableaux normatifs.