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;
+ }
}