L'intégration de documents Microsoft Word au sein d'applications web nécessite souvent une conversion vers le format HTML. Ce processus permet non seulement l'affichage direct dans un navigateur, mais aussi l'indexation du contenu et sa manipulation dynamique. Cet article détaille une approche robuste utilisant Apache POI pour l'extraction de données et Jsoup pour le nettoyage et la structuration du code HTML produit.
Configuration du projet et dépendances
Pour prendre en charge les formats anciens (.doc) et modernes (.docx), nous devons combiner plusieurs bibliothèques. Voici les dépendances Maven essentielles à inclure dans votre fichier pom.xml :
<dependencies>
<!-- Apache POI pour les formats .doc et .docx -->
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml</artifactId>
<version>4.1.2</version>
</dependency>
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-scratchpad</artifactId>
<version>4.1.2</version>
</dependency>
<!-- Convertisseur spécifique pour le format DOCX -->
<dependency>
<groupId>fr.opensagres.xdocreport</groupId>
<artifactId>fr.opensagres.poi.xwpf.converter.xhtml</artifactId>
<version>2.0.2</version>
</dependency>
<!-- Manipulation HTML -->
<dependency>
<groupId>org.jsoup</groupId>
<artifactId>jsoup</artifactId>
<version>1.17.2</version>
</dependency>
</dependencies>
Conversion du format moderne (.docx)
Le format .docx est basé sur XML (OpenXML). Nous utilisons la classe XWPFDocument associée au convertisseur de XDocReport pour générer un flux XHTML initial, que nous affinons ensuite avec Jsoup.
public String convertDocxToCleanHtml(InputStream wordStream) throws Exception {
// Chargement du document Word
XWPFDocument docx = new XWPFDocument(wordStream);
// Configuration des options de conversion
XHTMLOptions htmlOptions = XHTMLOptions.create();
htmlOptions.setFragment(true); // Génère un fragment sans balise html/body complète
ByteArrayOutputStream out = new ByteArrayOutputStream();
XHTMLConverter.getInstance().convert(docx, out, htmlOptions);
String rawHtml = out.toString("UTF-8");
// Nettoyage et transformation avec Jsoup
org.jsoup.nodes.Document soupDoc = Jsoup.parseBodyFragment(rawHtml);
transformerEnTitresSemantiques(soupDoc);
return soupDoc.html();
}
private void transformerEnTitresSemantiques(org.jsoup.nodes.Document doc) {
// Exemple : Transformer les paragraphes identifiés comme titres en balises h2
doc.select("p.Heading1, p.Titre1").forEach(element -> {
element.tagName("h2");
element.removeAttr("class");
element.attr("style", "color: #2c3e50; font-size: 1.5em;");
});
}
Conversion du format hérité (.doc)
Le format .doc (Binary Interchange File Format) nécessite l'utilisation de HWPFDocument et d'un gestionnaire spécifique pour l'extraction des images intégrées.
public String convertOldDocToHtml(InputStream wordStream) throws Exception {
HWPFDocument hwpf = new HWPFDocument(wordStream);
// Initialisation du document DOM pour la conversion
Document xmlBuilderDoc = DocumentBuilderFactory.newInstance()
.newDocumentBuilder().newDocument();
WordToHtmlConverter converter = new WordToHtmlConverter(xmlBuilderDoc);
// Gestionnaire d'images personnalisée
converter.setPicturesManager((content, type, name, width, height) -> {
// Logique de stockage d'image (ex: vers un S3 ou dossier local)
String storageUrl = saveMediaToStorage(name, content);
return storageUrl;
});
converter.processDocument(hwpf);
// Transformation en chaîne HTML
StringWriter writer = new StringWriter();
Transformer transformer = TransformerFactory.newInstance().newTransformer();
transformer.setOutputProperty(OutputKeys.ENCODING, "UTF-8");
transformer.setOutputProperty(OutputKeys.METHOD, "html");
transformer.transform(new DOMSource(converter.getDocument()), new StreamResult(writer));
return writer.toString();
}
Traitement des ressources multimédias
Lors de la conversion, les images ne sont pas directement intégrées dans le HTML (sauf via Base64). Il est préférable de les extraire et de mettre à jour les attributs src.
public void synchroniserImages(org.jsoup.nodes.Document doc, Map<String, String> mapImages) {
Elements images = doc.select("img");
for (Element img : images) {
String sourceInitiale = img.attr("src");
if (mapImages.containsKey(sourceInitiale)) {
// Remplacer le chemin local ou relatif par l'URL finale
img.attr("src", mapImages.get(sourceInitiale));
}
}
}
Optimisation stylistique
Le HTML généré par les convertisseurs de dcouments est souvent surchargé de styles en ligne inutiles. Jsoup permet de nettoyer le balisage pour garantir une cohérence visuelle avec votre charte graphique web :
- Suppression des polices spécifiques à Windows (Calibri, Arial).
- Uniformisation des tailles de caractères.
- Conversion des alignements de paragraphes en classes CSS.
public String finaliserLeStyle(String htmlBrut) {
org.jsoup.nodes.Document doc = Jsoup.parseBodyFragment(htmlBrut);
// Appliquer une police générique à tout le document
doc.select("span").forEach(span -> {
String style = span.attr("style");
if (style.contains("font-family")) {
span.attr("style", "font-family: sans-serif;");
}
});
return doc.body().html();
}