Skip to content

Commit 1c3443a

Browse files
Bump PDFBox to 3.0.3 and improve memory usage
Issue:202734
1 parent e242950 commit 1c3443a

File tree

2 files changed

+101
-56
lines changed

2 files changed

+101
-56
lines changed

java/pom.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -120,7 +120,7 @@
120120
<dependency>
121121
<groupId>org.apache.pdfbox</groupId>
122122
<artifactId>pdfbox</artifactId>
123-
<version>2.0.27</version>
123+
<version>3.0.3</version>
124124
</dependency>
125125
<dependency>
126126
<groupId>org.jsoup</groupId>

java/src/main/java/com/genexus/reports/PDFReportPDFBox.java

Lines changed: 100 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,7 @@
44
import java.awt.image.BufferedImage;
55
import java.io.*;
66
import java.net.URL;
7-
import java.util.ArrayList;
8-
import java.util.HashSet;
9-
import java.util.List;
10-
import java.util.Set;
7+
import java.util.*;
118
import java.util.concurrent.ConcurrentHashMap;
129

1310
import com.genexus.ApplicationContext;
@@ -47,7 +44,6 @@
4744
public class PDFReportPDFBox extends GXReportPDFCommons{
4845
private PDRectangle pageSize;
4946
private PDFont baseFont;
50-
private String baseFontName;
5147
private BitMatrix barcode = null;
5248
private String barcodeType = null;
5349
private PDDocument document;
@@ -87,10 +83,6 @@ protected void init() {
8783
}
8884
}
8985

90-
private PDPageContentStream getNewPDPageContentStream() throws IOException {
91-
return new PDPageContentStream(document, document.getPage(page - 1), PDPageContentStream.AppendMode.APPEND,false);
92-
}
93-
9486
private void drawRectangle(PDPageContentStream cb, float x, float y, float w, float h,
9587
int styleTop, int styleBottom, int styleRight, int styleLeft,
9688
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
231223
int styleTop, int styleBottom, int styleRight, int styleLeft, int cornerRadioTL, int cornerRadioTR, int cornerRadioBL, int cornerRadioBR) {
232224
PDPageContentStream cb = null;
233225
try{
234-
cb = useAuxContentStream ? auxContentStream : getNewPDPageContentStream();
226+
cb = useAuxContentStream ? auxContentStream : currentPageContentStream;
235227
float penAux = (float) convertScale(pen);
236228
float rightAux = (float) convertScale(right);
237229
float bottomAux = (float) convertScale(bottom);
@@ -322,7 +314,7 @@ else if (useAuxContentStream)
322314
}
323315

324316
public void GxDrawLine(int left, int top, int right, int bottom, int width, int foreRed, int foreGreen, int foreBlue, int style) {
325-
try (PDPageContentStream cb = getNewPDPageContentStream()){
317+
try (PDPageContentStream cb = currentPageContentStream){
326318

327319
float widthAux = (float)convertScale(width);
328320
float rightAux = (float)convertScale(right);
@@ -361,7 +353,7 @@ public void GxDrawLine(int left, int top, int right, int bottom, int width, int
361353
}
362354

363355
public void GxDrawBitMap(String bitmap, int left, int top, int right, int bottom, int aspectRatio) {
364-
try (PDPageContentStream cb = getNewPDPageContentStream()){
356+
try (PDPageContentStream cb = currentPageContentStream){
365357
PDImageXObject image;
366358
try {
367359
if (documentImages != null && documentImages.containsKey(bitmap)) {
@@ -427,6 +419,31 @@ public void GxDrawBitMap(String bitmap, int left, int top, int right, int bottom
427419
}
428420
}
429421

422+
private static final int MAX_FONT_CACHE_SIZE = 50;
423+
private final Map<String, PDFont> fontCache = new LinkedHashMap<String, PDFont>(MAX_FONT_CACHE_SIZE, 0.75f, true) {
424+
@Override
425+
protected boolean removeEldestEntry(Map.Entry<String, PDFont> eldest) {
426+
return size() > MAX_FONT_CACHE_SIZE;
427+
}
428+
};
429+
430+
private PDFont getCachedFont(String fontName, PDDocument document) throws IOException {
431+
PDFont font = fontCache.get(fontName);
432+
if (font == null) {
433+
File fontFile = new File(getFontLocation(fontName));
434+
if (fontFile.exists()) {
435+
font = PDType0Font.load(document, fontFile);
436+
} else {
437+
font = createPDType1FontFromName(fontName);
438+
if (font == null) {
439+
font = new PDType1Font(Standard14Fonts.FontName.HELVETICA);
440+
}
441+
}
442+
fontCache.put(fontName, font);
443+
}
444+
return font;
445+
}
446+
430447
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) {
431448
boolean isCJK = false;
432449
boolean embeedFont = isEmbeddedFont(fontName);
@@ -492,11 +509,8 @@ public void GxAttris(String fontName, int fontSize, boolean fontBold, boolean fo
492509
}
493510
}
494511
baseFont = createPDType1FontFromName(fontName);
495-
if (baseFont != null)
496-
baseFontName = baseFont.getName();
497512
if (baseFont == null){
498-
baseFont = PDType0Font.load(document, new File(getFontLocation(fontName)));
499-
baseFontName = fontName;
513+
baseFont = getCachedFont(fontName, document);
500514
}
501515

502516
}
@@ -518,19 +532,14 @@ public void GxAttris(String fontName, int fontSize, boolean fontBold, boolean fo
518532
if (fontPath.equals("")) {
519533
fontPath = PDFFontDescriptor.getTrueTypeFontLocation(fontName, props);
520534
if (fontPath.equals("")) {
521-
baseFont = PDType1Font.HELVETICA;
522-
baseFontName = baseFont.getName();
535+
baseFont = new PDType1Font(Standard14Fonts.FontName.HELVETICA);
523536
foundFont = false;
524537
}
525538
}
526539
if (foundFont){
527540
baseFont = createPDType1FontFromName(fontName);
528-
if (baseFont != null)
529-
baseFontName = baseFont.getName();
530-
else{
531-
baseFont = PDType0Font.load(document, new File(getFontLocation(fontName)));
532-
baseFontName = fontName;
533-
}
541+
if (baseFont == null)
542+
baseFont = getCachedFont(fontName, document);
534543
}
535544
}
536545
}
@@ -542,33 +551,33 @@ public void GxAttris(String fontName, int fontSize, boolean fontBold, boolean fo
542551
private static PDType1Font createPDType1FontFromName(String fontName) {
543552
switch (fontName) {
544553
case "Times-Roman":
545-
return PDType1Font.TIMES_ROMAN;
554+
return new PDType1Font(Standard14Fonts.FontName.TIMES_ROMAN);
546555
case "Times-Bold":
547-
return PDType1Font.TIMES_BOLD;
556+
return new PDType1Font(Standard14Fonts.FontName.TIMES_BOLD);
548557
case "Times-Italic":
549-
return PDType1Font.TIMES_ITALIC;
558+
return new PDType1Font(Standard14Fonts.FontName.TIMES_ITALIC);
550559
case "Times-BoldItalic":
551-
return PDType1Font.TIMES_BOLD_ITALIC;
560+
return new PDType1Font(Standard14Fonts.FontName.TIMES_BOLD_ITALIC);
552561
case "Helvetica":
553-
return PDType1Font.HELVETICA;
562+
return new PDType1Font(Standard14Fonts.FontName.HELVETICA);
554563
case "Helvetica-Bold":
555-
return PDType1Font.HELVETICA_BOLD;
564+
return new PDType1Font(Standard14Fonts.FontName.HELVETICA_BOLD);
556565
case "Helvetica-Oblique":
557-
return PDType1Font.HELVETICA_OBLIQUE;
566+
return new PDType1Font(Standard14Fonts.FontName.HELVETICA_OBLIQUE);
558567
case "Helvetica-BoldOblique":
559-
return PDType1Font.HELVETICA_BOLD_OBLIQUE;
568+
return new PDType1Font(Standard14Fonts.FontName.HELVETICA_BOLD_OBLIQUE);
560569
case "Courier":
561-
return PDType1Font.COURIER;
570+
return new PDType1Font(Standard14Fonts.FontName.COURIER);
562571
case "Courier-Bold":
563-
return PDType1Font.COURIER_BOLD;
572+
return new PDType1Font(Standard14Fonts.FontName.COURIER_BOLD);
564573
case "Courier-Oblique":
565-
return PDType1Font.COURIER_OBLIQUE;
574+
return new PDType1Font(Standard14Fonts.FontName.COURIER_OBLIQUE);
566575
case "Courier-BoldOblique":
567-
return PDType1Font.COURIER_BOLD_OBLIQUE;
576+
return new PDType1Font(Standard14Fonts.FontName.COURIER_BOLD_OBLIQUE);
568577
case "Symbol":
569-
return PDType1Font.SYMBOL;
578+
return new PDType1Font(Standard14Fonts.FontName.SYMBOL);
570579
case "ZapfDingbats":
571-
return PDType1Font.ZAPF_DINGBATS;
580+
return new PDType1Font(Standard14Fonts.FontName.ZAPF_DINGBATS);
572581
default:
573582
return null;
574583
}
@@ -578,17 +587,34 @@ public void setAsianFont(String fontName, String style) {
578587
try {
579588
String fontPath = getFontLocation(fontName);
580589
baseFont = PDType0Font.load(document, new File(fontPath));
581-
baseFontName = fontName;
582590
}
583591
catch(Exception e) {
584592
log.error("setAsianFont failed: ", e);
585593
}
586594
}
587595

596+
private PDFont getOrLoadFont(String fontName, PDDocument doc) throws IOException {
597+
PDFont cachedFont = fontCache.get(fontName);
598+
if (cachedFont != null) {
599+
return cachedFont;
600+
}
601+
PDFont font = createPDType1FontFromName(fontName);
602+
if (font == null) {
603+
String fontPath = getFontLocation(fontName);
604+
if (!fontPath.isEmpty()) {
605+
font = PDType0Font.load(doc, new File(fontPath));
606+
} else {
607+
font = new PDType1Font(Standard14Fonts.FontName.HELVETICA);
608+
}
609+
}
610+
fontCache.put(fontName, font);
611+
return font;
612+
}
613+
588614
public void GxDrawText(String sTxt, int left, int top, int right, int bottom, int align, int htmlformat, int border, int valign) {
589615
PDPageContentStream cb = null;
590616
try {
591-
cb = getNewPDPageContentStream();
617+
cb = currentPageContentStream;
592618
boolean printRectangle = false;
593619
if (props.getBooleanGeneralProperty(Const.BACK_FILL_IN_CONTROLS, true))
594620
printRectangle = true;
@@ -601,9 +627,8 @@ public void GxDrawText(String sTxt, int left, int top, int right, int bottom, in
601627

602628
sTxt = CommonUtil.rtrim(sTxt);
603629

604-
PDFont font = createPDType1FontFromName(baseFont.getFontDescriptor().getFontName());
605-
if (font == null)
606-
font = PDType0Font.load(document, new File(getFontLocation(baseFontName)));
630+
String descriptorFontName = baseFont.getFontDescriptor().getFontName();
631+
PDFont font = getOrLoadFont(descriptorFontName, document);
607632

608633
cb.setFont(font,fontSize);
609634
cb.setNonStrokingColor(foreColor);
@@ -669,7 +694,7 @@ else if (valign == PDFReportPDFBox.VerticalAlign.BOTTOM.value())
669694
GxStartPage();
670695

671696
cb.close();
672-
cb = getNewPDPageContentStream();
697+
cb = currentPageContentStream;
673698
}
674699
if (this.supportedHTMLTags.contains(element.normalName()))
675700
processHTMLElement(cb, htmlRectangle, spaceHandler, element);
@@ -784,7 +809,7 @@ else if (valign == PDFReportPDFBox.VerticalAlign.BOTTOM.value())
784809
underline.setUpperRightY(this.pageSize.getUpperRightY() - bottomAux - topMargin -bottomMargin + startHeight - underlineHeight);
785810
break;
786811
}
787-
PDPageContentStream contentStream = getNewPDPageContentStream();
812+
PDPageContentStream contentStream = currentPageContentStream;
788813
contentStream.setNonStrokingColor(foreColor);
789814
contentStream.addRect(underline.getLowerLeftX(), underline.getLowerLeftY(),underline.getWidth(), underline.getHeight());
790815
contentStream.fill();
@@ -815,7 +840,7 @@ else if (valign == PDFReportPDFBox.VerticalAlign.BOTTOM.value())
815840
underline.setUpperRightY(this.pageSize.getUpperRightY() - bottomAux - topMargin -bottomMargin + startHeight - underlineHeight + strikethruSeparation);
816841
break;
817842
}
818-
PDPageContentStream contentStream = getNewPDPageContentStream();
843+
PDPageContentStream contentStream = currentPageContentStream;
819844
contentStream.setNonStrokingColor(foreColor);
820845
contentStream.addRect(underline.getLowerLeftX(), underline.getLowerLeftY() - strikethruSeparation * 1/3, underline.getWidth(), underline.getHeight());
821846
contentStream.fill();
@@ -903,7 +928,7 @@ private void loadSupportedHTMLTags(){
903928
private void processHTMLElement(PDPageContentStream cb, PDRectangle htmlRectangle, SpaceHandler spaceHandler, Element blockElement) throws Exception{
904929
this.fontBold = false;
905930
String tagName = blockElement.normalName();
906-
PDFont htmlFont = PDType1Font.TIMES_ROMAN;
931+
PDFont htmlFont = new PDType1Font(Standard14Fonts.FontName.TIMES_ROMAN);
907932

908933
if (tagName.equals("div") || tagName.equals("span")) {
909934
for (Node child : blockElement.childNodes())
@@ -916,7 +941,7 @@ private void processHTMLElement(PDPageContentStream cb, PDRectangle htmlRectangl
916941
return;
917942
}
918943

919-
float lineHeight = (PDType1Font.TIMES_ROMAN.getFontDescriptor().getFontBoundingBox().getHeight() / 1000 * fontSize) * DEFAULT_PDFBOX_LEADING;
944+
float lineHeight = (new PDType1Font(Standard14Fonts.FontName.TIMES_ROMAN).getFontDescriptor().getFontBoundingBox().getHeight() / 1000 * fontSize) * DEFAULT_PDFBOX_LEADING;
920945
float leading = (float)(Double.valueOf(props.getGeneralProperty(Const.LEADING)).doubleValue());
921946

922947
float llx = htmlRectangle.getLowerLeftX();
@@ -1040,7 +1065,7 @@ public void setAvailableSpace(float availableSpace) {
10401065

10411066
private float renderHTMLContent(PDPageContentStream contentStream, String text, float fontSize, float llx, float lly, float urx, float ury) {
10421067
try {
1043-
PDFont defaultHTMLFont = PDType1Font.TIMES_ROMAN;
1068+
PDFont defaultHTMLFont = new PDType1Font(Standard14Fonts.FontName.TIMES_ROMAN);
10441069
List<String> lines = new ArrayList<>();
10451070
String[] words = text.split(" ");
10461071
StringBuilder currentLine = new StringBuilder();
@@ -1093,18 +1118,18 @@ private void resolveTextStyling(PDPageContentStream contentStream, String text,
10931118
contentStream.setLineWidth(fontSize * 0.05f);
10941119
contentStream.setRenderingMode(RenderingMode.FILL_STROKE);
10951120
contentStream.beginText();
1096-
contentStream.moveTextPositionByAmount(x, y);
1121+
contentStream.newLineAtOffset(x, y);
10971122
contentStream.setTextMatrix(new Matrix(1, 0, 0.2f, 1, x + 0.2f * y, y));
10981123
contentStream.newLineAtOffset(-0.2f * y, 0);
10991124
} else if (this.fontBold && !this.fontItalic){
11001125
contentStream.setStrokingColor(foreColor);
11011126
contentStream.setLineWidth(fontSize * 0.05f);
11021127
contentStream.setRenderingMode(RenderingMode.FILL_STROKE);
11031128
contentStream.beginText();
1104-
contentStream.moveTextPositionByAmount(x, y);
1129+
contentStream.newLineAtOffset(x, y);
11051130
} else if (!this.fontBold && this.fontItalic){
11061131
contentStream.beginText();
1107-
contentStream.moveTextPositionByAmount(x, y);
1132+
contentStream.newLineAtOffset(x, y);
11081133
contentStream.setTextMatrix(new Matrix(1, 0, 0.2f, 1, x + 0.2f * y, y));
11091134
contentStream.newLineAtOffset(-0.2f * y, 0);
11101135
} else {
@@ -1372,10 +1397,30 @@ public void GxEndDocument() {
13721397
log.error("GxEndDocument failed: ", e);;
13731398
}
13741399
}
1400+
1401+
private PDPageContentStream currentPageContentStream;
1402+
13751403
public void GxStartPage() {
1376-
document.addPage(new PDPage(this.pageSize));
1377-
pages = pages + 1;
1378-
page = page + 1;
1404+
PDPage page = new PDPage(this.pageSize);
1405+
document.addPage(page);
1406+
pages++;
1407+
this.page++;
1408+
try {
1409+
currentPageContentStream = new PDPageContentStream(document, page, PDPageContentStream.AppendMode.APPEND, false);
1410+
} catch (IOException e) {
1411+
log.error("Failed to start page content stream: ", e);
1412+
}
1413+
}
1414+
1415+
public void GxEndPage() {
1416+
try {
1417+
if (currentPageContentStream != null) {
1418+
currentPageContentStream.close();
1419+
currentPageContentStream = null;
1420+
}
1421+
} catch (IOException e) {
1422+
log.error("Failed to close page content stream: ", e);
1423+
}
13791424
}
13801425

13811426
}

0 commit comments

Comments
 (0)