Génération automatique de documents PDF avec Visual C++ 2005

Ce document est un tutoriel sur la génération automatique de PDF à partir de Visual C++ 2005

Article lu   fois.

L'auteur

Profil ProSite personnel

Liens sociaux

Viadeo Twitter Facebook Share on Google+   

I. Introduction

Vous souhaitez pouvoir générer un fichier PDF contenant vos propres données à partir de votre application en Visual C++ 2005 ?
Alors ce tutoriel est pour vous.

À travers ce cours de génération automatique de PDF seront abordés :

  • La création d'un fichier PDF gratuitement avec un formulaire
  • La construction d'un exemple avec Visual C++ 2005 et les bibliothèques MFC
  • La génération d'un fichier FDF
  • L'insertion de valeurs personnelles dans les champs du formulaire PDF

Bien évidemment, ce tutoriel en Visual C++ 2005 pourra être très facilement adapté dans un autre langage, pour faire, par exemple, une génération automatique de PDF avec Visual Basic ou bien votre langage préféré.

II. Prérequis

II-A. OpenOffice pour créer un PDF gratuitement

Le but ici est de pouvoir créer un fichier PDF contenant un formulaire.
Je vous propose ici une solution pour créer un PDF gratuitement à l'aide d'OpenOffice, mais vous pouvez cependant utiliser le célèbre Acrobat Professional à 500 $ ou un quelconque autre logiciel de création de PDF (sachant qu'Acrobat est quand même le meilleur des meilleurs !). J'ai choisi pour ma part de créer un pdf gratuit.
L'important est de pouvoir créer un formulaire dans le fichier PDF, car c'est grâce à celui-ci que nous pourrons remplir notre document avec nos données.

Vous pouvez télécharger gratuitement OpenOffice à cette adresse : http://fr.openoffice.org/about-downloads.html
Sachez que j'ai, pour ma part, utilisé la version 2.0.1.

II-B. La librairie gratuite PDFTK

Il vous faut ensuite la librairie gratuite PDFTK. C'est grâce à cette librairie que nous allons pouvoir effectuer le remplissage de nos champs de formulaire PDF.
Vous pouvez télécharger gratuitement la version pour Windows à cette adresse : http://www.accesspdf.com/article.php/20041130153545577

II-C. Visual C++

Enfin, j'utilise pour l'élaboration de mon exemple, le célèbre Visual Studio 2005 et particulièrement Visual C++ 2005. Si vous l'avez, vous êtes déjà comblés, sinon, utilisez votre logiciel de développement préféré et vous y arriverez aussi bien.

III. Création du fichier PDF avec formulaire

Lancer OpenOffice Writer.

Ajouter la barre d'outils de création de formulaire.

Image non disponible

Créer votre document comme vous l'entendez…

Il faut désormais ajouter un ou plusieurs champs de formulaires. Nous allons rajouter un champ texte. Utiliser la barre d'outils pour dessiner une zone de texte formulaire.

Image non disponible

Placer la zone à l'endroit adéquat (la zone est délimitée par les points verts).

Double-cliquez sur le formulaire et nommez votre formulaire.

Image non disponible

Enregistrer en PDF (bouton « exporter en pdf »).

Image non disponible

Bravo, vous avez ainsi créé votre fichier PDF gratuit avec formulaire.

IV. L'utilitaire de génération de PDF

Vous avez téléchargé le zip. Gardez-le au chaud pour l'instant, nous aurons besoin plus tard du fichier pdftk.exe

V. Création de l'application exemple pour la génération automatique du PDF

Je propose ici un exemple très basique, utilisant la bibliothèque MFC. Pour ceux qui ne maîtrisent pas cette programmation et qui possèdent le logiciel, je décris ici les étapes pour y parvenir.

Créer un nouveau projet Visual C++ / MFC / MFC Application.

Image non disponible

Le wizard se lance. Cliquez sur Next.

Image non disponible

Nous choisissons ici une application basée sur une boite de dialogue, en français.

Image non disponible

Ici, choisir les composants d'interfaces classiques à générer. Je les ai tous enlevé. Vous pouvez préciser ici le titre de la fenêtre : Génération automatique de PDF.

Image non disponible

Vu qu'on n'en a pas besoin, décochez l'utilisation de contrôles ActiveX.

Image non disponible

Ici on ne touche à rien, sauf si vous voulez changer le nom de vos classes et de vos fichiers CPP.

Image non disponible

Visual Studio vous a généré un certain nombre de fichiers.
Allez maintenant dans l'éditeur de ressources et rajouter deux labels, deux edit box, renommez les boutons, bref, essayer de faire en sorte que ça ressemble au dessin.

Image non disponible

Maintenant que l'interface graphique est prête, passons au code.
Rajoutons une fonction aux fichiers genPdfDlg.h et genPdfDlg.cpp
Dans le .h,

 
Sélectionnez
private:
   void generateFdfFile(CString, CString);

Dans le .cpp,

 
Sélectionnez
void CgenPdfDlg::generateFdfFile(CString pdfInput, CString fdfOutput)
{
}

Cette fonction va générer un fichier intermédiaire de type FDF qui va permettre le remplissage des champs. Je décris ce format un peu plus loin, ainsi que l'implémentation de cette fonction. ==> Y aller directement <==.

Nous allons de même rajouter une fonction qui va générer le fichier PDF. Dans le .h,

 
Sélectionnez
private:
    BOOL CgenPdfDlg::generatePDF(CString, CString, CString);

Dans le .cpp,

 
Sélectionnez
BOOL CgenPdfDlg::generatePDF(CString pdfInput, CString fdfInput, CString pdfOutput)			
{
}

Je décris cette fonction ainsi que son implémentation un peu plus bas. ==> Y aller directement <==

Enfin nous gérerons l'affichage du document PDF généré.

Remarque : Ce programme doit être bien sur amélioré en terme de fonctionnalités, de gestion d'erreur… Ce n'était pas le but de l'exercice.

VI. Génération d'un fichier FDF

Ce fichier est un format spécifique d'Adobe qui contient grosso-modo une description du formulaire et de ses champs, ainsi que le contenu que l'on veut affecter.
La structure de ce fichier est décrite au chapitre 8.9 de ce document Documentation Référence PDF

Ce qui est à retenir, c'est que le fichier doit contenir la liste des identifiants des champs (nom, prenom, etc.) et la liste des valeurs associées (Dupont, Jean, etc.)
le reste décrit l'entente et le corps du fichier. Il faut donc construire un identifiant de champ précédé de /T et la valeur précédée de /V, comme l'indique la ligne ci-dessous

 
Sélectionnez
<</T(identifiant)/V(valeur)>>

C'est une telle ligne que nous devons générer. C'est le travail de la fonction generateFdfFile, qui récupère les valeurs des edits box et créé ainsi le fichier FDF.

Vous trouverez ci dessous le code complet de la fonction. Elle prend en paramètre le fichier modèle pdf, le fichier temporaire FDF.
Le principe est simple, on utilise un CStdioFile pour créer un fichier FDF en mode texte.
Puis on crée une variable avec la date courante. On récupère les valeurs saisies dans la boite de dialogue et on crée le fichier FDF comme décrit précédemment.

 
Sélectionnez
void CgenPdfDlg::generateFdfFile(CString pdfInput, CString fdfOutput)
{
    CStdioFile fdfFile(fdfOutput, CFile::modeCreate | CFile::modeWrite | CFile::typeText ); 
    CString name, surName;
    CTime curTime = CTime::GetCurrentTime();
    CString curTimeString;
    curTimeString.Format("%d/%d/%d - %d:%d:%d", curTime.GetDay(), curTime.GetMonth(), curTime.GetYear(), 
                         curTime.GetHour(),curTime.GetMinute(),curTime.GetSecond());
    GetDlgItem(IDC_EDIT1)->GetWindowText(name);
    GetDlgItem(IDC_EDIT2)->GetWindowText(surName);
    CString fdfString;
    fdfString = "%FDF-1.2\n%âãÏÓ\n1 0 obj\n<< /FDF";
    fdfString += "<< /F(" + pdfInput + ") /Fields 2 0 R>>>>\n";
    fdfString += "endobj\n2 0 obj\n[\n";
    fdfString += "<< /T(TextBox) /V(" + name + " " + surName + ")>>\n";
    fdfString += "<< /T(TextBox1) /V(" + curTimeString + ")>>\n";
    fdfString += "]\nendobj\ntrailer\n<</Root 1 0 R>>\n%%EOF\n";
    fdfFile.WriteString(fdfString); 
    fdfFile.Close();
}

Remarques :
- Cette fonction a été grandement simplifiée pour les besoins de l'exemple, à savoir générer des champs zones de textes et leurs valeurs. Il faudrait bien sur se reporter à la description technique pour générer exhaustivement ce fichier.
- Le format de ce fichier évolue au fur et à mesure, il faudra peut-être réécrire cette fonction si le format de fichier n'est plus compatible.
- Il peut être intéressant d'utiliser le toolkit FDF, disponible gratuitement sur le site d'Adobe. Cependant, il faut savoir que la licence d'utilisation oblige à utiliser ce toolkit sur un serveur, ce qui nous est interdit dans notre cas.

VII. Génération automatique du PDF à partir du FDF avec PDFTK

C'est maintenant que nous allons utiliser la librairie gratuite PDFTK. Nous disposons donc d'un PDF « modèle » et d'un fichier FDF qui contient la description des champs et des valeurs à renseigner.
Nous allons donc demander maintenant à PDFTK de nous créer un nouveau PDF avec les champs renseignés.
Soit, la ligne de commande :

 
Sélectionnez
pdftk.exe modele.pdf fill_form fichierfdf.fdf output resultat.pdf

Nous lui passons le modèle en paramètre, l'action à effectuer est le remplissage du formulaire (fill_form) à partir du FDF fichierfdf.fdf pour nous produire la sortie (output) resultat.pdf
Très simple.
Comme décrit dans la FAQ Visual C++, nous allons exécuter cette commande et récupérer la sortie à l'aide d'un pipe (simple redirection).

 
Sélectionnez
BOOL CgenPdfDlg::generatePDF(CString pdfInput, CString fdfInput, CString pdfOutput)
{
    // pdftk form.pdf fill_form data.fdf output out.pdf
    CString commandLine = "pdftk.exe " + pdfInput + " fill_form " + fdfInput + " output " + pdfOutput;
    CString outputString;
    SECURITY_DESCRIPTOR sd;
    SECURITY_ATTRIBUTES sa;
    InitializeSecurityDescriptor(&sd, SECURITY_DESCRIPTOR_REVISION);
    SetSecurityDescriptorDacl(&sd, true, NULL, false);
    sa.nLength = sizeof(SECURITY_ATTRIBUTES);
    sa.bInheritHandle = true;
    sa.lpSecurityDescriptor = &sd;
    
    HANDLE hReadPipe;
    HANDLE hWritePipe;
    if (! CreatePipe(&hReadPipe, &hWritePipe, &sa, NULL)) 
        return FALSE;
    
    STARTUPINFO si;
    memset(&si, 0, sizeof(STARTUPINFO));
    si.cb = sizeof(STARTUPINFO);
    si.dwFlags = STARTF_USESHOWWINDOW |STARTF_USESTDHANDLES;
    si.wShowWindow = SW_HIDE;
    si.hStdOutput = hWritePipe;
    si.hStdError = hWritePipe;
    PROCESS_INFORMATION pi;
    
    if ( ! CreateProcess(NULL, commandLine.GetBuffer(commandLine.GetLength()), NULL, NULL, TRUE, 0, 0, 0, &si, &pi) )
        return FALSE;
    WaitForSingleObject(pi.hProcess, INFINITE);
    
    DWORD BytesRead ;
    char dest[2048];
    memset(dest, 0, 2048);
    DWORD byteAvail = 0;
    PeekNamedPipe(hReadPipe, NULL, 0, NULL, &byteAvail, NULL);
    if (!byteAvail) // la génération n'a rien produit, tout est ok
        return TRUE;
    while (ReadFile(hReadPipe, dest, 2048, &BytesRead, NULL))
    {
        if (!BytesRead)
            break;
        outputString += dest;
        PeekNamedPipe(hReadPipe, NULL, 0, NULL, &byteAvail, NULL);
        if (BytesRead<2048 || !byteAvail)
            break;
    }
    AfxMessageBox(outputString); // affiche l'erreur
    CloseHandle(hReadPipe);
    CloseHandle(hWritePipe);
    return FALSE;
}
            

S'il n'y a rien dans le pipe (utilisation de PeekNamedPipe pour être non bloquant), c'est que le programme n'a rencontré aucune erreur et que le PDF s'est bien généré.
Il ne nous reste plus qu'à l'afficher dans le visionneur par défaut de PDF, grâce à ShellExecute.

 
Sélectionnez
    CString tempFdf = "tempFDF.fdf";
    CString inputPdf = "diplome.pdf";
    CString outputPdf = "output.pdf";
    GetFileAttributes(outputPdf);
    if (GetLastError() != ERROR_FILE_NOT_FOUND)
        DeleteFile(outputPdf);
    generateFdfFile(inputPdf, tempFdf);
    if (generatePDF(inputPdf, tempFdf, outputPdf))
        ShellExecute(NULL,"open",outputPdf,"","",SW_NORMAL);
    else
        AfxMessageBox("Erreur lors de la génération automatique du PDF");
    DeleteFile(tempFdf);

Remarques :
- J'ai mis les noms de fichier en dur dans le code, il est bien évidement recommandé qu'ils soient soumis à paramétrage.
- Je supprime le fichier de sortie s'il existe déjà, il pourrait être intéressant de demander une confirmation.
- J'ai choisi de supprimer le fichier temporaire FDF, mais commentez la ligne si vous souhaitez voir à quoi il ressemble.

VIII. Le programme d'exemple

Le programme fournit en exemple vous demande un nom et un prénom, et génère un fichier PDF avec ces informations.
Vous pouvez télécharger les sources de l'exemple du tutoriel à cette adresse : Télécharger ici (1.47 Mo) - version ZIP
Vous pouvez télécharger une version compilée ici : Télécharger ici (1.45 Mo) - version ZIP
Pour fonctionner correctement, l'exécutable pdftk.exe devra être dans le même répertoire que notre application, ainsi que le fichier modèle, qui est codé en dur comme diplome.pdf
De même, pour fonctionner en debug, ces deux fichiers devront se trouver à la racine du projet.

Voilà, grâce à ce tutoriel, vous savez désormais générer automatiquement un PDF. C'est très simple et gratuit.

Remerciements

Je remercie toute l'équipe C++ pour leur relecture attentive du document.

Contact

Si vous constatez une erreur dans le tutorial, dans la source, dans la programmation ou pour toutes informations, n'hésitez pas à me contacter par mail, ou par le forum.

Vous avez aimé ce tutoriel ? Alors partagez-le en cliquant sur les boutons suivants : Viadeo Twitter Facebook Share on Google+   

  

Les sources présentées sur cette page sont libres de droits et vous pouvez les utiliser à votre convenance. Par contre, la page de présentation constitue une œuvre intellectuelle protégée par les droits d'auteur. Copyright © 2006 Nico-pyright(c). Aucune reproduction, même partielle, ne peut être faite de ce site et de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu'à trois ans de prison et jusqu'à 300 000 € de dommages et intérêts.