package com.megatim.manuel.core.convert; import javax.imageio.ImageIO; import java.awt.Graphics2D; import java.awt.RenderingHints; import java.awt.image.BufferedImage; import java.io.IOException; import java.io.OutputStream; import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Path; import java.util.regex.Matcher; import java.util.regex.Pattern; /** Outils partagés par tous les convertisseurs : nommage, échappement, écriture HTML et images. */ public abstract class AbstractHtmlConverter implements DocumentConverter { /** Reconnaît "Heading 2", "heading2", "Titre 3", "Título 1", "..._niveau_3"... */ private static final Pattern HEADING = Pattern.compile("(?i).*?(?:heading|titre|t[ií]tulo|niveau)[\\s_]*([1-6]).*"); protected final Path outputDir; protected AbstractHtmlConverter(Path outputDir) { this.outputDir = outputDir; } /** Niveau de titre déduit d'un nom/identifiant de style Word, ou 0 si ce n'est pas un titre. */ protected static int matchHeading(String s) { if (s == null) return 0; Matcher m = HEADING.matcher(s); if (m.matches()) return Integer.parseInt(m.group(1)); if (s.equalsIgnoreCase("Title") || s.equalsIgnoreCase("Titre")) return 1; return 0; } protected static String esc(String s) { if (s == null) return ""; return s.replace("&", "&").replace("<", "<").replace(">", ">").replace("\"", """); } protected static String stripExt(String n) { int i = n.lastIndexOf('.'); return i > 0 ? n.substring(0, i) : n; } protected static String sanitize(String n) { return n.replaceAll("[^A-Za-z0-9_-]", "_"); } /** Base de nom unique et ordonnée, ex. "03_mon_manuel". */ protected String safeBase(Path source, int order) { return String.format("%02d_%s", order, sanitize(stripExt(source.getFileName().toString()))); } protected String wrapHtml(String title, String body) { return "\n\n" + "\n" + "" + esc(title) + "\n" + "\n" + "\n\n" + body + "\n"; } protected void writeHtml(String fileName, String title, String body) throws IOException { Files.write(outputDir.resolve(fileName), wrapHtml(title, body).getBytes(StandardCharsets.UTF_8)); } /** Écrit une image binaire (extraite d'un document) et renvoie son chemin relatif. */ protected String writeImageBytes(String safe, int idx, String ext, byte[] data) throws IOException { String name = "images/" + safe + "_img" + idx + "." + ext; Path target = outputDir.resolve(name); Files.createDirectories(target.getParent()); Files.write(target, data); return name; } /** Écrit une image PNG (rendu d'une diapositive) et renvoie son chemin relatif. */ protected String writeImagePng(String safe, int idx, BufferedImage img) throws IOException { String name = "images/" + safe + "_img" + idx + ".png"; Path target = outputDir.resolve(name); Files.createDirectories(target.getParent()); try (OutputStream os = Files.newOutputStream(target)) { ImageIO.write(img, "png", os); } return name; } /** Réglages de rendu de qualité pour la peinture des diapositives. */ protected static void applyQualityHints(Graphics2D g) { g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); g.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY); g.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR); g.setRenderingHint(RenderingHints.KEY_FRACTIONALMETRICS, RenderingHints.VALUE_FRACTIONALMETRICS_ON); g.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON); } }