Génération améliorée de fichiers PDF

All contributions published for previous SPIP versions

La génération de fichiers au format PDF à partir de SPIP est une fonctionnalité intéressante, voire bientôt indispensable. Voici une amélioration, et quelques pistes supplémentaires pour la génération de ces fichiers à partir d’un site SPIP.

L’équipe dans laquelle je travaille utilise SPIP tous les jours. SPIP est employé en tant que base de connaissance en intranet, sur laquelle nous publions nos documentations techniques qui permettent le passage de connaissance dans un environnement mouvant.

L’avantage de cette utilisation de SPIP est que nous avons au bout de maintenant plus d’un an des articles qui sont suffisamment génériques pour être utilisables à l’extérieur. Et cette utilisation doit se faire sous une forme présentable, et peu facilement modifiable : le PDF.

Cet article vise deux points : décrire rapidement le fonctionnement de la génération [1]
de PDF depuis SPIP, puis de voir comment cette génération peut être améliorée : ce qui a été fait, et ce qui reste à faire.

Fonctionnement de la génération de PDF

La génération de PDF est basée sur... un squelette SPIP. Le PDF a donc actuellement une base HTML, qui est ensuite passée une classe dérivée de FPDF [2] qui se charge d’analyser le HTML généré par SPIP.

Il est important pour ce qui va suivre de savoir que ce HTML est propre. En effet, l’analyse de HTML s’appuie sur l’ouverture, mais aussi et surtout sur la fermeture des « tags » HTML. Notamment en ce qui concerne :
-  les listes (enfin pas encore, mais c’est comme ça que je le vois);
-  les champs <code> (on parle ici du marqueur SPIP, pas de celui de HTML);
-  les tableaux.

Pour plus de détails, voir les méthodes OpenTag et CloseTag de la classe PDF (fichier lib_pdf_global.php).

Pour ceux qui font de la rétro-conception (en anglais : reverse-engineering), le gros du code est dérivé des exemples de gestion de tableaux de FPDF, comme l’était le code 0.3 de la génération de PDF de Drop Zone City.

Capacités actuelles de l’extension PDF

Les capacités décrites ici s’appliquent à la version 0.4 (pourquoi pas ?) dont les sources sont joints à cet article.

A noter que cette distribution est partielle : il vous faut installer la version de Drop Zone City, puis remplacer les deux fichiers lib_pdf_global.php et lib_pdf_spip.php par ceux livrés ici. De plus, il vous faudra aussi modifier le fichier mes_fonctions.php3 avec le code fournis en annexe, et aussi remplacer le squelette de l’article par la version fournie ici (article_pdf.html).

J’ai donc fait quelques modifications sur la génération de PDF afin de :

-  gérer un peu mieux les balises SPIP &lt;code>, qui deviennent du texte entouré de marqueurs HTML <TT> ;
-  gérer les images dans le texte (ne pas oublier de n’autoriser que les formats PNG et JPEG en images dans un article, en virant de la table qui va bien les lignes qui vont aussi bien : FIXME) ;
-  gérer correctement les tableaux, pour éviter que le contenu ne déborde des cellules, et adapter la largeur des colonnes à leur contenu ;
-  gérer les balises cadre de SPIP de la même façon que les balises code (ce qui n’a pas été sans mal, cf. l’expression régulière dans mes_fonctions.php3).

A noter plusieurs choses :

-  la gestion des images gère trois cas :

  • images de largeur inférieure à 30 : pas de changement d’échelle, ou plutôt ratio fixe, c’est essentiellement bricolé pour avoir la puce SPIP dans le PDF;
  • image de 30 à 600 pixels de large : ratio fixe (pour avoir à peu près ce qu’on avoir sur un écran web, soit 600 pixels de largeur utile);
  • image de taille > à 600 pixels, on taille dans le gras, et on ne fait pas de détails;

-  si une image fait passer une page, on regarde si, à 80% de la taille, elle ne passe pas dans la page, de façon à remplir les pages (c’est un peu bête d’abattre des arbres pour 4 lignes entre deux images, surtout pour une documentation utilisateur);

Pour rappel, les images GIF ne sont pas gérées officiellement, avec le FPDF officiel en date d’aujourd’hui (1.51), et il n’est pas complètement souhaitable que ce le soit.

Cela dit, j’ai modifié FPDF pour utiliser les classes GIF (en PHP pur) de Yamasoft [3], et ça marche, à plusieurs conditions :

  • ne pas être pressé;
  • que votre hébergeur ne le soit pas non plus (temps maximal d’exécution d’un script PHP) ;
  • ne pas être gourmand, ni être limité en mémoire (les images sont construite dans l’espace mémoire de PHP).

Cette modification n’est pas encore publiée officiellement sur le site de FPDF, vous l’avez ici en avant-première.

Toujours pour des limitations de PDF (plutôt que de FPDF), les images PNG avec canal alpha ne sont pas non plus supportées.

Le mieux est donc :

  • d’interdire l’utilisation du GIF (avec phpMyAdmin, ça se fait simplement en détruisant la ligne GIF dans la table spip_types_documents, par un DELETE FROM spip_types_documents WHERE extension='gif' and inclus='image');
  • d’ajouter la ligne suivante pour modifier la puce par défaut dans mes_fonctions.php3 : $GLOBALS['puce'] = "<img src='puce.png' align='top' alt='- ' border='0'>";. Cette image en PNG pourra comporter une couleur transparente, mais pas de canal alpha.

-  Tableaux

Les tableaux sont gérés correctement en terme de largeur, avec un algorithme un peu brut qui consiste à réduire la taille de la police tant que la largeur du tableau dépasse la largeur imprimable de la page. Si vous avez beaucoup de tableaux de largeurs différentes dans votre article, le résultat peut ne pas être très esthétique.

Les entêtes de colonne (si elles sont spécifiées avec | {{ titre }} |) sont affichés en gras sur fond jaune (choix qui n’appartient qu’à moi).

-  Listes

Les listes sont gérées correctement, y compris les listes numérotées, avec indentation. On doit même pouvoir relativement facilement modifier le type de puce en fonction de la profondeur.

-   Entités HTML

Il y avait aussi quelques problèmes avec des entités HTML qui ont maintenant été corrigées, grâce à quelques ajouts dans la fonction de filtrage pdf_first_clean.

-  Cas particulier : marqueur SPIP code

Le marqueur SPIP <code> doit être marqué dans le source SPIP avec les marqueurs HTML : &lt;code&gt; et &lt;/code&gt;.

Ils sont donc passés en HTML avec les caractères qui vont bien, et au passage dans le générateur PDF, sont vus comme des marqueurs HTML code. Ce qui nous permet d’injecter dans le PDF le texte qui va bien pour obtenir le résultat voulu (bon OK, c’est une bidouille infâme, mais ça marche ;-).

-  Autre cas particulier : marqueur SPIP cadre

Alors que SPIP fait tout le travail avec le marqueur <code>, il en va différemment avec le marqueur . En effet, SPIP le transforme en bout de formulaire HTML (<TEXTAREA> pour les intimes), et c’est ensuite le navigateur qui fait tout, et en particulier le traitement des retours chariot. Il va donc nous falloir faire quelques petites choses avant de pouvoir utiliser ce qui se trouve entre les marqueurs <TEXTAREA> pour en faire quelque chose.

Tout d’abord, rappelons que la présence d’un caractère retour chariot ou saut de line dans un fichier HTML n’aide pas à la présentation du contenu, mais uniquement du source : ils sont interprétés comme des espaces. Sauf dans quelques cas : au sein de marqueurs <PRE>, <TEXTAREA>, et quelques autres. Dans ces cas-là, et uniquement ceux-là, les caractères en question font passer à la ligne. Et l’on trouve aux environs de la ligne 68 dans lib_pdf_global.php une ligne qui remplace toutes les occurences de saut de ligne (\n) par une espace.

Il est hors de question dans le filtre SPIP pour le PDF (pdf_first_clean) de remplacer - filtre vu avant la ligne 68 pré-citée - tous les \n par des &ltBR>, cela aurait un effet désastreux. D’où l’expression régulière qui permet de trouver toutes les occurences de marqueurs HTML <TEXTAREA>, et de remplacer entre les balises de début et de fin les \n par ce qu’il faut. Gymnastique un peu tordue, mais la moins pire que j’ai pu trouvé.

Ce qui reste à faire...

En 6 mois de développement erratique, cette liste s’est amoindrie (je n’ai plus l’historique, enfin si, regardez plus haut les améliorations apportées). Il reste peu de choses à mon sens à corriger :

  • un léger décalage à droite sur les élements de liste (<LI> quoi) entre la puce et les premiers caractères de la première ligne, les lignes suivantes du même élément se recadrant un pouillème plus à gauche ;
  • il semble y avoir des problèmes avec les liens internes, mais je n’ai pas envie de fouiller (j’utilise SPIP2PDF pour éviter de donner l’accès à tout mon SPIP);
  • encore un peu de nettoyage à faire dans le code, histoire d’y mettre les commentaires adéquats.

En enfin une suggestion que je devrais passer sur la liste : abandonner le GIF dans SPIP comme format d’image, et le remplacer par le PNG (sans canal alpha, please).

Annexes

-   Contenu de mes_fonctions.php3

function pdf_first_clean($texte) {
        // $texte = ereg_replace("<p class[^>]*>", "<P>", $texte);
        //Translation des codes iso
        // PB avec l'utilisation de <code>
        $trans = get_html_translation_table(HTML_ENTITIES);
        $trans = array_flip($trans);
        $trans["<br />\n"] = "<BR>";        // Pour éviter que le \n ne se tranforme en espace dans les <DIV class=spip_code> (TT, tag SPIP : code)
        $trans["&#339;"] = "oe";
        $trans["&#8230;"] = "...";
        $trans["'"] = "'";
        $trans["-"] = "-";
        $trans["'"] = "'";
        $trans["""] = "\"";
        $trans["""] = "\"";
        $trans["&ucirc;"] = "û";
        $trans['$'] = '\$';
        $trans['->'] = '-»';
        $trans['<-'] = '«-';
        $texte = strtr($texte, $trans);
                  
        // Echappement des "
          $texte = ereg_replace("\"", "\\\"", $texte);
 
        // Traitement des Espaces
         $texte = ereg_replace("(&nbsp;| )+", " ", $texte);

        // Traitement des cadres
        $trans=array("\n" => '<BR>');
        while (preg_match('/(.*<textarea[^>]*>)(.*\n.*)(<\/textarea>.*)/ims', $texte, $textarea)) {
                $rep=strtr($textarea[2], $trans);
                $texte=$textarea[1].$rep.$textarea[3];
        }

         return $texte;
}

INSTALLATION


-  Installer d’abord les fichiers de Drop Zone city ;
-  Puis installer les fichiers présents dans cet article. Ils vont écraser certains fichiers de DropZone City : c’est normal.

updated on 2 October 2019

Discussion

31 discussions

Comment on this article

Who are you?
  • [Log in]

To show your avatar with your message, register it first on gravatar.com (free et painless) and don’t forget to indicate your Email addresse here.

Enter your comment here

This form accepts SPIP shortcuts {{bold}} {italic} -*list [text->url] <quote> <code> and HTML code <q> <del> <ins>. To create paragraphs, just leave empty lines.

Add a document

Follow the comments: RSS 2.0 | Atom