Toujours dans la lignée avec ce que j’ai créé sur abricocotierfr.appspot.com, je voulais créer une application qui me serait vraiment utile tous les jours, et qui remplacerait (pourquoi pas avantageusement) une application que j’utilise chaque jour. Et ça tombe bien, j’utilise assez fréquemment un outil pour fusionner les PDF (celui-là, en l’occurence), mais il est truffé de pubs, et ne fonctionne plus correctement à partir d’un certain nombre de PDF. Bref, il n’est pas parfait. Pourquoi ne pas tenter, avec Online PDF Tools, d’en faire un moi-même sur AppEngine ?

Soyons clair, ce n’est pas ma première tentative avec cette application. J’avais déjà tenté une première approche il y a environ deux semaines. Approche qui s’était révélée infructueuse, devant la difficulté pour trouver des ressources en la matière (du coup, ça tombe bien : c’est moi qui vais les fournir, cette fois). En effet, sur internet, on trouve majoritairement deux librairies pour manipuler des PDF en python : PyPDF et ReportLab. ReportLab, dans sa version gratuite, permet de créer des PDF, mais pas vraiment de les lire ni de les fusionner. Cette fonctionnalité semble être offerte par un addon payant, PageCatcher.
A l’inverse, PyPDF propose une solution très rapidement maîtrisable pour lire des pages d’un PDF, les concaténer, et donc faire des opérations de fusion ou d’extraction de PDF de façon très rapide. Sauf… que pour AppEngine, il n’existait aucune ressource indiquant comment on pouvait “écrire” le fichier de sortie sur la sortie HTTP, soit donc pour le client. Je rappelle que la plateforme AppEngine ne permet pas réellement d’écrire et de créer des fichiers, et n’autorise cela que via des Blobs dans le DataStore (ou dans le BlobStore, si on a activé le billing, ce qui n’est pas mon cas). A l’inverse, il existait pas mal de ressources sur le net pour utiliser ReportLab avec AppEngine. Gloups, j’étais tiraillé entre deux librairies.
Après pas mal d’essais infructueux, j’ai eu, par chance, la possibilité d’obtenir enfin la création de PDF (avec PyPDF, et donc je n’utilise pas du tout ReportLab), puis celle de la lecture correcte de PDF venant des fichiers envoyés à la page (et non de fichiers lus en local, ce qui est différent). Et du coup, de fil en aiguille, j’ai fait l’outil qui est disponible là.
Plusieurs choses :
Le premier outil permet de fusionner des PDF. Le titre du document est réglé pour être celui du premier document dans la liste. Le serveur ne traite pas les documents n’ayant pas l’extension .pdf, et fait des try/catch sur la concaténation d’un PDF au fichier de sortie. En gros : si sur le lot, un PDF n’est pas lisible, le programme l’ignore, et continue sa route avec le reste.
Le deuxième outil permet d’enlever certaines pages à un PDF. Sachant qu’il faut impérativement lister correctement les pages entre virgules. C’est simple, le programme prend le fichier en entrée, ainsi que la liste des chiffres représentant les pages à ne pas mettre. Pour chaque page, le programme demande si le numéro de cette page fait partie de la liste des pages à ne pas mettre, et si oui, il ne l’ajoute pas au fichier de sortie.
Le troisième outil permet d’extraire une partie de PDF d’un PDF d’entrée. En gros, si, sur un PDF, vous ne voulez conserver qu’une partie, il suffit d’indiquer la page de début et la page de fin de la partie de document que vous voulez conserver.
Voilà ! C’est tout simple. Si vous avez des problèmes pour l’utilisation, n’hésitez pas à venir le dire ici, je tenterai ensuite de résoudre ce bug.
Pour le reste, voici le code python permettant de manipuler un PDF acquis en variable POST et de le renvoyer en réponse au client. C’est, en allégé, le code que j’utilise pour le premier programme. Voilà la partie du code qui est importante :
[sourcecode language=“python”] from pyPdf import PdfFileWriter, PdfFileReader
pdf_filename = ’’ output = PdfFileWriter() pdf_file = self.request.POST.get(“file”,None) pdf_filename = cgi.escape(pdf_file.filename) pdf_content = PdfFileReader(pdf_file.file) numpages = pdf_content.getNumPages() for i in range(0,numpages): output.addPage(pdf_content.getPage(i)) self.response.headers[‘Content-Type’] = ‘application/pdf’ self.response.headers[‘Content-Disposition’] = ‘pdf; filename=’+pdf_filename output.write(self.response.out) [/sourcecode]
Sympa comme outil simple et pratique. Je bookmark.
Demain je publie un billet sur un logiciel sympa, s'il existait en webware ce serait le top... enfin je dis ça comme ça... ;)
Même si je n'utilise pas de PDF, faut avouer qu'une fois de plus tu fais un truc sympa :)