diff --git a/java/src/main/java/com/genexus/reports/PDFReportPDFBox.java b/java/src/main/java/com/genexus/reports/PDFReportPDFBox.java index b4f81a368..727f8fb0e 100644 --- a/java/src/main/java/com/genexus/reports/PDFReportPDFBox.java +++ b/java/src/main/java/com/genexus/reports/PDFReportPDFBox.java @@ -30,8 +30,6 @@ import org.apache.pdfbox.pdmodel.graphics.state.RenderingMode; import org.apache.pdfbox.pdmodel.interactive.action.PDActionJavaScript; import org.apache.pdfbox.pdmodel.interactive.viewerpreferences.PDViewerPreferences; -import org.apache.pdfbox.text.PDFTextStripper; -import org.apache.pdfbox.text.TextPosition; import org.apache.pdfbox.util.Matrix; import org.jsoup.Jsoup; @@ -51,9 +49,6 @@ public class PDFReportPDFBox extends GXReportPDFCommons{ private String barcodeType = null; private PDDocument document; private PDDocumentCatalog writer; - private PDFont templateFont; - private float templateX; - private float templateY; public boolean lineCapProjectingSquare = true; public boolean barcode128AsImage = true; ConcurrentHashMap documentImages; @@ -63,6 +58,7 @@ public class PDFReportPDFBox extends GXReportPDFCommons{ private Set supportedHTMLTags = new HashSet<>(); private PDPageContentStream currentPageContentStream; private final static String PAGES_PLACEHOLDER = "{{pages}}"; + private final List pageCountPlaceholders = new ArrayList<>(); static { log = org.apache.logging.log4j.LogManager.getLogger(PDFReportPDFBox.class); @@ -647,6 +643,31 @@ public void GxDrawText(String sTxt, int left, int top, int right, int bottom, in int alignment = align & 3; boolean autoResize = ((align & 256) == 256); + if (sTxt.trim().equalsIgnoreCase(PAGES_PLACEHOLDER)) { + float width = baseFont.getStringWidth("999") / 1000 * fontSize; + + float x; + switch (align) { + case 1: + x = ((leftAux + rightAux) / 2) + leftMargin - width / 2; + break; + case 2: + x = rightAux + leftMargin - width; + break; + default: + x = leftAux + leftMargin; + break; + } + + float y = this.pageSize.getUpperRightY() - bottomAux - topMargin - bottomMargin - fontSize*0.2f ; + + int pageIndex = document.getNumberOfPages() - 1; + pageCountPlaceholders.add(new PageCountPlaceholder(pageIndex, x, y, baseFont, fontSize, foreColor)); + + return; + } + + // Handle HTML format if (htmlformat == 1) { log.debug("WARNING: HTML rendering is not natively supported by PDFBOX 2.0.27. Handcrafted support is provided but it is not intended to cover all possible use cases"); @@ -857,19 +878,6 @@ else if (barcodeType != null) { contentStream.fill(); } - // Handle {{Pages}} - if (sTxt.trim().equalsIgnoreCase(PAGES_PLACEHOLDER)) { - if (!templateCreated) { - templateFont = baseFont; - templateFontSize = fontSize; - templateColorFill = foreColor; - templateX = leftAux + leftMargin; - templateY = this.pageSize.getUpperRightY() - bottomAux - topMargin - bottomMargin; - templateCreated = true; - } - sTxt = PAGES_PLACEHOLDER; - } - float textBlockWidth = rightAux - leftAux; float txtWidth = baseFont.getStringWidth(sTxt) / 1000 * fontSize; boolean justified = (alignment == 3) && textBlockWidth < txtWidth; @@ -1271,75 +1279,22 @@ else if (length == 15840 && width == 12240) return new PDRectangle((int)(width / PAGE_SCALE_X) + leftMargin, (int)(length / PAGE_SCALE_Y) + topMargin); } - private void replaceTemplatePages() throws IOException { + private void writePageCountPlaceholders() { int totalPages = document.getNumberOfPages(); - for (int i = 0; i < totalPages; i++) { - final PDPage page = document.getPage(i); - final List replacements = new java.util.ArrayList<>(); - PDFTextStripper stripper = new PDFTextStripper() { - @Override - protected void writeString(String text, List textPositions) throws IOException { - String placeholder = PAGES_PLACEHOLDER; - int index = text.indexOf(placeholder); - while (index != -1 && index + placeholder.length() <= textPositions.size()) { - float minX = Float.MAX_VALUE; - float maxX = 0; - float minY = Float.MAX_VALUE; - float maxY = 0; - for (int j = index; j < index + placeholder.length(); j++) { - TextPosition tp = textPositions.get(j); - float tpX = tp.getXDirAdj(); - float tpY = tp.getYDirAdj(); - float tpWidth = tp.getWidthDirAdj(); - float tpHeight = tp.getHeightDir(); - if (tpX < minX) { - minX = tpX; - } - if (tpX + tpWidth > maxX) { - maxX = tpX + tpWidth; - } - if (tpY < minY) { - minY = tpY; - } - if (tpY + tpHeight > maxY) { - maxY = tpY + tpHeight; - } - } - float bboxWidth = maxX - minX; - float bboxHeight = maxY - minY; - float origBoxBottom = pageSize.getHeight() - maxY; - float originalCenterX = minX + bboxWidth / 2; - float originalCenterY = origBoxBottom + bboxHeight / 2; - float newCenterY = originalCenterY + (bboxHeight * 0.5f); - float enlargedWidth = bboxWidth * 2.5f; - float enlargedHeight = bboxHeight * 2.5f; - float rectX = originalCenterX - (enlargedWidth / 2); - float rectY = newCenterY - (enlargedHeight / 2); - float baselineY = newCenterY; - replacements.add(new float[] { rectX, rectY, enlargedWidth, enlargedHeight, baselineY }); - index = text.indexOf(placeholder, index + placeholder.length()); - } - super.writeString(text, textPositions); - } - }; - stripper.setStartPage(i + 1); - stripper.setEndPage(i + 1); - stripper.getText(document); - if (!replacements.isEmpty()) { - try (PDPageContentStream cs = new PDPageContentStream( - document, page, PDPageContentStream.AppendMode.APPEND, true, true)) { - for (float[] rep : replacements) { - cs.addRect(rep[0], rep[1], rep[2], rep[3]); - cs.setNonStrokingColor(java.awt.Color.WHITE); - cs.fill(); - cs.beginText(); - cs.setFont(templateFont, templateFontSize); - cs.setNonStrokingColor(templateColorFill); - cs.newLineAtOffset(rep[0], rep[4]); - cs.showText(String.valueOf(totalPages)); - cs.endText(); - } - } + + for (PageCountPlaceholder p : pageCountPlaceholders) { + PDPage page = document.getPage(p.pageIndex); + try (PDPageContentStream cs = new PDPageContentStream( + document, page, PDPageContentStream.AppendMode.APPEND, true)) { + + cs.beginText(); + cs.setFont(p.font, p.fontSize); + cs.setNonStrokingColor(p.color); + cs.newLineAtOffset(p.x, p.y); + cs.showText(String.valueOf(totalPages)); + cs.endText(); + } catch (IOException e) { + log.error("Failed to write page count placeholder", e); } } } @@ -1351,7 +1306,7 @@ public void GxEndDocument() { pages++; } GxEndPage(); - replaceTemplatePages(); + writePageCountPlaceholders(); int copies = 1; try { copies = Integer.parseInt(printerSettings.getProperty(form, Const.COPIES)); @@ -1488,4 +1443,22 @@ public void GxEndPage() { } } + private static class PageCountPlaceholder { + final int pageIndex; + final float x; + final float y; + final PDFont font; + final float fontSize; + final Color color; + + PageCountPlaceholder(int pageIndex, float x, float y, PDFont font, float fontSize, Color color) { + this.pageIndex = pageIndex; + this.x = x; + this.y = y; + this.font = font; + this.fontSize = fontSize; + this.color = color; + } + } + }