L’année dernière, j’envisageai du bout des doigts la conception d’un générateur de code-barres(1) pour InDesign. Arrêté dans mon élan par l’âpreté de la tâche et l’existence d’excellents plug-ins commerciaux tels celui de Mariusz Sobolewski, je me suis rabattu provisoirement sur une vieille « méthode de bourrin » qui a fait les choux gras de BlogNot ! Mais ce jeté d’éponge continuait de me travailler au niveau de l’amour-propre, parce qu’il n’y a rien de plus stupide qu’un code-barres, graphiquement parlant. Le recours à un système D barbant au possible (ou à des produits tiers non corvéables à mes critères d’utilisation) a fini d’user la bride qui me tenait à distance de ce défi.
(1) Les académiciens privilégient la lexie code à barres. Si vous n’y voyez pas d’inconvénient, nous laisserons quelques instants les académiciens sur le ballast et adopterons ce terme sauvage et wikipédique de code-barres, qui a les atouts du bisyllabisme anglais (barcode).Après tout, ce n’était pas la mer à boire que de faire tracer en Javascript trente barres verticales sur la page active ! Quant à y ajuster en pied une bande de treize chiffres, nous avons connu plus olympique dans cette rubrique. Concevoir une interface permettant à l’utilisateur la saisie contrôlée du code EAN ou de l’ISBN, ainsi que des dimensions de l’objet, ne me semblait pas davantage relever des douze travaux d’Hercule.
En réalité, chacun des processus en œuvre dans cette application est incroyablement rudimentaire. La difficulté, c’est l’agrégation de toutes ces briques, car elles appartiennent à des univers de programmation étanches (codage arithmétique, géométrie vectorielle, typographie, dialogue-utilisateur) tout en interagissant constamment. Par exemple, le code entré par l’utilisateur doit être visé (somme de contrôle, etc.) par un module numérique qui devra aussi, probablement, convertir l’information à l’usage du module graphique (le traceur de barres) et du module typographique (l’afficheur de chiffres). Chaque rouage représente la même information d’une façon différente(2), et il suffit qu’un paramètre change en amont ou en aval du point où l’on se trouve dans le flux algorithmique pour que l’intégrité du code-barres soit menacée.
(2) En termes de programmation objet, on pourrait songer ici à la collaboration des deux design patterns appelés Médiateur et Observateur, le premier encapsulant l’interaction entre des objets cosmopolites mais fortement corrélés, le second interfaçant la mise à jour de différentes « vues » après un changement d’état de l’information racine. Le paradigme modèle-vue-contrôleur (MVC), grand chouchou des développeurs de frameworks Internet, a considérablement popularisé cette approche.Mais restons zen ! Notre application ne présente pas une complexité de nature à requérir l’artillerie évoquée dans la note ci-dessus. Simplement, c’est une excellente occasion de mettre en œuvre la modularité « objet » dans un script InDesign, c’est-à-dire de penser en termes de classes, d’interface et de privatisation des processus. Certes, Javascript pour InDesign ne permet pas de déclarer des membres privés, mais la meilleure façon d’aborder conceptuellement un objet reste de détacher son interface de son implémentation(3).
(3) Dans le code, j’ai utilisé pour les composants dits « privés » la syntaxe this._method() ou this._property (underscore préfixé) : il s’agit bien sûr d’une simple convention d’écriture. Je me souviens ainsi que l’appel d’un membre dont le nom commence par un souligné n’est autorisé que depuis la classe elle-même. Si vous êtes amenés à développer à votre tour une usine à gaz, pensez donc à cette petite astuce : elle révèle immédiatement une défaillance de modélisation et garantit, une fois en place, un code relativement propre et facile à émanciper.La figure 1 (PDF à télécharger) concentre le scénario général du script en exposant seulement l’interface des objets (c’est-à-dire les méthodes réputées publiques) et la façon dont ils collaborent. La méthode racine app.main() crée une sorte de médiateur à travers l’objet preset = new TPreset(). La classe rattachée TPreset, malgré son volume, ne fait pratiquement rien d’autre que de communiquer avec les autres composants de l’architecture. Elle est chargée de maintenir les informations et les réglages présidant à la conception du code-barres telle que l’utilisateur la spécifiera. C’est elle, notamment, qui connecte et renseigne les « contrôles » (widgets) du dialogue-utilisateur afin qu’ils reflètent les paramètres du code-barres en formation :

Au lancement du script, l’utilisateur peut avoir sélectionné un bloc en guise de patron pour prédimensionner le code-barres (v. fig. 2). Sinon, un gabarit par défaut lui est proposé en fonction de la surface de la page courante. Dans tous les cas, les dimensions pressenties sont répercutées dans la boîte de dialogue et l’utilisateur peut les modifier avant de valider son choix. Bien entendu, il va saisir aussi le code à appliquer (EAN-13 ou ISBN-13, ce qui revient à peu près au même). TPreset est une classe « plateforme » en ce sens qu’elle met en relation les entrées de l’utilisateur avec des organes de validation interne (TCode et TMetrics) sans effectuer elle-même de calculs compliqués. Elle délègue à TCode le contrôle du code EAN et à TMetrics la délicate question du dimensionnement du code-barres.
Notez que le dialogue est affiché par Application::mainDialog(TPreset&) car il est naturel que l’application centrale soit responsable de la création et de la destruction d’un composant dialogué (collection dialogs). Cependant, c’est bien l’objet preset transmis comme argument qui assure la connectivité du dialogue en créant et en renseignant ses contrôles. Voilà qui explique l’étonnante compacité de la méthode mainDialog() :
Application.prototype.mainDialog = function(preset)
//----------------------------------------------------------
// Affiche le dialogue et renvoie TRUE si validation
{
// Construction du dialogue
var dlgTitle = SCRIPT_NAME + " " + SCRIPT_VERSION;
var dlg = this.dialogs.add( {name:dlgTitle, canCancel:true} );
preset.connectDialog(dlg); // c'est l'objet TPreset qui "connecte" le dialogue!
// Affichage du dialogue
var dlgOK = false;
while (dlg.show() == true)
{
if ( preset.keepDialog(dlg) ) continue;
dlgOK = true; break;
}
dlg.destroy();
return( dlgOK );
}
Le code ci-dessus montre également qu’il revient à TPreset, à travers sa méthode keepDialog(), d’autoriser ou de refuser la fermeture du dialogue. Tant que keepDialog(dlg) répond TRUE, les paramètres saisis par l’utilisateur ne sont pas validables et le dialogue doit se poursuivre. En effet, on ne peut pas générer un code-barres avec n’importe quelle séquence de chiffres ou avec n’importe quelles dimensions. Il faut d’une part vérifier la longueur du code et sa somme de contrôle (checksum) ; d’autre part s’assurer que les proportions du gabarit sont « tenables » compte tenu des contraintes de la grille (v. infra). Or, seule une partie de ces contraintes est gérable directement par le dialogue (champs de saisie avec valeurs min/max), le reste étant contrôlé par TPreset qui, en la matière, s’en réfère aux calculs spécialisés de TMetrics.
La classe TMetrics synthétise les propriétés géométriques du code-barres. J’avoue que c’est l’aspect du script qui m’a donné le plus de fil à retordre. Sur le plan graphique, un code-barres complet est formé de deux objets distincts mais solidaires : la grille, constituée d’une séquence de lignes verticales dont les épaisseurs et les espacements relatifs traduisent optiquement le code EAN ; puis la ligne de chiffres, reprenant le code en clair sous la forme d’une chaîne de caractères encastrée sous la grille.

On observe tout d’abord le chevauchement particulier de la grille et de la zone de chiffres. La première est « débordée » par la seconde vers la gauche et vers le bas. À vrai dire, je n’ai pas trouvé de spécification régissant formellement l’agencement du bloc-chiffres au sein du code-barres. Heureusement, plusieurs contraintes naturelles se sont révélées durant le développement et ont permis d’accoucher un cahier des charges :
1) Les barres verticales de la grille, de même que les espaces intercalaires, varient d’une épaisseur relative de 1 à 4. Cela résulte directement du système de codage graphique de l’EAN-13 (sur lequel je reviendrai plus loin). Le premier chiffre n’est pas codé graphiquement. Chacun des douze chiffres suivants est représenté par un jeu de quatre bandes alternées : une blanche, une noire, une blanche, une noire. L’épaisseur de chaque bande peut varier dans un rapport de 1 à 4, mais le fait important à retenir pour le moment est que la séquence des 4 bandes occupe toujours exactement 7 unités (si l’on convient d’appeler unité l’épaisseur d’une bande de rapport 1). Le zoom de la figure 4 illustre cet invariant :

On voit par exemple que le chiffre 1 est codé ici par la séquence : bande blanche de double épaisseur, bande noire de double épaisseur, bande blanche de double épaisseur, bande noire de simple épaisseur. On conviendra sans mal que cela fait un total de 2 + 2 + 2 + 1 = 7 unités. Ce résultat est constant pour tous les chiffres codés dans la grille. Partant de là, il est facile de découvrir le nombre exact d’unités utilisées par cette dernière. Le code est segmenté en deux groupes de 6 chiffres (2×6×7 = 84), auxquels s’ajoutent des lignes de garde latérales de 3 unités chacune (3 + 3 = 6) et des lignes de garde centrales occupant 5 unités. La largeur totale de la grille est donc de 84 + 6 + 5 = 95 unités. Bien entendu, il faut garder à l’esprit que l’unité est relative. Le calcul que nous venons d’opérer permet simplement de déterminer que si la largeur de la grille est fixée à Wg, alors l’épaisseur d’une bande élémentaire est de Wg / 95.
2) L’unité de travail calculée à l’étape précécente permet d’introduire un autre paramètre : chaque chiffre porté sous la grille doit en principe s’inscrire dans une cellule de 7 unités de large, faute de quoi il ne serait pas en phase avec sa représentation graphique. Cette contrainte n’est pas mise en œuvre par tous les générateurs de code-barres (certains se contentent de centrer les chaînes de chiffres dans l’espace vacant sans se soucier de les faire coïncider avec les barres correspondantes). Cependant, si l’on postule qu’un chiffre s’inscrit dans une largeur de 7 unités, alors la largeur totale du code-barres est calculable en fonction de la largeur de la grille. En effet, le premier chiffre en débord (que l’on alignera à gauche) ajoute simplement 7 unités à la grille, ce qui met le code-barres complet à 102 unités de large, soit 102/95 fois la largeur de la grille.
3) C’est dans le sens de la hauteur que les choses se corsent. Dans un premier temps, j’avais considéré ce paramètre comme arbitraire, ce qui a débouché sur des résultats hideux chaque fois que j’ai poussé le script dans ses retranchements. En effet, les rapports géométriques intervenant dans un code-barres sont fortement connectés, qu’il s’agisse de la hauteur relative des barres par rapport à celle des lignes de garde (environ 90 %), ou encore de la hauteur des chiffres par rapport à leur largeur de corps. Dans un code-barres satisfaisant, les chiffres devraient s’encastrer dans la zone inférieure sans s’étirer ni s’espacer outre mesure. Qui plus est, le débord de la zone chiffrée par rapport aux lignes de garde devrait être à peu près équivalent au débord des lignes de garde par rapport aux barres elles-mêmes, de telle sorte que les chiffres imprimés paraissent centrés verticalement dans l’espace vacant. Tout ceci conduit à un système d’équations à une demi-douzaine d’inconnues. Pour esquiver sa résolution, j’ai adopté un ratio optimal, une espèce de nombre d’or, régissant le rapport largeur/hauteur d’un code-barres. Estimé à 2 (et finalement fixé à 1,9), ce ratio permet de dériver les dimensions de tous les composants en fonction de la largeur ! Ainsi, lorsque l’utilisateur a fixé les dimensions de l’objet, le script commence par construire un code-barres « idéal » en se basant uniquement sur la largeur et le « nombre d’or », puis il ajuste la hauteur a posteriori sans dégrader les proportions optimales. La figure 5 rassemble toutes les cotes du système :
La charge principale de la classe TMetrics est de gérer ces calculs, tout en offrant des méthodes de conversion des dimensions selon les trois référentiels que constituent l’espace de la grille, l’espace de la zone chiffrée et l’espace global du code-barres. « EanDesign » permet notamment à l’utilisateur de spécifier, à sa convenance, soit les dimensions du code-barres complet, soit les dimensions de la grille. Les calculs effectués au sein de TMetrics garantissent un passage transparent et bijectif de l’un à l’autre de ces systèmes de mesure.
Via l’interface dialoguée, l’utilisateur pourra également agir, de diverses façons, sur la mise en forme : réduire la hauteur des barres, moduler l’espacement des chiffres, autoriser leur étirement en hauteur. Cependant, ces corrections « accessoires » n’interviennent qu’après l’établissement de la forme canonique, c’est pourquoi elles ne sont pas administrées par TMetrics, TPreset s’occupant d’adresser ces paramètres au « dessinateur » du code-barres (TBarCode), qui intervient par conséquent en aval.
Enfin, j’ai passé sous silence le coefficient purement typographique donnant, selon la fonte utilisée, le ratio hauteur/largeur d’une cellule chiffrée (carrés verts dans la figure 5). Cet aspect est pris en charge par la classe TDigFont documentée plus bas.
Pour comprendre comment fonctionne un code-barres basé sur la spécification EAN, je me suis référé à l’excellent article que Wikipedia lui consacre.
La classe TCode est responsable de cette partie de la mission. Elle s’occupe tout d’abord de charger et de valider un code EAN-13, constitué de 12 chiffres significatifs et d’un chiffre de contrôle. La méthode TCode::setDigits(eanStr) prend en argument une chaîne de 12 ou 13 chiffres selon que l’on spécifie ou non le chiffre de contrôle. Si la chaîne contient des caractères parasites (i.e. non numériques), ils sont tout bonnement ignorés, ce qui permet de fournir tout aussi bien un ISBN-13(4).
(4) L’ISBN, désormais à 13 chiffres, est rigoureusement identique à l’EAN après élimination des « tirets » de formatage.TCode::setDigits(eanStr) appelle irrémédiablement la méthode « privée » TCode::_checkSum() qui calcule (ou recalcule) le chiffre de contrôle :
TCode.prototype._checkSum = function()
//--------------------------------------------------------------------
// Calcule (ou recalcule) la checksum des 12 premiers chiffres de this._digits
{
var r = 0;
var mult = 1;
for (var d = 12; d >= 1; d--)
{
mult = 4 - mult;
r += ( mult * this._digits[d-1]);
}
r = 10 - ( r % 10 );
return( ( r!= 10 )? r : 0 );
}
Outre la maintenance des flags d’erreur, TCode offre une autre fonctionnalité vitale : le cryptage du code sous forme de « segments ». La méthode getSegments() renvoie, dans un tableau, 12 séquences de 4 valeurs numériques prises dans l’ensemble {1, 2, 3, 4}. Il s’agit, vous l’aurez compris, du codage des chiffres en termes d’épaisseurs de bandes. C’est ainsi que, sans avoir nullement conscience des dimensions réelles du code-barres, TCode fournit au médiateur (TPreset) tous les coefficients nécessaires au déploiement de la grille dans l’espace géométrique supervisé par TMetrics. Quant au module chargé de dessiner la grille, TGridShape, il opère directement avec les coefficients fournis sans se soucier du code sous-jacent. Il trace « bêtement » une succession de barres à partir des cotes initiales livrées en paramètres et de la distribution épaisseur/espacement spécifiée par TCode::getSegments().
La complexité de cette méthode getSegments() a été considérablement réduite grâce aux tableaux de correspondances intitulés CODES et TABLES, déclarés en variables globales au voisinage de la classe TCode et reflétant purement et simplement le procédé de cryptage de l’EAN. L’originalité de ce procédé réside dans le fait que le premier chiffre n’est pas codé explicitement par des barres, mais détermine de façon univoque la façon dont les chiffres suivants sont codés ! Je vous renvoie au script lui-même pour le détail de l’implémentation.
Instancié par le programme principal après la validation du dialogue, l’objet TBarCode est pour ainsi dire une enveloppe, timbrée et cachetée selon les valeurs armées dans TPreset. TBarCode n’est qu’un interlocuteur de façade : son unique méthode opérationnelle, TBarCode::_display() — appelée publiquement par TBarCode::execute() — ne fait qu’actionner deux modules auxiliaires, TGridShape et TDigsShape, architectes respectifs de la grille et du bloc-chiffres. Le code-barres complet est alors traité comme le groupe (au sens InDesign) formé de ces deux composants :
TBarCode.prototype._display = function()
//--------------------------------------------------------------------
// Affiche les composants du barcode
{
this._preset.setPointsUnits(true);
// affichage de la grille
this._gridShape.display(this._preset.getCodeSegments());
// affichage des chiffres
this._digsShape.display(this._preset.getCode(), this._preset.getFont());
var pg = this._preset.getPage();
var gr = pg.groups.add([this._gridShape.getShape(),this._digsShape.getShape()]);
gr.pageItems[1].bringToFront();
this._preset.tagBarCode(gr);
gr.select();
this._preset.setPointsUnits(false);
}
Cet échantillon du programme illustre parfaitement l’idée de modularité et de délégation des responsabilités. Les spécialistes en design patterns reconnaîtront immédiatement, derrière ces lignes de code, le comportement typique d’un composite. Pour se dessiner, l’objet TBarCode demande à chacun de ses composants de se dessiner eux-mêmes : this._gridShape.display(), this._digsShape.display(). Il ignore comment ils vont y parvenir mais il sait exactement de quels paramètres ils ont besoin. Il contrôle leur création, actionne leur interface et se charge de les assembler — pg.groups.add([this._gridShape.getShape(),this._digsShape.getShape()]) —, mais il n’a pas besoin d’en savoir davantage sur leur fonctionnement propre.
De leur côté, TGridShape (composant grille) et TDigsShape (composant chiffres) n’ont qu’une vision extrêmement étroite du contexte : ils n’ont à gérer que la spécificité de leur mission et seuls les arguments strictement nécessaires à son accomplissement leur sont transmis. À TGridShape, on communique les dimensions et autres coefficients métriques d’une grille, puis la distribution des « segments » à afficher. À TDigsShape, on fournit les dimensions et coefficients d’un bloc-chiffres, puis on transmet le code numérique et la fonte à utiliser. Les opérations assurées par chacune de ces deux classes sont purement graphiques : elles tracent des polygones sur la page selon un protocole rigide mais essentiellement aveugle. À la limite, elles pourraient servir une toute autre cause que la production de codes-barres...
Pour finir, TBarCode procède à l’assemblage des deux composants et l’objet Group ainsi obtenu est « marqué au fer rouge ». La méthode TPreset::tagBarCode(Group) utilise à cet effet le dispositif d’insertion de label — PageItem::insertLabel() — offert par l’architecture Javascript d’InDesign. Ce dernier permet d’attacher durablement au groupe les données sous-jacentes du code-barres. Du coup, lorsque vous rappellerez « EanDesign » par la suite (sous réserve d’avoir sélectionné un code-barres déjà produit par le script), l’ensemble des paramètres (EAN, dimensions, hauteur relative des barres, style et espacement des chiffres...) se réaffichera magiquement dans la boîte de dialogue. Cela permet d’actualiser très rapidement un code-barres (changement de code, changement de police, etc.) sans resaisir les options invariantes.
Je vous gardais pour la fin ma « fonte virtuelle », le gadget le plus fou qu’il m’ait été donné d’intégrer dans un script InDesign. La classe TDigFont encapsule la vectorisation de plusieurs polices de caractères et le tracé direct des chiffres sous la grille du code-barres. En clair, « EanDesign » n’utilise pas de blocs-texte ni de polices de caractères logicielles, il dessine lui-même les chiffres, presque point par point :

Avantages : les codes-barres générés sont 100 % portables, indépendants des polices installées sur le système de l’utilisateur, redimensionnables à volonté et parfaitement sécurisés pour l’export PDF. Si vous passez vos journées à créer des aplats de couverture ou du packaging commercial pour des centaines de clients, vous apprécierez de pouvoir vous reposer, enfin, sur un système de codes-barres autonomes, fiables, faciles à actualiser et ne s’appuyant sur aucune image ou police sous-jacente.
Pour réussir cette prouesse, TDigFont intègre dans son code les points de tracé (PathPoints) des chiffres de 0 à 9 obtenus par vectorisation de diverses polices de caractères. Dans sa version actuelle, « EanDesign » implémente ainsi quatre polices virtuelles : OCR B, Myriad Pro, Arial et CenturyGothic. L’utilisateur choisit la police à employer dans la liste « Style de chiffre » de la boîte de dialogue.
Le style de chiffre le plus répandu dans les codes-barres est l’OCR B qui, comme son nom l’indique, est le mieux adapté à la reconnaissance optique de caractères. Toutefois, la portion chiffrée du code-barres n’est en principe qu’un élément d’appoint puisque les scanners décryptent le code à partir de la distribution des barres. Les chiffres en clair sont donc plutôt destinés à la caissière lorsque le scanning ne fonctionne pas et qu’il faut saisir l’EAN manuellement. C’est pourquoi nous disposons d’une relative liberté dans le choix de la police utilisée — à tel point que certains éditeurs ou producteurs poussent le luxe typographique jusqu’à imposer leur propre police, histoire de marier les codes-barres avec le style maison.
Dans cette optique, l’interface de la classe TDigFont expose un utilitaire, scanFontToClipboard(), permettant de sérialiser une police quelconque pour l’intégrer ensuite dans le script. TDigFont::scanFontToClipboard() est une méthode statique prenant pour arguments un document (Document) et un bloc-texte (TextFrame). « EanDesign » vous propose cet outil lorsque vous sélectionnez un bloc-texte avant l’appel. Dans ce cas, le bloc en question est supposé contenir le style de caractères que l’on désire vectoriser. Par exemple, si vous souhaitez ajouter une police Frutiger à notre générateur de code-barres, il suffit de créer un bloc avec quelques caractères Frutiger (peu importe le corps utilisé) puis de lancer le script : scanFontToClipboard() injecte successivement dans le bloc de travail les chiffres de 0 à 9 et les vectorise via TextFrame::createOutlines()(5).
(5) Sachez que createOutlines() renvoie un tableau (Array) d’objets de type Polygon, mais que ce tableau ne comprend apparemment qu’un unique élément quel que soit le texte vectorisé. La forme résultante tient donc d’une pièce et se récupère ainsi :La forme ainsi produite est ensuite positionnée dans une cellule temporaire de hauteur unitaire et tous ses points sont décortiqués. À la fin du travail, scanFontToClipboard() place dans le presse-papier le code complet à insérer dans le script pour déclarer cette nouvelle police virtuelle. Ce code consiste en une nouvelle méthode arborant le squelette suivant :
TDigFont.prototype._setFontXXX = function()
//--------------------------------------------------------------------
{
this._pathFont =
[
// ...
// ici, une interminable sequence de valeurs numeriques
// ...
];
// coefficient metrique calcule par scanFontToClipboard()
this._maxWidth = 0.66261767608696; // valeur dependant de la police analysee
}
Il vous suffit de coller le code tel quel parmi l’une des méthodes _setFont... existantes. N’oubliez pas de remplacez le suffixe XXX par le nom usuel que vous voulez attribuer à la fonte, ce qui donnera par exemple une méthode intitulée _setFontFrutiger. Enfin, ajoutez ce nom usuel dans la méthode statique TDigFont::getFontList() qui se borne à fournir dans un tableau la liste des fontes émulées par la classe : return(["OCR","Myriad","Arial","CenturyGoth","Frutiger","etc."]) ;
Et voilà ! Votre version customisée d’« EanDesign » a gonflé d’une vingtaine de lignes et vous disposez désormais d’un nouveau style, Frutiger, pour les chiffres de vos codes-barres. Je vous concède que cette procédure peut intimider les utilisateurs débutants, mais je n’ai pas eu le courage de me pencher sur une solution plus automatique, qui eût probablement imposé la création et le management d’un fichier de données pour chaque nouvelle fonte sérialisée. Dans une prochaine version, peut-être...
Revenons maintenant au cœur de TDigFont, qui nous apprend beaucoup sur la gestion des tracés et des formes vectorielles dans InDesign. Le concept le plus important à apprivoiser est celui de Path (« chemin », « tracé »). L’objet Polygon, que l’on peut considérer comme un sommet générique dans la hiérarchie des composants vectoriels (Rectangle, Oval, GraphicLine...) expose une propriété .paths qui référence tous les tracés sous-jacents. Pourquoi un « polygone » peut-il contenir plusieurs tracés ? Parce qu’InDesign qualifie de Polygon toute forme vectorielle simple ou composée (compound path), aussi complexe soit-elle. Composée signifie ici : « combinant plusieurs formes en une seule pièce ». On obtient par exemple une forme composée en fusionnant, avec le PathFinder, deux ou plusieurs formes préexistantes, ou encore en vectorisant du texte :

Dans la figure 7a, il est immédiat que le polygone contient deux tracés, deux sous-formes topologiquement fermées. La propriété .paths indexe donc 2 éléments. Plus délicate est la situation illustrée par la figure 7b, dont le polygone combine non pas 4, mais 7 tracés (myPoly.paths.length == 7). Il faut en effet distinguer les tracés en plein et les tracés en creux que la forme utilise pour se dessiner complètement. Par exemple, le glyphe de la lettre a repose sur deux tracés fermés :

La cuisine interne d’InDesign fait que l’objet Polygon, sur la foi de sa collection .paths et de son ordonnancement interne, sait quels sont les tracés pleins et quels sont les tracés de défonce. Plus précisément, chaque tracé possède une polarité, un sens, indiquant indirectement où se situe son intérieur et son extérieur. Ce paramètre assez ésotérique, que l’utilisateur ou le programmeur Javascript ne manipule qu’à travers la méthode Path::reverse() (i.e. Objet / Inverser le tracé) impacte tout spécialement cette catégorie d’objets que la documentation d’InDesign appelle les tracés transparents :
Les tracés transparents consistent en un minimum de deux tracés simples qui entrent en interaction ou s’interceptent entre eux. Ils sont plus simples que les formes composées et sont reconnus par toutes les applications PostScript. Les tracés combinés dans un tracé transparent agissent comme un objet et partagent des attributs (tels que les couleurs et styles de contour). [...] Le sens d’un tracé détermine les zones remplies et l’application des formes de début et de fin (telles que des pointes de flèche).
L’objet Path incarne un tracé élémentaire et consiste essentiellement en une collection de points d’ancrage (PathPoints) :
Chaque point d’ancrage (objet PathPoint) est défini géométriquement par trois jeux de coordonnées de la forme [x,y]. La propriété PathPoint::anchor localise le point, les propriétés PathPoint::leftDirection et PathPoint::rightDirection renseignent le cas échéant les tangentes gauche et droite du point d’ancrage quand il s’agit d’un point de courbure (pathPoint.pointType == PointType.smooth). Les points d’ancrage simples (corner, « sommets ») n’utilisent pas ces deux attributs complémentaires.
Voici un aperçu simplifié de l’interface des objets mis en jeu :

Le principe de notre fonte virtuelle (TDigFont) consiste à mémoriser les coordonnées complètes de tous les points de tous les tracés composés pour chaque chiffre d’une fonte donnée, de telle sorte qu’on parvienne à reconstruire par la suite n’importe quel chiffre. Lors de l’accomplissement de cette tâche, la propriété Path::entirePath permet d’économiser un temps énorme. Elle évite en effet de traiter un tracé point par point, en synthétisant sa collection .pathPoints dans un super-tableau numérique. La grande puissance de .entirePath est de fonctionner en lecture et en écriture. Par conséquent, non seulement il est possible de transmettre un tracé sous forme de tableau, mais réciproquement nous pouvons injecter un tableau (préalablement mémorisé) dans la propriété .entirePath d’un objet Path et, comme par magie, le tracé ressurgit à l’identique (forme, dimension, emplacement), recréant en tâche de fond tous les points d’ancrages nécessaires.
Seule difficulté, il faut gérer à la main la combinaison des tracés au sein d’un polygone composé, c’est-à-dire recréer la collection .paths du polygone item par item. La méthode TDigFont::scanFontToClipboard() enregistre, pour chaque chiffre, la collection des propriétés .entirePath du polygone sous-jacent, ce qui produit un méga-tableau pléthorique de dimension 5 : chiffre(s) › tracé(s) › point(s) › ancrage(s) › coordonnée(s). Les trois dernières dimensions sont gérées par .entirePath(6).
(6) Rappelons qu’un point (PathPoint) peut, selon son type, posséder jusqu’à trois « ancrages » (.anchor, .leftDirection et .rightDirection). Les points de type PointType.corner n’utilisent toutefois que la propriété .anchor, auquel cas .entirePath répercute les coordonnées du point dans un tableau [x,y], et non pas dans un méta-tableau [ [x,y], null, null ].Inversement, la méthode privée TDigFont::_digToShape(int, Page) — appelée publiquement par TDigFont::createDig() — reconstitue graphiquement un chiffre en s’appuyant sur le méga-tableau codé en dur pour la police choisie :
TDigFont.prototype._digToShape = function(dig, page)
//--------------------------------------------------------------------
{
var digPaths = this._pathFont[dig]; // recupere les traces du chiffre dig
var sz = digPaths.length; // combien de traces a composer?
var shape = page.polygons.add(); // creation d'un polygone vierge
shape.paths[0].entirePath = digPaths[0]; // application du 1er trace
// dans certains cas (forme simple), le travail est fini
for ( var i=1; i < sz; i++ ) // s'il y a d'autres traces a combiner...
{
// ...on les ajoute a la collection en prenant soin de les fermer
shape.paths.add({pathType:PathType.closedPath,entirePath:digPaths[i]});
}
return(shape);
}
Reste bien sûr, pour TDigFont::createDig(), à positionner et dimensionner le chiffre sorti d’usine. Le client (TDigsShape, afficheur du bloc-chiffres) se charge de fournir tous les paramètres nécessaires.
Grâce à cette technique, les chiffres sont positionnés avec une extrême précision. Ils ne dépendent pas d’aléas typographiques (corps, graisse, approche...) que l’on aurait eu grand-peine à domestiquer en passant par des polices de caractères réelles. Au final, le code-barres généré par « EanDesign » respecte exactement les cotes prescrites par TMetrics.
Les instructions d’installation de « EanDesign » sont données en entête dans le script lui-même, EanDesign.js, inclus dans ce fichier zip téléchargeable. Comme d’habitude, le script commute automatiquement entre les langues française et anglaise selon les paramètres locaux d’InDesign.
L’utilisation la plus naturelle de ce script consiste à créer et sélectionner dans votre document un rectangle qui fera office de gabarit. Une fois « EanDesign » lancé, vous pouvez choisir de conserver ou de supprimer le gabarit. La zone de saisie « EAN-13 / ISBN-13 » propose un schéma de code par défaut : 978-2-XXXXX-YYY-K. Il est possible de spécialiser davantage ce schéma (par exemple si vous travaillez toujours avec le même éditeur) en modifiant la variable DEF_EAN13 du script.
Saisissez ou copiez le code à traiter (EAN-13 ou ISBN-13). Les options affichées par défaut dans le dialogue sont généralement optimales. Toutefois, vous pouvez modifier les dimensions et/ou préciser qu’il s’agit des dimensions internes de la grille (c’est-à-dire sans compter le bloc-chiffres). Le paramètre « Hauteur des barres », si vous l’abaissez sous la valeur normale de 100 %, accentue le décrochage des barres par rapport aux lignes de garde.
La liste « Style de chiffres » offre le choix de la police (virtuelle !) à appliquer au bloc-chiffres. Vous pouvez également moduler l’espacement des chiffres : à 0 %, les chiffres occuperont l’espace vacant de façon maximale, tandis qu’à 100 % ils seront très nettement espacés. Ordinairement, toute réduction de taille est homothétique et laisse donc la forme du caractère fidèle à la police d’origine. Cependant, si vous cochez la case « Autoriser l’étirement vertical », alors toute la hauteur disponible pour les chiffres sera utilisée indépendamment de leur espacement horizontal. Quelques illustrations :

Pour rééditer un code-barres, il suffit de le sélectionner puis de relancer le script. Vous pouvez ainsi tester aisément d’autres attributs de mise en forme ou changer le code lui-même.
Ce programme est en libre usage, mais n’oubliez pas d’indiquer la source et l’auteur si vous le mentionnez ou le réexploitez. Merci de signaler tout bug éventuel à l’adresse du rédacteur en chef.
(Oct. 2009) — This script will be updated soon @ Indiscripts.com