From a1b18c4a223eec7b3a159696957be449485f30da Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1s=20Sexenian?= <99925035+tomas-sexenian@users.noreply.github.com> Date: Sat, 2 Nov 2024 11:13:59 +0900 Subject: [PATCH 1/4] Encode with UTF-8 if getString has funny characters --- .../com/genexus/internet/HttpClientJavaLib.java | 17 +++++------------ 1 file changed, 5 insertions(+), 12 deletions(-) diff --git a/java/src/main/java/com/genexus/internet/HttpClientJavaLib.java b/java/src/main/java/com/genexus/internet/HttpClientJavaLib.java index 875cbb51f..c476d9adb 100644 --- a/java/src/main/java/com/genexus/internet/HttpClientJavaLib.java +++ b/java/src/main/java/com/genexus/internet/HttpClientJavaLib.java @@ -724,23 +724,16 @@ public String getString() { return ""; try { this.setEntity(); - ContentType contentType = ContentType.getOrDefault(entity); - Charset charset; - if (contentType.equals(ContentType.DEFAULT_TEXT)) { - charset = StandardCharsets.UTF_8; - } else { - charset = contentType.getCharset(); - if (charset == null) { - charset = StandardCharsets.UTF_8; - } - } + Charset charset = ContentType.getOrDefault(entity).getCharset(); String res = EntityUtils.toString(entity, charset); + if (res.matches(".*[Ã-ÿ].*")) { + res = EntityUtils.toString(entity, StandardCharsets.UTF_8); + } eof = true; return res; } catch (IOException e) { setExceptionsCatch(e); - } catch (IllegalArgumentException e) { - } + } catch (IllegalArgumentException e) {} return ""; } From e242950a90dd1e73de60c9adba29325f5ea84035 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1s=20Sexenian?= <99925035+tomas-sexenian@users.noreply.github.com> Date: Sat, 30 Nov 2024 21:39:57 +0700 Subject: [PATCH 2/4] Fix streams concurrency issues Issue:202643 --- .../com/genexus/reports/PDFReportPDFBox.java | 73 +++++++++++-------- 1 file changed, 43 insertions(+), 30 deletions(-) diff --git a/java/src/main/java/com/genexus/reports/PDFReportPDFBox.java b/java/src/main/java/com/genexus/reports/PDFReportPDFBox.java index 0522cae00..779fa7ac5 100644 --- a/java/src/main/java/com/genexus/reports/PDFReportPDFBox.java +++ b/java/src/main/java/com/genexus/reports/PDFReportPDFBox.java @@ -28,7 +28,6 @@ import org.apache.pdfbox.pdmodel.*; import org.apache.pdfbox.pdmodel.common.PDRectangle; import org.apache.pdfbox.pdmodel.font.*; -import org.apache.pdfbox.pdmodel.graphics.form.PDFormXObject; import org.apache.pdfbox.pdmodel.graphics.image.LosslessFactory; import org.apache.pdfbox.pdmodel.graphics.image.PDImageXObject; import org.apache.pdfbox.pdmodel.graphics.state.RenderingMode; @@ -61,6 +60,8 @@ public class PDFReportPDFBox extends GXReportPDFCommons{ ConcurrentHashMap documentImages; public int runDirection = 0; private int page; + private PDPageContentStream auxContentStream; + private boolean useAuxContentStream; private final float DEFAULT_PDFBOX_LEADING = 1.2f; @@ -228,41 +229,41 @@ 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 = getNewPDPageContentStream()){ - - float penAux = (float)convertScale(pen); - float rightAux = (float)convertScale(right); - float bottomAux = (float)convertScale(bottom); - float leftAux = (float)convertScale(left); - float topAux = (float)convertScale(top); + PDPageContentStream cb = null; + try{ + cb = useAuxContentStream ? auxContentStream : getNewPDPageContentStream(); + float penAux = (float) convertScale(pen); + float rightAux = (float) convertScale(right); + float bottomAux = (float) convertScale(bottom); + float leftAux = (float) convertScale(left); + float topAux = (float) convertScale(top); cb.saveGraphicsState(); float x1, y1, x2, y2; x1 = leftAux + leftMargin; - y1 = pageSize.getUpperRightY() - bottomAux - topMargin -bottomMargin; + y1 = pageSize.getUpperRightY() - bottomAux - topMargin - bottomMargin; x2 = rightAux + leftMargin; - y2 = pageSize.getUpperRightY() - topAux - topMargin -bottomMargin; + y2 = pageSize.getUpperRightY() - topAux - topMargin - bottomMargin; cb.setLineWidth(penAux); cb.setLineCapStyle(2); - if (cornerRadioBL==0 && cornerRadioBR==0 && cornerRadioTL==0 && cornerRadioTR==0 && styleBottom==0 && styleLeft==0 && styleRight==0 && styleTop==0) { + if (cornerRadioBL == 0 && cornerRadioBR == 0 && cornerRadioTL == 0 && cornerRadioTR == 0 && styleBottom == 0 && styleLeft == 0 && styleRight == 0 && styleTop == 0) { if (pen > 0) cb.setStrokingColor(foreRed, foreGreen, foreBlue); else - cb.setStrokingColor (backRed, backGreen, backBlue); + cb.setStrokingColor(backRed, backGreen, backBlue); cb.addRect(x1, y1, x2 - x1, y2 - y1); - if (backMode!=0) { + if (backMode != 0) { cb.setNonStrokingColor(new Color(backRed, backGreen, backBlue)); cb.fillAndStroke(); } cb.closePath(); cb.stroke(); - } - else { + } else { float w = x2 - x1; float h = y2 - y1; if (w < 0) { @@ -274,18 +275,18 @@ public void GxDrawRect(int left, int top, int right, int bottom, int pen, int fo h = -h; } - float cRadioTL = (float)convertScale(cornerRadioTL); - float cRadioTR = (float)convertScale(cornerRadioTR); - float cRadioBL = (float)convertScale(cornerRadioBL); - float cRadioBR = (float)convertScale(cornerRadioBR); + float cRadioTL = (float) convertScale(cornerRadioTL); + float cRadioTR = (float) convertScale(cornerRadioTR); + float cRadioBL = (float) convertScale(cornerRadioBL); + float cRadioBR = (float) convertScale(cornerRadioBR); - int max = (int)Math.min(w, h); - cRadioTL = Math.max(0, Math.min(cRadioTL, max/2)); - cRadioTR = Math.max(0, Math.min(cRadioTR, max/2)); - cRadioBL = Math.max(0, Math.min(cRadioBL, max/2)); - cRadioBR = Math.max(0, Math.min(cRadioBR, max/2)); + int max = (int) Math.min(w, h); + cRadioTL = Math.max(0, Math.min(cRadioTL, max / 2)); + cRadioTR = Math.max(0, Math.min(cRadioTR, max / 2)); + cRadioBL = Math.max(0, Math.min(cRadioBL, max / 2)); + cRadioBR = Math.max(0, Math.min(cRadioBR, max / 2)); - if (backMode!=0) { + if (backMode != 0) { cb.setStrokingColor(backRed, backGreen, backBlue); cb.setLineWidth(0); roundRectangle(cb, x1, y1, w, h, @@ -308,6 +309,15 @@ public void GxDrawRect(int left, int top, int right, int bottom, int pen, int fo log.debug("GxDrawRect -> (" + left + "," + top + ") - (" + right + "," + bottom + ") BackMode: " + backMode + " Pen:" + pen); } catch (Exception e) { log.error("GxDrawRect failed: ", e); + } finally { + try { + if (cb != null && !useAuxContentStream) + cb.close(); + else if (useAuxContentStream) + useAuxContentStream = false; + } catch (IOException ioe) { + log.error("Failed to close content stream", ioe); + } } } @@ -574,6 +584,7 @@ public void setAsianFont(String fontName, String style) { log.error("setAsianFont failed: ", e); } } + public void GxDrawText(String sTxt, int left, int top, int right, int bottom, int align, int htmlformat, int border, int valign) { PDPageContentStream cb = null; try { @@ -583,6 +594,8 @@ public void GxDrawText(String sTxt, int left, int top, int right, int bottom, in printRectangle = true; if (printRectangle && (border == 1 || backFill)) { + auxContentStream = cb; + useAuxContentStream = true; GxDrawRect(left, top, right, bottom, border, foreColor.getRed(), foreColor.getGreen(), foreColor.getBlue(), backFill ? 1 : 0, backColor.getRed(), backColor.getGreen(), backColor.getBlue(), 0, 0); } @@ -739,11 +752,9 @@ else if (valign == PDFReportPDFBox.VerticalAlign.BOTTOM.value()) rectangle.setUpperRightY(this.pageSize.getUpperRightY() - topAux - topMargin -bottomMargin); break; } - PDPageContentStream contentStream = getNewPDPageContentStream(); - contentStream.setNonStrokingColor(backColor); - contentStream.addRect(rectangle.getLowerLeftX(), rectangle.getLowerLeftY(),rectangle.getWidth(), rectangle.getHeight()); - contentStream.fill(); - contentStream.close(); + cb.setNonStrokingColor(backColor); + cb.addRect(rectangle.getLowerLeftX(), rectangle.getLowerLeftY(),rectangle.getWidth(), rectangle.getHeight()); + cb.fill(); } float underlineSeparation = lineHeight / 5; @@ -1075,6 +1086,8 @@ private float renderHTMLContent(PDPageContentStream contentStream, String text, private void resolveTextStyling(PDPageContentStream contentStream, String text, float x, float y, boolean isWrapped){ try { + contentStream.setNonStrokingColor(foreColor); + contentStream.setRenderingMode(RenderingMode.FILL); if (this.fontBold && this.fontItalic){ contentStream.setStrokingColor(foreColor); contentStream.setLineWidth(fontSize * 0.05f); From 1c3443a1bb5aea1ea9dfcb96cc9c1113bd77c095 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1s=20Sexenian?= <99925035+tomas-sexenian@users.noreply.github.com> Date: Fri, 6 Dec 2024 18:14:01 +0700 Subject: [PATCH 3/4] Bump PDFBox to 3.0.3 and improve memory usage Issue:202734 --- java/pom.xml | 2 +- .../com/genexus/reports/PDFReportPDFBox.java | 155 +++++++++++------- 2 files changed, 101 insertions(+), 56 deletions(-) diff --git a/java/pom.xml b/java/pom.xml index 82af66eff..d02843cf3 100644 --- a/java/pom.xml +++ b/java/pom.xml @@ -120,7 +120,7 @@ org.apache.pdfbox pdfbox - 2.0.27 + 3.0.3 org.jsoup diff --git a/java/src/main/java/com/genexus/reports/PDFReportPDFBox.java b/java/src/main/java/com/genexus/reports/PDFReportPDFBox.java index 779fa7ac5..eb9d6c7d5 100644 --- a/java/src/main/java/com/genexus/reports/PDFReportPDFBox.java +++ b/java/src/main/java/com/genexus/reports/PDFReportPDFBox.java @@ -4,10 +4,7 @@ import java.awt.image.BufferedImage; import java.io.*; import java.net.URL; -import java.util.ArrayList; -import java.util.HashSet; -import java.util.List; -import java.util.Set; +import java.util.*; import java.util.concurrent.ConcurrentHashMap; import com.genexus.ApplicationContext; @@ -47,7 +44,6 @@ public class PDFReportPDFBox extends GXReportPDFCommons{ private PDRectangle pageSize; private PDFont baseFont; - private String baseFontName; private BitMatrix barcode = null; private String barcodeType = null; private PDDocument document; @@ -87,10 +83,6 @@ 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) { @@ -231,7 +223,7 @@ public void GxDrawRect(int left, int top, int right, int bottom, int pen, int fo int styleTop, int styleBottom, int styleRight, int styleLeft, int cornerRadioTL, int cornerRadioTR, int cornerRadioBL, int cornerRadioBR) { PDPageContentStream cb = null; try{ - cb = useAuxContentStream ? auxContentStream : getNewPDPageContentStream(); + cb = useAuxContentStream ? auxContentStream : currentPageContentStream; float penAux = (float) convertScale(pen); float rightAux = (float) convertScale(right); float bottomAux = (float) convertScale(bottom); @@ -322,7 +314,7 @@ else if (useAuxContentStream) } public void GxDrawLine(int left, int top, int right, int bottom, int width, int foreRed, int foreGreen, int foreBlue, int style) { - try (PDPageContentStream cb = getNewPDPageContentStream()){ + try (PDPageContentStream cb = currentPageContentStream){ float widthAux = (float)convertScale(width); float rightAux = (float)convertScale(right); @@ -361,7 +353,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 = getNewPDPageContentStream()){ + try (PDPageContentStream cb = currentPageContentStream){ PDImageXObject image; try { if (documentImages != null && documentImages.containsKey(bitmap)) { @@ -427,6 +419,31 @@ public void GxDrawBitMap(String bitmap, int left, int top, int right, int bottom } } + private static final int MAX_FONT_CACHE_SIZE = 50; + private final Map fontCache = new LinkedHashMap(MAX_FONT_CACHE_SIZE, 0.75f, true) { + @Override + protected boolean removeEldestEntry(Map.Entry eldest) { + return size() > MAX_FONT_CACHE_SIZE; + } + }; + + private PDFont getCachedFont(String fontName, PDDocument document) throws IOException { + PDFont font = fontCache.get(fontName); + if (font == null) { + File fontFile = new File(getFontLocation(fontName)); + if (fontFile.exists()) { + font = PDType0Font.load(document, fontFile); + } else { + font = createPDType1FontFromName(fontName); + if (font == null) { + font = new PDType1Font(Standard14Fonts.FontName.HELVETICA); + } + } + fontCache.put(fontName, font); + } + return font; + } + public void GxAttris(String fontName, int fontSize, boolean fontBold, boolean fontItalic, boolean fontUnderline, boolean fontStrikethru, int Pen, int foreRed, int foreGreen, int foreBlue, int backMode, int backRed, int backGreen, int backBlue) { boolean isCJK = false; boolean embeedFont = isEmbeddedFont(fontName); @@ -492,11 +509,8 @@ public void GxAttris(String fontName, int fontSize, boolean fontBold, boolean fo } } baseFont = createPDType1FontFromName(fontName); - if (baseFont != null) - baseFontName = baseFont.getName(); if (baseFont == null){ - baseFont = PDType0Font.load(document, new File(getFontLocation(fontName))); - baseFontName = fontName; + baseFont = getCachedFont(fontName, document); } } @@ -518,19 +532,14 @@ public void GxAttris(String fontName, int fontSize, boolean fontBold, boolean fo if (fontPath.equals("")) { fontPath = PDFFontDescriptor.getTrueTypeFontLocation(fontName, props); if (fontPath.equals("")) { - baseFont = PDType1Font.HELVETICA; - baseFontName = baseFont.getName(); + baseFont = new PDType1Font(Standard14Fonts.FontName.HELVETICA); foundFont = false; } } if (foundFont){ baseFont = createPDType1FontFromName(fontName); - if (baseFont != null) - baseFontName = baseFont.getName(); - else{ - baseFont = PDType0Font.load(document, new File(getFontLocation(fontName))); - baseFontName = fontName; - } + if (baseFont == null) + baseFont = getCachedFont(fontName, document); } } } @@ -542,33 +551,33 @@ public void GxAttris(String fontName, int fontSize, boolean fontBold, boolean fo private static PDType1Font createPDType1FontFromName(String fontName) { switch (fontName) { case "Times-Roman": - return PDType1Font.TIMES_ROMAN; + return new PDType1Font(Standard14Fonts.FontName.TIMES_ROMAN); case "Times-Bold": - return PDType1Font.TIMES_BOLD; + return new PDType1Font(Standard14Fonts.FontName.TIMES_BOLD); case "Times-Italic": - return PDType1Font.TIMES_ITALIC; + return new PDType1Font(Standard14Fonts.FontName.TIMES_ITALIC); case "Times-BoldItalic": - return PDType1Font.TIMES_BOLD_ITALIC; + return new PDType1Font(Standard14Fonts.FontName.TIMES_BOLD_ITALIC); case "Helvetica": - return PDType1Font.HELVETICA; + return new PDType1Font(Standard14Fonts.FontName.HELVETICA); case "Helvetica-Bold": - return PDType1Font.HELVETICA_BOLD; + return new PDType1Font(Standard14Fonts.FontName.HELVETICA_BOLD); case "Helvetica-Oblique": - return PDType1Font.HELVETICA_OBLIQUE; + return new PDType1Font(Standard14Fonts.FontName.HELVETICA_OBLIQUE); case "Helvetica-BoldOblique": - return PDType1Font.HELVETICA_BOLD_OBLIQUE; + return new PDType1Font(Standard14Fonts.FontName.HELVETICA_BOLD_OBLIQUE); case "Courier": - return PDType1Font.COURIER; + return new PDType1Font(Standard14Fonts.FontName.COURIER); case "Courier-Bold": - return PDType1Font.COURIER_BOLD; + return new PDType1Font(Standard14Fonts.FontName.COURIER_BOLD); case "Courier-Oblique": - return PDType1Font.COURIER_OBLIQUE; + return new PDType1Font(Standard14Fonts.FontName.COURIER_OBLIQUE); case "Courier-BoldOblique": - return PDType1Font.COURIER_BOLD_OBLIQUE; + return new PDType1Font(Standard14Fonts.FontName.COURIER_BOLD_OBLIQUE); case "Symbol": - return PDType1Font.SYMBOL; + return new PDType1Font(Standard14Fonts.FontName.SYMBOL); case "ZapfDingbats": - return PDType1Font.ZAPF_DINGBATS; + return new PDType1Font(Standard14Fonts.FontName.ZAPF_DINGBATS); default: return null; } @@ -578,17 +587,34 @@ public void setAsianFont(String fontName, String style) { try { String fontPath = getFontLocation(fontName); baseFont = PDType0Font.load(document, new File(fontPath)); - baseFontName = fontName; } catch(Exception e) { log.error("setAsianFont failed: ", e); } } + private PDFont getOrLoadFont(String fontName, PDDocument doc) throws IOException { + PDFont cachedFont = fontCache.get(fontName); + if (cachedFont != null) { + return cachedFont; + } + PDFont font = createPDType1FontFromName(fontName); + if (font == null) { + String fontPath = getFontLocation(fontName); + if (!fontPath.isEmpty()) { + font = PDType0Font.load(doc, new File(fontPath)); + } else { + font = new PDType1Font(Standard14Fonts.FontName.HELVETICA); + } + } + fontCache.put(fontName, font); + return font; + } + public void GxDrawText(String sTxt, int left, int top, int right, int bottom, int align, int htmlformat, int border, int valign) { PDPageContentStream cb = null; try { - cb = getNewPDPageContentStream(); + cb = currentPageContentStream; boolean printRectangle = false; if (props.getBooleanGeneralProperty(Const.BACK_FILL_IN_CONTROLS, true)) printRectangle = true; @@ -601,9 +627,8 @@ public void GxDrawText(String sTxt, int left, int top, int right, int bottom, in sTxt = CommonUtil.rtrim(sTxt); - PDFont font = createPDType1FontFromName(baseFont.getFontDescriptor().getFontName()); - if (font == null) - font = PDType0Font.load(document, new File(getFontLocation(baseFontName))); + String descriptorFontName = baseFont.getFontDescriptor().getFontName(); + PDFont font = getOrLoadFont(descriptorFontName, document); cb.setFont(font,fontSize); cb.setNonStrokingColor(foreColor); @@ -669,7 +694,7 @@ else if (valign == PDFReportPDFBox.VerticalAlign.BOTTOM.value()) GxStartPage(); cb.close(); - cb = getNewPDPageContentStream(); + cb = currentPageContentStream; } if (this.supportedHTMLTags.contains(element.normalName())) processHTMLElement(cb, htmlRectangle, spaceHandler, element); @@ -784,7 +809,7 @@ else if (valign == PDFReportPDFBox.VerticalAlign.BOTTOM.value()) underline.setUpperRightY(this.pageSize.getUpperRightY() - bottomAux - topMargin -bottomMargin + startHeight - underlineHeight); break; } - PDPageContentStream contentStream = getNewPDPageContentStream(); + PDPageContentStream contentStream = currentPageContentStream; contentStream.setNonStrokingColor(foreColor); contentStream.addRect(underline.getLowerLeftX(), underline.getLowerLeftY(),underline.getWidth(), underline.getHeight()); contentStream.fill(); @@ -815,7 +840,7 @@ else if (valign == PDFReportPDFBox.VerticalAlign.BOTTOM.value()) underline.setUpperRightY(this.pageSize.getUpperRightY() - bottomAux - topMargin -bottomMargin + startHeight - underlineHeight + strikethruSeparation); break; } - PDPageContentStream contentStream = getNewPDPageContentStream(); + PDPageContentStream contentStream = currentPageContentStream; contentStream.setNonStrokingColor(foreColor); contentStream.addRect(underline.getLowerLeftX(), underline.getLowerLeftY() - strikethruSeparation * 1/3, underline.getWidth(), underline.getHeight()); contentStream.fill(); @@ -903,7 +928,7 @@ private void loadSupportedHTMLTags(){ private void processHTMLElement(PDPageContentStream cb, PDRectangle htmlRectangle, SpaceHandler spaceHandler, Element blockElement) throws Exception{ this.fontBold = false; String tagName = blockElement.normalName(); - PDFont htmlFont = PDType1Font.TIMES_ROMAN; + PDFont htmlFont = new PDType1Font(Standard14Fonts.FontName.TIMES_ROMAN); if (tagName.equals("div") || tagName.equals("span")) { for (Node child : blockElement.childNodes()) @@ -916,7 +941,7 @@ private void processHTMLElement(PDPageContentStream cb, PDRectangle htmlRectangl return; } - float lineHeight = (PDType1Font.TIMES_ROMAN.getFontDescriptor().getFontBoundingBox().getHeight() / 1000 * fontSize) * DEFAULT_PDFBOX_LEADING; + float lineHeight = (new PDType1Font(Standard14Fonts.FontName.TIMES_ROMAN).getFontDescriptor().getFontBoundingBox().getHeight() / 1000 * fontSize) * DEFAULT_PDFBOX_LEADING; float leading = (float)(Double.valueOf(props.getGeneralProperty(Const.LEADING)).doubleValue()); float llx = htmlRectangle.getLowerLeftX(); @@ -1040,7 +1065,7 @@ public void setAvailableSpace(float availableSpace) { private float renderHTMLContent(PDPageContentStream contentStream, String text, float fontSize, float llx, float lly, float urx, float ury) { try { - PDFont defaultHTMLFont = PDType1Font.TIMES_ROMAN; + PDFont defaultHTMLFont = new PDType1Font(Standard14Fonts.FontName.TIMES_ROMAN); List lines = new ArrayList<>(); String[] words = text.split(" "); StringBuilder currentLine = new StringBuilder(); @@ -1093,7 +1118,7 @@ private void resolveTextStyling(PDPageContentStream contentStream, String text, contentStream.setLineWidth(fontSize * 0.05f); contentStream.setRenderingMode(RenderingMode.FILL_STROKE); contentStream.beginText(); - contentStream.moveTextPositionByAmount(x, y); + contentStream.newLineAtOffset(x, y); contentStream.setTextMatrix(new Matrix(1, 0, 0.2f, 1, x + 0.2f * y, y)); contentStream.newLineAtOffset(-0.2f * y, 0); } else if (this.fontBold && !this.fontItalic){ @@ -1101,10 +1126,10 @@ private void resolveTextStyling(PDPageContentStream contentStream, String text, contentStream.setLineWidth(fontSize * 0.05f); contentStream.setRenderingMode(RenderingMode.FILL_STROKE); contentStream.beginText(); - contentStream.moveTextPositionByAmount(x, y); + contentStream.newLineAtOffset(x, y); } else if (!this.fontBold && this.fontItalic){ contentStream.beginText(); - contentStream.moveTextPositionByAmount(x, y); + contentStream.newLineAtOffset(x, y); contentStream.setTextMatrix(new Matrix(1, 0, 0.2f, 1, x + 0.2f * y, y)); contentStream.newLineAtOffset(-0.2f * y, 0); } else { @@ -1372,10 +1397,30 @@ public void GxEndDocument() { log.error("GxEndDocument failed: ", e);; } } + + private PDPageContentStream currentPageContentStream; + public void GxStartPage() { - document.addPage(new PDPage(this.pageSize)); - pages = pages + 1; - page = page + 1; + PDPage page = new PDPage(this.pageSize); + document.addPage(page); + pages++; + this.page++; + try { + currentPageContentStream = new PDPageContentStream(document, page, PDPageContentStream.AppendMode.APPEND, false); + } catch (IOException e) { + log.error("Failed to start page content stream: ", e); + } + } + + public void GxEndPage() { + try { + if (currentPageContentStream != null) { + currentPageContentStream.close(); + currentPageContentStream = null; + } + } catch (IOException e) { + log.error("Failed to close page content stream: ", e); + } } } \ No newline at end of file From 5f543ce7d97c9af3f162786869f4cb8f760a7070 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1s=20Sexenian?= <99925035+tomas-sexenian@users.noreply.github.com> Date: Fri, 13 Dec 2024 22:53:10 +0700 Subject: [PATCH 4/4] Fix font loading --- .../com/genexus/reports/PDFReportPDFBox.java | 77 +++++-------------- 1 file changed, 20 insertions(+), 57 deletions(-) diff --git a/java/src/main/java/com/genexus/reports/PDFReportPDFBox.java b/java/src/main/java/com/genexus/reports/PDFReportPDFBox.java index eb9d6c7d5..f3ed4e8a3 100644 --- a/java/src/main/java/com/genexus/reports/PDFReportPDFBox.java +++ b/java/src/main/java/com/genexus/reports/PDFReportPDFBox.java @@ -44,6 +44,7 @@ public class PDFReportPDFBox extends GXReportPDFCommons{ private PDRectangle pageSize; private PDFont baseFont; + private String baseFontName; private BitMatrix barcode = null; private String barcodeType = null; private PDDocument document; @@ -56,12 +57,9 @@ public class PDFReportPDFBox extends GXReportPDFCommons{ ConcurrentHashMap documentImages; public int runDirection = 0; private int page; - private PDPageContentStream auxContentStream; - private boolean useAuxContentStream; - private final float DEFAULT_PDFBOX_LEADING = 1.2f; - private Set supportedHTMLTags = new HashSet<>(); + private PDPageContentStream currentPageContentStream; static { log = org.apache.logging.log4j.LogManager.getLogger(PDFReportPDFBox.class); @@ -221,9 +219,8 @@ 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) { - PDPageContentStream cb = null; - try{ - cb = useAuxContentStream ? auxContentStream : currentPageContentStream; + try { + PDPageContentStream cb = currentPageContentStream; float penAux = (float) convertScale(pen); float rightAux = (float) convertScale(right); float bottomAux = (float) convertScale(bottom); @@ -301,15 +298,6 @@ public void GxDrawRect(int left, int top, int right, int bottom, int pen, int fo log.debug("GxDrawRect -> (" + left + "," + top + ") - (" + right + "," + bottom + ") BackMode: " + backMode + " Pen:" + pen); } catch (Exception e) { log.error("GxDrawRect failed: ", e); - } finally { - try { - if (cb != null && !useAuxContentStream) - cb.close(); - else if (useAuxContentStream) - useAuxContentStream = false; - } catch (IOException ioe) { - log.error("Failed to close content stream", ioe); - } } } @@ -427,23 +415,6 @@ protected boolean removeEldestEntry(Map.Entry eldest) { } }; - private PDFont getCachedFont(String fontName, PDDocument document) throws IOException { - PDFont font = fontCache.get(fontName); - if (font == null) { - File fontFile = new File(getFontLocation(fontName)); - if (fontFile.exists()) { - font = PDType0Font.load(document, fontFile); - } else { - font = createPDType1FontFromName(fontName); - if (font == null) { - font = new PDType1Font(Standard14Fonts.FontName.HELVETICA); - } - } - fontCache.put(fontName, font); - } - return font; - } - public void GxAttris(String fontName, int fontSize, boolean fontBold, boolean fontItalic, boolean fontUnderline, boolean fontStrikethru, int Pen, int foreRed, int foreGreen, int foreBlue, int backMode, int backRed, int backGreen, int backBlue) { boolean isCJK = false; boolean embeedFont = isEmbeddedFont(fontName); @@ -510,7 +481,8 @@ public void GxAttris(String fontName, int fontSize, boolean fontBold, boolean fo } baseFont = createPDType1FontFromName(fontName); if (baseFont == null){ - baseFont = getCachedFont(fontName, document); + baseFont = getOrLoadFont(fontName, document); + baseFontName = fontName; } } @@ -530,16 +502,22 @@ public void GxAttris(String fontName, int fontSize, boolean fontBold, boolean fo String fontPath = getFontLocation(fontName); boolean foundFont = true; if (fontPath.equals("")) { - fontPath = PDFFontDescriptor.getTrueTypeFontLocation(fontName, props); + fontName = fontName.endsWith(style) ? fontName.substring(0, fontName.length() - style.length()) : fontName; + fontPath = getFontLocation(fontName); + baseFont = getOrLoadFont(fontName, document); + baseFontName = fontName; if (fontPath.equals("")) { baseFont = new PDType1Font(Standard14Fonts.FontName.HELVETICA); + baseFontName = "Helvetica"; foundFont = false; } } if (foundFont){ baseFont = createPDType1FontFromName(fontName); - if (baseFont == null) - baseFont = getCachedFont(fontName, document); + if (baseFont == null){ + baseFont = getOrLoadFont(fontName, document); + baseFontName = fontName; + } } } } @@ -585,8 +563,7 @@ private static PDType1Font createPDType1FontFromName(String fontName) { public void setAsianFont(String fontName, String style) { try { - String fontPath = getFontLocation(fontName); - baseFont = PDType0Font.load(document, new File(fontPath)); + baseFont = getOrLoadFont(fontName, document); } catch(Exception e) { log.error("setAsianFont failed: ", e); @@ -612,23 +589,19 @@ private PDFont getOrLoadFont(String fontName, PDDocument doc) throws IOException } public void GxDrawText(String sTxt, int left, int top, int right, int bottom, int align, int htmlformat, int border, int valign) { - PDPageContentStream cb = null; try { - cb = currentPageContentStream; + PDPageContentStream cb = currentPageContentStream; boolean printRectangle = false; if (props.getBooleanGeneralProperty(Const.BACK_FILL_IN_CONTROLS, true)) printRectangle = true; if (printRectangle && (border == 1 || backFill)) { - auxContentStream = cb; - useAuxContentStream = true; GxDrawRect(left, top, right, bottom, border, foreColor.getRed(), foreColor.getGreen(), foreColor.getBlue(), backFill ? 1 : 0, backColor.getRed(), backColor.getGreen(), backColor.getBlue(), 0, 0); } sTxt = CommonUtil.rtrim(sTxt); - String descriptorFontName = baseFont.getFontDescriptor().getFontName(); - PDFont font = getOrLoadFont(descriptorFontName, document); + PDFont font = getOrLoadFont(baseFontName, document); cb.setFont(font,fontSize); cb.setNonStrokingColor(foreColor); @@ -693,7 +666,6 @@ else if (valign == PDFReportPDFBox.VerticalAlign.BOTTOM.value()) GxEndPage(); GxStartPage(); - cb.close(); cb = currentPageContentStream; } if (this.supportedHTMLTags.contains(element.normalName())) @@ -780,6 +752,7 @@ else if (valign == PDFReportPDFBox.VerticalAlign.BOTTOM.value()) cb.setNonStrokingColor(backColor); cb.addRect(rectangle.getLowerLeftX(), rectangle.getLowerLeftY(),rectangle.getWidth(), rectangle.getHeight()); cb.fill(); + cb.setNonStrokingColor(foreColor); } float underlineSeparation = lineHeight / 5; @@ -813,7 +786,7 @@ else if (valign == PDFReportPDFBox.VerticalAlign.BOTTOM.value()) contentStream.setNonStrokingColor(foreColor); contentStream.addRect(underline.getLowerLeftX(), underline.getLowerLeftY(),underline.getWidth(), underline.getHeight()); contentStream.fill(); - contentStream.close(); + cb.setNonStrokingColor(foreColor); } if (fontStrikethru) { @@ -844,7 +817,6 @@ else if (valign == PDFReportPDFBox.VerticalAlign.BOTTOM.value()) contentStream.setNonStrokingColor(foreColor); contentStream.addRect(underline.getLowerLeftX(), underline.getLowerLeftY() - strikethruSeparation * 1/3, underline.getWidth(), underline.getHeight()); contentStream.fill(); - contentStream.close(); } if(sTxt.trim().equalsIgnoreCase("{{Pages}}")) { @@ -902,13 +874,6 @@ 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); - } } } @@ -1398,8 +1363,6 @@ public void GxEndDocument() { } } - private PDPageContentStream currentPageContentStream; - public void GxStartPage() { PDPage page = new PDPage(this.pageSize); document.addPage(page);