package com.zestedesavoir.zestwriter.utils; import com.openhtmltopdf.DOMBuilder; import com.openhtmltopdf.extend.HttpStream; import com.openhtmltopdf.extend.HttpStreamFactory; import com.openhtmltopdf.pdfboxout.PdfRendererBuilder; import com.openhtmltopdf.util.XRLog; import com.zestedesavoir.zestwriter.MainApp; import lombok.extern.slf4j.Slf4j; import okhttp3.OkHttpClient; import okhttp3.Request; import okhttp3.Response; import org.apache.pdfbox.multipdf.PDFMergerUtility; import org.apache.pdfbox.pdmodel.PDDocument; import org.apache.pdfbox.pdmodel.PDPage; import org.apache.pdfbox.pdmodel.PDPageContentStream; import org.apache.pdfbox.pdmodel.font.PDFont; import org.apache.pdfbox.pdmodel.font.PDTrueTypeFont; import org.jsoup.Jsoup; import java.awt.*; import java.io.*; import java.net.URL; import java.util.ArrayList; import java.util.List; @Slf4j public class PdfUtilExport { private String titleContent; private String authorContent; private String srcHtmlPath; private String destPdfPath; private final static int FONT_SIZE_TITLE = 20; private final static int FONT_SIZE_AUTHOR = 12; private static PDFont FONT_STYLE_COVER; private static final String FONT_MERRIWEATHER_REGULAR="assets/static/fonts/Merriweather-Regular.ttf"; private static final String FONT_MERRIWEATHER_BOLD="assets/static/fonts/Merriweather-Bold.ttf"; private static final String FONT_MERRIWEATHER_ITALIC="assets/static/fonts/Merriweather-Italic.ttf"; private static final String FONT_SOURCE_CODE_PRO="assets/static/fonts/SourceCodePro-Regular.ttf"; public PdfUtilExport(String titleContent, String authorContent, String srcHtmlPath, String destPdfPath) { this.titleContent = titleContent.toUpperCase(); this.authorContent = authorContent; this.srcHtmlPath = srcHtmlPath; this.destPdfPath = destPdfPath; XRLog.setLoggingEnabled(false); } private List<String> wrapText(float width) throws IOException { List<String> lines = new ArrayList<>(); int lastSpace = -1; while (titleContent.length() > 0) { int spaceIndex = titleContent.indexOf(' ', lastSpace + 1); if (spaceIndex < 0) spaceIndex = titleContent.length(); String subString = titleContent.substring(0, spaceIndex); float size = FONT_SIZE_TITLE * FONT_STYLE_COVER.getStringWidth(subString) / 1000; if (size > width) { if (lastSpace < 0) lastSpace = spaceIndex; subString = titleContent.substring(0, lastSpace); lines.add(subString); titleContent = titleContent.substring(lastSpace).trim(); lastSpace = -1; } else if (spaceIndex == titleContent.length()) { lines.add(titleContent); titleContent = ""; } else { lastSpace = spaceIndex; } } return lines; } private void addCoverpage() throws IOException { float leading = 1.5f * FONT_SIZE_TITLE; PDDocument document = new PDDocument(); PDPage page = new PDPage(); document.addPage(page); FONT_STYLE_COVER = PDTrueTypeFont.loadTTF(document, MainApp.class.getResourceAsStream(FONT_MERRIWEATHER_BOLD)); PDPageContentStream contentStream = new PDPageContentStream(document, page); contentStream.setNonStrokingColor(25, 81, 107); contentStream.fillRect(0, 0, page.getMediaBox().getWidth(), (page.getMediaBox().getHeight() / 2) - 10); contentStream.fillRect(0, (page.getMediaBox().getHeight() / 2) + 10, page.getMediaBox().getWidth(), (page.getMediaBox().getHeight() / 2) - 10); contentStream.setNonStrokingColor(248, 173, 50); contentStream.fillRect(0, (page.getMediaBox().getHeight() / 2) - 10, page.getMediaBox().getWidth(), 20); contentStream.beginText(); contentStream.setNonStrokingColor(Color.WHITE); contentStream.setFont(FONT_STYLE_COVER, FONT_SIZE_AUTHOR); contentStream.newLineAtOffset(20, 20); contentStream.showText(authorContent); contentStream.setFont(FONT_STYLE_COVER, FONT_SIZE_TITLE); contentStream.newLineAtOffset((page.getMediaBox().getWidth() / 2) - 20, 600); List<String> lines = wrapText((page.getMediaBox().getWidth() / 2) - 20); for (String line : lines) { contentStream.showText(line); contentStream.newLineAtOffset(0, -leading); } contentStream.endText(); contentStream.close(); File temp = File.createTempFile("coverpage-zds", ".pdf"); document.save(temp); document.close(); PDFMergerUtility mergerUtility = new PDFMergerUtility(); mergerUtility.addSource(temp); mergerUtility.addSource(destPdfPath); mergerUtility.setDestinationFileName(destPdfPath); mergerUtility.mergeDocuments(); } public boolean exportToPdf() { log.info("Tentative d'export PDF du fichier '"+srcHtmlPath+"' vers '"+destPdfPath+"'"); try(OutputStream os = new FileOutputStream(destPdfPath)) { // There are more options on the builder than shown below. PdfRendererBuilder builder = new PdfRendererBuilder(); builder.withUri(destPdfPath); builder.toStream(os); builder.useFont(new File(MainApp.class.getResource(FONT_MERRIWEATHER_REGULAR).getFile()), "Merriweather",400, PdfRendererBuilder.FontStyle.NORMAL,true); builder.useFont(new File(MainApp.class.getResource(FONT_MERRIWEATHER_ITALIC).getFile()), "Merriweather", 400, PdfRendererBuilder.FontStyle.ITALIC,true); builder.useFont(new File(MainApp.class.getResource(FONT_MERRIWEATHER_BOLD).getFile()), "Merriweather", 700, PdfRendererBuilder.FontStyle.NORMAL, true); builder.useFont(new File(MainApp.class.getResource(FONT_SOURCE_CODE_PRO).getFile()), "Source Code Pro"); builder.withW3cDocument(html5ParseDocument(srcHtmlPath, 1000), srcHtmlPath); builder.useHttpStreamImplementation(new OkHttpStreamFactory()); builder.run(); log.debug("Tentative d'ajout de la page de la couverture"); addCoverpage(); log.info("Fichier PDF crée avec succès"); return true; } catch (IOException e1) { log.error(e1.getMessage(), e1); } catch (Exception e) { log.error(e.getMessage(), e); } return false; } private org.w3c.dom.Document html5ParseDocument(String urlStr, int timeoutMs) throws IOException { URL url = new URL(urlStr); org.jsoup.nodes.Document doc; if (url.getProtocol().equalsIgnoreCase("file")) { doc = Jsoup.parse(new File(url.getPath()), "UTF-8"); } else { doc = Jsoup.parse(url, timeoutMs); } return DOMBuilder.jsoup2DOM(doc); } } @Slf4j class OkHttpStreamFactory implements HttpStreamFactory { private final OkHttpClient client = new OkHttpClient(); @Override public HttpStream getUrl(String url) { Request request = new Request.Builder() .url(url) .build(); try { final Response response = client.newCall(request).execute(); return new HttpStream() { @Override public InputStream getStream() { return response.body().byteStream(); } @Override public Reader getReader() { return response.body().charStream(); } }; } catch (IOException e) { log.error(e.getMessage(), e); } return null; } }