Aller au contenu

Graphismes : primitives de tracé, de dégradé et de transformation

Le module Graphics traduit l’intention de dessin en opérateurs graphiques PDF. Il couvre les tracés, les styles de ligne, les espaces colorimétriques, les transformations, les dégradés, les motifs, les simili-gravures et le chargement d’images.

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

Graphics fournit la couche de dessin vectoriel et raster. Il produit les séquences d’opérateurs que les modules ContentStream et Writer sérialisent dans un PDF. Un flux de contenu encode le contenu d’une page sous la forme d’une séquence ordonnée d’opérateurs graphiques — ISO 32000-2 §8. Le module émet ces opérateurs, mais n’écrit pas le fichier.

DrawingEngine est l’interface principale. C’est un constructeur fluide et à état. Chaque setter renvoie self, accumule un changement d’état graphique ou un opérateur de peinture de tracé, puis l’ajoute à un tampon interne que tu lis avec getStream(). Le moteur modélise directement l’état graphique du PDF — largeur de ligne, style de ligne, couleur de trait et de remplissage, alpha et mode de fusion, limite d’onglet, masque adouci, détourage, surimpression, planéité, lissage, intention de rendu, génération de noir et retrait de sous-couleur correspondent chacun à un opérateur documenté. Les setters liés à la couleur acceptent un objet valeur Color ou un ColorSpace explicite, de sorte que les espaces de périphérique et les espaces fondés sur CIE utilisent la même forme d’appel.

Trois familles de composants accompagnent le moteur. La première couvre l’entrée d’image. ImageLoader décode un fichier ou un blob en mémoire en ImageLoadResult. ImageRegistry déduplique et suit les images décodées avec un MemoryReport, afin que les gros documents respectent un budget mémoire. La deuxième couvre l’import vectoriel. SvgParser et EpsParser traduisent des formats vectoriels externes vers le même flux d’opérateurs, avec getBoundingBox() exposé pour la mise en page. La troisième concerne la fidélité des couleurs de périphérique : les dégradés (ShadingManager, les familles Type2/Type3 et de maillage), les motifs (PatternFill), les simili-gravures (Type1/Type5/Type6/ Type10/Type16), les fonctions de transfert et les espaces colorimétriques fondés sur ICC.

TransformEngine est un complément spécialisé pour les transformations de coordonnées. Il encadre une transformation avec startTransform() et stopTransform(), qui émettent la paire q et Q save/restore. Il propose des opérations affines nommées — scale, translate, rotate, skew, mirrorH, mirrorV — chacune acceptant un pivot optionnel. La matrice de transformation projette un espace de coordonnées interne vers l’espace de coordonnées cible. C’est le même modèle qu’ISO 32000-2 applique aux domaines de dégradé — §8.7.4.

La gestion des couleurs suit l’ADR-012 : les espaces colorimétriques ICCBased et fondés sur CIE émettent des opérateurs de flux de contenu cs/CS explicites plutôt que de s’appuyer sur le repli vers une couleur de périphérique. Les profils ICC sont enveloppés dans un flux ICCBased avec le bon nombre de composants selon ISO 32000-2 §8.6.5.5.

ClasseMéthodes clésRôle
DrawingEnginegetStream(), reset(), setLineWidth(), setLineStyle(), setDrawColor(), setFillColor(), setAlpha(), setSoftMask(), clip(), setOverprint(), setRenderingIntent(), line(), rect(), circle(), ellipse(), polygon(), linearGradient()Constructeur à état d’opérateurs de tracé et d’état graphique
TransformEnginestartTransform(), stopTransform(), scale(), translate(), rotate(), skew(), mirrorH(), mirrorV(), getStream()Transformations affines de coordonnées
ImageLoaderload(string $filePath), loadFromString(string $data, string $mimeType)Décode les images en ImageLoadResult
ImageRegistryload(), loadFromString(), getMetadata(), memoryUsage(), reset()Cache d’images avec déduplication et rapport mémoire
SvgParserparse(), parseFile()Traduit le SVG en flux d’opérateurs
EpsParserparse(), parseFile(), getBoundingBox()Traduit l’EPS en flux d’opérateurs
ShadingManagerenregistrement des dégradés + émission du dictionnaireDégradés axiaux, radiaux et maillés
Halftone (abstraite)halftoneType(), toDict(), hasStream(), getStream()Trames de simili-gravure (type 1/5/6/10/16)

Exécute composer docs:generate-api-php -- --module=Graphics pour obtenir le tableau PHPDoc complet.

Source : examples/06-colors-and-drawing.php.

<?php
declare(strict_types=1);
require_once __DIR__ . '/../vendor/autoload.php';
use NextPDF\Graphics\Color;
use NextPDF\Graphics\DrawingEngine;
use NextPDF\Graphics\LineStyle;
$engine = new DrawingEngine();
$engine
->setLineWidth(1.5)
->setDrawColor(Color::rgb(0, 51, 102))
->setFillColor(Color::rgb(230, 240, 250))
->rect(20.0, 20.0, 160.0, 80.0)
->line(20.0, 110.0, 180.0, 110.0, new LineStyle(dash: [3.0, 2.0]));
$contentStreamBytes = $engine->getStream();

Ce code met en place un registre d’images avec rapport mémoire et une transformation encadrée. Il reprend la structure utilisée dans examples/07-images.php et examples/21-transforms.php.

<?php
declare(strict_types=1);
require_once __DIR__ . '/../vendor/autoload.php';
use NextPDF\Graphics\DrawingEngine;
use NextPDF\Graphics\ImageRegistry;
use NextPDF\Graphics\TransformEngine;
$registry = new ImageRegistry();
$image = $registry->load('/srv/assets/logo.png');
$report = $registry->memoryUsage();
if ($report->bytes > 32 * 1024 * 1024) {
// Decoded image cache exceeded the budget — reset before the next page.
$registry->reset();
}
$transform = new TransformEngine();
$transform
->startTransform()
->translate(40.0, 700.0)
->scale(0.5, 0.5)
->stopTransform();
$engine = new DrawingEngine();
$engine->reset();
$page = $transform->getStream() . $engine->getStream();
  • DrawingEngine est à état. Appelle reset() entre des pages indépendantes, sinon l’état graphique précédent se propage au flux suivant.
  • TransformEngine exige une paire startTransform()/stopTransform() appariée. Un encadrement déséquilibré laisse un q en suspens et corrompt la pile save/restore en aval, dans le Writer.
  • setSoftMask(), setOverprint(), setBlackGeneration() et setUnderColorRemoval() écrivent des marqueurs d’état graphique étendu. Ils sont inertes avec un profil qui rejette la fonctionnalité. Vérifie le mécanisme de garde du profil avant de te fier au résultat visuel.
  • ImageRegistry déduplique par contenu. Deux chemins aux octets identiques partagent un même objet. Ne suppose pas une image PDF par appel à load().
  • EpsParser::getBoundingBox() renvoie la boîte englobante analysée, pas la boîte de page. Applique ton propre détourage si l’EPS déborde du rectangle cible.
  • La compensation du point noir est indicative et fondée sur des marqueurs. Elle ne transforme pas les pixels par elle-même.

Deux changements côté producteur sont cassants. Dans les deux cas, une corruption auparavant silencieuse devient une défaillance explicite au site d’appel.

La validation des entrées lève désormais une exception (note de migration). L’entrée de dessin est validée avant d’atteindre le flux d’opérateurs et rejette les valeurs malformées avec InvalidArgumentException. Les appelants qui transmettaient auparavant NaN, Infinity ou des valeurs hors plage produisaient silencieusement des opérateurs corrompus ; la même entrée lève maintenant une exception. Les contraintes validées sont les suivantes :

  • L’alpha d’une couleur doit être fini et compris dans [0, 1].
  • Les opérandes de la CTM, les dimensions de gabarit, les coordonnées de sommet de dégradé et les coordonnées de patch de maillage doivent être finies — pas de NaN ni Infinity.
  • Le drapeau d’arête d’un patch de dégradé doit valoir l’une des valeurs {0, 1, 2, 3}.
  • Les paramètres des fonctions de type 2/3/4 et les paramètres de simili-gravure sont contrôlés par rapport à leurs bornes.
  • Les noms des colorants sont échappés.
  • Le nom d’un calque OCG (contenu optionnel) doit être non vide.

Audite les sites d’appel qui calculent des coordonnées ou un alpha à partir de données en amont avant la mise à niveau : une valeur acceptée auparavant est maintenant une erreur bloquante.

Le /N ICCBased échoue en mode fermé par défaut. La sortie PDF simple rejette un espace colorimétrique ICCBased dont le nombre de composants /N est en dehors de {1, 3, 4}, et met en cohérence le /N déclaré avec le profil embarqué et l’espace /Alternate. Cela suit la règle ISO 32000-2 §8.6.5.5 pour le flux ICCBased, qui porte /N aux côtés d’un espace /Alternate. Un profil ICC à N canaux (par exemple un profil hexachromie avec N = 6) n’est conservé que lorsqu’un profil PDF/A ou PDF/X, activé via IccConformancePolicy::ProfileGated, est actif. C’est un garde-fou structurel sur le nombre de composants, pas une revendication de certification PDF/A ou PDF/X.

L’émission des opérateurs est linéaire en nombre d’appels de dessin — O(n) ajouts à un tampon, sans recalcul de mise en page. Le coût de décodage d’une image est dominé par le codec et le nombre de pixels, pas par le registre. La déduplication par empreinte de contenu assurée par le registre est le principal levier pour les gros documents : les ressources réutilisées coûtent un décodage et un objet PDF. Le performance_budget pour la charge de référence de ce module est de 1500 ms en temps réel et 64 Mo en pic. Utilise ImageRegistry::memoryUsage() pour observer l’empreinte des images décodées et reset() pour la libérer entre les groupes de pages.

SvgParser et EpsParser consomment une entrée vectorielle non fiable. Traite les deux comme des analyseurs de données hostiles. Impose des limites de taille d’entrée avant d’appeler parse(). Exécute l’extraction dans un worker contraint lorsque la source est fournie par un utilisateur. L’EPS est un dialecte PostScript. L’analyseur traduit un sous-ensemble contraint et n’exécute pas d’interpréteur général, mais tu dois quand même borner la taille d’entrée et le temps d’analyse. Les chargeurs d’images décodent des codecs tiers. Garde à jour les extensions d’image du runtime et plafonne les dimensions décodées. Consulte le modèle de menace du moteur dans /modules/core/security/ pour la frontière de confiance et les conseils d’isolation par worker.

Le module émet des structures d’opérateurs graphiques PDF cohérentes avec ISO 32000-2 §8, des dictionnaires d’espace colorimétrique ICCBased selon §8.6.5.5, et des dictionnaires de dégradé dont Domain, Function, Matrix et BBox suivent §8.7.4. Ce sont des faits d’implémentation : les formes d’opérateur et de dictionnaire sont produites par src/Graphics/ et exercées par tests/Unit/Graphics/, plus les références tests/Golden/PdfWriter/PdfWriterShadingGoldenBaselineSmokeTest et PdfWriterExtGStateGoldenSmokeTest. Elles ne constituent pas une déclaration de conformité PDF 2.0 ou PDF/X de bout en bout. La conformité du document complet est validée séparément par l’oracle et les suites golden décrits dans /modules/core/conformance/. Le comportement de profil pour les OutputIntents ICC est décidé par l’ADR-011 et l’ADR-012, pas par ce module seul.