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▲
Lancez OpenOffice Writer.
Ajoutez la barre d'outils de création de formulaire.
Créez 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.
Placez 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.
Enregistrez en PDF (bouton « exporter en pdf »).
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éez un nouveau projet Visual C++ / MFC / MFC Application.
Le wizard se lance. Cliquez sur Next.
Nous choisissons ici une application basée sur une boite de dialogue, en français.
Ici, choisir les composants d'interfaces classiques à générer. Je les ai tous enlevés. Vous pouvez préciser ici le titre de la fenêtre : Génération automatique de PDF.
Vu qu'on n'en a pas besoin, décochez l'utilisation de contrôles ActiveX.
Ici on ne touche à rien, sauf si vous voulez changer le nom de vos classes et de vos fichiers CPP.
Visual Studio vous a généré un certain nombre de fichiers.
Allez maintenant dans l'éditeur de ressources et rajoutez deux labels, deux edit box, renommez les boutons, bref, essayez de faire en sorte que ça ressemble au dessin.
Maintenant que l'interface graphique est prête, passons au code.
Rajoutons une fonction aux fichiers genPdfDlg.h et genPdfDlg.cpp
Dans le .h,
private
:
void
generateFdfFile(CString, CString);
Dans le .cpp,
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,
private
:
BOOL CgenPdfDlg::
generatePDF(CString, CString, CString);
Dans le .cpp,
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 sûr amélioré en termes 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
<</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 edit 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.
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
%âãÏÓ
\n
1 0 obj
\n
<< /FDF"
;
fdfString +=
"<< /F("
+
pdfInput +
") /Fields 2 0 R>>>>
\n
"
;
fdfString +=
"endobj
\n
2 0 obj
\n
[
\n
"
;
fdfString +=
"<< /T(TextBox) /V("
+
name +
" "
+
surName +
")>>
\n
"
;
fdfString +=
"<< /T(TextBox1) /V("
+
curTimeString +
")>>
\n
"
;
fdfString +=
"]
\n
endobj
\n
trailer
\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 texte et leurs valeurs. Il faudrait bien sûr 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 :
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).
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.
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 évidemment 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 fourni 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.
IX. Remerciements▲
Je remercie toute l'équipe C++ pour leur relecture attentive du document.
X. Contact▲
Si vous constatez une erreur dans le tutoriel, dans la source, dans la programmation ou pour toutes informations, n'hésitez pas à me contacter par mail, ou par le forum.