Polices Freetype et gd-2.0

Voici le deuxième article concernant gd. Il traite de l'utilisation de Freetype pour inclure du texte dans vos images et des nouveautés apportées à gd par la version 2.

Introduction

L'inclusion de textes dans les images est quelque chose de très utile, pour ne pas dire indispensable et cette facette de gd méritait bien un article à elle toute seule. Je parlerai donc ici de l'utilisation de la librairie freetype-2.x pour la génération d'images avec gd-2.0.1, en langage c. En effet, à l'heure où j'écris ces lignes la librairie gd-2 n'est pas encore stable mais bon, elle le sera bientôt et les seules nouveautés d'ici là ne seront que corrections d'éventuels bugs. Pour ce qui est du choix de ne traiter que la librairie freetype-2, il vient du fait que la version 2 de gd fait pareil. Les anciennes fonction liées à freetype-1 (celles qui se terminent par TTF) ne font que renvoyer vers celles liées à freetype-2 (celles qui se terminent par FT).
A l'issue de cette première partie de l'article, vous pourrez utilisez vos polices de windows à partir de programmes c sous linux, ainsi que toutes les autres polices truetype, freetype, ... Voir http://www.freetype.org pour plus d'infos...
La deuxième partie de cet article examinera les nouveautés de gd dans sa deuxième version.

Préparatifs

Compilation et installation de freetype-2

Téléchargez la dernière version de Freetype-2. Lors de l'criture de l'article, il s'agissait de la version 2.0.3.
Voici comment on l'installe...
Placez vous dans le répertoire dans lequel vous avez téléchargé freetype puis tapez :

tar xvfy freetype-2.0.3.tar.bz2
cd freetype-2.0.3
make setup
make
su
make install
Cela installe freetype sous /usr/local. Vous pouvez ensuite redevenir "simple utilisateur".

Compilation et installation de gd

Pour avoir plus d'info sur l'installation de gd, consultez le premier article sur gd, sur léa.

Placez vous dans le répertoire dans lequel vous avez téléchargé gd puis tapez

tar xvfz gd-2.0.1.tar.gz
cd gd-2.0.1
Editez le début du Makefile comme suit :
COMPILER=gcc
AR=ar
CFLAGS=-g -DHAVE_LIBPNG -DHAVE_LIBJPEG -DHAVE_LIBFREETYPE
LIBS=-lgd -lpng -lz -ljpeg -lfreetype -lm
INCLUDEDIRS=-I. -I/usr/local/include/freetype2
LIBDIRS=
INSTALL_LIB=/usr/local/lib
INSTALL_INCLUDE=/usr/local/include
INSTALL_BIN=/usr/local/bin
Puis tapez:
su
make install
Il ne s'agit pas d'une erreur de ma part, il faut bien taper directement make install, c'est comme ça !
Si vous obtenez une erreur de unresolved symbol (sur gdImageCreateFromPng par exemple) :

Utilisation

Pour les notions de base concernant gd, reportez vous à l'article précédent, celui-ci constitue une suite.

Exemple1

Dans cet exemple nous allons recréer l'image simple du premier exercice du premier article mais ajouterons dans la croix la mention santé écrite en Comics, par exemple, et ce, en jaune.
Il faut :

La declaration de la fonction permettant d'écrire du texte en utilisant FreeType est la suivante :
char *gdImageStringFT(gdImagePtr im,
                            int *brect,
                            int color,
                            char *fontname,
                            double ptsize,
                            double angle,
                            int x, int y,
                            char *chaine)
où :
Le code :
/*
 * Fichier expl_gd2_1.c
 *
 * Pour les explications de base,
 * se reporter à l'article sur gd.
 */
#include <stdlib.h>
#include <gd.h>

// Deux macros qui simplifient la vie
#define brect_largeur (brect[4]-brect[0])
#define brect_hauteur (brect[1]-brect[5])

int main(void) {
  gdImagePtr image;
  FILE *image_png;
  char *err;
  int rouge, blanc, jaune, noir;
  char *chaine = "santé"; // La chaîne à écrire
  char *font = "/usr/share/ttf/comic.ttf"; // La police
  double taille = 20; // La taille de la police
  int brect[8]; // Les coordonnées du rectangle entourant le texte entier.
  /* brect[0] X bas gauche
   * brect[1] Y bas gauche
   * brect[2] X bas droit
   * brect[3] Y bas droit
   * brect[4] X haut droit
   * brect[5] Y haut droit
   * brect[6] X haut gauche
   * brect[7] Y haut gauche
   */

  image = gdImageCreate(100, 100);
  blanc = gdImageColorAllocate(image, 255, 255, 255);
  rouge = gdImageColorAllocate(image, 255, 0, 0);
  jaune = gdImageColorAllocate(image, 255, 255, 0);
  noir = gdImageColorAllocate(image, 0, 0, 0);

  gdImageFilledRectangle(image, 20, 40, 80, 60, rouge);
  gdImageFilledRectangle(image, 40, 20, 60, 80, rouge);

  /* Les nouveautés commencent ici */
  /* On a droit à 60x20 pour placer notre chaîne
   * soit brect[4]-brect[0]<60 ET brect[1]-brect[5]<20
   * --> Pour récupérer brect sans écrire le texte, on place im à NULL
   * Si on a un dépassement on réduit la taille de la police et on recommence
   */
  do {
    err = gdImageStringFT (NULL, brect,
                           jaune, font,
                           taille--,
                           0,
                           0,
                           0,
                           chaine);
    if (err) fprintf(stderr, "%s\n", err);
    fprintf (stderr, "Essai taille : %.0f\n", taille+1);
    fprintf (stderr,
             "* bas gauche ( %d, %d ), haut droite ( %d, %d )\n",
             brect[0], brect[1],
             brect[4], brect[5]);
    fprintf (stderr,
             "* largeur x hauteur : %dx%d\n",
             brect_largeur,
             brect_hauteur);
  } while ( ( brect_hauteur >= 20 ) || ( brect_largeur >= 60 ) );

  /* A décommenter pour voir le brect
  gdImageRectangle (image,
                    50-brect_largeur/2,
                    50-brect_hauteur/2,
                    50+brect_largeur/2,
                    50+brect_hauteur/2,
                    noir);
  */

  /* Une fois ici on a la bonne taille moins un.
   * Le milieu de l'image la moitié de la largeur nous donne le x gauche.
   * Le milieu de l'image + la moitié de la hauteur nous donne le y bas.
   * On retracnche la moitié des brect[0] et brect[1]
   * car on a vu qu'il ne valaient pas nécessairement 0 d'où un décalage
   */
  err = gdImageStringFT (image,
                         brect,
                         // mettre -jaune pour supprimer l'anti-aliasing
                         jaune,
                         font,
                         ++taille,
                         0,
                         50-(brect_largeur-brect[0])/2,
                         50+(brect_hauteur-brect[1])/2,
                         chaine);
  if (err) fprintf(stderr, "%s\n", err);

  /* Les nouveautés s'arrêtent ici */

  image_png = fopen("expl1.png", "w");
  gdImagePng(image, image_png);
  fclose(image_png);

  gdImageDestroy(image);

  exit (0);
}
On compile en tapant
gcc -o expl_gd2_1 expl_gd2_1.c -lgd -ljpeg -lpng -lfreetype
Après exécution par ./expl_gd2_1, vous obtenez dans le répertoire courant une image expl1.png, qui sera une des 4 présentées ci-dessous selon les modifications que vous aurez apporté au code :
Image normale Sans anti-aliasing (-jaune) Avec dessin du brect Avec brect / Sans anti-aliasing

Remarque : Dans une application réelle, une fois connue la bonne taille de police, vous enleveriez la boucle permettant de la trouver, vous ne traceriez pas le brect, etc ... Je l'ai fait ici dans un but pédagogique.

Exemple2 : Selon un angle ?

Nous allons maintenant écrire, selon un angle. Pas de gros changements ... On en profitera toutefois au passage pour faire connaissance avec le type gdPoint, utilisé pour tracer des polygones de façon aisée. Il nous servira pour tracer le brect de ce texte "en pente", grâce à la fonction gdImagePolygon.
On note qu'un type gdPoint a deux champs, x et y. C'est pas plus compliqué que ça !
Le code :

/*
 * Fichier expl_gd2_2.c
 *
 * Pour les explications de base,
 * se reporter à l'article sur gd.
 */
#include <stdlib.h>
#include <stdio.h>
#include <gd.h>
#include <math.h>

// Une macro pour transformer des degrés en radians
// NB: M_PI est définie dans math.h
#define en_radians(ndeg) (M_PI*(ndeg)/180)

// Deux macros qui simplifient la vie
#define brect_largeur (brect[4]-brect[0])
#define brect_hauteur (brect[1]-brect[5])

int main(void) {
  gdImagePtr image;
  FILE *image_png;
  char *err;
  int rouge, blanc, jaune, noir;
  char *chaine = "santé"; // La chaîne à écrire
  char *font = "/home/xavier/Docs/contribs_lea/c4/comic.ttf";
  double taille = 20; // La taille de la police
  int brect[8]; // Les coordonnées du rectangle entourant le texte entier.
  gdPoint brect_points[4]; // Le tableau de points pour tracer le polygone

  image = gdImageCreate(100, 100);
  blanc = gdImageColorAllocate(image, 255, 255, 255);
  rouge = gdImageColorAllocate(image, 255, 0, 0);
  jaune = gdImageColorAllocate(image, 255, 255, 0);
  noir = gdImageColorAllocate(image, 0, 0, 0);

  gdImageFilledRectangle(image, 20, 40, 80, 60, rouge);
  gdImageFilledRectangle(image, 40, 20, 60, 80, rouge);

  err = gdImageStringFT (NULL, brect,
                         jaune, font,
                         taille,
                         en_radians(45),
                         0,
                         0,
                         chaine);
  /* on stocke les points dans le tableau de gdPoints */
  brect_points[0].x = brect[0]+50-(brect_largeur-brect[0])/2;
  brect_points[0].y = brect[1]+50+(brect_hauteur-brect[1])/2;
  brect_points[1].x = brect[2]+50-(brect_largeur-brect[0])/2;
  brect_points[1].y = brect[3]+50+(brect_hauteur-brect[1])/2;
  brect_points[2].x = brect[4]+50-(brect_largeur-brect[0])/2;
  brect_points[2].y = brect[5]+50+(brect_hauteur-brect[1])/2;
  brect_points[3].x = brect[6]+50-(brect_largeur-brect[0])/2;
  brect_points[3].y = brect[7]+50+(brect_hauteur-brect[1])/2;

  /* On trace le polygone */
  gdImagePolygon (image, brect_points, 4, noir);

  /* On ajoute la chaîne de caractères comme avant, mais avec un angle */
  err = gdImageStringFT (image,
                         brect,
                         jaune,
                         font,
                         taille,
                         en_radians(45),
                         50-(brect_largeur-brect[0])/2,
                         50+(brect_hauteur-brect[1])/2,
                         chaine);
  if (err) fprintf(stderr, "%s\n", err);

  image_png = fopen("expl2.png", "w");
  gdImagePng(image, image_png);
  fclose(image_png);

  gdImageDestroy(image);

  exit (0);
}
On compile en tapant
gcc -o expl_gd2_2 expl_gd2_2.c -lgd -ljpeg -lpng -lfreetype
Après exécution par ./expl_gd2_2, vous obtenez dans le répertoire courant une image expl2.png.

Quoi de neuf dans gd-2 ?

Ben oui !!! Vous devez bien vous le demander ! Alors voici ce que dit (en résumé) la section "what's new ?" de la page officielle pour la version 2.0.1 par rapport à la 1.8.3 du précédent article :

Plus des améliorations et corrections que je vous conseille de lire directement sur la page, http://www.boutell.com/gd/
Notez que ce lien vous emmène pour l'instant sur la page de la version stable, qui, à l'heure où ces lignes sont écrites, est encore la 1.8.4. Il y a toutefois un (gros) lien vers la page des versions 2.0.x.

Exemple3 : Transparence

/*
 * Fichier expl_gd2_3.c
 *
 * Pour les explications de base,
 * se reporter à l'article sur gd.
 */
#include <stdlib.h>
#include <gd.h>

int main(void) {
  FILE *image_png;
  gdImagePtr image, image_d_avant;
  int orange, bleu;

  // On crée une image TrueColor
  image = gdImageCreateTrueColor(100, 100);
  // On alloue une couleur
  orange = gdTrueColor (255, 128, 0);
  // On trace un rectangleorange
  gdImageFilledRectangle(image, 0, 0, 100, 100, orange);

  // On ouvre l'image du premier exercice et on en crée une image
  image_png = fopen("expl1.png", "r");
  image_d_avant = gdImageCreateFromPng (image_png);
  fclose (image_png);

  // on copie avec remise à l'échelle de 100x100 pixels en partant de (0,0) de l'ancienne image
  // on place le résultat de taille 90x90 à (5,5) dans la nouvelle
  gdImageCopyResampled(image, image_d_avant, 5, 5, 0, 0, 90, 90, 100, 100);
  // on détruit l'ancienne image en mémoire.
  gdImageDestroy (image_d_avant);

  // On alloue une couleur bleue semi transparente
  bleu = gdTrueColorAlpha(0, 0, 255, gdAlphaTransparent / 2);
  // on se met en mode écrasement
  // La où on dessinera il y aura du bleu semi-transparent, c'est tout
  gdImageAlphaBlending(image, 0);
  // ontrca eun rectangle
  gdImageFilledRectangle(image, 10, 10, 90, 30, bleu);

  // On se met en mode "mélange"
  // Ce qu'on dessine, se mélange avec ce qu'il y a dessous
  // 50% de bleu et 50% du dessous
  gdImageAlphaBlending(image, 1);
  gdImageFilledRectangle(image, 40, 10, 90, 90, bleu);

  image_png = fopen("expl3.png", "w");
  gdImagePng(image, image_png);
  fclose(image_png);

  gdImageDestroy(image);

  exit (0);
}
On compile en tapant
gcc -o expl_gd2_3 expl_gd2_3.c -lgd -ljpeg -lpng -lfreetype
On obtient après exécution, dans gimp :

Notez :

C'est tout pour cette fois !
N'hésitez pas à envoyer vos commentaires par mail en cliquant sur mon nom en haut de la page.
Retrouvez prochainement l'article remis en forme, les sources, archives et binaires sur mon site perso..., http://www.xgarreau.org/.

a+

Auteur : Xavier GARREAU
Modifié le 14.09.2004