diff --git a/java/src/main/java/com/genexus/reports/PDFReportItext8.java b/java/src/main/java/com/genexus/reports/PDFReportItext8.java index 02a1e613d..1a189a4ed 100644 --- a/java/src/main/java/com/genexus/reports/PDFReportItext8.java +++ b/java/src/main/java/com/genexus/reports/PDFReportItext8.java @@ -14,6 +14,7 @@ import com.itextpdf.html2pdf.HtmlConverter; import com.itextpdf.html2pdf.resolver.font.DefaultFontProvider; import com.itextpdf.io.font.PdfEncodings; +import com.itextpdf.io.font.TrueTypeFont; import com.itextpdf.io.font.constants.StandardFonts; import com.itextpdf.io.font.otf.Glyph; import com.itextpdf.io.font.otf.GlyphLine; @@ -36,6 +37,7 @@ import com.itextpdf.layout.borders.Border; import com.itextpdf.layout.element.*; import com.itextpdf.layout.element.Image; +import com.itextpdf.layout.font.FontProvider; import com.itextpdf.layout.layout.LayoutArea; import com.itextpdf.layout.layout.LayoutContext; import com.itextpdf.layout.properties.*; @@ -48,7 +50,9 @@ import java.io.File; import java.io.IOException; import java.net.URL; +import java.util.Hashtable; import java.util.List; +import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.zip.Deflater; @@ -87,7 +91,7 @@ protected void init() { pdfDocument = new PdfDocument(writer); pdfDocument.setDefaultPageSize(this.pageSize); document = new Document(pdfDocument); - document.setFontProvider(new DefaultFontProvider()); + document.setFontProvider(new DefaultFontProvider(false, true, false)); } catch (Exception e){ log.error("Failed to initialize new iText7 document: ", e); } @@ -599,8 +603,25 @@ else if (valign == VerticalAlign.BOTTOM.value()) boolean autoResize = (align & 256) == 256; if (htmlformat == 1) { - log.debug("As of now, you might experience unexpected behaviour since not all possible HTML code is supported"); try { + ConverterProperties converterProperties = new ConverterProperties(); + FontProvider fontProvider = document.getFontProvider(); + if (baseFont.getFontProgram() instanceof TrueTypeFont) { + Hashtable locations = getFontLocations(); + Set fontNames = locations.keySet(); + for(String fontName: fontNames) { + String fontPath = (String) locations.get(fontName); + if (fontPath == null || fontPath.equals("")) { + PDFFontDescriptor fontDescriptor = new PDFFontDescriptor(); + fontPath = fontDescriptor.getTrueTypeFontLocation(fontName, props); + } + if (!(fontPath == null || fontPath.equals(""))) { + fontProvider.addFont(fontPath); + } + } + } + converterProperties.setFontProvider(fontProvider); + bottomAux = (float)convertScale(bottom); topAux = (float)convertScale(top); float drawingPageHeight = this.pageSize.getTop() - topMargin - bottomMargin; @@ -614,12 +635,35 @@ else if (valign == VerticalAlign.BOTTOM.value()) Rectangle htmlRectangle = new Rectangle(llx, lly, urx - llx, ury - lly); YPosition yPosition = new YPosition(htmlRectangle.getTop()); - ConverterProperties converterProperties = new ConverterProperties(); - converterProperties.setFontProvider(document.getFontProvider()); + Canvas htmlCanvas = getNewCanvas(cb, htmlRectangle, fontProvider); + //Iterate over the elements (a.k.a the parsed HTML string) and handle each case accordingly List elements = HtmlConverter.convertToElements(sTxt, converterProperties); - for (IElement element : elements) - processHTMLElement(htmlRectangle, yPosition, (IBlockElement) element); + for (IElement element : elements) { + float blockElementHeight = getBlockElementHeight((IBlockElement)element, htmlRectangle); + if (pageHeightExceeded(bottomMargin, yPosition.getCurrentYPosition())) + { + llx = leftAux + leftMargin; + lly = drawingPageHeight - bottomAux; + urx = rightAux + leftMargin; + ury = drawingPageHeight - topAux; + htmlRectangle = new Rectangle(llx, lly, urx - llx, ury - lly); + yPosition = new YPosition(htmlRectangle.getTop()); + bottomAux -= drawingPageHeight; + GxEndPage(); + GxStartPage(); + + cb = new PdfCanvas(pdfPage); + sTxt = sTxt.replaceAll("\\s+$", ""); + cb.setFontAndSize(this.baseFont, fontSize); + cb.setFillColor(new DeviceRgb(foreColor)); + + htmlCanvas = getNewCanvas(cb, htmlRectangle, fontProvider); + } + processHTMLElement((IBlockElement)element, alignment, htmlCanvas); + yPosition.setCurrentYPosition(yPosition.getCurrentYPosition() - blockElementHeight); + } + } catch (Exception e) { log.error("GxDrawText failed to print HTML text : ", e); } @@ -742,53 +786,23 @@ else if (valign == VerticalAlign.BOTTOM.value()) } } - void processHTMLElement(Rectangle htmlRectangle, YPosition currentYPosition, IBlockElement blockElement){ - if (blockElement instanceof Div) { - Div div = (Div) blockElement; - // Iterate through the children of the Div and process each child element recursively - for (IElement child : div.getChildren()) - if (child instanceof IBlockElement) - processHTMLElement(htmlRectangle, currentYPosition, (IBlockElement) child); - } - - float blockElementHeight = getBlockElementHeight(blockElement, htmlRectangle); - float availableSpace = currentYPosition.getCurrentYPosition() - htmlRectangle.getBottom(); - if (blockElementHeight > availableSpace){ - log.error("You are trying to render an element of height " + blockElementHeight + " in a space of height " + availableSpace); - return; - } + private Canvas getNewCanvas(PdfCanvas pdfCanvas, Rectangle rectangle, FontProvider fontProvider) { + Canvas canvas = new Canvas(pdfCanvas, rectangle); + canvas.setFontProvider(fontProvider); + return canvas; + } - if (blockElement instanceof Paragraph){ + void processHTMLElement(IBlockElement blockElement, int alignment, Canvas htmlCanvas){ + if (blockElement instanceof Paragraph) { Paragraph p = (Paragraph) blockElement; - p.setFixedPosition(page, htmlRectangle.getX(), currentYPosition.getCurrentYPosition() - blockElementHeight, htmlRectangle.getWidth()); - document.add(p); - } else if (blockElement instanceof Table){ - Table table = (Table) blockElement; - table.setFixedPosition(page, htmlRectangle.getX(), currentYPosition.getCurrentYPosition() - blockElementHeight, htmlRectangle.getWidth()); - 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(),currentYPosition.getCurrentYPosition() - blockElementHeight, htmlRectangle.getWidth()); - document.add(list); + if (alignment != 0) + p.setTextAlignment(getTextAlignment(alignment)); } - currentYPosition.setCurrentYPosition(currentYPosition.getCurrentYPosition() - blockElementHeight); + htmlCanvas.add(blockElement); } 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(); - } else if (blockElement instanceof Div){ - Div div = (Div) blockElement; - return div.createRendererSubTree().setParent(document.getRenderer()).layout(new LayoutContext(new LayoutArea(page, htmlRectangle))).getOccupiedArea().getBBox().getHeight(); - } - throw new RuntimeException("getBlockElementHeight failed, you might be trying to render something that is not a

, or a
  • "); + return blockElement.createRendererSubTree().setParent(document.getRenderer()).layout(new LayoutContext(new LayoutArea(page, htmlRectangle))).getOccupiedArea().getBBox().getHeight(); } public class YPosition { diff --git a/java/src/main/java/com/genexus/reports/PDFReportPDFBox.java b/java/src/main/java/com/genexus/reports/PDFReportPDFBox.java index 33d6f82c8..30f8d59cd 100644 --- a/java/src/main/java/com/genexus/reports/PDFReportPDFBox.java +++ b/java/src/main/java/com/genexus/reports/PDFReportPDFBox.java @@ -86,6 +86,10 @@ protected void init() { } } + private PDPageContentStream getNewPDPageContentStream() throws IOException { + return new PDPageContentStream(document, document.getPage(page - 1), PDPageContentStream.AppendMode.APPEND,false); + } + private void drawRectangle(PDPageContentStream cb, float x, float y, float w, float h, int styleTop, int styleBottom, int styleRight, int styleLeft, float radioTL, float radioTR, float radioBL, float radioBR, float penAux, boolean hideCorners) { @@ -224,7 +228,7 @@ private void roundRectangle(PDPageContentStream cb, float x, float y, float w, f public void GxDrawRect(int left, int top, int right, int bottom, int pen, int foreRed, int foreGreen, int foreBlue, int backMode, int backRed, int backGreen, int backBlue, int styleTop, int styleBottom, int styleRight, int styleLeft, int cornerRadioTL, int cornerRadioTR, int cornerRadioBL, int cornerRadioBR) { - try (PDPageContentStream cb = new PDPageContentStream(document, document.getPage(page - 1),PDPageContentStream.AppendMode.APPEND,false)){ + try (PDPageContentStream cb = getNewPDPageContentStream()){ float penAux = (float)convertScale(pen); float rightAux = (float)convertScale(right); @@ -308,7 +312,7 @@ public void GxDrawRect(int left, int top, int right, int bottom, int pen, int fo } public void GxDrawLine(int left, int top, int right, int bottom, int width, int foreRed, int foreGreen, int foreBlue, int style) { - try (PDPageContentStream cb = new PDPageContentStream(document, document.getPage(page - 1),PDPageContentStream.AppendMode.APPEND,false)){ + try (PDPageContentStream cb = getNewPDPageContentStream()){ float widthAux = (float)convertScale(width); float rightAux = (float)convertScale(right); @@ -347,7 +351,7 @@ public void GxDrawLine(int left, int top, int right, int bottom, int width, int } public void GxDrawBitMap(String bitmap, int left, int top, int right, int bottom, int aspectRatio) { - try (PDPageContentStream cb = new PDPageContentStream(document, document.getPage(page - 1),PDPageContentStream.AppendMode.APPEND,false)){ + try (PDPageContentStream cb = getNewPDPageContentStream()){ PDImageXObject image; try { if (documentImages != null && documentImages.containsKey(bitmap)) { @@ -571,7 +575,9 @@ public void setAsianFont(String fontName, String style) { } } public void GxDrawText(String sTxt, int left, int top, int right, int bottom, int align, int htmlformat, int border, int valign) { - try (PDPageContentStream cb = new PDPageContentStream(document, document.getPage(page - 1),PDPageContentStream.AppendMode.APPEND,false)){ + PDPageContentStream cb = null; + try { + cb = getNewPDPageContentStream(); boolean printRectangle = false; if (props.getBooleanGeneralProperty(Const.BACK_FILL_IN_CONTROLS, true)) printRectangle = true; @@ -635,11 +641,26 @@ else if (valign == PDFReportPDFBox.VerticalAlign.BOTTOM.value()) loadSupportedHTMLTags(); - Document document = Jsoup.parse(sTxt); - Elements allElements = document.getAllElements(); - for (Element element : allElements) + Document htmlDocument = Jsoup.parse(sTxt); + Elements allElements = htmlDocument.getAllElements(); + for (Element element : allElements){ + if (pageHeightExceeded(bottomMargin, spaceHandler.getCurrentYPosition())) { + llx = leftAux + leftMargin; + lly = drawingPageHeight - bottomAux; + urx = rightAux + leftMargin; + ury = drawingPageHeight - topAux; + htmlRectangle = new PDRectangle(llx, lly, urx - llx, ury - lly); + spaceHandler = new SpaceHandler(htmlRectangle.getUpperRightY(), htmlRectangle.getHeight()); + bottomAux -= drawingPageHeight; + GxEndPage(); + GxStartPage(); + + cb.close(); + cb = getNewPDPageContentStream(); + } if (this.supportedHTMLTags.contains(element.normalName())) processHTMLElement(cb, htmlRectangle, spaceHandler, element); + } } catch (Exception e) { log.error("GxDrawText failed to print HTML text : ", e); @@ -718,7 +739,7 @@ else if (valign == PDFReportPDFBox.VerticalAlign.BOTTOM.value()) rectangle.setUpperRightY(this.pageSize.getUpperRightY() - topAux - topMargin -bottomMargin); break; } - PDPageContentStream contentStream = new PDPageContentStream(document, document.getPage(page - 1),PDPageContentStream.AppendMode.APPEND,false); + PDPageContentStream contentStream = getNewPDPageContentStream(); contentStream.setNonStrokingColor(backColor); contentStream.addRect(rectangle.getLowerLeftX(), rectangle.getLowerLeftY(),rectangle.getWidth(), rectangle.getHeight()); contentStream.fill(); @@ -752,7 +773,7 @@ else if (valign == PDFReportPDFBox.VerticalAlign.BOTTOM.value()) underline.setUpperRightY(this.pageSize.getUpperRightY() - bottomAux - topMargin -bottomMargin + startHeight - underlineHeight); break; } - PDPageContentStream contentStream = new PDPageContentStream(document, document.getPage(page - 1),PDPageContentStream.AppendMode.APPEND,false); + PDPageContentStream contentStream = getNewPDPageContentStream(); contentStream.setNonStrokingColor(foreColor); contentStream.addRect(underline.getLowerLeftX(), underline.getLowerLeftY(),underline.getWidth(), underline.getHeight()); contentStream.fill(); @@ -783,7 +804,7 @@ else if (valign == PDFReportPDFBox.VerticalAlign.BOTTOM.value()) underline.setUpperRightY(this.pageSize.getUpperRightY() - bottomAux - topMargin -bottomMargin + startHeight - underlineHeight + strikethruSeparation); break; } - PDPageContentStream contentStream = new PDPageContentStream(document, document.getPage(page - 1),PDPageContentStream.AppendMode.APPEND,false); + PDPageContentStream contentStream = getNewPDPageContentStream(); contentStream.setNonStrokingColor(foreColor); contentStream.addRect(underline.getLowerLeftX(), underline.getLowerLeftY() - strikethruSeparation * 1/3, underline.getWidth(), underline.getHeight()); contentStream.fill(); @@ -799,7 +820,7 @@ else if (valign == PDFReportPDFBox.VerticalAlign.BOTTOM.value()) templateCreated = true; } PDFormXObject form = new PDFormXObject(document); - PDPageContentStream contentStream = new PDPageContentStream(document, document.getPage(page - 1),PDPageContentStream.AppendMode.APPEND,false); + PDPageContentStream contentStream = getNewPDPageContentStream(); contentStream.transform(Matrix.getTranslateInstance(leftAux + leftMargin, leftAux + leftMargin)); contentStream.drawForm(form); contentStream.close(); @@ -852,6 +873,13 @@ else if (valign == PDFReportPDFBox.VerticalAlign.BOTTOM.value()) } } catch (Exception ioe){ log.error("GxDrawText failed: ", ioe); + } finally { + try { + if (cb != null) cb.close(); + } + catch (IOException ioe) { + log.error("GxDrawText failed to close a content stream to one of it's pages: ", ioe); + } } } @@ -880,7 +908,7 @@ private void processHTMLElement(PDPageContentStream cb, PDRectangle htmlRectangl } if (spaceHandler.getAvailableSpace() <= 0){ - log.error("You ran out of available space while rendering HTML"); + log.debug("You ran out of available space in page #" + this.page + " while rendering HTML"); return; } @@ -1047,7 +1075,7 @@ private float renderHTMLContent(PDPageContentStream contentStream, String text, contentStream.setRenderingMode(RenderingMode.FILL); // Default text rendering mode for PDFBox 2.0.27 return lines.size(); } catch (IOException ioe) { - log.error("failed to draw wrapped text: ", ioe); + log.error("failed to render HTML text: ", ioe); return -1; } } @@ -1222,7 +1250,7 @@ public void GxEndDocument() { template.endText(); template.close(); for (PDPage page : document.getPages()){ - try (PDPageContentStream templatePainter = new PDPageContentStream(document, page,PDPageContentStream.AppendMode.APPEND,false)) { + try (PDPageContentStream templatePainter = new PDPageContentStream(document, page, PDPageContentStream.AppendMode.APPEND,false)) { templatePainter.drawForm(formXObjecttemplate); } } @@ -1305,8 +1333,8 @@ public void GxEndDocument() { try { document.save(outputStream); document.close(); - } catch (IOException ioe) { - log.error("GxEndDocument: failed to save document to the output stream", ioe); + } catch (IOException | IllegalStateException e) { + log.error("GxEndDocument: failed to save document to the output stream", e); } log.debug("GxEndDocument!");