Aghja, le paradigme

Architecture XML

L’outil Aghja est composé de XML sous forme XML ou XSL ou SVG, et de scripts shell. Les scripts shell exécutent par des commandes Saxonb les transformations XSL sur les sources XML et SVG pour produire des scripts FontForge.

Outillage et sources

En synthèse…

  • Vous devez installer le Saxon-B XSLT Processor et FontForge pour utiliser l’outil Aghja.
  • Téléchargez et extrayez le tar . Vous disposez,
    • dans le répertoire principal livré nommé Aghja.V2 :
      • des scripts shell à exécuter pour générer votre fonte,
      • d’autres fichiers que vous ne devriez pas avoir à modifier ;
    • dans le sous-répertoire source :
      • du fichier font.xml contenant les informations générales de votre fonte,
      • du fichier glyphs.xml contenant les définitions des caractères, hors leurs dessins,
      • des fichiers suffixés .ghj.svg contenant les dessins de vos glyphes.

Illustration : le script genfont.sh

#!/bin/sh
# Settings
SOURCE="source"
TARGET="target"
FONT="Aghja"

# 1 - Initialize the font

# 1.0 - Initializes or cleans directories
[ -d temp ] && rm -r temp
mkdir temp
[ -d $TARGET ] || mkdir $TARGET
[ -f ${TARGET}/$FONT.sfd ] && rm ${TARGET}/$FONT.sfd

[ -f ${SOURCE}/stroke.css ] && cp ${SOURCE}/stroke.css temp/ || cp stroke.css temp/
[ -f ${SOURCE}/font.xml ] && cp ${SOURCE}/font.xml temp/ || cp font.xml temp/
[ -f ${SOURCE}/glyphs.xml ] && cp ${SOURCE}/glyphs.xml temp/ || cp glyphs.xml temp/

# 1.1 - Creates the font and initializes the variants :
#       (temp/font.xml, temp/glyphs.xml) -[genFont.xsl]-> target/$FONT.Source.sfd
saxonb-xslt -xsl:genFont.xsl -it:'template-genFont' -o:temp/genFont.pe && {
	chmod a+x temp/genFont.pe
	./temp/genFont.pe $TARGET
}

exit

(juste pour la compréhension de l’architecture : les scripts ne sont pas destinés à être modifiés !)

Glyphes au format SVG

L’outil Aghja est écrit en XML et le format XML de dessin vectoriel est le SVG, que FontForge sait exporter et importer. D’ailleurs dans la pratique FontForge est mon éditeur SVG préféré. Naturellement, les sources des glyphes pour l’outil Aghja sont au format SVG.

Précisément le nom des fichiers sources des glyphes doivent être de la forme (glyphname).ghj.svg

Source du caractère arobase : at.ghj.svg

<?xml version="1.0" standalone="no"?>
<?xml-stylesheet type="text/css" href="../stroke.css" charset="utf-8"?>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="-600 0 1000 1000">
 <g transform="matrix(1 0 0 -1 0 600)">
  <path class="base"
	d="M 280,180
	   c 0,0 -17,-160 -100,-160
	   s -79,140 0,140
	   c 70,0 70,-100 140,-100
	   s 70,200 -100,200 
	   c -256,0 -254,-432 100,-293" />
 </g>
</svg>
Arobase

Dessin d'un caractère

L’outil Aghja prendra de ce source le ou les éléments path qui dessinent la ou les lignes du caractère. Remarquez l’attribut class=base. Les éléments path qui n’ont pas d’attributs class signifiants sont ignorés.

J’utilise FontForge pour éditer le dessin :

Edition du caractère arobase

Glyphes dessinés sans épaisseur

L’outil Aghja part de lignes courbes que FontForge épaissira pour en faire des traits. La fonction FontForge utilisée est ExpandStroke. Elle s’apparente à l’attribut SVG stroke-width ce qui permet de prévisualiser ainsi les glyphes.

La syntaxe SVG représentant l'action de FontForge est :

<svg xmlns="http://www.w3.org/2000/svg" height="1000" stroke-width="40" stroke-linecap="round"></svg>.

Le cadratin imposé par le format otf est 1000. La police maquette fait le choix d’un trait de 40 ce qui correspond à une graisse book. Cette épaisseur de trait est paramétrable. Par contre les extrémités arrondies sont imposées car elles structurent les connexions des lettres.

Dessin de la lettre a
Lettre a générée

Note de design

Dans vos dessins SVG vous devez tenir compte de ce que vos lignes seront épaissies et relever l’ordonnée de vos points d’une demi graisse.

Lettres connectées

Aghja connecte les lettres. Cette opération est réalisée par un dispositif des fontes opentype : calt ; les alternatives contextuelles si vous préférez.

Lorsque deux lettres sont l’une à coté de l’autre leurs glyphes isolés sont remplacés par des glyphes alternatifs qui se chevauchent.

Isolated letters
Connected letters

Plusieurs glyphes représentent ainsi une même lettre : autant que de contexte de connexion. En jouant sur l'attribut class l'ensemble des variantes sera cependant consolidé dans un seul source SVG.

Vous remarquerez dans l’exemple de ce mécanisme que le j n’a pas de point. Cela reflète que dans le modèle Aghja les alternatives contextuelles s’appliquent aux lettres sans diacritiques. Le cas échéant, les accents, les points, les cédilles, etc. se rajoutent de par d’autres dispositifs : les décompositions de caractères et les marques de lettres de base.

Connexions à la lettre suivante

C’est quand on a fini ou presque d’écrire une lettre que l’on se pose la question de comment la connecter à la suivante. Par exemple les lettres dont le trait fini dans un empattement proche de la ligne de base se prolongent d'un simple rebond alors que celles qui forment une boucle descendante se connectent dans une transformation de cette boucle.

L’outil Aghja permet de définir jusque huit types de connexions numérotés de 1 à 8. La fonte Aghja n’en utilise que sept. Nous y reviendrons mais sachez déjà que l’attribut correspondant dans la description du glyphe est after.

Une lettre impose sa façon de se connecter à sa suivante.

Le glyphe d'une lettre connectée s’arrête au point d’inflexion de son trait par rapport à celui de la lettre isolée. Il se prolonge dans un recouvrement au tracé modifié du glyphe de la lettre suivante. Les parties claires des dessins ci-contre appartiennent à une lettre suivante virtuelle.

Connections to subsequent letters

Lettres connectées par leurs précédentes

Les lettres dans les mots voient leurs traits modifiés par les connexions de leurs précédentes. Ces variations sont décrites dans les fichiers (lettre).ghj.svg par des éléments path ayant pour attribut deb(1 à 8).

Si l’on récapitule, la lettre peut ne pas être précédée ou l’être par l’un des huit types de connexions possibles, soit neuf cas à multiplier par deux selon que la lettre est suivie ou pas : dix-huit variantes ! L’outil Aghja les génére dans le répertoire temporaire temp puis les place dans la fonte en private area et aussi génère les règles des alternatives contextuelles (calt) afin de les substituer à bon escient. Merci qui ?

Les fichiers .ghj.svg contiennent le dessin des glyphes. Les autres caractéristiques des caractères sont regroupées dans le fichier glyphs.xml. Par exemple c'est là que l’on trouve l’attribut after qui défini pour les lettres leur type de connexion.

Note de design

Les variantes d’une lettre ont toutes la même chasse. Bien réfléchi ce n’est pas une contrainte créative. Techniquement cela implique que pour le glyphe qui suit les points de recouvrements sont à des abscisses négatives. Par ailleurs, pour les moteurs de rendu, vous ne devez pas faire de recouvrement sur la ligne de chasse.

Connections to previous letters

Fichier font.xml

Les informations générales de la fonte sont dans ce fichier.

Placez votre version du fichier dans le répertoire source. Vous pouvez supprimer celle du répertoire principal.

name
Nom de la fonte/police/famille. L'outil ne gère qu’un nom.
version
Nombres séparés par des points (#version.#sous-version)
copyright
Je vous recommande d’indiquer à minima Copyright (année) (votre nom).
weight
Indicatif: Light, Book, Medium, Bold, etc.
thickness
Epaisseur du trait.
width
Largeur de l’espace.
xheight
Hauteur d’x (avec le trait épaissi).
ascent
Ascendante : devrait s’approcher de 500 + xheight/2.
linegap
Valeur minimale : (hauteur de la plus haute majuscule) + (hauteur de la plus haute diacritique top) - xheight - 1000 .

Source du fichier font.xml

<root>
	<name>TODO</name>
	<version>0.0</version>
	<copyright>Copyright (c)</copyright>
	<weight>Book</weight>
	<thickness>40</thickness>
	<width>200</width>
	<xheight>200</xheight>
	<ascent>600</ascent>
	<linegap>40</linegap>
</root>

Fichiers .ghj.svg

Ces fichiers contiennent les dessins SVG des glyphes. Dans le cas des lettres un seul fichier consolide les variantes.

L’attribut class des éléments path est requis.

class="base"
Caractère isolé ou partie de lettre commune à toutes ses variantes.
class="fin0"
Fin isolée de lettre.
class="finx"
Fin connectée de lettre.
class="deb0"
Début isolé de lettre.
class="deb(1 à 8)"
Débuts connectés de lettre
Dessin de la lettre a

Source de la lettre a : a.ghj.svg

<?xml version="1.0" standalone="no"?>
<?xml-stylesheet type="text/css" href="./stroke.css" charset="utf-8"?>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="-600 0 1000 1000">
 <g transform="matrix(1 0 0 -1 0 600)">
  <path class="base"
	d="M 140,180
	   c -126,0 -126,-160 -40,-160
	   c 63,0 95,50 95,100"/>
  <path class="base"
	d="M 200,180
	   c -11,-70 -5,-160 40,-160"/>
  <!-- fin modèle after="1" -->
  <path class="fin0"
        d="M 240,20
	   c 6,0 14,1 20,2"/>
  <!-- deb modèle (a, d, g, q) + deb3 et deb5 du o -->
  <path class="deb0"
	d="M 190,151
	   c -10,17 -27,29 -50,29"/>
  <path class="deb1"
	d="M -40,20
	   c 267,0 267,160 180,160"/>
  <path class="deb2"
	d="M -100,20
	   c 326,0 326,160 240,160"/>
  <path class="deb3"
	d="M -100,20
	   c -63,0 -63,80 60,80
	   c 310,0 224,80 180,80"/>
  <path class="deb5"
	d="M -40,180
	   c 0,-40 80,-70 140,-70
	   c 100,0 100,70 40,70"/>
  <path class="deb6"
        d="M -100,180
	   c 53,0 100,-70 200,-70
	   s 100,70 40,70"/>
  <path class="deb7"
	d="M -80,-180
	   c 0,180 275,140 275,300
	   c 0,33 -25,60 -55,60"/>
  <path class="deb8"
	d="M -160,-180
	   c -28,0 -50,22 -50,50
	   c 0,129 405,95 405,250
	   c 0,33 -25,60 -55,60"/>
 </g>
</svg>

Note technique

Vous devrez ajuster manuellement deux valeurs pour que vos glyphes SVG soient parfaitement représentatifs de ceux de la fonte.

  1. Dans les fichiers .ghj.svg modifiez la dernière valeur du paramètre matrix à la valeur de l’ascendante de la fonte.
  2. Dans le fichier stroke.css du répertoire source, modifiez le paramètre stroke-width à la valeur de la graisse de votre fonte.

Fichier glyphs.xml

Placez votre version du fichier dans le répertoire source. Vous pouvez supprimer celle du répertoire principal.

Comme son nom ne l’indique pas ce fichier contient les informations sur les caractères en dehors du dessin de leurs glyphes. En plus de son root ce fichier ne contient que des éléments glyph. Le caractère space (U0020) en est exclu. Vous voudrez sans doute compléter cette liste mais je vous déconseille d’en supprimer des entrées existantes.

L’élément glyph

Trois attributs sont requis pour l’élément glyph.

unicode="(valeur héxadécimale)"
Indique l’emplacement du glyphe. La valeur est imposée par l’unicode.
name="(glyphname)"
Indique le nom du glyphe. Si celui-ci est rattaché à un dessin celui-ci devra s’appeller (glypname).ghj.svg. La valeur correspond à un standard de fait.
class="(base ou mark ou accented)"
Les autres attributs dépendent de la valeur de l'attribut class.
class="base"
Défini un caractère ordinaire.
width="(nombre)" (requis dans ce cas)
La largeur est unique pour le glyphe de base et ses variantes.
anchors="true"
Indique une lettre qui peut être accentuée. Cet attribut est nécessaire aux lettres qui composent des variantes accentuées (de class=accented).
after="(1 à 8)"
Indique une lettre qui se connecte à une suivante et son type de connexion.
before="true"
Indique une lettre qui peut être connectée par sa précédente. Les attributs after et before sont indépendants l’un de l’autre. Les majuscules devraient avoir un attribut after mais pas d’attributs before. Ce peut être l’inverse pour des lettres minuscules que l’on a du mal à connecter après ; peut-être le x par exemple.
g(1 à 8)(0 ou x)="(valeur héxadécimale)"
Indiquent l’emplacement des variantes en private area.
class="mark"
Défini une diacritique.
width="0"
La largueur d'une diacritique devrait toujours être zéro.
position="(top ou bottom)"
Cette attribut est requis pour une diacritique. En l’état l’outil ne gère que deux positions : au-dessus et au-dessous de la lettre. Il ne connais pas non plus les mark to mark (les accents combinés).
class="accented"
Défini un caractère accentué composé d’un caractère ordinaire (espace ou lettre) et d’une diacritique. Il n’est ainsi pas directement associé à fichier ghj.svg.
base=(glyphname)
Le caractère de base.
mark=(glyphname)
La diacritique.

Extraits du fichier glyphs.xml

<root>
[...]
	<glyph unicode="0226" class="accented" name="Adotaccent" base="A" mark="dotaccentcomb" />
	<glyph unicode="0227" class="accented" name="adotaccent" base="a" mark="dotaccentcomb" />
	<glyph unicode="0228" class="accented" name="Ecedilla" base="E" mark="cedillacomb" />
	<glyph unicode="0229" class="accented" name="ecedilla" base="e" mark="cedillacomb" />
	<glyph unicode="022E" class="accented" name="Odotaccent" base="O" mark="dotaccentcomb" />
	<glyph unicode="022F" class="accented" name="odotaccent" base="o" mark="dotaccentcomb" />
	<glyph unicode="0232" class="accented" name="Ymacron" base="Y" mark="macroncomb" />
	<glyph unicode="0233" class="accented" name="ymacron" base="y" mark="macroncomb" />
	<glyph unicode="0237" class="base" name="dotlessj" after="8" before="true" anchors="true"
width="240" g00="E5A0" g0x="E5A1" g10="E5A2" g1x="E5A3" g20="E5A4" g2x="E5A5" g30="E5A6" g3x="E5A7"
g40="E5A8" g4x="E5A9" g50="E5AA" g5x="E5AB" g60="E5AC" g6x="E5AD" g70="E5AE" g7x="E5AF" g80="E5B0"
g8x="E5B1" />
	<glyph unicode="02D8" class="accented" name="breve" base="space" mark="brevecomb" />
	<glyph unicode="02D9" class="accented" name="dotaccent" base="space" mark="dotaccentcomb" />
	<glyph unicode="02DA" class="accented" name="ring" base="space" mark="ringcomb" />
	<glyph unicode="02DC" class="accented" name="tilde" base="space" mark="tildecomb" />
	<glyph unicode="0300" class="mark" name="gravecomb" position="top" width="0" />
	<glyph unicode="0301" class="mark" name="acutecomb" position="top" width="0" />
	<glyph unicode="0302" class="mark" name="circumflexcomb" position="top" width="0" />
	<glyph unicode="0303" class="mark" name="tildecomb" position="top" width="0" />
	<glyph unicode="0304" class="mark" name="macroncomb" position="top" width="0" />
	<glyph unicode="0306" class="mark" name="brevecomb" position="top" width="0" />
	<glyph unicode="0307" class="mark" name="dotaccentcomb" position="top" width="0" />
	<glyph unicode="0308" class="mark" name="dieresiscomb" position="top" width="0" />
	<glyph unicode="0309" class="mark" name="hookabovecomb" position="top" width="0" />
	<glyph unicode="030A" class="mark" name="ringcomb" position="top" width="0" />
	<glyph unicode="030B" class="mark" name="hungarumlautcomb" position="top" width="0" />
	<glyph unicode="030C" class="mark" name="caroncomb" position="top" width="0" />
	<glyph unicode="030F" class="mark" name="dblgravecomb" position="top" width="0" />
	<glyph unicode="0311" class="mark" name="invertedbrevecomb" position="top" width="0" />
	<glyph unicode="0312" class="mark" name="cedillaabovecomb" position="top" width="0" />
	<glyph unicode="0317" class="mark" name="acutebelowcomb" position="bottom" width="0" />
	<glyph unicode="0323" class="mark" name="dotbelowcomb" position="bottom" width="0" />
	<glyph unicode="0324" class="mark" name="dieresisbelowcomb" position="bottom" width="0" />
	<glyph unicode="0325" class="mark" name="ringbelowcomb" position="bottom" width="0" />
	<glyph unicode="0326" class="mark" name="commabelowcomb" position="bottom" width="0" />
	<glyph unicode="0327" class="mark" name="cedillacomb" position="bottom" width="0" />
[...]
</root>

Les scripts

L’outil Aghja est scindé en plusieurs étapes représentées par des scripts qui doivent être exécutés dans l’ordre. Cette manière de faire facilite la maintenance et permet d’agir manuellement au sortir de chaque script. Deux sources FontForge sont successivement créés dans ce processus :

  1. Le source Source (fontname)Source.sfd, dans lequel les glyphes sont encore sans épaisseur,
  2. Le source (fontname).sfd, après l’expandstroke fait par gensfd.sh.

Les répertoires

genfont.sh est le premier script à éxcécuter. Outre le répertoire source, il créé deux autres répertoires.

temp
Répertoire de travail.
target
Répertoire cible pour les fontes aux formats sfd et otf.

Ordinogramme de l’outil Aghja

Ordinogramme

Script genfont.sh

genfont.sh créé les répertoires de travail et cible et initialise (fontname)Source.sfd.

Script gencalt.sh

La place naturelle de l’action de ce script serait intégrée à genfont.sh. Malheureusement George Williams n’a pas écrit la fonction batch pour modifier le tables calt : FontForge est le travail d’un homme seul... A défaut, gencalt.sh produit dans le répertoire temp le fichier genCalt.txt. Son contenu est à copier/coller par l’interface graphique de FontForge. Cette étape fastidieuse peut-être réalisée dès que le source FontForge est créé ou plus tard. Dans tous les cas elle mérite alors un archivage manuel !

Copie de temp/genCalt.txt dans les dialogues FontForge

Cette opération fastidieuse prend environ 30 minutes.

  1. Ouvrez avec un éditeur de texte temp/genCalt.txt.
  2. Ouvrez avec FontForge target/(fontname)Source.sfd.
  3. Du menu de FontForge, sélectionnez Element/Font Info.
  4. Dans le dialogue des Infos Fonte, sélectionnez Lookups.
  5. Dans l’onglet GSUB déroulez calt_pass1 et sélectionnez calt_pass1_
  6. Appuyez sur Edit Data pour ouvrir le dialogue des substitutions contextuelles.
  7. Passez la fenêtre suivante d’un habile next.
  8. Commencer par copier les Match Classes (Classes de motif en VF).
  9. Continuez avec les Matching rules based on a list classes.
  10. Appuyez sur OK et enregistrez la fonte au passage.
  11. Recommencez la même opération avec calt_pass2.
Edit Chaining Substitution cover
Lookups dialog Edit Chaining Substitution cover

(note technique)

Un ticket est ouvert auprès des développeurs du neo-FontForge à ce sujet.

Script genglyphs.sh

genglyph.sh peuple la fonte des glyphes issus des sources (glyphname).ghj.svg.

Les glyphes sont d’abord générés en SVG dans le répertoire temp puis importés dans la fonte. Dans le cas des lettres connectées, avec des attributs g(b)(a), le source (glyphname).ghj.svg créera plusieurs variantes en private area avec un nom de la forme glyphname.g(b)(a), le (b) valant de 0 à 8 pour la connexion de la lettre précédente, ou pas, et le (a) valant x pour une variante de lettre connectée à sa suivante, 0 sinon.

On peut rééxécuter ce script tant que l’on a pas modifié glyphs.xml. Si l’on y a changé un attribut width c’est encore possible en le répercutant manuellement dans (fontname)Source.sfd.

(notes techniques)

Pour contourner une régression du neo‑FontForge j’ai réécrit l’outil en remplaçant la création d’une fonte de travail SVG par le répertoire temp. Ce n’est pas forcément une mauvaise chose car le support des fontes SVG est abandonné de partout. Je crois cependant qu’un format XML de fonte est un besoin et j’espère toujours une évolution en ce sens. L’outil en serait alors simplifié.

Pour contourner un bug de FontForge dans la fonction expandStroke le script genglyphs.sh passe la fonte en layers quadratic avant les import ce qui a pour effet d’ajouter des points de contrôles. En cas de transposition à un autre éditeur on devra retirer cette bidouille.

Script gensfd.sh

gensfd.sh applique aux glyphes les fonctions ExpandStroke puis RemoveOverlap, puis AddExtrema et créé le source FontForge opérationnel (fontname).sfd.

Ces trois algorithmes très complexes ne fonctionnent pas toujours correctement. Cela explique les balbutiements dans le script car, de mes tâtonnements, ils corrigent la plupart des mauvais cas, que l’on utilise FontForge (GW) ou le neo‑FontForge. Si pourtant des erreurs apparaissent encore il faut les analyser. Selon les cas on les ignorera ou on les corrigera en modifiant le dessin des glyphes à problèmes.

Script genanchors.sh

genanchors.sh place les ancres de diacritiques sur les glyphes ayant l’attribut anchors=’true’. L’outil ne gère que les deux positions de base : top et bottom.

Les corrections a posteriori sont bien sûr possibles.

Script genotf.sh

genotf.sh créé la fonte OpenType.

Vous pouvez modifier la première partie de ce script pour y placer des fonctions FontForge venant compléter les automatisations de l’outil.

Script genoblique.sh

genoblique.sh est un exemple de génération d’une variante de style de la police.

  1. Copier le répertoire (fontname) en (fontname)-Oblique.
  2. Renommez votre (fontname)Source.sfd en (fontname)-ObliqueSource.sfd.
  3. Répercutez ce changement dans les fichiers font.xml.
  4. Répercutez ce changement dans les Font Info/Names de FontForge.
  5. Repartez au gensfd.sh.

Vous risquez de devoir faire encore quelques petits ajustements avec FontForge.