Convertir un EPUB en PDF avec Java

Convertir un livre au format EPUB (un zip contenant XHTML + CSS + images) vers PDF est un besoin récurrent : archivage, impression, distribution. Java ne propose aucune API native, mais plusieurs bibliothèques open source s'en chargent très bien.

Stack recommandée : epublib + Flying Saucer

Deux bibliothèques éprouvées :

  • epublib (nl.siegmann.epublib) lit et extrait le contenu XHTML de l'EPUB.
  • Flying Saucer (org.xhtmlrenderer) rend du XHTML en PDF via iText.

Dépendances Maven

<dependencies>
  <dependency>
    <groupId>nl.siegmann.epublib</groupId>
    <artifactId>epublib-core</artifactId>
    <version>3.1</version>
  </dependency>
  <dependency>
    <groupId>org.xhtmlrenderer</groupId>
    <artifactId>flying-saucer-pdf-openpdf</artifactId>
    <version>9.5.0</version>
  </dependency>
</dependencies>

Conversion basique

import nl.siegmann.epublib.domain.*;
import nl.siegmann.epublib.epub.EpubReader;
import org.xhtmlrenderer.pdf.ITextRenderer;

import java.io.*;
import java.nio.charset.StandardCharsets;

public class EpubToPdf {
    public static void convert(File epubFile, File pdfFile) throws Exception {
        Book book;
        try (InputStream in = new FileInputStream(epubFile)) {
            book = new EpubReader().readEpub(in);
        }

        StringBuilder html = new StringBuilder();
        html.append("<html><head><meta charset='UTF-8'/>");
        html.append("<style>body{font-family:serif;line-height:1.5;} h1,h2{page-break-before:always;}</style>");
        html.append("</head><body>");

        for (Resource chapter : book.getContents()) {
            String chapterHtml = new String(chapter.getData(), StandardCharsets.UTF_8);
            // On garde uniquement le contenu du <body>
            int start = chapterHtml.indexOf("<body");
            int end = chapterHtml.lastIndexOf("</body>");
            if (start >= 0 && end > start) {
                chapterHtml = chapterHtml.substring(chapterHtml.indexOf('>', start) + 1, end);
            }
            html.append(chapterHtml);
        }
        html.append("</body></html>");

        try (OutputStream out = new FileOutputStream(pdfFile)) {
            ITextRenderer renderer = new ITextRenderer();
            renderer.setDocumentFromString(html.toString());
            renderer.layout();
            renderer.createPDF(out);
        }
    }

    public static void main(String[] args) throws Exception {
        convert(new File("livre.epub"), new File("livre.pdf"));
        System.out.println("Conversion terminée");
    }
}

Gérer le XHTML strict

Flying Saucer exige du XHTML bien formé. Les fichiers EPUB le respectent généralement, mais certains contiennent des balises HTML5 que Flying Saucer ne connaît pas (<nav>, <section>, <figure>). Solution : passer par jsoup pour nettoyer avant rendu :

import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Entities;

Document doc = Jsoup.parse(chapterHtml);
doc.outputSettings().syntax(Document.OutputSettings.Syntax.xml);
doc.outputSettings().escapeMode(Entities.EscapeMode.xhtml);
String propre = doc.html();

Gérer les images

Flying Saucer ne charge pas les images embarquées dans l'EPUB automatiquement. Deux options :

  1. Extraire les images dans un dossier temporaire et mettre à jour les chemins src="...".
  2. Embarquer en base64 directement dans le HTML : src="data:image/jpeg;base64,...".
for (Resource img : book.getResources().getAll()) {
    if (img.getMediaType().getName().startsWith("image/")) {
        Path out = Paths.get("/tmp/epub-images/", img.getHref());
        Files.createDirectories(out.getParent());
        Files.write(out, img.getData());
    }
}

Gérer les polices

Par défaut Flying Saucer n'intègre que Helvetica, Courier et Times. Pour des polices personnalisées (dont les caractères accentués), enregistrez-les :

renderer.getFontResolver().addFont(
    "/usr/share/fonts/truetype/dejavu/DejaVuSerif.ttf",
    "DejaVu Serif",
    "UTF-8",
    true,
    null
);

Et dans votre CSS :

body { font-family: 'DejaVu Serif', serif; }

Alternatives

Appeler Calibre en ligne de commande

Calibre est l'outil de référence, plus complet que n'importe quelle bibliothèque Java. Il peut être invoqué depuis Java via ProcessBuilder :

Process p = new ProcessBuilder("ebook-convert", "livre.epub", "livre.pdf")
    .inheritIO()
    .start();
p.waitFor();

Très bonne qualité de rendu, mais dépendance externe et démarrage plus lent.

Pandoc + LaTeX

Pour une qualité typographique optimale (livres publiés) :

pandoc livre.epub -o livre.pdf --pdf-engine=xelatex

iText 8 (solution commerciale)

iText 8 possède des modules capables de convertir HTML/EPUB vers PDF avec un excellent rendu, mais nécessite une licence commerciale au-delà des usages open source (AGPL).

Limites à connaître

  • Les EPUB 3 avec composants interactifs (JavaScript, audio) perdent leurs fonctions en PDF.
  • Les tables des matières ne sont pas toujours recréées automatiquement — il faut les régénérer à partir du fichier toc.ncx ou nav.xhtml.
  • Les fichiers protégés par DRM (Adobe ADEPT, Barnes & Noble) ne peuvent pas être convertis.

Pour un besoin ponctuel, Calibre en ligne de commande est imbattable. Pour un service automatisé en production, epublib + Flying Saucer offre un compromis solide entre indépendance et qualité.