3030import org .apache .pdfbox .pdmodel .graphics .state .RenderingMode ;
3131import org .apache .pdfbox .pdmodel .interactive .action .PDActionJavaScript ;
3232import org .apache .pdfbox .pdmodel .interactive .viewerpreferences .PDViewerPreferences ;
33- import org .apache .pdfbox .text .PDFTextStripper ;
34- import org .apache .pdfbox .text .TextPosition ;
3533import org .apache .pdfbox .util .Matrix ;
3634
3735import org .jsoup .Jsoup ;
@@ -51,9 +49,6 @@ public class PDFReportPDFBox extends GXReportPDFCommons{
5149 private String barcodeType = null ;
5250 private PDDocument document ;
5351 private PDDocumentCatalog writer ;
54- private PDFont templateFont ;
55- private float templateX ;
56- private float templateY ;
5752 public boolean lineCapProjectingSquare = true ;
5853 public boolean barcode128AsImage = true ;
5954 ConcurrentHashMap <String , PDImageXObject > documentImages ;
@@ -63,6 +58,7 @@ public class PDFReportPDFBox extends GXReportPDFCommons{
6358 private Set <String > supportedHTMLTags = new HashSet <>();
6459 private PDPageContentStream currentPageContentStream ;
6560 private final static String PAGES_PLACEHOLDER = "{{pages}}" ;
61+ private final List <PageCountPlaceholder > pageCountPlaceholders = new ArrayList <>();
6662
6763 static {
6864 log = org .apache .logging .log4j .LogManager .getLogger (PDFReportPDFBox .class );
@@ -647,6 +643,31 @@ public void GxDrawText(String sTxt, int left, int top, int right, int bottom, in
647643 int alignment = align & 3 ;
648644 boolean autoResize = ((align & 256 ) == 256 );
649645
646+ if (sTxt .trim ().equalsIgnoreCase (PAGES_PLACEHOLDER )) {
647+ float width = baseFont .getStringWidth ("999" ) / 1000 * fontSize ;
648+
649+ float x ;
650+ switch (align ) {
651+ case 1 :
652+ x = ((leftAux + rightAux ) / 2 ) + leftMargin - width / 2 ;
653+ break ;
654+ case 2 :
655+ x = rightAux + leftMargin - width ;
656+ break ;
657+ default :
658+ x = leftAux + leftMargin ;
659+ break ;
660+ }
661+
662+ float y = this .pageSize .getUpperRightY () - bottomAux - topMargin - bottomMargin - fontSize *0.2f ;
663+
664+ int pageIndex = document .getNumberOfPages () - 1 ;
665+ pageCountPlaceholders .add (new PageCountPlaceholder (pageIndex , x , y , baseFont , fontSize , foreColor ));
666+
667+ return ;
668+ }
669+
670+
650671 // Handle HTML format
651672 if (htmlformat == 1 ) {
652673 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" );
@@ -857,19 +878,6 @@ else if (barcodeType != null) {
857878 contentStream .fill ();
858879 }
859880
860- // Handle {{Pages}}
861- if (sTxt .trim ().equalsIgnoreCase (PAGES_PLACEHOLDER )) {
862- if (!templateCreated ) {
863- templateFont = baseFont ;
864- templateFontSize = fontSize ;
865- templateColorFill = foreColor ;
866- templateX = leftAux + leftMargin ;
867- templateY = this .pageSize .getUpperRightY () - bottomAux - topMargin - bottomMargin ;
868- templateCreated = true ;
869- }
870- sTxt = PAGES_PLACEHOLDER ;
871- }
872-
873881 float textBlockWidth = rightAux - leftAux ;
874882 float txtWidth = baseFont .getStringWidth (sTxt ) / 1000 * fontSize ;
875883 boolean justified = (alignment == 3 ) && textBlockWidth < txtWidth ;
@@ -1271,75 +1279,22 @@ else if (length == 15840 && width == 12240)
12711279 return new PDRectangle ((int )(width / PAGE_SCALE_X ) + leftMargin , (int )(length / PAGE_SCALE_Y ) + topMargin );
12721280 }
12731281
1274- private void replaceTemplatePages () throws IOException {
1282+ private void writePageCountPlaceholders () {
12751283 int totalPages = document .getNumberOfPages ();
1276- for (int i = 0 ; i < totalPages ; i ++) {
1277- final PDPage page = document .getPage (i );
1278- final List <float []> replacements = new java .util .ArrayList <>();
1279- PDFTextStripper stripper = new PDFTextStripper () {
1280- @ Override
1281- protected void writeString (String text , List <TextPosition > textPositions ) throws IOException {
1282- String placeholder = PAGES_PLACEHOLDER ;
1283- int index = text .indexOf (placeholder );
1284- while (index != -1 && index + placeholder .length () <= textPositions .size ()) {
1285- float minX = Float .MAX_VALUE ;
1286- float maxX = 0 ;
1287- float minY = Float .MAX_VALUE ;
1288- float maxY = 0 ;
1289- for (int j = index ; j < index + placeholder .length (); j ++) {
1290- TextPosition tp = textPositions .get (j );
1291- float tpX = tp .getXDirAdj ();
1292- float tpY = tp .getYDirAdj ();
1293- float tpWidth = tp .getWidthDirAdj ();
1294- float tpHeight = tp .getHeightDir ();
1295- if (tpX < minX ) {
1296- minX = tpX ;
1297- }
1298- if (tpX + tpWidth > maxX ) {
1299- maxX = tpX + tpWidth ;
1300- }
1301- if (tpY < minY ) {
1302- minY = tpY ;
1303- }
1304- if (tpY + tpHeight > maxY ) {
1305- maxY = tpY + tpHeight ;
1306- }
1307- }
1308- float bboxWidth = maxX - minX ;
1309- float bboxHeight = maxY - minY ;
1310- float origBoxBottom = pageSize .getHeight () - maxY ;
1311- float originalCenterX = minX + bboxWidth / 2 ;
1312- float originalCenterY = origBoxBottom + bboxHeight / 2 ;
1313- float newCenterY = originalCenterY + (bboxHeight * 0.5f );
1314- float enlargedWidth = bboxWidth * 2.5f ;
1315- float enlargedHeight = bboxHeight * 2.5f ;
1316- float rectX = originalCenterX - (enlargedWidth / 2 );
1317- float rectY = newCenterY - (enlargedHeight / 2 );
1318- float baselineY = newCenterY ;
1319- replacements .add (new float [] { rectX , rectY , enlargedWidth , enlargedHeight , baselineY });
1320- index = text .indexOf (placeholder , index + placeholder .length ());
1321- }
1322- super .writeString (text , textPositions );
1323- }
1324- };
1325- stripper .setStartPage (i + 1 );
1326- stripper .setEndPage (i + 1 );
1327- stripper .getText (document );
1328- if (!replacements .isEmpty ()) {
1329- try (PDPageContentStream cs = new PDPageContentStream (
1330- document , page , PDPageContentStream .AppendMode .APPEND , true , true )) {
1331- for (float [] rep : replacements ) {
1332- cs .addRect (rep [0 ], rep [1 ], rep [2 ], rep [3 ]);
1333- cs .setNonStrokingColor (java .awt .Color .WHITE );
1334- cs .fill ();
1335- cs .beginText ();
1336- cs .setFont (templateFont , templateFontSize );
1337- cs .setNonStrokingColor (templateColorFill );
1338- cs .newLineAtOffset (rep [0 ], rep [4 ]);
1339- cs .showText (String .valueOf (totalPages ));
1340- cs .endText ();
1341- }
1342- }
1284+
1285+ for (PageCountPlaceholder p : pageCountPlaceholders ) {
1286+ PDPage page = document .getPage (p .pageIndex );
1287+ try (PDPageContentStream cs = new PDPageContentStream (
1288+ document , page , PDPageContentStream .AppendMode .APPEND , true )) {
1289+
1290+ cs .beginText ();
1291+ cs .setFont (p .font , p .fontSize );
1292+ cs .setNonStrokingColor (p .color );
1293+ cs .newLineAtOffset (p .x , p .y );
1294+ cs .showText (String .valueOf (totalPages ));
1295+ cs .endText ();
1296+ } catch (IOException e ) {
1297+ log .error ("Failed to write page count placeholder" , e );
13431298 }
13441299 }
13451300 }
@@ -1351,7 +1306,7 @@ public void GxEndDocument() {
13511306 pages ++;
13521307 }
13531308 GxEndPage ();
1354- replaceTemplatePages ();
1309+ writePageCountPlaceholders ();
13551310 int copies = 1 ;
13561311 try {
13571312 copies = Integer .parseInt (printerSettings .getProperty (form , Const .COPIES ));
@@ -1488,4 +1443,22 @@ public void GxEndPage() {
14881443 }
14891444 }
14901445
1446+ private static class PageCountPlaceholder {
1447+ final int pageIndex ;
1448+ final float x ;
1449+ final float y ;
1450+ final PDFont font ;
1451+ final float fontSize ;
1452+ final Color color ;
1453+
1454+ PageCountPlaceholder (int pageIndex , float x , float y , PDFont font , float fontSize , Color color ) {
1455+ this .pageIndex = pageIndex ;
1456+ this .x = x ;
1457+ this .y = y ;
1458+ this .font = font ;
1459+ this .fontSize = fontSize ;
1460+ this .color = color ;
1461+ }
1462+ }
1463+
14911464}
0 commit comments