44import java .awt .image .BufferedImage ;
55import java .io .*;
66import 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 .*;
118import java .util .concurrent .ConcurrentHashMap ;
129
1310import com .genexus .ApplicationContext ;
@@ -60,12 +57,9 @@ public class PDFReportPDFBox extends GXReportPDFCommons{
6057 ConcurrentHashMap <String , PDImageXObject > documentImages ;
6158 public int runDirection = 0 ;
6259 private int page ;
63- private PDPageContentStream auxContentStream ;
64- private boolean useAuxContentStream ;
65-
6660 private final float DEFAULT_PDFBOX_LEADING = 1.2f ;
67-
6861 private Set <String > supportedHTMLTags = new HashSet <>();
62+ private PDPageContentStream currentPageContentStream ;
6963
7064 static {
7165 log = org .apache .logging .log4j .LogManager .getLogger (PDFReportPDFBox .class );
@@ -87,10 +81,6 @@ protected void init() {
8781 }
8882 }
8983
90- private PDPageContentStream getNewPDPageContentStream () throws IOException {
91- return new PDPageContentStream (document , document .getPage (page - 1 ), PDPageContentStream .AppendMode .APPEND ,false );
92- }
93-
9484 private void drawRectangle (PDPageContentStream cb , float x , float y , float w , float h ,
9585 int styleTop , int styleBottom , int styleRight , int styleLeft ,
9686 float radioTL , float radioTR , float radioBL , float radioBR , float penAux , boolean hideCorners ) {
@@ -229,9 +219,8 @@ private void roundRectangle(PDPageContentStream cb, float x, float y, float w, f
229219
230220 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 ,
231221 int styleTop , int styleBottom , int styleRight , int styleLeft , int cornerRadioTL , int cornerRadioTR , int cornerRadioBL , int cornerRadioBR ) {
232- PDPageContentStream cb = null ;
233- try {
234- cb = useAuxContentStream ? auxContentStream : getNewPDPageContentStream ();
222+ try {
223+ PDPageContentStream cb = currentPageContentStream ;
235224 float penAux = (float ) convertScale (pen );
236225 float rightAux = (float ) convertScale (right );
237226 float bottomAux = (float ) convertScale (bottom );
@@ -309,20 +298,11 @@ public void GxDrawRect(int left, int top, int right, int bottom, int pen, int fo
309298 log .debug ("GxDrawRect -> (" + left + "," + top + ") - (" + right + "," + bottom + ") BackMode: " + backMode + " Pen:" + pen );
310299 } catch (Exception e ) {
311300 log .error ("GxDrawRect failed: " , e );
312- } finally {
313- try {
314- if (cb != null && !useAuxContentStream )
315- cb .close ();
316- else if (useAuxContentStream )
317- useAuxContentStream = false ;
318- } catch (IOException ioe ) {
319- log .error ("Failed to close content stream" , ioe );
320- }
321301 }
322302 }
323303
324304 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 () ){
305+ try (PDPageContentStream cb = currentPageContentStream ){
326306
327307 float widthAux = (float )convertScale (width );
328308 float rightAux = (float )convertScale (right );
@@ -361,7 +341,7 @@ public void GxDrawLine(int left, int top, int right, int bottom, int width, int
361341 }
362342
363343 public void GxDrawBitMap (String bitmap , int left , int top , int right , int bottom , int aspectRatio ) {
364- try (PDPageContentStream cb = getNewPDPageContentStream () ){
344+ try (PDPageContentStream cb = currentPageContentStream ){
365345 PDImageXObject image ;
366346 try {
367347 if (documentImages != null && documentImages .containsKey (bitmap )) {
@@ -427,6 +407,14 @@ public void GxDrawBitMap(String bitmap, int left, int top, int right, int bottom
427407 }
428408 }
429409
410+ private static final int MAX_FONT_CACHE_SIZE = 50 ;
411+ private final Map <String , PDFont > fontCache = new LinkedHashMap <String , PDFont >(MAX_FONT_CACHE_SIZE , 0.75f , true ) {
412+ @ Override
413+ protected boolean removeEldestEntry (Map .Entry <String , PDFont > eldest ) {
414+ return size () > MAX_FONT_CACHE_SIZE ;
415+ }
416+ };
417+
430418 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 ) {
431419 boolean isCJK = false ;
432420 boolean embeedFont = isEmbeddedFont (fontName );
@@ -492,10 +480,8 @@ public void GxAttris(String fontName, int fontSize, boolean fontBold, boolean fo
492480 }
493481 }
494482 baseFont = createPDType1FontFromName (fontName );
495- if (baseFont != null )
496- baseFontName = baseFont .getName ();
497483 if (baseFont == null ){
498- baseFont = PDType0Font . load ( document , new File ( getFontLocation ( fontName )) );
484+ baseFont = getOrLoadFont ( fontName , document );
499485 baseFontName = fontName ;
500486 }
501487
@@ -516,19 +502,20 @@ public void GxAttris(String fontName, int fontSize, boolean fontBold, boolean fo
516502 String fontPath = getFontLocation (fontName );
517503 boolean foundFont = true ;
518504 if (fontPath .equals ("" )) {
519- fontPath = PDFFontDescriptor .getTrueTypeFontLocation (fontName , props );
505+ fontName = fontName .endsWith (style ) ? fontName .substring (0 , fontName .length () - style .length ()) : fontName ;
506+ fontPath = getFontLocation (fontName );
507+ baseFont = getOrLoadFont (fontName , document );
508+ baseFontName = fontName ;
520509 if (fontPath .equals ("" )) {
521- baseFont = PDType1Font . HELVETICA ;
522- baseFontName = baseFont . getName () ;
510+ baseFont = new PDType1Font ( Standard14Fonts . FontName . HELVETICA ) ;
511+ baseFontName = "Helvetica" ;
523512 foundFont = false ;
524513 }
525514 }
526515 if (foundFont ){
527516 baseFont = createPDType1FontFromName (fontName );
528- if (baseFont != null )
529- baseFontName = baseFont .getName ();
530- else {
531- baseFont = PDType0Font .load (document , new File (getFontLocation (fontName )));
517+ if (baseFont == null ){
518+ baseFont = getOrLoadFont (fontName , document );
532519 baseFontName = fontName ;
533520 }
534521 }
@@ -542,68 +529,79 @@ public void GxAttris(String fontName, int fontSize, boolean fontBold, boolean fo
542529 private static PDType1Font createPDType1FontFromName (String fontName ) {
543530 switch (fontName ) {
544531 case "Times-Roman" :
545- return PDType1Font . TIMES_ROMAN ;
532+ return new PDType1Font ( Standard14Fonts . FontName . TIMES_ROMAN ) ;
546533 case "Times-Bold" :
547- return PDType1Font . TIMES_BOLD ;
534+ return new PDType1Font ( Standard14Fonts . FontName . TIMES_BOLD ) ;
548535 case "Times-Italic" :
549- return PDType1Font . TIMES_ITALIC ;
536+ return new PDType1Font ( Standard14Fonts . FontName . TIMES_ITALIC ) ;
550537 case "Times-BoldItalic" :
551- return PDType1Font . TIMES_BOLD_ITALIC ;
538+ return new PDType1Font ( Standard14Fonts . FontName . TIMES_BOLD_ITALIC ) ;
552539 case "Helvetica" :
553- return PDType1Font . HELVETICA ;
540+ return new PDType1Font ( Standard14Fonts . FontName . HELVETICA ) ;
554541 case "Helvetica-Bold" :
555- return PDType1Font . HELVETICA_BOLD ;
542+ return new PDType1Font ( Standard14Fonts . FontName . HELVETICA_BOLD ) ;
556543 case "Helvetica-Oblique" :
557- return PDType1Font . HELVETICA_OBLIQUE ;
544+ return new PDType1Font ( Standard14Fonts . FontName . HELVETICA_OBLIQUE ) ;
558545 case "Helvetica-BoldOblique" :
559- return PDType1Font . HELVETICA_BOLD_OBLIQUE ;
546+ return new PDType1Font ( Standard14Fonts . FontName . HELVETICA_BOLD_OBLIQUE ) ;
560547 case "Courier" :
561- return PDType1Font . COURIER ;
548+ return new PDType1Font ( Standard14Fonts . FontName . COURIER ) ;
562549 case "Courier-Bold" :
563- return PDType1Font . COURIER_BOLD ;
550+ return new PDType1Font ( Standard14Fonts . FontName . COURIER_BOLD ) ;
564551 case "Courier-Oblique" :
565- return PDType1Font . COURIER_OBLIQUE ;
552+ return new PDType1Font ( Standard14Fonts . FontName . COURIER_OBLIQUE ) ;
566553 case "Courier-BoldOblique" :
567- return PDType1Font . COURIER_BOLD_OBLIQUE ;
554+ return new PDType1Font ( Standard14Fonts . FontName . COURIER_BOLD_OBLIQUE ) ;
568555 case "Symbol" :
569- return PDType1Font . SYMBOL ;
556+ return new PDType1Font ( Standard14Fonts . FontName . SYMBOL ) ;
570557 case "ZapfDingbats" :
571- return PDType1Font . ZAPF_DINGBATS ;
558+ return new PDType1Font ( Standard14Fonts . FontName . ZAPF_DINGBATS ) ;
572559 default :
573560 return null ;
574561 }
575562 }
576563
577564 public void setAsianFont (String fontName , String style ) {
578565 try {
579- String fontPath = getFontLocation (fontName );
580- baseFont = PDType0Font .load (document , new File (fontPath ));
581- baseFontName = fontName ;
566+ baseFont = getOrLoadFont (fontName , document );
582567 }
583568 catch (Exception e ) {
584569 log .error ("setAsianFont failed: " , e );
585570 }
586571 }
587572
573+ private PDFont getOrLoadFont (String fontName , PDDocument doc ) throws IOException {
574+ PDFont cachedFont = fontCache .get (fontName );
575+ if (cachedFont != null ) {
576+ return cachedFont ;
577+ }
578+ PDFont font = createPDType1FontFromName (fontName );
579+ if (font == null ) {
580+ String fontPath = getFontLocation (fontName );
581+ if (!fontPath .isEmpty ()) {
582+ font = PDType0Font .load (doc , new File (fontPath ));
583+ } else {
584+ font = new PDType1Font (Standard14Fonts .FontName .HELVETICA );
585+ }
586+ }
587+ fontCache .put (fontName , font );
588+ return font ;
589+ }
590+
588591 public void GxDrawText (String sTxt , int left , int top , int right , int bottom , int align , int htmlformat , int border , int valign ) {
589- PDPageContentStream cb = null ;
590592 try {
591- cb = getNewPDPageContentStream () ;
593+ PDPageContentStream cb = currentPageContentStream ;
592594 boolean printRectangle = false ;
593595 if (props .getBooleanGeneralProperty (Const .BACK_FILL_IN_CONTROLS , true ))
594596 printRectangle = true ;
595597
596598 if (printRectangle && (border == 1 || backFill )) {
597- auxContentStream = cb ;
598- useAuxContentStream = true ;
599599 GxDrawRect (left , top , right , bottom , border , foreColor .getRed (), foreColor .getGreen (), foreColor .getBlue (), backFill ? 1 : 0 , backColor .getRed (), backColor .getGreen (), backColor .getBlue (), 0 , 0 );
600600 }
601601
602602 sTxt = CommonUtil .rtrim (sTxt );
603603
604- PDFont font = createPDType1FontFromName (baseFont .getFontDescriptor ().getFontName ());
605- if (font == null )
606- font = PDType0Font .load (document , new File (getFontLocation (baseFontName )));
604+ PDFont font = getOrLoadFont (baseFontName , document );
607605
608606 cb .setFont (font ,fontSize );
609607 cb .setNonStrokingColor (foreColor );
@@ -668,8 +666,7 @@ else if (valign == PDFReportPDFBox.VerticalAlign.BOTTOM.value())
668666 GxEndPage ();
669667 GxStartPage ();
670668
671- cb .close ();
672- cb = getNewPDPageContentStream ();
669+ cb = currentPageContentStream ;
673670 }
674671 if (this .supportedHTMLTags .contains (element .normalName ()))
675672 processHTMLElement (cb , htmlRectangle , spaceHandler , element );
@@ -755,6 +752,7 @@ else if (valign == PDFReportPDFBox.VerticalAlign.BOTTOM.value())
755752 cb .setNonStrokingColor (backColor );
756753 cb .addRect (rectangle .getLowerLeftX (), rectangle .getLowerLeftY (),rectangle .getWidth (), rectangle .getHeight ());
757754 cb .fill ();
755+ cb .setNonStrokingColor (foreColor );
758756 }
759757
760758 float underlineSeparation = lineHeight / 5 ;
@@ -784,11 +782,11 @@ else if (valign == PDFReportPDFBox.VerticalAlign.BOTTOM.value())
784782 underline .setUpperRightY (this .pageSize .getUpperRightY () - bottomAux - topMargin -bottomMargin + startHeight - underlineHeight );
785783 break ;
786784 }
787- PDPageContentStream contentStream = getNewPDPageContentStream () ;
785+ PDPageContentStream contentStream = currentPageContentStream ;
788786 contentStream .setNonStrokingColor (foreColor );
789787 contentStream .addRect (underline .getLowerLeftX (), underline .getLowerLeftY (),underline .getWidth (), underline .getHeight ());
790788 contentStream .fill ();
791- contentStream . close ( );
789+ cb . setNonStrokingColor ( foreColor );
792790 }
793791
794792 if (fontStrikethru ) {
@@ -815,11 +813,10 @@ else if (valign == PDFReportPDFBox.VerticalAlign.BOTTOM.value())
815813 underline .setUpperRightY (this .pageSize .getUpperRightY () - bottomAux - topMargin -bottomMargin + startHeight - underlineHeight + strikethruSeparation );
816814 break ;
817815 }
818- PDPageContentStream contentStream = getNewPDPageContentStream () ;
816+ PDPageContentStream contentStream = currentPageContentStream ;
819817 contentStream .setNonStrokingColor (foreColor );
820818 contentStream .addRect (underline .getLowerLeftX (), underline .getLowerLeftY () - strikethruSeparation * 1 /3 , underline .getWidth (), underline .getHeight ());
821819 contentStream .fill ();
822- contentStream .close ();
823820 }
824821
825822 if (sTxt .trim ().equalsIgnoreCase ("{{Pages}}" )) {
@@ -877,13 +874,6 @@ else if (valign == PDFReportPDFBox.VerticalAlign.BOTTOM.value())
877874 }
878875 } catch (Exception ioe ){
879876 log .error ("GxDrawText failed: " , ioe );
880- } finally {
881- try {
882- if (cb != null ) cb .close ();
883- }
884- catch (IOException ioe ) {
885- log .error ("GxDrawText failed to close a content stream to one of it's pages: " , ioe );
886- }
887877 }
888878 }
889879
@@ -903,7 +893,7 @@ private void loadSupportedHTMLTags(){
903893 private void processHTMLElement (PDPageContentStream cb , PDRectangle htmlRectangle , SpaceHandler spaceHandler , Element blockElement ) throws Exception {
904894 this .fontBold = false ;
905895 String tagName = blockElement .normalName ();
906- PDFont htmlFont = PDType1Font . TIMES_ROMAN ;
896+ PDFont htmlFont = new PDType1Font ( Standard14Fonts . FontName . TIMES_ROMAN ) ;
907897
908898 if (tagName .equals ("div" ) || tagName .equals ("span" )) {
909899 for (Node child : blockElement .childNodes ())
@@ -916,7 +906,7 @@ private void processHTMLElement(PDPageContentStream cb, PDRectangle htmlRectangl
916906 return ;
917907 }
918908
919- float lineHeight = (PDType1Font . TIMES_ROMAN .getFontDescriptor ().getFontBoundingBox ().getHeight () / 1000 * fontSize ) * DEFAULT_PDFBOX_LEADING ;
909+ float lineHeight = (new PDType1Font ( Standard14Fonts . FontName . TIMES_ROMAN ) .getFontDescriptor ().getFontBoundingBox ().getHeight () / 1000 * fontSize ) * DEFAULT_PDFBOX_LEADING ;
920910 float leading = (float )(Double .valueOf (props .getGeneralProperty (Const .LEADING )).doubleValue ());
921911
922912 float llx = htmlRectangle .getLowerLeftX ();
@@ -1040,7 +1030,7 @@ public void setAvailableSpace(float availableSpace) {
10401030
10411031 private float renderHTMLContent (PDPageContentStream contentStream , String text , float fontSize , float llx , float lly , float urx , float ury ) {
10421032 try {
1043- PDFont defaultHTMLFont = PDType1Font . TIMES_ROMAN ;
1033+ PDFont defaultHTMLFont = new PDType1Font ( Standard14Fonts . FontName . TIMES_ROMAN ) ;
10441034 List <String > lines = new ArrayList <>();
10451035 String [] words = text .split (" " );
10461036 StringBuilder currentLine = new StringBuilder ();
@@ -1093,18 +1083,18 @@ private void resolveTextStyling(PDPageContentStream contentStream, String text,
10931083 contentStream .setLineWidth (fontSize * 0.05f );
10941084 contentStream .setRenderingMode (RenderingMode .FILL_STROKE );
10951085 contentStream .beginText ();
1096- contentStream .moveTextPositionByAmount (x , y );
1086+ contentStream .newLineAtOffset (x , y );
10971087 contentStream .setTextMatrix (new Matrix (1 , 0 , 0.2f , 1 , x + 0.2f * y , y ));
10981088 contentStream .newLineAtOffset (-0.2f * y , 0 );
10991089 } else if (this .fontBold && !this .fontItalic ){
11001090 contentStream .setStrokingColor (foreColor );
11011091 contentStream .setLineWidth (fontSize * 0.05f );
11021092 contentStream .setRenderingMode (RenderingMode .FILL_STROKE );
11031093 contentStream .beginText ();
1104- contentStream .moveTextPositionByAmount (x , y );
1094+ contentStream .newLineAtOffset (x , y );
11051095 } else if (!this .fontBold && this .fontItalic ){
11061096 contentStream .beginText ();
1107- contentStream .moveTextPositionByAmount (x , y );
1097+ contentStream .newLineAtOffset (x , y );
11081098 contentStream .setTextMatrix (new Matrix (1 , 0 , 0.2f , 1 , x + 0.2f * y , y ));
11091099 contentStream .newLineAtOffset (-0.2f * y , 0 );
11101100 } else {
@@ -1372,10 +1362,28 @@ public void GxEndDocument() {
13721362 log .error ("GxEndDocument failed: " , e );;
13731363 }
13741364 }
1365+
13751366 public void GxStartPage () {
1376- document .addPage (new PDPage (this .pageSize ));
1377- pages = pages + 1 ;
1378- page = page + 1 ;
1367+ PDPage page = new PDPage (this .pageSize );
1368+ document .addPage (page );
1369+ pages ++;
1370+ this .page ++;
1371+ try {
1372+ currentPageContentStream = new PDPageContentStream (document , page , PDPageContentStream .AppendMode .APPEND , false );
1373+ } catch (IOException e ) {
1374+ log .error ("Failed to start page content stream: " , e );
1375+ }
1376+ }
1377+
1378+ public void GxEndPage () {
1379+ try {
1380+ if (currentPageContentStream != null ) {
1381+ currentPageContentStream .close ();
1382+ currentPageContentStream = null ;
1383+ }
1384+ } catch (IOException e ) {
1385+ log .error ("Failed to close page content stream: " , e );
1386+ }
13791387 }
13801388
13811389}
0 commit comments