PDF et ligne de commande - Génération d'image pour iDevices


26 août 2013

Notes en vrac

#GHOSTSCRIPT (à intégrer à mon futur post)
#Sources utilisées pour convertir comme il faut un PDF en image
#http://stackoverflow.com/questions/7446552/resizing-a-pdf-using-ghostscript
#http://svn.ghostscript.com/ghostscript/tags/ghostscript-9.01/doc/Devices.htm#PNG
#http://www.ghostscript.com/doc/9.06/Use.htm
#Un iPad : 2048*1536 pixels, 264 pixels par inch, 9.7 inch de diagonale = 7.757*5.818 inches = 558.504 * 418.896 points
#Pour générer une image de 2048 (Z=1) :
#gs -sDEVICE=png16m -dNOPAUSE -dBATCH -dSAFER -sOutputFile=test1024.png -r264 -dDEVICEWIDTHPOINTS=558.504 -dDEVICEHEIGHTPOINTS=418.896 -dPDFFitPage source/67-batifile-CEBI-17-N0-PL-MIS-CVC-01-K.pdf
#Pour générer une image de 4096 (Z=2) :
#gs -sDEVICE=png16m -dNOPAUSE -dBATCH -dSAFER -sOutputFile=test2048.png -r528 -dDEVICEWIDTHPOINTS=558.504 -dDEVICEHEIGHTPOINTS=418.896 -dPDFFitPage source/67-batifile-CEBI-17-N0-PL-MIS-CVC-01-K.pdf
#for i in tiff12nc tiff24nc tiff32nc tiff48nc tiff64nc tiffcrle tiffg3 tiffg32d tiffg4 tiffgray tifflzw tiffpack tiffscaled tiffsep; do echo $i; gs -sDEVICE=$i -dNOPAUSE -dBATCH -dSAFER -sOutputFile=p300-$i.tif -r300x300 67-batifile-CEBI-17-N0-PL-MIS-CVC-01-K.pdf; done

Coran généré sur OSX


14 octobre 2012

Si l'on veut lire un passage du Coran et qu'on a oublié de le prendre avec soi, il suffit d'avoir un Mac à portée de main et d'exécuter le script Bash suivant :

for i in `seq -w 1 114`; do curl -s "http://www.hisnulmuslim.com/coran/index.php?num_sourate=$i" | iconv -f iso-8859-1 -t utf-8 | php -r '$C=str_replace(Array("\n", "\r", "\t", chr(146)), Array("", "", "", "A".chr(39)), file_get_contents("php://stdin")); preg_match("@&lth1><span class=\"[^\"]*\">(.*?)</span>(.*?)</h1>.*?<em>(.*?)</em>.*?<p class=\"texte\">(.*?)</p>@m",$C, $M)."\n"; echo $M[1]."".$M[2]."\n".$M[3]."\n".str_replace("<br /> ", "\n", preg_replace("@<span class=\"num_verset\"[^>]*>([^<]*)</span>(.*?)@", "$1 : $2", $M[4])); echo "\n";' > sourat$i.txt; cupsfilter -o media=A4 -o landscape -o cpi=14 -o lpi=8 -o page-top=50 -o page-bottom=50 -o page-left=50 -o page-right=50 -o wrap sourat$i.txt > sourat$i.pdf 2>/dev/null; done && /System/Library/Automator/Combine\ PDF\ Pages.action/Contents/Resources/join.py -o coran.pdf sourat*pdf && rm -f sourat*

En plus de produire le Coran suivant le texte de la librairie Tawhid (grande qualité de traduction) ce script illustre les points suivants :

  • Comment générer un PDF à partir de texte brut : cupsfilter, tout simplement.
  • Comment combiner/joindre/concaténer des fichiers PDF sur OSX : le script Automator /System/Library/Automator/Combine\ PDF\ Pages.action.
  • On est aussi confortable sur un Mac que sous Linux : la CLI est au même niveau (Bash 3.X) et les commandes disponibles sont les mêmes. C'est un Unix :)

Bien évidemment je remercie hisnulmuslim.com pour la mise en ligne du texte Français.

Sources :

Moralité : il faudra réaliser une version 2 de ce script afin de générer un PDF encore plus avancé.

Génération de PDF


26 août 2007

Applications -> Framework

#include <libxml/parser.h>
#include "pdflib.h"
    //Essai d'utilisation de LibXML2
    xmlDocPtr doc;
    xmlNodePtr cur;
    doc = xmlParseFile("synthereports.xml");
    //doc = xmlParseFile("test.xml");
    xmlErrorPtr LastErr = xmlGetLastError();
    if (doc == NULL ) {
        fprintf(stderr, "Document not parsed successfully. \n");
        return EXIT_FAILURE;
    }
    cur = xmlDocGetRootElement(doc);
    if (cur == NULL) {
        fprintf(stderr,"empty document\n");
        xmlFreeDoc(doc);
        return EXIT_FAILURE;
    }
    if (xmlStrcmp(cur->name, (const xmlChar *) "html")) {
        fprintf(stderr,"document of the wrong type, root node != html");
        xmlFreeDoc(doc);
        return EXIT_FAILURE;
    }
    //Essai d'utilisation de PDFLib
    PDF *p;
    int font;
    // create a new PDFlib object
    if ((p = PDF_new()) == (PDF *) 0)
    {
        printf("Couldn't create PDFlib object (out of memory)!\n");
        return(2);
    }

    PDF_TRY(p) {
        /* This means we must check return values of load_font() etc. */
        PDF_set_parameter(p, "errorpolicy", "return");

        if (PDF_begin_document(p, "hello.pdf", 0, "") == -1) {
            printf("Error: %s\n", PDF_get_errmsg(p));
            return(2);
        }

        /* This line is required to avoid problems on Japanese systems */
        PDF_set_parameter(p, "hypertextencoding", "host");

        PDF_set_info(p, "Creator", "hello.c");
        PDF_set_info(p, "Author", "Thomas Merz");
        PDF_set_info(p, "Title", "Hello, world (C)!");

        PDF_begin_page_ext(p, a4_width, a4_height, "");

        /* Change "host" encoding to "winansi" or whatever you need! */
        font = PDF_load_font(p, "Helvetica-Bold", 0, "host", "");
        if (font == -1) {
            printf("Error: %s\n", PDF_get_errmsg(p));
            PDF_delete(p);
            return(2);
        }
        PDF_setfont(p, font, 24);
        PDF_set_text_pos(p, 50, 700);
        PDF_show(p, "Hello, world!");
        PDF_continue_text(p, "(says C)");
        //
        int font2 = PDF_load_font(p, "Times", 0, "host", "");
        if (font2 == -1) {
            printf("Error: %s\n", PDF_get_errmsg(p));
            PDF_delete(p);
            return(2);
        }
        PDF_setfont(p, font2, 24);
        PDF_set_text_pos(p, 50, 500);
        PDF_show(p, "Suite...");
        PDF_continue_text(p, "(says Flubb)");
        //
        PDF_end_page_ext(p, "");

        PDF_end_document(p, "");
    }

    PDF_CATCH(p) {
        printf("PDFlib exception occurred in hello sample:\n");
        printf("[%d] %s: %s\n",
                PDF_get_errnum(p), PDF_get_apiname(p), PDF_get_errmsg(p));
        PDF_delete(p);
        return(2);
    }

   PDF_delete(p);

Convertisseur HTML vers PDF


8 avril 2007

Convertisseur de fichier HTML en PDF

Objectif : réaliser une combinaison logicielle permettant, quelque soit la page HTML, d'en déduire un fichier PDF le plus fidèle possible. Piste : l'ensemble contiendra une application C++ (aka binaire) rapide qu'on nourrira de la page HTML qui, utilisant PDFLib, générera le fichier PDF. Note : il ne faut pas oublier la gestion des fontes à insérer dans les PDFs produits. Gabarit :

//On récupère le flux d'entrée
    HTMLFile = stdin();
    //On vérifie que l'entrée suit bien les specs XHTML
    if (!(ParsedDatas = parseXML(HTMLFile)))        return EXIT_FAILURE;
    //On crée le document PDF
    Doc = new PDFDocument();
    if (!Doc->generatePDF(ParsedDatas))     return EXIT_FAILURE;
    //On envoie le PDF produit sur la sortie standard
    Doc->echo();
    return EXIT_SUCCESS;
        

Bien sûr les fonctions/méthodes utilisées (stdin(), parseXML() etc) n'existent pas encore, mais le but de ce document est d'y remédier. Le choix est de recevoir des données depuis stdin et d'émettre le produit sur stdout pour rendre l'usage de l'application multiple : aujourd'hui, nous avons besoin de transformer des pages web depuis une application serveur PHP, mais demain les spécifications peuvent évoluer (Java, transformation PDF en batch etc) il faut donc qu'elles puissent s'adapter à ce genre de situations. Je veux pouvoir être capable d'écrire les lignes de scripts suivantes :

    cat test.html | pdfconverter > test.pdf
    wget "http://new.google.com/" | pdfconverter > new.google.com.pdf
    wget "http://new.google.com/" | pdfconverter > test.pdf && echo "Texte du mail" | mutt -a test.pdf -t "Titre" 42@flubb.net
    pdfconverter --input-file test.html > test.pdf
    pdfconverter --input-file test.html --output-file test.pdf', "Insertion du convertisseur de HTML en PDF dans n'importe quelle chaîne de production

Il faut à présent détailler toutes ces étapes : 1- interception du flux d'entrée depuis l'entrée standard 2- parsing XML utilisant libxml2 3- implémentation d'une classe PDFDocument 4- émission sur la sortie standard Le point 3 est le plus problématique mais les trois autres ne sont pas à négliger : elles peuvent en effet poser problèmes plus tard si elles ne sont pas bien affutées maintenant. On va donc les traiter dans le désordre :

1- interception du flux d'entrée depuis l'entrée standard

�a peut être fait de façon simpl(e|iste) mais si on veut essayer de minimiser les new (en gros n'en faire qu'un seul), la façon ci-dessous correspond :

void    getSTDIn(char** ptr, int& nbLines, int& totalLength)
{
    int     LineLength = 1000;
    nbLines = 0;
    totalLength = 0;
    char*   Line = new char[LineLength];
    char*   FGetsStatus = NULL;
    FILE*   STDInCopy = tmpfile();
    //
    do  if (FGetsStatus = fgets(Line, LineLength, stdin))       {
        int Ln = strlen(Line);
        fwrite(Line, Ln, 1, STDInCopy);     //###CHECK
        nbLines++;
        totalLength += Ln;
    } while(FGetsStatus != NULL);
    delete []Line;
    //
    rewind(STDInCopy);
    //
    *ptr = new char[totalLength+1];
    (*ptr)[totalLength] = 0;
    fread((*ptr), totalLength, 1, STDInCopy);       //###CHECK
    fclose(STDInCopy);      //On efface le fichier temporaire
        }
Lire stdin en deux passes pour minimiser les new

2- parsing XML utilisant libxml2

void    displayNode(xmlNodePtr node, int level)
{
    if (!node)      return;
    while (node)        {
        int i;
        for(i=0; i<level; i++)      printf(\"  \");
        printf(\"%s, type %d, Contenu : %s\\n\", node->name, node->type, node->content);
        if (node->children)     displayNode(node->children, level+1);
        node = node->next;
    }
}

//Affiche l'arbre de parsing de libXML2
void showXMLTree(xmlDocPtr doc)
{
    xmlNodePtr  HTMLNode;
    printf(\"Document : %s\\n\", doc->name);
    xmlNodePtr  CurNode = doc->children;
    displayNode(CurNode, 1);
}


int main(int argc, char *argv[])
{
    // Buffer contient le texte à parser
    xmlDocPtr   HTMLDoc;
    HTMLDoc = xmlParseDoc((xmlChar*)Buffer);
    showXMLTree(HTMLDoc);
    //...
        }

Ce qui produit :

Pour l'entrée HTML suivante on obtient l'output au-dessous :

<html>
    <head>
        <title>Titre de ma page</title>
    </head>
    <body>
        <p>Premier paragraphe</p>
    </body>
</html>

-------------------------------------

Document : (null)
    html, type 1, Contenu : (null)
        text, type 3, Contenu :

        head, type 1, Contenu : (null)
            text, type 3, Contenu :

            title, type 1, Contenu : (null)
                text, type 3, Contenu : Titre de ma page
            text, type 3, Contenu :

        text, type 3, Contenu :

        body, type 1, Contenu : (null)
            text, type 3, Contenu :

            p, type 1, Contenu : (null)
                text, type 3, Contenu : Premier paragraphe
            text, type 3, Contenu :

   text, type 3, Contenu :

4- émission sur la sortie standard

On utilise simplement la fonction puts pour envoyer un contenu sur le terminal (ou même un - encore plus simple - printf). On peut donc passer aux choses sérieuses...

3- implémentation d'une classe PDFDocument

On reprend le code de PDFDocument.php et html2pdf.php pour commencer le travail. Question de debug : comment débugguer un programme que l'on veut nourrir par un pipe ? Je ne vois pas comment faire ça sous KDevelop. C'est ça qui m'empêchait de debugguer le fgets qui était pending (les io étaient bloquantes). Deux choses : essayer stdio sans lock et voir comment envoyer des données à un programme qui tourne (un pipe est activé une fois que l'applicatino est lancée donc).

Le programme de développement

Pour la version 0.1 : - générer un PDF simple pour tester les bindings de PDFLib6. Pour la version 0.2 : - Gérer plusieurs formats de pages (a4, a3 etc). - Récupérer le code du texteur PHP pour afficher du texte multi-ligne. Pour la version 0.3 : - faire que les scripts du tableau 2 fonctionnent. - gérer les éléments HTML suivants : p, b, i, u, a, div. Pour la version 0.4 : - gérer les styles suivants : font-style, color, background-color Pour la version 0.5 : - gérer les images (essentiel mais très utile également pour le débug des tableaux de la phase suivante). Pour la version 0.6 : - gérer les tableaux Pour la version 0.7 : - gérer la pagination - gérer les headers et les footers - ajouter la gestion d'autres styles. Pour la version 0.8 : - afficher correctement la fiche contact de Linda - ajouter la gestion d'autres styles. Pour la version 0.9 : - afficher correctement un rapport NCA de Linda - ajouter la gestion d'autres styles. Pour la version 1.0 : - afficher correctement deux synthereports de GED-Pro : hopital-couple-enfant et chu-rennes. - finir la gestion des styles.

Accueil