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 ;
4744public 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