Retrouvez cet article dans : Linux Magazine 77
Installation
Il nous faut tout d’abord télécharger Tclkit pour Linux (http://www.equi4.com/pub/tk/8.4.11/tclkit-linux-x86.gz). Il s’agit d’un unique fichier binaire contenant un interpréteur TCL/Tk qui s’avère très pratique. Lancé seul, il offre une console semblable à wish, utile pour des démonstrations ou développements rapides (on lui préfèrera toutefois Tkcon, la Tk Advanced Console pour une utilisation au quotidien). Une fois téléchargé, il nous faut le décompresser, renommer, rendre exécutable et copier quelque part où l’on puisse le trouver (gzip -d tclkit-*.gz mv tclkit-* tclkit chmod +x tclkit mv tclkit /usr/bin/On peut ensuite, pour des raisons pratiques, télécharger SDX, le Starkit Developer eXtension. (http://www.equi4.com/pub/sk/sdx.kit) Ce dernier nous permettra de packager et dépackager les starkits. Il nous reste ensuite à télécharger
$ tclkit critcl.kit
To compile and run a tcl script
critcl [-force] [-keep] file[.tcl]
To compile and build a shared library or package
critcl [-force] [-keep] [-lib|-pkg] name [infiles...]
-force force compilation (ignores cache)
-keep keep intermediate C files
-lib generate $name.so
-pkg generate a package structure under lib/$name
all options may be abbreviated to the first character
Cet affichage vous sera vraisemblablement présenté dans une fenêtre Tk à part, sauf si vous n’utilisez pas de serveur X à ce moment-là.
$ tclkit sdx.kit unwrap critcl.kit 48 updates appliedOn va maintenant ajouter tout ce qui est fourni par Critcl à notre environnement TCL. On commence par unwrapper le tout. On obtient un répertoire
lappend auto_path ./critcl.vfs/libEn travaillant avec Tclkit, on peut s’affranchir de cette étape en " sourçant " le kit, comme on le verra par la suite, mais il est important de savoir que cette solution existe.
Test
Il convient de vérifier le bon déroulement de l’installation. Pour cela, on lance Tclsh, Tclkit ou Tkcon et au prompt de l’interpréteur, on tape les lignes suivantes (éventuellement après s’être placé dans le bon répertoire)% lappend auto_path ./critcl.vfs/lib /usr/bin/tclkit/lib/tcl8.4 /usr/bin/tclkit/lib ./critcl.vfs/lib % package require critcl 0.34Si vous obtenez un résultat semblable, l’installation s’est déroulée correctement.
Utilisation
Dans les exemples suivants, je partirai du principe que la modification d’Une somme
On va commencer par créer une procédure pour faire des sommes, quelque chose de simple pour commencer. Créons donc un scriptpackage require critcl
::critcl::cproc somme {int n1 int n2} int {
return (n1 + n2);
}
On rencontre ici% source somme.tcl % set neuf [somme 4 5] 9 % somme wrong # args: should be "somme n1 n2" % somme A 4 expected integer but got "A"Les messages d’erreur que l’on peut observer ci-dessus sont générés par TCL et Critcl automatiquement, à partir de la définition de votre
Critcl et GD
Maintenant que nous avons vu une utilisation basique de Critcl, on va s’intéresser à un exemple plus utile. On va créer une procédure permettant de générer une image contenant un texte écrit dans une taille donnée, avec une police donnée. Et pour atteindre ce but, on va utiliser la librairieNotre GD
Il faut commencer par vérifier que nous avons bien tous les éléments nécessaires de la chaîne de compilation (configure, gcc, make) et les headers des bibliothèques nécessaires. A titre d’information, sur une Debian, voici les paquets nécessaires (hors compilateurs et accessoires) :libjpeg62-devlibpng12-devlibx11-devlibxpm-devlibfontconfig1-devlibfreetype6-devzlib1g-dev
$ tar xvfz gd-2.0.33.tar.gz
$ cd gd-2.0.33
$ ./configure --prefix=${HOME}/myprefix --without-x
...
Support for PNG library: yes
Support for JPEG library: yes
Support for Freetype 2.x library: yes
Support for Fontconfig library: yes
Support for Xpm library: no
Support for pthreads: yes
$ make
$ make install
Le export CPATH=${HOME}/myprefix/include/
Retour à Critcl
On doit commencer par charger le packageexport CPATH=${HOME}/myprefix/include/
Ensuite, on doit ajouter des options au linker pour que ce dernier puisse trouver les librairies nécessaires. On utilise pour cela la comme package require critcl ...Il faut également signaler au préprocesseur où aller chercher les fichiers d’en-tête. On utilise à cette fin la commande
::critcl::cheaders {-I${HOME}/myprefix/include}
...
Enfin, on utilise la commande::critcl::ccode {
#include <stdlib.h>
#include <stdio.h>
#include <gd.h>
}
...
On peut ensuite attaquer le code de génération de l’image lui-même. On va créer une procédure nommée ::critcl::cproc drawTextToFile {int fontsize char* font char* s char* pngname} int {
gdImagePtr im;
FILE *pngout;
int bgColor;
int fgColor;
int brect[8];
int w, h;
char *err;
...
Détaillons un peu :
imservira à stocker les données de notre image. Le typegdImagePtrnous permet de faire abstraction du type d’image généré à la fin de nos aventures.pngoutest un pointeur sur type représentant un flux correspondant à un fichier, lequel récoltera la merveilleuse image que nous allons générer.bgColoretfgColorseront les index de nos couleurs dans l’image finale. Je parle ici d’index mais l’image peut malgré tout ne pas utiliser de palette indexée.brectest un tableau qui nous servira pour le positionnement de notre texte et pour déterminer la taille de l’image.wethseront utilisées pour représenter la largeur et la hauteur de notre image.errreçoit un éventuel message d’erreur en cas de problème. Nous nous contenterons ici dans un premier temps de vérifier que le pointeur reste nul.
unsigned long bgR, bgG, bgB, fgR, fgG, fgB; bgR = 0xff; bgG = 0xff; bgB = 0xff; fgR = 0; fgG = 0; fgB = 0; ...On signale à
gdFTUseFontConfig(1); ...On supprime les retours à la ligne en fin de chaîne.
while (s[strlen(s)-1]==’\n’ || s[strlen(s)-1]==’\r’) s[strlen(s)-1]=’\0’; ...Enfin, on détermine la taille d’image nécessaire pour notre texte.
err = gdImageStringFT(NULL,&brect[0],0,font,fontsize,0.,0,0,s);
if (err) {
return TCL_ERROR;
}
Les arguments pris par- Une variable de type
gdImagePtr. Ici, on veut juste trouver la taille alors, on utiliseNULL. - Un tableau de 8 entiers. Après l’appel de la fonction, on y trouve, rangés par index :

- La couleur à utiliser pour dessiner le texte. Ici, on s’en fiche puisque le texte ne sera pas effectivement dessiné. En revanche lors de l’utilisation d’une vraie couleur, on peut mettre un - devant pour supprimer l’anti-aliasing.
- La police à utiliser. Celle fournie par celui qui utilise la fonction.
- La taille de police à utiliser.
- La rotation à appliquer au texte
- La coordonnée en X où dessiner le texte
- La coordonnée en Y où dessiner le texte
- Le texte à dessiner (enfin)
Si cette instruction se passe mal, on renvoie TCL_ERROR à l’interpréteur pour faire savoir qu’il y a eu un problème.
On détermine la largeur et la hauteur nécessaire à notre texte.
w = brect[2]-brect[6]; h = brect[3]-brect[7]; ...On crée l’image, on alloue les couleurs nécessaires, puis on dessine le texte, " pour de vrai ".
im = gdImageCreate(w,h);
bgColor = gdImageColorResolve(im, bgR, bgG, bgB);
fgColor = gdImageColorResolve(im, fgR, fgG, fgB);
err = gdImageStringFT(im,&brect[0],fgColor,font,fontsize,0.,-brect[6],-brect[7],s);
if (err) {
return TCL_ERROR;
}
...
On finit.
pngout = fopen(pngname, “w”); gdImagePng(im, pngout); fclose(pngout); gdImageDestroy(im); return TCL_OK; }
- Il faut d’abord ouvrir un fichier qui contiendra l’image (
fopen). Pour bien faire, il faudrait vérifier que ça a marché en vérifiant la valeur de pngout ... - Une fois ouvert, on envoie le contenu de l’image dedans (
gdImagePng), notez que ce n’est qu’ici qu’on choisit le format utilisé. - Le fichier ne sert plus à rien, on peut le fermer (
fclose). - Notre image ne sert plus non plus, on la détruit (
gdImageDestroy). - On fait savoir à l’interpréteur que tout s’est bien passé en renvoyant
TCL_OK.
$ tclkit % source critcl.kit % source drawText.tcl % drawTextToFile 48 Mono:Bold "test" mono.png 0 %Notez que dans Tclkit, on peut directement " sourcer " un .kit. La valeur 0 correspond à

Mon extension drawText
Tout cela fonctionne, mais n’est pas vraiment pratique. On va donc générer une extension drawText. Tout a été pensé dans ce sens et ce n’est donc qu’une formalité... Il suffit d’ajouter une ligne en tête du fichierpackage provide drawText 1.0Puis d’invoquer la commande suivante :
$ tclkit critcl.kit -pkg drawText Source: drawText.tcl Library: drawText.dylib Package: /Users/zazou/Devel/Tcl-Tk/critcl/lib/drawText
% package require drawText 1.0 % drawTextToFile 24 Futura:italic “Bonjour le monde !” futura.png 0

Tâchons de faire propre
Le code vu précédemment fonctionne, mais il reste un problème critique en termes de performances. On passe par un fichier pour charger le png obtenu dans une image Tk. Dans le but de supprimer les temps d’accès au disque, on va dans cet exemple retourner directement les données de l’image à l’interpréteur TCL. Dans la foulée, je vous montrerai comment renvoyer des messages à l’utilisateur. En effet TCL_OK et TCL_ERROR sont des valeurs de retour qui ne s’utilisent quand dans les " vraies " commandes TCL.
Expliquons donc le code de la nouvelle procédure drawTextToObj.
::critcl::cproc drawTextToObj {Tcl_Interp* interp int fontsize char* font char* s} void {
...
On trouve en premier paramètre un pointeur vers l’interpréteur TCL courant. Ce dernier nous est nécessaire pour afficher les messages d’erreur et renvoyer l’objet contenant les données de l’image à qui de droit. Il n’est cependant pas nécessaire lors de l’appel de la commande d’en tenir compte, ce pointeur nous sera automatiquement transmis par TCL. Le premier argument de la commande sera donc toujours gdImagePtr im; int size; char* pngPtr; int bgColor; int fgColor; int brect[8]; int w, h; char *err; unsigned long bgR, bgG, bgB, fgR, fgG, fgB; Tcl_Obj* objResult; ...On trouve ci-dessus la déclaration d’un
bgR = 0xff;
bgG = 0xff;
bgB = 0xff;
fgR = 0;
fgG = 0;
fgB = 0;
gdFTUseFontConfig(1);
while (s[strlen(s)-1]==‘\n‘ || s[strlen(s)-1]==‘\r‘) s[strlen(s)-1]=‘\0‘;
err = gdImageStringFT(NULL,&brect[0],0,font,fontsize,0.,0,0,s);
if (err) {
Tcl_SetResult (interp, err, TCL_VOLATILE);
return;
}
...
Ici, en cas d’erreur, on récupère le message généré par - Un pointeur sur l’interpréteur TCL concerné ;
- Une chaîne de caractères ;
- Une constante informant TCL du type de chaîne transmise. On trouve
TCL_STATICpour les chaînes statiques (On le rencontrera plus bas),TCL_VOLATILE, comme ici, pour informer TCL qu’il devrait se presser de " copier " le contenu de la variable avant qu’elle ne soit modifiée etTCL_DYNAMICpour spécifier qu’il s’agit d’un buffer de mémoire allouée et qu’il devra se charger de le libérer lorsqu’il n’en aura plus besoin.
w = brect[2]-brect[6];
h = brect[3]-brect[7];
im = gdImageCreate(w,h);
bgColor = gdImageColorResolve(im, bgR, bgG, bgB);
fgColor = gdImageColorResolve(im, fgR, fgG, fgB);
err = gdImageStringFT(im,&brect[0],fgColor,font,fontsize,0.,-brect[6],-brect[7],s);
if (err) {
Tcl_SetResult (interp, err, TCL_VOLATILE);
return;
}
...
On retrouve ci-dessus un message d’erreur de type pngPtr = gdImagePngPtr(im, &size); gdImageDestroy(im); ...Arrivé au terme du " traitement d’image ", au lieu de l’envoyer dans un fichier avec
objResult = Tcl_NewByteArrayObj(pngPtr, size); ...Fort des renseignements acquis, on peut maintenant créer un objet de type tableau d’octets suffisamment grand pour contenir les données de l’image. On passe l’adresse des données à y copier en premier paramètre de la fonction
if (!objResult) {
Tcl_SetResult (interp, "Erreur lors de la création de l’objet image.", TCL_STATIC);
return;
}
...
On vérifie que la création de l’objet s’est bien passée. Dans le cas contraire, on fait parvenir un message à l’interpréteur. Le message étant ici statique, on utilise la fonction Tcl_SetObjResult(interp, objResult); ...On déclare que l’objet
gdFree(pngPtr); }Enfin, on libère le buffer utilisé pour stocker l’image... Le code TCL pour utiliser la nouvelle procédure est donné ci-dessous. Notez que vous devez disposer du package
% package require Tk 8.4 % package require img::png 1.3 % package require drawText 1.0 % set data [drawTextToObj 24 Futura:italic "Test Obj"] ‰PNG ... % image create photo -format png -data $data image1 % pack [label .l -image image1]On voit en retour de la procédure

Utilisation avancée
::critcl::tk
Cette commande informe Critcl que nous aurons également besoin de la partie Tk pour nos fonctions, les headers concernant Tk sont alors ajoutés aux fichiers générés et on peut alors travailler avec des images, des widgets, etc.
::critcl::csources
Cette commande permet de spécifier à Critcl qu’il devra compiler des sources C supplémentaires pour construire l’extension. Ceci permet par exemple de faire son développement de fonctions de façon classique. Le fichier C étant ensuite ajouté au projet TCL, une fois mis au point. Comme spécifié plus loin, les modifications de ces sources ne sont pas prises en compte par Critcl pour déterminer s’il doit ou non reconstruire la librairie. On doit donc, le cas échéant, le forcer à recompiler. A titre d’exemple, on peut reprendre notre fonction somme et l’écrire dans un fichier somme.c.
int csomme (int a, int b) {
return a+b;
}
package require critcl
::critcl::csources somme.c
::critcl::cproc somme {int a int b} int {
return csomme (a, b);
}
On utilise ensuite la procédure somme de la même façon.
% source critcl.kit % source somme.tcl % somme 4 5 9
::critcl::tsources
Cette commande permet d’ajouter des sources .tcl nécessaires à nos extensions. Les fichiers sont ajoutés dans les extensions, dans un sous-répertoire TCL.::critcl::config keepsrc 1
Critcl utilise un sous-répertoire .critcl de votre répertoire utilisateur. Il y génère des fichiers sources à partir desquels il crée une librairie dynamique, plus tard chargée par TCL. Cette commande spécifie à Critcl de conserver les sources intermédiaires. Ce qui peut être utile pour du débogage ou si vous ne voulez utiliser Critcl que pour vous générer un squelette d’extension TCL.::critcl::config force 1
On peut grâce à cette commande forcer Critcl à recompiler la librairie dynamique à chaque chargement. Cela permet de passer outre le fait que Critcl ne détecte pas les changements survenus dans les fichiers liés par la commande::critcl::ccommand
critcl::ccommand procname {dummy interp objc objv} {
...
}
C’est une méthode plus puissante pour créer de nouvelles commandes. On doit spécifier le nom de la commande à créer et le nom des variables transmises.
package require critcl
::critcl::csources somme.c
::critcl::ccommand somme {dummy interp objc objv} {
int a, b, s;
Tcl_Obj* o;
if (objc <= 2) {
Tcl_WrongNumArgs(interp, 1, objv, "a b");
return TCL_ERROR;
}
Tcl_GetIntFromObj(interp, objv[1], &a);
Tcl_GetIntFromObj(interp, objv[2], &b);
o = Tcl_NewIntObj (csomme (a, b));
Tcl_SetObjResult (interp, o);
return TCL_OK;
}
C’est plus long, certes, mais on pourrait ainsi très facilement ajouter d’autres nombres à notre somme.
Pour ce qui est des nouveautés :
Tcl_WrongNumArgspermet de renvoyer un message d’erreur. Le premier paramètre est l’interpréteur, le deuxième est le nombre d’objets du troisième paramètre devant être écrits avant la chaîne contenue dans le quatrième paramètre.Tcl_GetIntFromObjpermet de créer unintau sens C à partir d’un " objet " TCL.Tcl_NewIntObjautorise le contraire, à savoir créer un objet TCL à partir d’unintC.
Un cas concret
Si cette introduction à Critcl vous a donné envie d’en savoir plus, vous pouvez consulter les sources d’une extension basée sur Critcl. La société La Rochelle Innovation, qui héberge le wiki TCL francophone a récemment publié une extension permettant d’appliquer divers traitements à une image nommée- GD : http://www.boutell.com/gd/
- Fontconfig : http://www.fontconfig.org/
- Critcl : http://www.equi4.com/critcl.html
- Critcl manual : http://wiki.tcl.tk/11814 http://www.tcl.tk/man/tcl8.4/
- Wiki TCL : http://wiki.tcl.tk/
- Wiki TCL francophone : http://wfr.tcl.tk/
- Tclkit : http://www.equi4.com/tclkit.html
- SDX : http://www.equi4.com/sdx.html
- Le package Img : http://sourceforge.net/projects/tkimg/
- Tkcon, un outil indispensable : http://tkcon.sourceforge.net/
- LRIPhoto : http://wfr.tcl.tk/1004





Donnez votre avis
Vous devez avoir ouvert une session pour écrire un commentaire.