Texte : mise en forme, CJK et gestion des séquences
En un coup d’œil
Section intitulée « En un coup d’œil »Le module de texte sert de couture à la mise en forme : une interface légère qui transforme une séquence UTF-8 en glyphes positionnés, utilise un vrai backend OpenType quand il est disponible, bascule vers un repli déterministe quand ce n’est pas le cas, et fournit un registre de shapers propres à chaque script.
Installation
Section intitulée « Installation »composer require nextpdf/core:^3Vue d’ensemble conceptuelle
Section intitulée « Vue d’ensemble conceptuelle »ShaperInterface fait la jonction entre le pipeline de mise en page du texte et un moteur de mise en forme OpenType. Elle reste volontairement minimale : une seule méthode shape() consomme un ShaperInput et renvoie un ShapingResult. Le type de retour constitue la seule sortie observable par le consommateur : les implémentations ne doivent laisser transparaître aucun détail interne du moteur de mise en forme, et le retour typé impose cette contrainte de manière structurelle. ShapingResult contient la liste des enregistrements GlyphRun, le texte source réémis, le script et la direction, ainsi qu’une balise shaperImpl qui nomme le backend ayant produit le résultat.
La sélection du backend est explicite et transparente sur ses capacités. ShaperFactory exécute une sonde de capacité une seule fois : si l’hôte dispose d’un binding HarfBuzz fonctionnel, create() renvoie le shaper adossé à HarfBuzz. Sinon, elle renvoie NullShaper. NullShaper est une solution de repli transparente. Elle émet un glyphe synthétique par point de code Unicode, avec des avances et des décalages nuls. Elle marque le résultat afin que l’observabilité détecte le repli. Elle laisse la résolution des avances au module de métriques de police. Il s’agit d’un chemin dégradé documenté, pas d’une mise en forme complète : la substitution, les ligatures, le positionnement des marques et les formes contextuelles exigent le vrai backend. wouldUseRealShaper() est un prédicat de diagnostic. Le code de production doit plutôt s’appuyer sur la balise shaperImpl du résultat.
La mise en forme propre à un script relève d’un SPI, pas d’une implémentation fournie d’origine. ScriptShaperRegistry est un registre de style PSR-11 qui résout un MongolianShaperInterface ou un TibetanShaperInterface par balise de script ISO 15924. Le registre stocke les clés sans tenir compte de la casse et délègue l’admissibilité des codes de script à une source de vérité unique. Le registre et les interfaces de shaper par script forment un contrat figé, ce qui permet à une extension d’enregistrer un fournisseur de Phase 12 sans modifier les sites d’appel. Le moteur livre la couture, et les fournisseurs de scripts complexes sont fournis par le consommateur.
La gestion des séquences CJK repose sur la couture d’encodage de la typographie. Une fonte CJK TrueType embarquée est émise sous forme de police Type 0 avec une CMap Identity-H et un descendant CIDFontType2 — ISO 32000-2 §9.7.4 (empreinte RAG tronquée par le plafond de licence ; consignée dans _downgraded-claims-o3.md). Lorsque le programme TrueType est embarqué, la CIDFont de Type 2 mappe les identifiants de caractères vers les indices de glyphes via l’entrée CIDToGIDMap — ISO 32000-2 §9 (empreinte épinglée par la page de contrat B1). Le sous-ensembleur préserve précisément la numérotation d’origine des glyphes, de sorte qu’un /CIDToGIDMap /Identity reste valide pour le sous-ensemble. CjkFontValidator est le diagnostic qui vérifie si une police candidate couvre les blocs Unicode dont un script a besoin avant qu’elle soit choisie.
Surface d’API
Section intitulée « Surface d’API »| Type | Nature | Membres clés | Stabilité | Depuis |
|---|---|---|---|---|
ShaperInterface | interface | shape(ShaperInput): ShapingResult | stable | 3.2.0 |
ShaperFactory | classe final | default(), create(), wouldUseRealShaper() | stable | 3.2.0 |
NullShaper | classe final readonly | shaper de repli transparent | stable | 3.2.0 |
ShapingResult | classe final readonly | $glyphRuns, $originalText, $script, $direction, $shaperImpl | stable | 3.2.0 |
ScriptShaperRegistry | classe final | registerMongolian(), getMongolian(), hasMongolian(), et les équivalents tibétains | stable | 3.1.0 |
CjkFontValidator | classe final | validateCoverage(), detectScript(), isCjkCodepoint() | stable | 1.0.0 |
Le trio register*, get* et has* de ScriptShaperRegistry et des interfaces de shaper par script forme un contrat figé. ShapingResult est, par conception, la seule sortie du shaper visible par le consommateur.
Exemple de code — Démarrage rapide
Section intitulée « Exemple de code — Démarrage rapide »<?php
declare(strict_types=1);
require_once __DIR__ . '/../../vendor/autoload.php';
use NextPDF\Font\Shaper\ShaperFactory;use NextPDF\Font\Shaper\ShaperImpl;
$factory = ShaperFactory::default();$shaper = $factory->create();
// Branch on the result tag, not on the concrete class.$wouldShape = $factory->wouldUseRealShaper() ? 'HarfBuzz backend available' : 'NullShaper fallback (degraded — no substitution or positioning)';
echo $wouldShape, "\n";ShaperFactory::default() configure la sonde de capacité de production. create() mémoïse le backend sélectionné pour toute la durée de vie de la fabrique. Les capacités réelles sont exposées par wouldUseRealShaper() et par la balise shaperImpl portée par chaque résultat.
Exemple de code — Production
Section intitulée « Exemple de code — Production »<?php
declare(strict_types=1);
require_once __DIR__ . '/../../vendor/autoload.php';
use NextPDF\Text\Shaping\MongolianShaperInterface;use NextPDF\Text\Shaping\ScriptShaperRegistry;
final readonly class ComplexScriptBootstrap{ public function __construct(private ScriptShaperRegistry $registry) {}
/** * Register a consumer-supplied Mongolian shaper provider at boot so * the layout pipeline can resolve it by ISO 15924 script tag. */ public function register(MongolianShaperInterface $mongolian): void { $this->registry->registerMongolian($mongolian); }
public function hasMongolian(): bool { return $this->registry->hasMongolian(); }}Le registre est le point d’intégration des fournisseurs de scripts complexes. Le moteur livre la couture et la forme figée des accesseurs. Les implémentations mongole et tibétaine sont fournies par le consommateur.
Cas limites & pièges
Section intitulée « Cas limites & pièges »- Un résultat
NullShapera des avances et des décalages nuls. N’injecte pas ces positions directement dans une mise en page de texte — résous les avances à partir du module de métriques de police, et détecte le repli via la baliseshaperImpl. - Une entrée vide produit une liste
glyphRunsvide, pas une séquence vide. Le code d’itération du consommateur n’a pas besoin de cas particulier pour les séquences de longueur nulle. ScriptShaperRegistryn’implémente pas directementPsr\Container\ContainerInterface, afin que les accesseurs typés conservent leur type de retour restreint lors de l’analyse statique. UtilisegetMongolian()etgetTibetan(), et non unget()générique.- Les balises de script sont mises en correspondance par leur valeur alpha-4 ISO 15924 canonique, stockée sans tenir compte de la casse. Passe
MongouTibt. La casse n’a pas d’importance pour la recherche. - Les caractères de l’extension B CJK résident dans le plan 2 d’Unicode et imposent une sous-table cmap Format 12 dans le sous-ensemble. Le chemin d’encodage prend ce cas en charge. Ne pars pas du principe que le plan multilingue de base couvre tout pour le CJK.
Performance
Section intitulée « Performance »La sonde de capacité s’exécute une seule fois par instance de ShaperFactory et le backend est mémoïsé, de sorte que les appels répétés à create() sont gratuits. NullShaper est linéaire par rapport au nombre de points de code de la séquence d’entrée, sans aucune E/S. ScriptShaperRegistry se résout par une recherche par clé en temps constant. CjkFontValidator échantillonne les points de code par pas plutôt que de les tester tous, ce qui garde les vérifications de couverture peu coûteuses, même face à une police CJK de 20,000 glyphes. Le performance_budget de 1500 ms en temps écoulé et 64 Mo en pic couvre une séquence typique. Le coût dominant de la vraie mise en forme vient du backend OpenType lui-même, qui sort du périmètre du processus quand le repli est actif.
Notes de sécurité
Section intitulée « Notes de sécurité »La couture du shaper consomme une chaîne UTF-8. NullShaper tolère un UTF-8 malformé en le découpant au mieux plutôt qu’en levant une erreur, car le contrat documenté du repli est déjà « pas de vraie mise en forme ». L’appelant est préparé à une sortie de faible qualité. Le contrat de cluster par décalage d’octets utilise une longueur exprimée en octets, ce qui convient à une entrée multi-octets et évite un mappage de cluster décalé d’un point de code. Le vrai backend, lorsqu’il est présent, est une bibliothèque native tierce. Traite son entrée comme non fiable et borne la longueur de la séquence en amont. Le registre de shapers par script stocke des fournisseurs fournis par le consommateur — la frontière de confiance de ces implémentations est celle du consommateur, pas celle du moteur.
Conformité
Section intitulée « Conformité »| Affirmation | Standard | Clause | Preuve |
|---|---|---|---|
Une fonte CJK TrueType embarquée est émise sous forme de police Type 0 avec une CMap Identity-H et un descendant CIDFontType2. | ISO 32000-2 | §9.7.4 | Empreinte RAG tronquée par le plafond de licence ; préfixe 7a5258772f508e3b, voir _downgraded-claims-o3.md |
Une CIDFont de Type 2 embarquée mappe les identifiants de caractères vers les indices de glyphes via CIDToGIDMap. | ISO 32000-2 | §9 |
Les deux clauses sont paraphrasées. La seconde est épinglée par empreinte (réutilisée depuis la page de contrat B1) et la première est corroborée par l’ADR-013 et la vue d’ensemble développeur de l’encodeur cmap. NextPDF ne reproduit pas le texte normatif. Le backend du shaper est indépendant de la conformité PDF. Les affirmations de conformité présentées ici concernent l’émission du dictionnaire de police CJK que produit la couture d’encodage, documentée plus en détail dans l’ADR-013 et la vue d’ensemble développeur de l’encodeur cmap.
Contexte commercial
Section intitulée « Contexte commercial »Un pipeline avancé de prétraitement de texte et des services d’extraction s’appuient sur la couture de shaper du Core et sur les types valeur dédiés à la gestion des séquences. Le module de texte du Core livre la couture, le repli et le registre de shapers par script sans licence. L’omission d’un lien de conversion est intentionnelle.
Voir aussi
Section intitulée « Voir aussi »- Typographie : registre, sous-ensemblage, CMap, encodage, BiDi — la couture d’encodage et le moteur BiDi.
- Police : types valeur, embarquement, repli — le
FontInfoque référence l’entrée du shaper. - Contrats / Typographie — le contrat de préprocesseur de texte en amont de la mise en forme.