diff --git a/CHANGES.md b/CHANGES.md index 9d799dbb..c36aa3a3 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -6,6 +6,12 @@ of error reporting by ensuring that only meaningful references are included in the ServiceError object. - REF275-1: Enforce JJWT to use Jackson 3 - RpcDelegate and RpcResult: refactoring +- PdfBuilder: extra space for single page PDF + - removed **`PostOffice`** parameter, replaced with **`SinglePageHeightCalculationListener.onHeightCalculationRetry()`** to let caller decide what to do when retry happened. + - Multiple **`SinglePageHeightCalculationListener`** can be added via **`PdfBuilder.addListener()`** + - Multiple **`SinglePageHeightCalculationListener`** can be removed via **`PdfBuilder.removeListener()`** + - A one time **`SinglePageHeightCalculationListener`** can be provided via **`htmlToPdf(...)`** to override added listeners + - max retry can be defined by caller via **`PdfBuilderConfig.setSinglePageHeightCalculationMaxIteration(int)`**, the default value is 3; it was previously hardcoded to 2. ## Version 2.7.4 (2026-10-24) diff --git a/pom.xml b/pom.xml index abfd650d..14e42f61 100644 --- a/pom.xml +++ b/pom.xml @@ -80,10 +80,10 @@ 2.2.1 3.4.0 3.12.0 - 3.1.4 + 3.2.0 3.2.8 1.7.0 - 0.10.0 + 0.12.0 @@ -157,17 +157,15 @@ UTF-8 - - 21 3.5.0 - 3.15.0 - 3.10.0 - 3.5.0 + 3.16.0 + 3.11.0 + 3.5.1 3.5.0 - 3.5.5 - 3.12.0 - 3.1.4 + 3.6.1 + 3.12.0 + 3.2.0 3.21.0 1.11.0 @@ -193,12 +191,12 @@ 33.7.2-jre 4.36.2 - 2.2.55 + 3.0.0 - 4.1.0 + 4.2.0 4.0.0 3.0.0 @@ -876,9 +874,10 @@ ${maven-compiler.version} true - - - + + -parameters + + @@ -922,7 +921,7 @@ org.apache.maven.plugins maven-javadoc-plugin - ${maven-doc-version} + ${maven-javadoc-version} --allow-script-in-comments diff --git a/src/main/java/org/summerboot/jexpress/integration/pdf/PdfBuilder.java b/src/main/java/org/summerboot/jexpress/integration/pdf/PdfBuilder.java index 4b35a707..c684f354 100644 --- a/src/main/java/org/summerboot/jexpress/integration/pdf/PdfBuilder.java +++ b/src/main/java/org/summerboot/jexpress/integration/pdf/PdfBuilder.java @@ -19,7 +19,6 @@ import com.google.protobuf.ByteString; import com.openhtmltopdf.util.XRLog; -import freemarker.template.Template; import org.apache.logging.log4j.Level; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; @@ -28,9 +27,7 @@ import org.summerboot.jexpress.api.common.BootPoi; import org.summerboot.jexpress.api.common.Err; import org.summerboot.jexpress.api.common.SessionContext; -import org.summerboot.jexpress.api.mail.PostOffice; import org.summerboot.jexpress.boot.BootConstants; -import org.summerboot.jexpress.integration.mail.config.SmtpClientConfig; import org.summerboot.jexpress.util.format.PageCssUtil; import java.io.File; @@ -40,9 +37,7 @@ import java.nio.file.Paths; import java.util.ArrayList; import java.util.Base64; -import java.util.HashMap; import java.util.List; -import java.util.Map; import java.util.zip.CRC32; /** @@ -65,22 +60,27 @@ public static PdfBuilder init(File templateDir, File fontDir, File fontCacheDir, return pdfBuilder; } - public final File dumpDir; - - public boolean isDumpEnabled() { - return BootConstants.isDebugMode() && dumpDir.exists(); + public static interface SinglePageHeightCalculationListener { + /** + * + * @param retry + * @param pageCount + * @param requesterTxId + * @param htmlContent + * @param agent + * @param extraSpace + * @param pageHeightMillimeters + */ + void onHeightCalculationRetry(int retry, int pageCount, String requesterTxId, String htmlContent, PdfBuilderConfig.Agent agent, int extraSpace, float pageHeightMillimeters); } - private File htmlTemplateDir; - + public final File dumpDir; + private final File htmlTemplateDir; + protected final PdfBoxAgent pdfBoxAgent; + protected final ItextAgent agentIText; + protected final List listeners = new ArrayList<>(); //private static final WriterProperties WRITER_PROPS = IText.buildDefaultAccessPermission(null, null, true, PdfVersion.PDF_1_7); - private final Map FreeMarkerTemplates = new HashMap<>(); - - protected PdfBoxAgent pdfBoxAgent; - protected ItextAgent agentIText; - - /** * Initializes the PDF builder with template and font directories. * @@ -113,21 +113,66 @@ protected PdfBuilder(File htmlTemplateDir, File fontDir, File fontCacheDir, File this.dumpDir = dumpDir; } - public byte[] html2PDF(String requesterTxId, String htmlContent, PdfBuilderConfig cfg, PostOffice po, SessionContext context) throws IOException { - return html2PDF(requesterTxId, htmlContent, false, cfg, po, context); + public File getDumpDir() { + return dumpDir; + } + + public File getHtmlTemplateDir() { + return htmlTemplateDir; } - public byte[] html2PDF(String requesterTxId, String htmlContent, boolean isSinglePage, PdfBuilderConfig cfg, PostOffice po, SessionContext context) throws IOException { - return html2PDF(requesterTxId, htmlContent, isSinglePage, 5, cfg, po, context); + public void addListener(SinglePageHeightCalculationListener listener) { + if (listener == null) { + return; + } + listeners.add(listener); } - public byte[] html2PDF(String requesterTxId, String htmlContent, boolean isSinglePage, int extraSpace, PdfBuilderConfig cfg, PostOffice po, SessionContext context) throws IOException { + public boolean removeListener(SinglePageHeightCalculationListener listener) { + return listeners.remove(listener); + } + + public boolean isDumpEnabled() { + return BootConstants.isDebugMode() && dumpDir.exists(); + } + + /** + * Build a multipage PDF + * + * @param requesterTxId + * @param htmlContent + * @param cfg + * @param context + * @return + * @throws IOException + */ + public byte[] htmlToPdf(String requesterTxId, String htmlContent, PdfBuilderConfig cfg, SessionContext context) throws IOException { + return htmlToPdf(requesterTxId, htmlContent, cfg, false, 0, null, context); + } + + /** + * Build a single page PDF + * + * @param requesterTxId + * @param htmlContent + * @param cfg + * @param extraSpace + * @param onetimeListener + * @param context + * @return + * @throws IOException + */ + public byte[] htmlToPdf(String requesterTxId, String htmlContent, PdfBuilderConfig cfg, int extraSpace, SinglePageHeightCalculationListener onetimeListener, SessionContext context) throws IOException { + return htmlToPdf(requesterTxId, htmlContent, cfg, true, extraSpace, onetimeListener, context); + } + + protected byte[] htmlToPdf(String requesterTxId, String htmlContent, PdfBuilderConfig cfg, boolean isSinglePage, int extraSpace, SinglePageHeightCalculationListener onetimeListener, SessionContext context) throws IOException { context.poi(BootPoi.PDF_BEGIN); PdfBuilderConfig.Agent agent = cfg.getAgent(); if (extraSpace < 1) { extraSpace = 5; } - final String sessionName = requesterTxId + "_" + agent + "_" + isSinglePage + "_" + extraSpace; + final String sessionName = requesterTxId + "[" + agent + "]" + (isSinglePage ? ("SP+" + extraSpace + "mm") : "MP"); byte[] pdf = null; try { if (isSinglePage) { @@ -138,21 +183,33 @@ public byte[] html2PDF(String requesterTxId, String htmlContent, boolean isSingl int pageCount = layoutInfo.getPageCount(); float pageHeightMillimeters = layoutInfo.getPageHeight() * pageCount / PdfBoxAgent.POINTS_PER_MM; String htmlTemplate = htmlContent; - int retry = 0; - while (pageCount > 1 && retry < 2) { + int iterationCount = 0; + while (pageCount > 1 && iterationCount < cfg.getSinglePageHeightCalculationMaxIteration()) { pageHeightMillimeters += extraSpace;//add extra space htmlTemplate = PageCssUtil.setHeight(htmlContent, pageHeightMillimeters + "mm;"); layoutInfo = pdfBoxAgent.layoutThenGetInfo(htmlTemplate, htmlTemplateDir); pageCount = layoutInfo.getPageCount(); - retry++; - context.poi(BootPoi.PDF_HV); + iterationCount++; + context.poi(BootPoi.PDF_HV) + .memo("iterationCount=" + iterationCount + ",pages=" + pageCount + ", extraSpace=" + extraSpace + "mm, height=" + pageHeightMillimeters + "mm"); } - if (retry > 1) { - context.level(Level.WARN).memo("template height oversize retry = " + retry).error(new Err(0, null, null, null, sessionName + " template height oversize retry = " + retry)); - //log.fatal("template height oversize (retry={}) for TxId={}", retry, sessionName); - if (po != null) { - po.sendAlertAsync(SmtpClientConfig.cfg.getEmailToDevelopment(), "HTML oversize @" + sessionName, "retry=" + retry, null, true); + if (iterationCount > 1) { + context.level(Level.WARN) + .memo("template height oversize iterationCount = " + iterationCount) + .error(new Err(0, null, null, null, sessionName + " template height oversize iterationCount = " + iterationCount)); + if (onetimeListener == null) { + for (SinglePageHeightCalculationListener addedListener : listeners) { + if (addedListener == null) { + continue; + } + addedListener.onHeightCalculationRetry(iterationCount, pageCount, requesterTxId, htmlContent, agent, extraSpace, pageHeightMillimeters); + } + } else { + onetimeListener.onHeightCalculationRetry(iterationCount, pageCount, requesterTxId, htmlContent, agent, extraSpace, pageHeightMillimeters); } +// if (po != null) { +// po.sendAlertAsync(SmtpClientConfig.cfg.getEmailToDevelopment(), "HTML oversize @" + sessionName, "iterationCount=" + iterationCount, null, true); +// } } htmlContent = htmlTemplate; } @@ -192,7 +249,6 @@ public byte[] html2PDF(String requesterTxId, String htmlContent, boolean isSingl return pdf; } - public List pdf2Images(String requesterTxId, byte[] pdf, String password, ImageType imageType, float imageDPI, String imageFormat, RenderDestination renderDestination, SessionContext context) throws IOException { context.poi(BootPoi.PDF2IMG_BEGIN); String sessionName = requesterTxId + "_" + imageType + "_" + renderDestination + "_" + imageDPI + "_" + imageFormat; diff --git a/src/main/java/org/summerboot/jexpress/integration/pdf/PdfBuilderConfig.java b/src/main/java/org/summerboot/jexpress/integration/pdf/PdfBuilderConfig.java index ef9c14ac..0fdb926a 100644 --- a/src/main/java/org/summerboot/jexpress/integration/pdf/PdfBuilderConfig.java +++ b/src/main/java/org/summerboot/jexpress/integration/pdf/PdfBuilderConfig.java @@ -49,6 +49,7 @@ public static final PdfBuilderConfig buildUnprotectedConfig() { private String userPwd = null; private int encryptionKeyLength = ProtectionSpec.EncryptionKeyLength; // PDFBox private int encryptionAlgorithm = ProtectionSpec.EncryptionAlgorithm; // iText + private int singlePageHeightCalculationMaxIteration = 3; public PdfBuilderConfig() { this(null); @@ -168,4 +169,12 @@ public int getEncryptionAlgorithm() { public void setEncryptionAlgorithm(int encryptionAlgorithm) { this.encryptionAlgorithm = encryptionAlgorithm; } + + public int getSinglePageHeightCalculationMaxIteration() { + return singlePageHeightCalculationMaxIteration; + } + + public void setSinglePageHeightCalculationMaxIteration(int singlePageHeightCalculationMaxIteration) { + this.singlePageHeightCalculationMaxIteration = singlePageHeightCalculationMaxIteration; + } }