/*
    Fichier gentexte.c
    Auteur Bernard Chardonneau

    Logiciel libre, droits d'utilisation précisés en français
    dans le fichier : licence-fr.txt

    Traductions des droits d'utilisation dans les fichiers :
    licence-en.txt , licence-es.txt , licence-pt.txt ,
    licence-eo.txt , licence-eo-utf.txt

    Droits d'utilisation également sur la page web :
    http://cyloop.tuxfamily.org/voir.php?page=droits


    Bibliothèque de fonctions destinées à rajouter du texte
    dans les images générées par cylgraph.
*/


#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "types.h"
#include "messages.h"
#include "image.h"
#include "gentexte.h"


// variables globales à cette bibliothèque

// tables UTF-8 en mémoire

// une table par valeur du 1er caractère entre C0 et DF
static octet *table_utf [32];

// un fichier par valeur du 1er caractère entre E0 et FF
static FILE *fic_utf [32];

// indique quelles valeurs des 2 tables précédentes ont été initialisées
static int  init_utf [64] =
            { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
              0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
              0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
              0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };

// autres variables globales

// descripteur de fichier pour les fontes chargées en mémoire
FILE   *ficfonte;

// mémorise si on a chargé la bonne fonte iso-8858-n
octet  fonte_base_OK = 0;

// jeu de caractères ASCII étendu (caractères sur un octet)
// les caractères de controle ne seront pas affichés
extern octet fonte [224][hauteur_fonte];



/* indique si le caractère passé en paramètre est un caractère hexadécimal */

int carhexa (char caract)
{
    if ('0' <= caract && caract <= '9')
        return 1;

    caract = caract | 0x20;

    return ('a' <= caract && caract <= 'f');
}



/* retourne la valeur du caractère hexadécimal passé en paramètre */

int valhexa (char caract)
{
    if ('0' <= caract && caract <= '9')
        return (caract & 0x0F);
    else
        return (caract & 0x0F) + 9;
}



/*
   saute les caractères blancs ou tabulations
   éventuels à partir de la position courante

   retourne :
   - la valeur du code hexadécimal qui suit, si c'en est un
   - -1 si les caractères qui suivent n'expriment pas un code hexadécimal
   - -2 si fin de fichier
*/


int lecvalhexa ()
{
    int caract;  // caractère du fichier fonte
    int valeur;  // valeur hexadécimale correspondante


    // lire un caractère
    caract = getc (ficfonte);

    // terminé si fin de fichier
    if (caract == EOF)
        return -2;

    // tant qu'on est sur un blanc ou une tabulation
    while (caract == ' ' || caract == '\t')
        // lire le caractère suivant
        caract = getc (ficfonte);

    // si on est tombé sur un caractère hexadécimal
    if (carhexa (caract))
    {
        // récupérer la valeur correspondant à
        // 2 caractères hexadécimaux successifs
        valeur = valhexa (caract);

        caract = getc (ficfonte);

        if (carhexa (caract))
            valeur = (valeur << 4) | valhexa (caract);
        else
            // -1 si on n'avait pas 2 caractères hexadécimaux successifs
            valeur = -1;
    }
    // sinon
    else
    {
        // on n'a pas trouvé de valeur hexadécimale
        valeur = -1;

        // sauter le reste de la ligne
        while (caract != '\n' && caract != EOF)
            caract = getc (ficfonte);
    }

    // retourner la valeur récupérée ou le code d'erreur
    return (valeur);
}



/* retourne le nom du répertoire contenant les fichiers fonte */

char *dirfonte ()
{
    static char *dirf = NULL;  // static pour ne faire la recherche qu'une fois


    // si ce répertoire n'a pas encore été cherché
    if (! dirf)
    {
        // si les commandes de cyloop sont implantées dans /usr/bin
        if (strcmp (dircom (), "/usr/bin") == 0)
        {
            // les fontes sont dans /usr/share/cyloop/fontes
            dirf = malloc (25);
            strcpy (dirf, "/usr/share/cyloop/fontes");
        }
        // sinon, les fontes sont dans les répertoire fontes situé au
        // même niveau que le répertoire bin des commandes exécutables
        else
        {
            // calcul de la taille de mémoir nécessaire et allocation
            dirf = malloc (strlen (dircom ()) + 3);

            // copie du répertoire d'implantation des commandes de cyloop
            strcpy (dirf, dircom ());

            // on remplace bin par fontes dans la chaine précédente
            strcpy (dirf + strlen (dirf) - 3, "fontes");
        }
    }

    // retourner le résultat
    return (dirf);
}



/*
   extrait dans le fichier fonte UTF-8 identifié par carbase
   la représentation graphique de caractère correspondant à
   la séquence suiteseq et mémorise le résulat dans dessincar
*/


void recupfonte (octet carbase, octet *suiteseq, octet *dessincar)
{
    int   pospremier; // position du premier caractère dans suiteseq
    octet car2;       // 2ème caractère de la séquence UTF-8
    octet car3;       // 3ème caractère de la séquence UTF-8
    int   numcar;     // compteur
    int   val;        // valeur codée en hexadécimal dans le fichier
    int   i, j;       // compteurs



    printf ("Appel de recupfonte (%02X ", carbase);

    // position dans le tableau suiteseq du 2ème caractère de la séquence
    // (l'ordre des caractères est inversé dans suiteseq)
    if (carbase < 0xF0)
        pospremier = 1;
    else
        pospremier = 2;

    if (pospremier == 1)
        printf ("%02X %02X)\n", suiteseq [1], suiteseq [0]);
    else
        printf ("%02X %02X %02X)\n", suiteseq [2], suiteseq [1], suiteseq [0]);

    // initialisation dessincar à blanc
    for (i = 0; i < hauteur_fonte; i++)
        dessincar [i] = 0;

    // sélectionner le fichier fonte à lire
    ficfonte = fic_utf [carbase & 0x1F];

    // rembobinage du fichier
    rewind (ficfonte);

    // pour éviter d'accéder souvent à un tableau
    car2 = suiteseq [pospremier];
    car3 = suiteseq [pospremier - 1];

    // recherche dans le fichier fonte
    do
    {
        // lire la première valeur hexadécimale de la ligne
        val = lecvalhexa ();

        // si les 2 premières valeurs hexadécimales
        // de la ligne sont celles recherchées
        if ((val == car2) && (lecvalhexa () == car3))
        {
            // ainsi que le 3ème si elle existe
            if ((carbase < 0xF0) || (lecvalhexa () == *suiteseq))
            {
                // récupérer l'image graphique du caractère
                for (i = 0; i < hauteur_fonte; i++)
                    dessincar [i] = lecvalhexa ();

                // recherche du caractère terminée avec succés
                return;
            }
        }

        // passage à la ligne suivante du fichier fonte

        // répéter
        do
        {
            // passer au caractère suivant
            val = getc (ficfonte);

            // si fin de fichier, sortir
            if (val == EOF)
                return;
        }
        // jusque changement de ligne
        while (val != '\n');
    }
    while (i != -2);  // on arrête en fin de fichier fonte
}



/*
   charge en mémoire un fichier fonte

   paramètres : - adresse de la table contenant le dessin des caractères
                - valeur du premier caractère mémorisé dans la table

   le fichier fonte a été ouvert par chargefonte_iso ou chargefonte_utf
   on utilise le descripteur de fichier obtenu lors de cette ouverture
*/


void chargefonte (octet fonte [][hauteur_fonte], octet valdeb)
{
    int  val;   // valeur codée en hexadécimal dans le fichier
    int  i, j;  // compteurs


    printf ("Appel de chargefonte (%08X, %02X)\n", fonte, valdeb);

    // sauter les lignes de commentaires dans le fichier fonte
    do
        i = lecvalhexa ();
    while (i == -1);

    // tant qu'on trouve des lignes commençant par un code hexadécimal
    while (i >= 0)
    {
        // si le code correspond à celui d'un caractère autorisé
        if (i >= valdeb)
        {
            // calcul de la position de ce code dans la table
            i = i - valdeb;

            // décoder et recopier dans la table les valeurs
            // correspondant au dessin du caractère
            j = 0;

            do
            {
                val = lecvalhexa ();

                if (val >= 0 && j < hauteur_fonte)
                    fonte [i][j++] = val;
            }
            while (val >= 0);
        }
        // sinon message d'erreur
        else
            // "Présence du caractère %02X non autorisé dans le fichier fonte"
            aff_err_argnum ("CARFONTE_INTERDIT", i);

        // rechercher une autre ligne avec un code de caractère à traiter
        do
            i = lecvalhexa ();
        while (i == -1);
    }
}



/*
   ouvre un fichier fonte UTF-8 et le charge en mémoire
   s'il concerne des séquences UTF-8 de 2 caractères

   pour les séquences UTF-8 sur 3 ou 4 caractères
   on garde en mémoire le descripteur du fichier

   paramètre : premier caractère de la séquence UTF-8
*/


void chargefonte_utf (octet carbase)
{
    int   postable;    // numéro de l'élément de table_utf chargé en mémoire
    char  *chemfonte;  // chemin d'accés au fichier fonte
    octet *sous_table; // image en mémoire du fichier chargé


    printf ("Appel de chargefonte_utf (%02X)\n", carbase);

    // fabriquer le chemin d'accès au fichier fonte
    chemfonte = malloc (strlen (dirfonte ()) + 9);
    sprintf (chemfonte, "%s/utf8-%02x", dirfonte (), carbase);

    // position dans la table des fontes UTF-8 chargées en mémoire
    // ou dans la liste des descripteurs de fichiers
    postable = carbase & 0x1F;

    // si les séquences UTF-8 concernées par le
    // fichier fonte tiennent sur 2 caractères
    if (carbase < 0xE0)
    {
        // cas général
        if (carbase > 0xC3 || carbase == 0xC0)
        {
            // allouer une zone mémoire pour stocker la sous
            // table UTF-8 mémorisée dans le fichier fonte
            sous_table = malloc (64 * hauteur_fonte);

            // mémoriser l'adresse mémoire dans table_utf
            table_utf [postable] = sous_table;

            // si allocation mémoire réussie
            if (sous_table)
            {
                // initialiser cette zone mémoire
                memset (sous_table, 0, 64 * hauteur_fonte);

                // cas très particulier :
                // le premier caractère de la séquence UTF-8 est C0
                // (ne devrait jamais se produire comme pour le caractère C1
                //  car les caractères concernés peuvent être codés sur un
                //  octet, mais il n'est pas difficile de traiter ce cas)
                if (carbase == 0xC0)
                    // recopier le morceau de la table ASCII
                    // (ou ISO-8859-1) concernant ces caractères
                    memcpy (sous_table + (32 * hauteur_fonte), fonte,
                                                  32 * hauteur_fonte);
            }
            // sinon message d'erreur
            else
            {
                // "Manque de place mémoire"
                // "certains caractères seront remplacés par des blancs\n"
                affiche_err ("MANQUE_MEMOIRE");
                affiche_err ("CAR_RPL_BLANCS");

                // plus la peine de lire le fichier fonte dans ce cas
                return;
            }
        }
        // sinon la sous table UTF-8 correspond à une
        // partie de la table ISO-8859-1 déjà en mémoire
        else
            table_utf [postable] = fonte [(64 * postable) - 32];

        // ouvrir le fichier fonte en lecture
        ficfonte = fopen (chemfonte, "r");

        // si ce fichier a pu être ouvert
        if (ficfonte)
            // charger son contenu en mémoire
            chargefonte ((void *) table_utf [postable], 0x80);

        // sinon message d'erreur si la fonte à récupérer
        // n'est pas une partie de la table ISO-8859-1
        else if (carbase > 0xC3)
        {
            // "Fichier fonte %s manquant\n"
            // "certains caractères seront remplacés par des blancs\n"
            aff_err_arg ("FICFONTE_MANQUANT", chemfonte);
            affiche_err ("CAR_RPL_BLANCS");
        }
    }
    // sinon, les séquences UTF-8 concernées par le
    // fichier fonte tiennent plus de 2 caractères
    else
    {
        // ouvrir le fichier fonte en lecture et mémoriser son descripteur
        fic_utf [postable] = fopen (chemfonte, "r");

        // message d'erreur si ce fichier n'a pas pu être ouvert
        if (!fic_utf [postable])
        {
            // "Fichier fonte %s manquant\n"
            // "certains caractères seront remplacés par des blancs\n"
            aff_err_arg ("FICFONTE_MANQUANT", chemfonte);
            affiche_err ("CAR_RPL_BLANCS");
        }
    }
}



/*
   charge un fichier fonte pour remplacer
   certains caractères du jeu iso-8859-1

   paramètre : nom du fichier fonte à charger en mémoire
*/


void chargefonte_iso (char *nomfonte)
{
    int  numiso;     // 2ème numéro de la fonte ISO sélectionée, -1 si non ISO
    int  debinit;    // numéro de 1er caractère du tableau fonte à initialiser
    char *chemfonte; // chemin d'accés au fichier fonte
    int  i, j;       // compteurs


    printf ("Appel de chargefonte_iso (%s)\n", nomfonte);

    // si fonte iso-8859
    if (memcmp (nomfonte, "iso8859", 7) == 0)
        // extraire le numéro
        numiso = atoi (nomfonte + 8);
    // sinon
    else
        // autre cas
        numiso = -1;

    // déterminer à partir de quelle case de la table
    // iso8859-1 on va réinitialiser les valeurs
    switch (numiso)
    {
        // réinitalisation à partir de la valeur A0h
        case  5 :
        case  6 :
        case 11 : debinit = 128;
                  break;

        // réinitalisation à partir de la valeur C0h
        case  7 :
        case  8 : debinit = 160;
                  break;

        // pas de réinitialisation : écrasement lorsque caractère différent
        default : debinit = 224;
                  break;
    }

    // réinitialiser tout ou partie de la table selon la valeur de debinit
    for (i = debinit; i < 224; i++)
        for (j = 0; j < hauteur_fonte; j++)
            fonte [i][j] = 0;

    // fabriquer le chemin d'accès au fichier fonte
    chemfonte = malloc (strlen (dirfonte ()) + strlen (nomfonte) + 2);
    sprintf (chemfonte, "%s/%s", dirfonte (), nomfonte);

    // ouvrir ce fichier en lecture
    ficfonte = fopen (chemfonte, "r");

    // message d'erreur si fichier non trouvé
    if (! ficfonte)
    {
        // sauf si on utilise le jeu ISO-8859-1
        if (numiso != 1)
        {
            // "Fichier fonte %s manquant\n"
            aff_err_arg ("FICFONTE_MANQUANT", chemfonte);

            // on édite le message d'erreur le mieux adapté

            if (numiso == -1 || debinit == 96)
                // "certains caractères seront remplacés par des blancs\n"
                affiche_err ("CAR_RPL_BLANCS");

            else if (debinit == 224)
            // "certains caractères seront remplacés par ceux du jeu ISO-8859-1"
                affiche_err ("CAR_RPL_ISO-8859-1");
            else
            {
                // "certains caractères seront remplacés par des blancs\n"
                // "d'autres par ceux du jeu ISO-8859-1"
                affiche_err ("CAR_RPL_BLANCS");
                affiche_err ("CAR_RPL_ISO-8859b");
            }
        }

        // fin de la fonction dans ce cas
        return;
    }

    // charger en mémoire le fichier fonte
    chargefonte (fonte, 0x20);

    // on peut libérer le fichier fonte
    fclose (ficfonte);
}



/* Charge une fonte à la place de iso-8859-1 si nécessaire */

void choix_fontebase ()
{
    char *sysfont;       // chemin d'accès au fichier sysfont
    char nomfonte [20];  // nom du fichier fonte mémorisé dans sysfont
    FILE *descsysfont;   // descripteur du fichier sysfont
    int  caract;         // caractère du fichier sysfont
    int  i;              // compteur


    // recherche du fichier sysfont

    // si les commandes de cyloop sont implantées dans /usr/bin
    if (strcmp (dircom (), "/usr/bin") == 0)
    {
        // le fichier sysfont est dans /usr/share/cyloop
        sysfont = malloc (26);
        strcpy (sysfont, "/usr/share/cyloop/sysfont");
    }
    // sinon, le fichier sysfont est dans le
    // répertoire bin des commandes exécutables
    else
    {
        // calcul de la taille de mémoire nécessaire et allocation
        sysfont = malloc (strlen (dircom ()) + 9);

        // copie du répertoire d'implantation des commandes de cyloop
        strcpy (sysfont, dircom ());

        // on rajoute /sysfont à la chaine précédente
        strcpy (sysfont + strlen (sysfont), "/sysfont");
    }

    // si le fichier sysfont existe
    if (access (sysfont, 0) == 0)
    {
        // l'ouvrir en lecture
        descsysfont = fopen (sysfont, "r");

        // si ce fichier est accessible en lecture
        if (descsysfont)
        {
            i = 0;

            // lire le premier caractère de sysfont
            caract = getc (descsysfont);

            // tant que le dernier caractère lu
            // peut faire partie d'un nom de fichier
            while (caract > 0x20 && i < sizeof (nomfonte) - 1)
            {
                // mémoriser ce caractère après l'avoir mis en minuscules
                nomfonte [i++] = caract | 0x20;

                // lire le caractère suivant
                caract = getc (descsysfont);
            }

            // terminer la chaine de caractère contenue dans nomfonte
            nomfonte [i] = '\0';

            // on a fini d'utiliser le fichier sysfont
            fclose (descsysfont);

            // on peut charger la bonne fonte
            chargefonte_iso (nomfonte);
        }
        else
        {
            // "fichier sysfont présent mais protégé en lecture"
            // "le jeu de caractères ISO-8859-1 sera utilisé"
            affiche_err ("SYSFONT_PROTEGE");
            affiche_err ("UTIL_ISO-8859-1");
        }
    }

    // on n'a besoin d'exécuter cette fonction qu'une fois
    fonte_base_OK = 1;
}



/*
   insertion du dessin d'un caractère dans l'image

   paramètres : - adresse du dessin du caractère en mémoire
                - coordonnées du caractère dans l'image
*/


int dessinecar (octet *dessincar, int y, int x)
{
    octet pixligne;   // image du caractère sur la ligne en cours de traitement
    octet masque;     // comme son nom l'indique, masque sur les bits
    octet *posligne;  // adresse d'un pixel dans l'image
    int   i, j;       // compteurs


    // calculer l'adresse du coin haut gauche du caractère dans l'image
    posligne = image + (y * larg_totale) + x;

    // pour toutes les lignes du dessin du caractère
    for (i = 0; i < hauteur_fonte; i++)
    {
        // initialisation masque
        masque = 0x80;

        // extraire une ligne de pixels du dessin du caractère
        pixligne = dessincar [i];

        // pour tous les pixels de la ligne extraite
        for (j = 0; j < largeur_fonte; j++)
        {
            // si ce pixel est sombre
            if (pixligne & masque)
                // colorier ce pixel dans l'image
                *(posligne + j) = coulbase_texte;

            // passage au pixel suivant
            masque = masque >> 1;
        }

        // descendre d'une ligne dans l'image
        posligne = posligne - larg_totale;
    }
}



/*
   Fabrique une chaine de caractère décrivant une séquence UTF-8 incomplète

   Le contenu est exprimé d'abord sous forme hexadécimale
   puis sous formes de caractèes encodés UTF-8
*/


char * affseq (octet carbase, octet * suiteseq, int resteseq)
{
    static char resultat [40]; // chaine de caractères générée
    int  pospremier;           // position du premier caractère dans suiteseq
    int  numcar;               // compteur


    // premier caractère de la séquence sous forme hexadécimale
    sprintf (resultat, "%02X", carbase);

    // si séquence d'au moins 3 caractères
    // (la séquence incomplète en comporte au moins 2)
    if (carbase >= 0xE0)
    {
        // position dans le tableau suiteseq du 2ème caractère de la séquence
        // (l'ordre des caractères est inversé dans suiteseq)
        if (carbase < 0xF0)
            pospremier = 1;
        else
            pospremier = 2;

        // suite de la séquence sous forme hexadécimale
        for (numcar = pospremier; numcar >= resteseq; numcar --)
            sprintf (resultat + strlen (resultat), " %02X", suiteseq [numcar]);
    }

    // premier caractère de la séquence sous forme de caractère encodé UTF-8
    sprintf (resultat + strlen (resultat), " (Ã%c", carbase);

    if (carbase >= 0xE0)
        // suite de la séquence sous forme de caractère encodé UTF-8
        for (numcar = pospremier; numcar >= resteseq; numcar --)
            sprintf (resultat + strlen (resultat), "%Ã%c", suiteseq [numcar]);

    // fin de la chaine générée
    strcat (resultat, ")");

    // renvoyer le résultat à la fonction appelante
    return (resultat);
}



/*
   insère dans l'image le caractère passé en paramètre aux
   coordonnées indiquées

   en cas de besoin, charge en mémoire des jeux de caractères

   cette fonction traite les caractères codés UTF-8 sur plusieurs octets
*/


int affcar (octet caract, int y, int x)
{
    // mémorisation d'une séquence UTF-8 ou fur et à mesure des appels de affcar
    static int   resteseq = 0; // caractères d'une séquence UTF-8 restants
    static octet carbase;      // premier caractère d'une séquence UTF-8
    static octet suiteseq [3]; // autres caractères dans une séquence UTF-8

    // adresse de la représentation graphique d'un groupe de 64 caractères UTF-8
    octet  * sous_table;
    // représentation graphique d'un caractères UTF-8
    octet  dessincar [hauteur_fonte];


    // si caractère ASCII 7 bits ou on utilise un
    // jeu de caractères sur 8 bits déjà en mémoire
    if (caract < 0x80 || fonte_base_OK)
    {
        // message d'erreur si une séquence utf8 était en cours
        if (resteseq)
        {
            // "Séquence UTF-8 codée %s incomplète"
       aff_err_arg ("SEQ_UTF8_TRONQUEE", affseq (carbase, suiteseq, resteseq));

            // on oublie cette séquence
            resteseq = 0;
        }

        // si le caractère n'est pas un caractère de controle
        if (caract >= ' ')
            // insérer le dessin du caractère dans l'image
            dessinecar (fonte [caract - 0x20], y, x);

        // code de retour : un caractère a été rajouté
        return 1;
    }
    // sinon si jeu de caractère UTF-8
    else if (util_utf8 ())
    {
        // si caractère entre C0 et FF
        // (premier caractère d'une séquence UTF-8)
        if (caract >= 0xC0)
        {
            // message d'erreur si une autre séquence utf8 était déjà en cours
            if (resteseq)
                // "Séquence UTF-8 codée %s incomplète"
       aff_err_arg ("SEQ_UTF8_TRONQUEE", affseq (carbase, suiteseq, resteseq));

            // mémoriser le premier caractère de la séquence
            carbase = caract;

            // si on n'a pas encore chargé la table ou ouvert le fichier
            // fonte associé aux séquences commençant par ce caractère
            if (! init_utf [caract & 0x3F])
            {
                // le faire
                chargefonte_utf (carbase);

                // et mémoriser l'opération (qu'elle ait fonctionné ou non)
                init_utf [caract & 0x3F] = 1;
            }

            // calculer le nombre de caractères restant dans la séquence
            if (caract < 0xE0)
                resteseq = 1;
            else if (caract < 0xF0)
                resteseq = 2;
            else
                resteseq = 3;

            // code de retour : aucun caractère n'a été rajouté dans l'image
            return 0;
        }
        // sinon caractère entre 80 et BF
        // (2ème, 3ème ou 4ème caractère d'une séquence UTF-8)
        else
        {
            // si on est dans une séquence de caractères UTF-8
            if (resteseq)
            {
                // mémoriser le caractère
                suiteseq [--resteseq] = caract;

                // si toute la séquence a été mémorisée
                if (! resteseq)
                {
                    // si séquence UTF-8 sur 2 caractères (fonte en mémoire)
                    if (carbase < 0xE0)
                    {
                        // sélectionner la table en mémoire qui
                        // contient le dessin du caractère
                        sous_table = table_utf [carbase & 0x1F];

                        // insérer le dessin du caractère dans l'image
                        dessinecar (sous_table + ((*suiteseq & 0x3F)
                                                    * hauteur_fonte), y, x);
                    }
                    // sinon séquence UTF-8 sur 3 ou 4 caractères
                    else
                    {
                        // récupérer la représentation graphique
                        // du caractère dans le fichier fonte
                        recupfonte (carbase, suiteseq, dessincar);

                        // insérer le dessin du caractère dans l'image
                        dessinecar (dessincar, y, x);
                    }

                    // code de retour : un caractère a été rajouté
                    return 1;
                }
                else
                    // code de retour : aucun caractère rajouté dans l'image
                    return 0;
            }
            // sinon
            else
            {
                // caractère %02X (%c) en dehors d'une séquence UTF-8
                printf (message ("CAR_HORS_SEQ_UTF8"), caract, caract);
                putchar ('\n');

                // code de retour : aucun caractère rajouté dans l'image
                return 0;
            }
        }
    }
    // sinon (jeu de caractères sur 8 bits qui n'est pas encore en mémoire)
    else
    {
        // charger en mémoire le jeu de caractères
        // (pour la partie qui diffère du jeu ISO-8859-1)
        choix_fontebase ();

        // insérer le dessin du caractère dans l'image
        dessinecar (fonte [caract - 0x20], y, x);

        // code de retour : un caractère a été rajouté
        return 1;
    }
}



/*
   insère dans l'image la chaine de caractères passée
   en paramètre à partir des coordonnées indiquées

   vérifie auparavant les coordonnées demandées pour la chaine
*/


void aff_texte (octet *chaine, int y, int x, int alignement)
{
    int   longueur;  // longueur de la chaine de caractères
    octet *c;        // pointeur sur un caractère de la chaine


    // si écriture demandé trop bas ou trop haut dans l'image
    if (y < hauteur_fonte || y > haut_totale)
        // on n'écrira rien
        return;

    // si alignement à droite ou centré
    if (alignement != align_gauche)
    {
        // on commence par calculer la longueur de la chaine

        // si jeu de caractère UTF-8
        if (util_utf8 ())
        {
            // initialisation
            c = chaine;
            longueur = 0;

            // déterminer la longueur de la chaine
            while (*c)
            {
                // compter les caractères ASCII et le premier
                // octet des codes UTF-8 sur plusieurs octets
                if (*c <= 0x7F || *c >= 0xC0)
                    longueur++;

                // avancer d'un caractère dans la chaine
                c++;
            }
        }
        // sinon
        else
            // la longueur de la chaine est plus facile à obtenir
            longueur = strlen (chaine);

        // changement d'unité, la longueur de la chaine est comptée en pixels
        longueur = largeur_fonte * longueur;

        // calculer les coordonnées du caractère de la chaine le plus à gauche
        if (alignement == align_droite)
            x = x - longueur;
        else // alignement centré
            x = x - (longueur / 2);
    }

    // si le début de la chaine est trop à gauche dans l'image
    if (x < 0)
    {
        // on va sauter les premiers caractères pour essayer d'afficher le reste

        // si jeu de caractère UTF-8
        if (util_utf8 ())
        {
            // parcourir la chaine
            while (*chaine && x < 0)
            {
                // en comptant les caractères à afficher
                if (*chaine <= 0x7F || *chaine >= 0xC0)
                    x = x + largeur_fonte;

                chaine ++;
            }

            // sauter la fin des codes UTF-8 sur plusieurs octets
            while (*chaine >= 0x80 && *chaine <= 0xBF)
                chaine ++;
        }
        // sinon (caractères ASCII ou ISO-8859 ou similaire)
        else
        {
            // progression dans la chaine plus simple
            while (*chaine && x < 0)
            {
                x = x + largeur_fonte;
                chaine ++;
            }
        }
    }

    // tant que chaine non recopiée et il reste de la place dans la ligne
    while (*chaine && (x + largeur_fonte) < larg_totale)
    {
        // afficher un caractère
        if (affcar (*chaine, y, x))
            // et comptabiliser le déplacement
            // dans le cas du codage UTF-8, plusieurs octets peuvent
            // être nécessaire pour représenter un caractère
            x = x + largeur_fonte;

        // passer au caractère suivant dans la chaine à recopier
        chaine ++;
    }
}



/*
   insère dans l'image la valeur numérique entière passée
   en paramètre à partir des coordonnées indiquées
*/


void aff_entier (long valeur, int y, int x, int alignement)
{
    char chaine [12];  // chaine contenant la valeur numérique


    // convertir la valeur numérique en chaine de caractères
    sprintf (chaine, "%d", valeur);

    // afficher cette chaine de caractères
    aff_texte (chaine, y, x, alignement);
}



/*
   insère dans l'image la valeur numérique flottante passée
   en paramètre à partir des coordonnées indiquées
*/


void aff_float (float valeur, char *format, int y, int x, int alignement)
{
    char conversion [10]; // format de conversion
    char chaine [20];     // chaine contenant la valeur numérique convertie


    // générer le paramètre format complet pour convertir la valeur numérique
    if (*format == '%')
        strcpy (conversion, format);
    else
    {
        *conversion = '%';
        strcpy (conversion + 1, format);
    }

    // convertir la valeur numérique en chaine de caractères
    sprintf (chaine, conversion, valeur);

    // afficher cette chaine de caractères
    aff_texte (chaine, y, x, alignement);
}