3232import org .apache .pdfbox .pdmodel .graphics .state .RenderingMode ;
3333import org .apache .pdfbox .pdmodel .interactive .action .PDActionJavaScript ;
3434import org .apache .pdfbox .pdmodel .interactive .viewerpreferences .PDViewerPreferences ;
35- import org .apache .pdfbox .text .PDFTextStripper ;
36- import org .apache .pdfbox .text .TextPosition ;
3735import org .apache .pdfbox .util .Matrix ;
3836
3937import org .jsoup .Jsoup ;
@@ -53,9 +51,6 @@ public class PDFReportPDFBox extends GXReportPDFCommons{
5351 private String barcodeType = null ;
5452 private PDDocument document ;
5553 private PDDocumentCatalog writer ;
56- private PDFont templateFont ;
57- private float templateX ;
58- private float templateY ;
5954 public boolean lineCapProjectingSquare = true ;
6055 public boolean barcode128AsImage = true ;
6156 ConcurrentHashMap <String , PDImageXObject > documentImages ;
@@ -65,6 +60,7 @@ public class PDFReportPDFBox extends GXReportPDFCommons{
6560 private Set <String > supportedHTMLTags = new HashSet <>();
6661 private PDPageContentStream currentPageContentStream ;
6762 private final static String PAGES_PLACEHOLDER = "{{pages}}" ;
63+ private final List <PageCountPlaceholder > pageCountPlaceholders = new ArrayList <>();
6864
6965 static {
7066 log = org .apache .logging .log4j .LogManager .getLogger (PDFReportPDFBox .class );
@@ -668,6 +664,31 @@ public void GxDrawText(String sTxt, int left, int top, int right, int bottom, in
668664 int alignment = align & 3 ;
669665 boolean autoResize = ((align & 256 ) == 256 );
670666
667+ if (sTxt .trim ().equalsIgnoreCase (PAGES_PLACEHOLDER )) {
668+ float width = baseFont .getStringWidth ("999" ) / 1000 * fontSize ;
669+
670+ float x ;
671+ switch (align ) {
672+ case 1 :
673+ x = ((leftAux + rightAux ) / 2 ) + leftMargin - width / 2 ;
674+ break ;
675+ case 2 :
676+ x = rightAux + leftMargin - width ;
677+ break ;
678+ default :
679+ x = leftAux + leftMargin ;
680+ break ;
681+ }
682+
683+ float y = this .pageSize .getUpperRightY () - bottomAux - topMargin - bottomMargin - fontSize *0.2f ;
684+
685+ int pageIndex = document .getNumberOfPages () - 1 ;
686+ pageCountPlaceholders .add (new PageCountPlaceholder (pageIndex , x , y , baseFont , fontSize , foreColor ));
687+
688+ return ;
689+ }
690+
691+
671692 // Handle HTML format
672693 if (htmlformat == 1 ) {
673694 log .debug ("WARNING: HTML rendering is not natively supported by PDFBOX 2.0.27. Handcrafted support is provided but it is not intended to cover all possible use cases" );
@@ -878,19 +899,6 @@ else if (barcodeType != null) {
878899 contentStream .fill ();
879900 }
880901
881- // Handle {{Pages}}
882- if (sTxt .trim ().equalsIgnoreCase (PAGES_PLACEHOLDER )) {
883- if (!templateCreated ) {
884- templateFont = baseFont ;
885- templateFontSize = fontSize ;
886- templateColorFill = foreColor ;
887- templateX = leftAux + leftMargin ;
888- templateY = this .pageSize .getUpperRightY () - bottomAux - topMargin - bottomMargin ;
889- templateCreated = true ;
890- }
891- sTxt = PAGES_PLACEHOLDER ;
892- }
893-
894902 float textBlockWidth = rightAux - leftAux ;
895903 float txtWidth = baseFont .getStringWidth (sTxt ) / 1000 * fontSize ;
896904 boolean justified = (alignment == 3 ) && textBlockWidth < txtWidth ;
@@ -1292,75 +1300,22 @@ else if (length == 15840 && width == 12240)
12921300 return new PDRectangle ((int )(width / PAGE_SCALE_X ) + leftMargin , (int )(length / PAGE_SCALE_Y ) + topMargin );
12931301 }
12941302
1295- private void replaceTemplatePages () throws IOException {
1303+ private void writePageCountPlaceholders () {
12961304 int totalPages = document .getNumberOfPages ();
1297- for (int i = 0 ; i < totalPages ; i ++) {
1298- final PDPage page = document .getPage (i );
1299- final List <float []> replacements = new java .util .ArrayList <>();
1300- PDFTextStripper stripper = new PDFTextStripper () {
1301- @ Override
1302- protected void writeString (String text , List <TextPosition > textPositions ) throws IOException {
1303- String placeholder = PAGES_PLACEHOLDER ;
1304- int index = text .indexOf (placeholder );
1305- while (index != -1 && index + placeholder .length () <= textPositions .size ()) {
1306- float minX = Float .MAX_VALUE ;
1307- float maxX = 0 ;
1308- float minY = Float .MAX_VALUE ;
1309- float maxY = 0 ;
1310- for (int j = index ; j < index + placeholder .length (); j ++) {
1311- TextPosition tp = textPositions .get (j );
1312- float tpX = tp .getXDirAdj ();
1313- float tpY = tp .getYDirAdj ();
1314- float tpWidth = tp .getWidthDirAdj ();
1315- float tpHeight = tp .getHeightDir ();
1316- if (tpX < minX ) {
1317- minX = tpX ;
1318- }
1319- if (tpX + tpWidth > maxX ) {
1320- maxX = tpX + tpWidth ;
1321- }
1322- if (tpY < minY ) {
1323- minY = tpY ;
1324- }
1325- if (tpY + tpHeight > maxY ) {
1326- maxY = tpY + tpHeight ;
1327- }
1328- }
1329- float bboxWidth = maxX - minX ;
1330- float bboxHeight = maxY - minY ;
1331- float origBoxBottom = pageSize .getHeight () - maxY ;
1332- float originalCenterX = minX + bboxWidth / 2 ;
1333- float originalCenterY = origBoxBottom + bboxHeight / 2 ;
1334- float newCenterY = originalCenterY + (bboxHeight * 0.5f );
1335- float enlargedWidth = bboxWidth * 2.5f ;
1336- float enlargedHeight = bboxHeight * 2.5f ;
1337- float rectX = originalCenterX - (enlargedWidth / 2 );
1338- float rectY = newCenterY - (enlargedHeight / 2 );
1339- float baselineY = newCenterY ;
1340- replacements .add (new float [] { rectX , rectY , enlargedWidth , enlargedHeight , baselineY });
1341- index = text .indexOf (placeholder , index + placeholder .length ());
1342- }
1343- super .writeString (text , textPositions );
1344- }
1345- };
1346- stripper .setStartPage (i + 1 );
1347- stripper .setEndPage (i + 1 );
1348- stripper .getText (document );
1349- if (!replacements .isEmpty ()) {
1350- try (PDPageContentStream cs = new PDPageContentStream (
1351- document , page , PDPageContentStream .AppendMode .APPEND , true , true )) {
1352- for (float [] rep : replacements ) {
1353- cs .addRect (rep [0 ], rep [1 ], rep [2 ], rep [3 ]);
1354- cs .setNonStrokingColor (java .awt .Color .WHITE );
1355- cs .fill ();
1356- cs .beginText ();
1357- cs .setFont (templateFont , templateFontSize );
1358- cs .setNonStrokingColor (templateColorFill );
1359- cs .newLineAtOffset (rep [0 ], rep [4 ]);
1360- cs .showText (String .valueOf (totalPages ));
1361- cs .endText ();
1362- }
1363- }
1305+
1306+ for (PageCountPlaceholder p : pageCountPlaceholders ) {
1307+ PDPage page = document .getPage (p .pageIndex );
1308+ try (PDPageContentStream cs = new PDPageContentStream (
1309+ document , page , PDPageContentStream .AppendMode .APPEND , true )) {
1310+
1311+ cs .beginText ();
1312+ cs .setFont (p .font , p .fontSize );
1313+ cs .setNonStrokingColor (p .color );
1314+ cs .newLineAtOffset (p .x , p .y );
1315+ cs .showText (String .valueOf (totalPages ));
1316+ cs .endText ();
1317+ } catch (IOException e ) {
1318+ log .error ("Failed to write page count placeholder" , e );
13641319 }
13651320 }
13661321 }
@@ -1372,7 +1327,7 @@ public void GxEndDocument() {
13721327 pages ++;
13731328 }
13741329 GxEndPage ();
1375- replaceTemplatePages ();
1330+ writePageCountPlaceholders ();
13761331 int copies = 1 ;
13771332 try {
13781333 copies = Integer .parseInt (printerSettings .getProperty (form , Const .COPIES ));
@@ -1509,4 +1464,22 @@ public void GxEndPage() {
15091464 }
15101465 }
15111466
1467+ private static class PageCountPlaceholder {
1468+ final int pageIndex ;
1469+ final float x ;
1470+ final float y ;
1471+ final PDFont font ;
1472+ final float fontSize ;
1473+ final Color color ;
1474+
1475+ PageCountPlaceholder (int pageIndex , float x , float y , PDFont font , float fontSize , Color color ) {
1476+ this .pageIndex = pageIndex ;
1477+ this .x = x ;
1478+ this .y = y ;
1479+ this .font = font ;
1480+ this .fontSize = fontSize ;
1481+ this .color = color ;
1482+ }
1483+ }
1484+
15121485}
0 commit comments