Faciliter la conversion de documents avec « unoconv »
Si votre application dépend encore d’unoconv, commencez par vérifier son environnement Python/UNO et convertir un document dont vous connaissez le contenu. Ce guide couvre un exécuteur Debian 12 existant, un script de traitement par lot de DOCX et les vérifications PDF nécessaires avant d’accepter les fichiers produits.
Unoconv est obsolète et son dépôt est archivé. Pour les nouvelles implémentations, utilisez unoserver. Conservez cette procédure pour maintenir un flux de travail unoconv existant pendant que vous planifiez cette migration.
Introduction à « unoconv » et à ses fonctionnalités open source
Unoconv est un outil open source en ligne de commande qui appelle LibreOffice par l’intermédiaire de ses bindings UNO. LibreOffice fournit les filtres d’importation et d’exportation ; installer uniquement un script Python ne suffit pas à assurer la conversion de documents. Les polices influent également sur la mise en page obtenue.
Configurer et installer « unoconv » sur votre système
Prérequis
Utilisez un exécuteur hérité dédié, doté d’un environnement déjà provisionné aux versions figées.
L’exécuteur testé utilisait Debian 12 Bookworm, Python 3.11.2 de la distribution,
python3-uno compatible, unoconv 0.7 issu du paquet Debian 0.7-2,
LibreOffice Writer 7.4.7.2 et Liberation Sans issu de fonts-liberation2.
Ces versions ont été testées pour la maintenance d’un environnement hérité ; cette page ne
recommande pas d’installer une ancienne pile logicielle pour une nouvelle application.
L’exécuteur a besoin d’un profil utilisateur LibreOffice et d’un répertoire temporaire accessibles
en écriture. Exécutez un seul lot à la fois, sans session LibreOffice de bureau ni processus
d’écoute partagé utilisant ce profil. Pour la première conversion, utilisez des documents DOCX
et ODT connus et fiables. Installez les polices utilisées par vos documents réels avant d’évaluer
la pagination. Les commandes d’inspection ci-dessous nécessitent le paquet
poppler-utils de Poppler sur la machine où vous inspectez les PDF. Il peut
s’agir de l’exécuteur ou d’une autre machine ayant accès aux mêmes fichiers de sortie.
Installation sous Linux (Ubuntu/Debian)
Le paquet Bookworm de Debian fournit cette ancienne version
d’unoconv. Conservez ensemble les paquets Python et UNO de la distribution. La vérification
suivante suppose que python3 correspond à
/usr/bin/python3, hors d’un environnement virtuel. Elle vérifie chaque commande
séparément et confirme que cet interpréteur peut importer UNO avant la conversion :
(
for tool in python3 unoconv libreoffice fc-match; do
command -v "$tool" >/dev/null || { printf 'Missing %s\n' "$tool" >&2; exit 1; }
done
python3 -c 'import uno, unohelper; print(uno.__file__)' &&
python3 --version &&
unoconv --version &&
libreoffice --version &&
fc-match -f '%{family}\n' 'Liberation Sans'
)
Sur l’exécuteur testé, UNO provenait de /usr/lib/python3/dist-packages/uno.py. Arrêtez-vous si
l’importation échoue ou si les versions indiquées diffèrent de l’image vérifiée de votre
déploiement. Il s’agit d’une vérification d’un exécuteur existant, pas d’une procédure
d’installation pour les versions actuelles d’Ubuntu ou de Debian.
Sur la machine où vous inspecterez les fichiers produits, vérifiez également les outils PDF. Les vérifications présentées ici ont utilisé Poppler 26.08.0 :
(
for tool in pdfinfo pdftotext pdftoppm; do
command -v "$tool" >/dev/null || { printf 'Missing %s\n' "$tool" >&2; exit 1; }
done
pdfinfo -v
)
Installation sous macOS
Homebrew indique qu’unoconv est désactivé. Installer LibreOffice seul ne suffit pas à obtenir une combinaison unoconv/Python fonctionnelle. Ce guide ne vérifie pas d’installation sous macOS ; suivez les instructions d’installation du successeur maintenu lorsque vous abandonnez un exécuteur macOS hérité.
Installation sous Windows
Les installations Windows natives et WSL sortent du périmètre testé dans ce guide. WSL ne fournit pas à lui seul la version compatible de Python, les bindings UNO, les polices ou les filtres LibreOffice. Ne considérez pas le passage à WSL comme une solution à un environnement non vérifié.
Guide pas à pas : convertir des documents docx en PDF
Placez un document DOCX connu à l’emplacement document.docx et un document ODT
connu à l’emplacement notes.odt dans un répertoire de travail. Notez le
nombre de pages attendu, les titres, les derniers paragraphes et les éventuels tableaux ou
éléments graphiques. Ces commandes directes écrivent document.pdf et
notes.pdf à côté des fichiers d’entrée et peuvent remplacer des PDF
existants ; utilisez donc un répertoire où ces fichiers de destination sont absents :
unoconv -f pdf ./document.docx &&
unoconv -f pdf ./notes.odt
Inspectez le résultat de la conversion du DOCX avec ces commandes. Elles écrivent
document.txt et des fichiers PNG numérotés tels que
document-page-1.png ; répéter la vérification remplace ces fichiers d’inspection :
pdfinfo -box ./document.pdf &&
pdftotext -layout ./document.pdf ./document.txt &&
pdftoppm -cropbox -r 96 -png ./document.pdf ./document-page
Comparez le nombre de pages et le texte extrait avec le document source, y compris le dernier
paragraphe de chaque page. Ouvrez chaque PNG rendu et vérifiez les tableaux, les éléments
graphiques, les marges et les sauts de page. Le rendu du CropBox montre la page visible ; le
contenu situé en dehors ne peut donc pas masquer un problème de contenu tronqué. Répétez
l’inspection pour notes.pdf en utilisant
notes.txt et le préfixe notes-page. Un fichier non
vide, un en-tête PDF ou une commande exécutée avec succès ne prouvent pas que le bon document
a été converti intégralement.
Automatiser les tâches de conversion de documents avec des scripts
Pour traiter séquentiellement un lot de DOCX sur cette installation fonctionnelle, enregistrez
ce script sous le nom convert_documents.py. Il sélectionne les fichiers ordinaires
portant le suffixe .docx, sans tenir compte de la casse, ignore les
répertoires et les liens symboliques, et exige un nouveau répertoire de sortie. Sa vérification
élémentaire du conteneur d’archive reconnaît les espaces de noms du document principal des
variantes Transitional et Strict d’OOXML ; ce n’est pas un validateur complet du schéma OOXML.
import os
from pathlib import Path
import posixpath
import subprocess
import sys
from tempfile import TemporaryDirectory
from urllib.parse import unquote
from xml.etree import ElementTree
from zipfile import BadZipFile, ZipFile
import zlib
def check_docx(path):
relationship_types = {
'http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument',
'http://purl.oclc.org/ooxml/officeDocument/relationships/officeDocument',
}
document_tags = {
'{http://schemas.openxmlformats.org/wordprocessingml/2006/main}document',
'{http://purl.oclc.org/ooxml/wordprocessingml/main}document',
}
with ZipFile(path) as archive:
if archive.testzip() is not None:
raise ValueError('Damaged DOCX member')
relationships = ElementTree.fromstring(archive.read('_rels/.rels'))
main_parts = [item for item in relationships
if item.get('Type') in relationship_types
and item.get('TargetMode', 'Internal') == 'Internal']
if len(main_parts) != 1:
raise ValueError('Missing main document relationship')
part = posixpath.normpath(unquote(main_parts[0].get('Target', '')).lstrip('/'))
types = ElementTree.fromstring(archive.read('[Content_Types].xml'))
content_type = 'application/vnd.openxmlformats-officedocument.wordprocessingml.document.main+xml'
if not any(item.get('PartName') == '/' + part
and item.get('ContentType') == content_type for item in types):
raise ValueError('Not a DOCX main part')
if ElementTree.fromstring(archive.read(part)).tag not in document_tags:
raise ValueError('Not a Word document')
def convert_documents(input_dir, output_dir):
source = Path(input_dir).resolve(strict=True)
files = sorted(path for path in source.iterdir()
if not path.is_symlink() and path.is_file()
and path.suffix.lower() == '.docx')
if not files:
print('No DOCX files found', file=sys.stderr)
return 1
output = Path(output_dir).absolute()
output.mkdir(mode=0o700)
failures = 0
for path in files:
try:
check_docx(path)
with TemporaryDirectory(prefix='.conversion-', dir=output) as temporary:
candidate = Path(temporary) / 'result.pdf'
subprocess.run(
['unoconv', '-f', 'pdf', '-o', str(candidate), str(path)],
check=True, timeout=120,
)
if not candidate.is_file() or candidate.stat().st_size == 0:
raise ValueError('No nonempty PDF was produced')
# Hard-link publication fails instead of replacing a destination that appeared.
os.link(candidate, output / (path.stem + '.pdf'))
print(f'Converted {path.name}')
except (OSError, ValueError, KeyError, BadZipFile, ElementTree.ParseError,
zlib.error, NotImplementedError, RuntimeError, subprocess.SubprocessError):
failures += 1
print(f'Conversion failed: {path.name}', file=sys.stderr)
return 1 if failures else 0
if __name__ == '__main__':
if len(sys.argv) != 3:
sys.exit('Usage: python3 convert_documents.py <input-directory> <new-output-directory>')
try:
sys.exit(convert_documents(sys.argv[1], sys.argv[2]))
except (OSError, ValueError):
sys.exit('Cannot prepare conversion directories; use an existing input and a new output directory')
Exécutez-le avec vos fichiers DOCX dans docs et un répertoire
new-pdfs qui n’existe pas encore :
python3 convert_documents.py docs new-pdfs
Le script tente de traiter les fichiers suivants après un échec et renvoie le code de sortie 1 si un fichier échoue. Il indique le nom du fichier en échec, supprime les fichiers candidats temporaires et préserve les fichiers de destination existants, y compris en cas de collision entre noms ayant le même radical. Les PDF produits avec succès restent disponibles lorsqu’un autre fichier échoue. Inspectez-les à l’aide des vérifications du texte et du rendu décrites ci-dessus avant de les utiliser. La publication signifie uniquement que le convertisseur s’est terminé avec succès et a produit un fichier candidat non vide.
Pour réessayer, conservez les PDF déjà publiés et utilisez un autre répertoire de sortie neuf. Une sélection vide renvoie le code de sortie 1 sans créer le répertoire de sortie. Le délai maximal de 120 secondes limite la durée de chaque processus client ; il ne garantit pas que LibreOffice a cessé de travailler. Un exécuteur interrompu peut laisser des répertoires de préparation ; inspectez donc l’espace de stockage qui lui appartient avant de réessayer. Ce script est destiné à un traitement local par lot de fichiers fiables, pas à un point de terminaison acceptant des téléversements non fiables. Un tel exécuteur nécessite sa propre supervision des processus, des limites de ressources et une isolation du système de fichiers.
Résoudre les problèmes courants
Conflits de versions de Python
Si la vérification de l’importation d’UNO échoue, vérifiez quel Python s’exécute et s’il est compatible avec le pont UNO installé. Le projet amont documente cette exigence concernant l’interpréteur. Un environnement virtuel quelconque ne suffit pas : un venv par défaut masque le module UNO de la distribution. Changer uniquement la version de Python n’installe pas de pont compatible.
Compatibilité des versions de LibreOffice
La version Debian d’unoconv testée émet un avertissement de dépréciation concernant
distutils sous Python 3.11 ; une conversion réussie exige toujours de
vérifier les PDF réellement produits. Conservez la combinaison Python/UNO/LibreOffice vérifiée
dans votre image héritée. Si un document échoue, essayez d’ouvrir et d’exporter ce même document
dans LibreOffice pour distinguer un problème d’importation ou d’exportation d’un problème lié
au chemin d’exécution du client unoconv.
Considérations pour le déploiement en conteneur
Un conteneur doit fournir à son utilisateur de conversion des répertoires de profil, de cache,
de données, d’exécution et temporaires accessibles en écriture. Créez les répertoires avant de
démarrer LibreOffice lorsque vous définissez XDG_CONFIG_HOME,
XDG_CACHE_HOME, XDG_DATA_HOME ou
XDG_RUNTIME_DIR. L’absence de profil accessible en écriture peut empêcher le
démarrage avant la conversion du document. Gardez les tâches séquentielles et arrêtez l’ensemble
de l’exécuteur dont vous avez la maîtrise lorsqu’un client dépasse le délai maximal ; tuer
uniquement le client ne prouve pas que le nettoyage est terminé.
Alternatives modernes et comparaison
| Déploiement | Choix |
|---|---|
| Exécuteur hérité existant aux versions figées | Maintenir unoconv tout en testant des documents représentatifs. |
| Nouveau déploiement | Utiliser unoserver. |
Unoserver (recommandé)
Unoserver sépare un exécuteur LibreOffice persistant de son client de conversion. Son serveur a toujours besoin d’un interpréteur Python capable d’importer UNO ; il ne supprime pas cette dépendance. Suivez les instructions d’installation d’unoserver, puis consultez la conversion de documents depuis Rust pour l’intégration maintenue. Les tests de cette page couvrent unoconv, pas ce flux de travail serveur distinct.
Interface en ligne de commande de LibreOffice
Pour une conversion ponctuelle, LibreOffice propose également une
option d’exportation en ligne de commande.
Sur l’exécuteur Debian testé, cette commande écrit document.pdf dans le
répertoire courant :
libreoffice --headless --convert-to pdf ./document.docx
Utilisez une destination qui n’existe pas encore et effectuez la même inspection du PDF. LibreOffice peut remplacer un PDF existant, et un code de sortie seul ne permet pas de confirmer le contenu obtenu. Cette méthode contourne le client unoconv, mais utilise toujours les filtres et les polices de LibreOffice.
Solutions cloud
Si vous souhaitez passer par une API plutôt qu’exploiter un exécuteur bureautique, évaluez le service de conversion de documents de Transloadit (English) avec vos documents représentatifs. Il s’agit d’une intégration distincte ; les commandes locales ci-dessus ne testent pas un service hébergé.
