From 3fc063feebd845274db235e0c347d0f562b2dff4 Mon Sep 17 00:00:00 2001 From: tomas-sexenian Date: Tue, 30 May 2023 13:32:59 -0300 Subject: [PATCH 1/2] Better support for HTML rendering when using iText8 report implementation Issue: 103050 --- .../main/java/com/genexus/GXWebReport.java | 4 ++-- .../java/com/genexus/reports/GXReport.java | 4 ++-- ...ReportItext7.java => PDFReportItext8.java} | 20 ++++++++++++++++--- 3 files changed, 21 insertions(+), 7 deletions(-) rename java/src/main/java/com/genexus/reports/{PDFReportItext7.java => PDFReportItext8.java} (97%) diff --git a/java/src/main/java/com/genexus/GXWebReport.java b/java/src/main/java/com/genexus/GXWebReport.java index ee6aa69c2..98bbe1a55 100644 --- a/java/src/main/java/com/genexus/GXWebReport.java +++ b/java/src/main/java/com/genexus/GXWebReport.java @@ -38,8 +38,8 @@ protected void initState(ModelContext context, UserInformation ui) String implementation = com.genexus.Application.getClientContext().getClientPreferences().getPDF_RPT_LIBRARY(); if (implementation.equals("ITEXT")) reportHandler = new PDFReportItext2(context); - else if (implementation.equals("ITEXT7")) - reportHandler = new PDFReportItext7(context); + else if (implementation.equals("ITEXT8")) + reportHandler = new PDFReportItext8(context); else reportHandler = new PDFReportPDFBox(context); initValues(); diff --git a/java/src/main/java/com/genexus/reports/GXReport.java b/java/src/main/java/com/genexus/reports/GXReport.java index a13e28331..9ed15ec4a 100644 --- a/java/src/main/java/com/genexus/reports/GXReport.java +++ b/java/src/main/java/com/genexus/reports/GXReport.java @@ -75,8 +75,8 @@ else if (getOutputType() == OUTPUT_PDF) String implementation = com.genexus.Application.getClientContext().getClientPreferences().getPDF_RPT_LIBRARY(); if (implementation.equals("ITEXT")) reportHandler = new PDFReportItext2(context); - else if (implementation.equals("ITEXT7")) - reportHandler = new PDFReportItext7(context); + else if (implementation.equals("ITEXT8")) + reportHandler = new PDFReportItext8(context); else reportHandler = new PDFReportPDFBox(context); ((GXReportPDFCommons) reportHandler).setOutputStream(httpContext.getOutputStream()); diff --git a/java/src/main/java/com/genexus/reports/PDFReportItext7.java b/java/src/main/java/com/genexus/reports/PDFReportItext8.java similarity index 97% rename from java/src/main/java/com/genexus/reports/PDFReportItext7.java rename to java/src/main/java/com/genexus/reports/PDFReportItext8.java index 26960c750..dbef39e53 100644 --- a/java/src/main/java/com/genexus/reports/PDFReportItext7.java +++ b/java/src/main/java/com/genexus/reports/PDFReportItext8.java @@ -46,7 +46,7 @@ import java.util.concurrent.ConcurrentHashMap; import java.util.zip.Deflater; -public class PDFReportItext7 extends GXReportPDFCommons { +public class PDFReportItext8 extends GXReportPDFCommons { private PageSize pageSize; private PdfFont baseFont; private Barcode128 barcode = null; @@ -61,10 +61,10 @@ public class PDFReportItext7 extends GXReportPDFCommons { ConcurrentHashMap documentImages; static { - log = org.apache.logging.log4j.LogManager.getLogger(PDFReportItext7.class); + log = org.apache.logging.log4j.LogManager.getLogger(PDFReportItext8.class); } - public PDFReportItext7(ModelContext context) { + public PDFReportItext8(ModelContext context) { super(context); document = null; pdfDocument = null; @@ -757,6 +757,20 @@ void processHTMLElement(Canvas canvas, Rectangle htmlRectangle, YPosition curren canvas.showTextAligned(p, htmlRectangle.getLeft(), currentYPosition.getCurrentYPosition() - p.getMarginTop().getValue(), txtAlignment); currentYPosition.setCurrentYPosition(currentYPosition.getCurrentYPosition() - (height + padding)); + } else if (blockElement instanceof Table){ + Table table = (Table) blockElement; + table.setFixedPosition(page, htmlRectangle.getX(), htmlRectangle.getY(), htmlRectangle.getWidth()); + if ((currentYPosition.getCurrentYPosition() - table.getHeight().getValue()) < htmlRectangle.getBottom()) { + pdfPage = pdfDocument.addNewPage(); + pages++; + currentYPosition.setCurrentYPosition(htmlRectangle.getTop()); + } + document.add(table); + + } else if (blockElement instanceof com.itextpdf.layout.element.List){ + com.itextpdf.layout.element.List list = (com.itextpdf.layout.element.List) blockElement; + list.setFixedPosition(page, htmlRectangle.getX(), htmlRectangle.getY(), htmlRectangle.getWidth()); + document.add(list); } else if (blockElement instanceof Div) { Div div = (Div) blockElement; // Iterate through the children of the Div From df7bdc97a3aa90498533cba82ef2b9fd843f5b7e Mon Sep 17 00:00:00 2001 From: tomas-sexenian Date: Tue, 6 Jun 2023 11:52:04 -0300 Subject: [PATCH 2/2] Better blockElement positioning and support for more block elements --- .../com/genexus/reports/PDFReportItext8.java | 77 ++++++++++--------- .../com/genexus/reports/PDFReportPDFBox.java | 2 +- 2 files changed, 40 insertions(+), 39 deletions(-) diff --git a/java/src/main/java/com/genexus/reports/PDFReportItext8.java b/java/src/main/java/com/genexus/reports/PDFReportItext8.java index dbef39e53..d05454fff 100644 --- a/java/src/main/java/com/genexus/reports/PDFReportItext8.java +++ b/java/src/main/java/com/genexus/reports/PDFReportItext8.java @@ -9,7 +9,9 @@ import com.genexus.webpanels.HttpContextWeb; import com.itextpdf.barcodes.Barcode128; +import com.itextpdf.html2pdf.ConverterProperties; import com.itextpdf.html2pdf.HtmlConverter; +import com.itextpdf.html2pdf.resolver.font.DefaultFontProvider; import com.itextpdf.io.font.PdfEncodings; import com.itextpdf.io.font.constants.StandardFonts; import com.itextpdf.io.font.otf.Glyph; @@ -33,6 +35,8 @@ import com.itextpdf.layout.borders.Border; import com.itextpdf.layout.element.*; import com.itextpdf.layout.element.Image; +import com.itextpdf.layout.layout.LayoutArea; +import com.itextpdf.layout.layout.LayoutContext; import com.itextpdf.layout.properties.*; import com.itextpdf.kernel.pdf.PdfDocument; import com.itextpdf.layout.splitting.DefaultSplitCharacters; @@ -79,7 +83,7 @@ protected void init() { pdfDocument = new PdfDocument(writer); pdfDocument.setDefaultPageSize(this.pageSize); document = new Document(pdfDocument); - + document.setFontProvider(new DefaultFontProvider()); } catch (Exception e){ log.error("Failed to initialize new iText7 document: ", e); } @@ -525,6 +529,7 @@ else if (fontBold) else baseFont = PdfFontFactory.createFont(fontPath, PdfEncodings.IDENTITY_H, EmbeddingStrategy.PREFER_EMBEDDED); } + document.getFontProvider().addFont(baseFont.getFontProgram()); } catch(IOException ioe) { log.error("GxAttris failed: ", ioe); @@ -586,8 +591,7 @@ else if (valign == VerticalAlign.BOTTOM.value()) boolean autoResize = (align & 256) == 256; if (htmlformat == 1) { - //As of now, you might experience unexpected behaviour since not all possible - //HTML code is supported + log.info("As of now, you might experience unexpected behaviour since not all possible HTML code is supported"); try { bottomAux = (float)convertScale(bottom); topAux = (float)convertScale(top); @@ -604,10 +608,13 @@ else if (valign == VerticalAlign.BOTTOM.value()) YPosition yPosition = new YPosition(htmlRectangle.getTop()); TextAlignment txtAlignment = getTextAlignment(alignment); + ConverterProperties converterProperties = new ConverterProperties(); + converterProperties.setFontProvider(document.getFontProvider()); //Iterate over the elements (a.k.a the parsed HTML string) and handle each case accordingly - List elements = HtmlConverter.convertToElements(sTxt); + List elements = HtmlConverter.convertToElements(sTxt, converterProperties); for (IElement element : elements) - processHTMLElement(canvas,htmlRectangle, yPosition, txtAlignment, (IBlockElement) element); + processHTMLElement(htmlRectangle, yPosition, txtAlignment, (IBlockElement) element); + } catch (Exception e) { log.error("GxDrawText failed to print HTML text : ", e); } @@ -734,54 +741,48 @@ else if (valign == VerticalAlign.BOTTOM.value()) } } - void processHTMLElement(Canvas canvas, Rectangle htmlRectangle, YPosition currentYPosition, TextAlignment txtAlignment, IBlockElement blockElement){ - float padding = 5f; + void processHTMLElement(Rectangle htmlRectangle, YPosition currentYPosition, TextAlignment txtAlignment, IBlockElement blockElement){ if (blockElement instanceof Paragraph){ Paragraph p = (Paragraph) blockElement; - - // calculate the height of the Paragraph - float marginTop = (p.getMarginTop() != null) ? p.getMarginTop().getValue() : 0; - float marginBottom = (p.getMarginBottom() != null) ? p.getMarginBottom().getValue() : 0; - float paddingTop = (p.getPaddingTop() != null) ? p.getPaddingTop().getValue() : 0; - float paddingBottom = (p.getPaddingBottom() != null) ? p.getPaddingBottom().getValue() : 0; - float height = marginTop + marginBottom + paddingTop + paddingBottom; - - // check if the currentYPosition has enough space for the new Paragraph - if ((currentYPosition.getCurrentYPosition() - height) < htmlRectangle.getBottom()) { - // add new page and reset the currentYPosition - pdfPage = pdfDocument.addNewPage(); - pages++; - canvas = new Canvas(new PdfCanvas(pdfPage), htmlRectangle); - currentYPosition.setCurrentYPosition(htmlRectangle.getTop()); - } - - canvas.showTextAligned(p, htmlRectangle.getLeft(), currentYPosition.getCurrentYPosition() - p.getMarginTop().getValue(), txtAlignment); - currentYPosition.setCurrentYPosition(currentYPosition.getCurrentYPosition() - (height + padding)); + float paragraphHeight = getBlockElementHeight(blockElement, htmlRectangle); + document.showTextAligned(p, htmlRectangle.getLeft(), currentYPosition.getCurrentYPosition(), txtAlignment); + currentYPosition.setCurrentYPosition(currentYPosition.getCurrentYPosition() - paragraphHeight); } else if (blockElement instanceof Table){ Table table = (Table) blockElement; - table.setFixedPosition(page, htmlRectangle.getX(), htmlRectangle.getY(), htmlRectangle.getWidth()); - if ((currentYPosition.getCurrentYPosition() - table.getHeight().getValue()) < htmlRectangle.getBottom()) { - pdfPage = pdfDocument.addNewPage(); - pages++; - currentYPosition.setCurrentYPosition(htmlRectangle.getTop()); - } + float tableHeight = getBlockElementHeight(blockElement, htmlRectangle); + table.setFixedPosition(page, htmlRectangle.getX(), currentYPosition.getCurrentYPosition() - tableHeight, htmlRectangle.getWidth()); + currentYPosition.setCurrentYPosition(currentYPosition.getCurrentYPosition() - tableHeight); document.add(table); - } else if (blockElement instanceof com.itextpdf.layout.element.List){ com.itextpdf.layout.element.List list = (com.itextpdf.layout.element.List) blockElement; - list.setFixedPosition(page, htmlRectangle.getX(), htmlRectangle.getY(), htmlRectangle.getWidth()); + float listHeight = getBlockElementHeight(blockElement, htmlRectangle); + list.setFixedPosition(page, htmlRectangle.getX(),currentYPosition.getCurrentYPosition() - listHeight, htmlRectangle.getWidth()); + currentYPosition.setCurrentYPosition(currentYPosition.getCurrentYPosition() - listHeight); document.add(list); } else if (blockElement instanceof Div) { Div div = (Div) blockElement; - // Iterate through the children of the Div + // Iterate through the children of the Div and process each child element recursively for (IElement child : div.getChildren()) if (child instanceof IBlockElement) - // Process the child element recursively - processHTMLElement(canvas,htmlRectangle, currentYPosition, txtAlignment, (IBlockElement) child); + processHTMLElement(htmlRectangle, currentYPosition, txtAlignment, (IBlockElement) child); + } + } + + private float getBlockElementHeight(IBlockElement blockElement, Rectangle htmlRectangle) throws RuntimeException{ + if (blockElement instanceof Paragraph){ + Paragraph p = (Paragraph) blockElement; + return p.createRendererSubTree().setParent(document.getRenderer()).layout(new LayoutContext(new LayoutArea(page, htmlRectangle))).getOccupiedArea().getBBox().getHeight(); + } else if (blockElement instanceof Table){ + Table table = (Table) blockElement; + return table.createRendererSubTree().setParent(document.getRenderer()).layout(new LayoutContext(new LayoutArea(page, htmlRectangle))).getOccupiedArea().getBBox().getHeight(); + } else if (blockElement instanceof com.itextpdf.layout.element.List){ + com.itextpdf.layout.element.List list = (com.itextpdf.layout.element.List) blockElement; + return list.createRendererSubTree().setParent(document.getRenderer()).layout(new LayoutContext(new LayoutArea(page, htmlRectangle))).getOccupiedArea().getBBox().getHeight(); } + throw new RuntimeException("getBlockElementHeight failed to calculate the height of the block element"); } - public class YPosition { + private class YPosition { float currentYPosition; public YPosition(float currentYPosition) { diff --git a/java/src/main/java/com/genexus/reports/PDFReportPDFBox.java b/java/src/main/java/com/genexus/reports/PDFReportPDFBox.java index 5894ae563..f69fbb5b7 100644 --- a/java/src/main/java/com/genexus/reports/PDFReportPDFBox.java +++ b/java/src/main/java/com/genexus/reports/PDFReportPDFBox.java @@ -594,7 +594,7 @@ else if (valign == PDFReportPDFBox.VerticalAlign.BOTTOM.value()) boolean autoResize = (align & 256) == 256; if (htmlformat == 1) { - //As for now, HTML printing is not supported + log.info("As for now, HTML printing is not supported while generating reports using PDFBox"); } else if (barcodeType != null){