Automatiser la conversion de docx en pdf 6


Dernièrement j’ai eu le problème suivant : soit un fichier Microsoft Word en .doc ou .docx, créer un programme sous Windows qui permette de convertir ce fichier en .pdf en gardant la mise en page.

Le premier réflexe : “ben je vais utiliser LibreOffice”. Une belle API Python, et tout, et tout. Mais couac, quelques fichiers de tests prouveront rapidement que la mise en page pouvait parfois être légèrement différente, ce qui ne remplissait pas le cahier des charges.

Tentative de recherche du côté de PanDoc qui prends en charge plus de formats que je ne peux en nommer. Malheureusement, le docx n’est supporté qu’en écriture. Diantre.

Une option serait d’acheter une licence Office et son plugin PDF et de scripter ça avec l’API COM, mais là ça commence à revenir cher et c’est très lourd.

Finalement j’ai opté pour un bidouillage : utiliser Microsoft Word Viewer et son addon pour docx. Ensuite, pour la conversion en PDF, on installe CC PDF Converter, qui va nous mettre une imprimante virtuelle vers du PDF.

Enfin, il fallait trouver un moyen de scripter tout ça. J’ai tenté pywinauto. Fail. AutoHotKey, erreurs aléatoires. Et finalement, AutoIt, trouvé via Sebsauvage.

Le langage est de script n’est pas (trop) horrible, et facile à comprendre :

; Matcher le nom des fenêtre par morceau sans se
; soucier de la casse.
AutoItSetOption('WinTitleMatchMode', -2) 

; Chopper les options de la ligne de commande
$VIEWER_EXE = "C:\Program Files (x86)\Microsoft Office\OFFICE11\WORDVIEW.EXE"
$FROM = $CmdLine[1] 
$TO = $CmdLine[2]

; Fermer ou force killer tout ce qu'on a pu ouvrir précédement
Winclose('reader')
Winclose('viewer')
Winkill('reader')
Winkill('viewer')

; Ouvrir MS Word Viewer, et répondre "oui" à 
; tout pop up qui s'ouvre en appuiyant sur
; Entrée
Run($VIEWER_EXE & " " & $FROM)
WinWaitActive('viewer', '', 2)
Send('{Enter}')

; Utiliser Ctrl + P pour demander une
; impression. Les paramètres par défaut
; sont ok : couleur, 1 copie, le tout
; sur le Bureau.
Send('^p')
WinWaitActive('imprimer', '', 2)
Send('{Enter}')

; On attent qu'apparaisse la fenêtre 
; de choix de nom de fichier pour la 
; sortie en PDF. Le focus est automatiquement
; sur le champ "name"; on a juste à écrire
; le nom de fichier de dans et à faire
; Entrée.
WinWaitActive('select a filename', '', 2)
Send($TO)
Send('{Enter}')

; Si le fichier existe déjà, il va 
; demander confirmation d'écrasement
; ce qu'on fait. Si la fenêtre 
; n'apparait pas, Alt+O ne fait rien
; de bien méchant de toute façon.
WinWaitActive('confirmer', '', 2)
Send('!o')

; On attend que le lecteur PDF se lance,
; prouvant que le fichier est prêt.
WinWaitActive('reader')

; Le PDF est prêt. On va pisser un coup.
; On ferme aussi toutes les fenêtres à 
; coup de marteau.
Winclose('reader')
Winclose('viewer')
Winkill('reader')
Winkill('viewer')

Voilà c’était le code dégueulasse du jour, Max serait fier de moi. Evidement c’est très fragile : dépendant de la config, de la langue, etc.

Mais zob, j’ai mon PDF.

6 thoughts on “Automatiser la conversion de docx en pdf

  • Sam Post author

    PDF Creator est maintenant plein de composant close source, en installe en prime un logiciel commercial (PDF architecte) et une barre de recherche amazon sur les navigateurs si on fait pas gaffe. Triste.

    Mais de toute façon, ça permet pas d’automatiser la tâche.

  • Out for à while

    Pour scripter windows office (excel principalement) j’utilise win32com.

    Évidemment ça suppose d’être sous windows, d’avoir les applis windows installées, il n’y a pas beaucoup de doc, c’est tordu mais ça marche.

    C’est mal maître ?

    Ps: bravo pour ce blog de cul et vulgarisation en français que je découvre à l’instant

Leave a comment

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <pre> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>

Des questions Python sans rapport avec l'article ? Posez-les sur IndexError.