Search code examples
javapdfbox

How to make Graphicstate Image appear on top and not in the background


I'm working on a pdf project using Apache PDFBox, it displays a list of transactions that extends to new pages once the current page is filled, for each transaction that is displayed purple color is used to create the row and when the next is displayed white color creates the row and so on. I want to place a stamp at the bottom of each page which I have done, I set the stamp image using graphics state to increase and decrease the opacity of the image, however, the image appears under the transaction row like a watermark but I want in to appear ontop. How to can I achieve that? Below is my code

JsonArray transactionList = SampleDataGenerator
                    .generateSampleData()
                    .getJsonObject("data")
                    .getJsonArray("transactionList");
            for (int i = 0; i < transactionList.size(); i++) {
                JsonObject transaction = transactionList.getJsonObject(i);

PDPageContentStream newPagesContentStream = new PDPageContentStream(document, page, PDPageContentStream.AppendMode.APPEND, true);

                if (dataYPosition - rowHeight < margin) {
                    page = new PDPage();
                    document.addPage(page);
                    dataYPosition = page.getMediaBox().getHeight() - 130f; // Adjust newPage rowHeight
                    PdfImageDrawer.drawColumnHeader(newPagesContentStream, HELVETICA, columnXPosition, columnYPosition + 85f);
                    PdfImageDrawer.drawLogo(document, newPagesContentStream);
                }

                
                

                List<String> transactionValues = generateTransactionRow.doGenerateRow(transaction);
                for (String dataValues : transactionValues) {
                    newPagesContentStream.setNonStrokingColor(i % 2 == 0 ? new Color(229, 229, 255) : Color.WHITE);
                    newPagesContentStream.addRect(dataXPosition - 35f, dataYPosition, tableWidth, rectangleHeight);
                    newPagesContentStream.fill();
                    newPagesContentStream.setNonStrokingColor( new Color(112, 112, 112));

                    newPagesContentStream.beginText();
                    newPagesContentStream.setFont(HELVETICA, 6.5f);
                    newPagesContentStream.newLineAtOffset(dataXPosition, dataYPosition + 25f);
                    newPagesContentStream.showText(dataValues);
                    newPagesContentStream.endText();
                    dataXPosition += 100f;
                }
                dataXPosition += -600f;
                dataYPosition -= rowHeight;

                contentStream.saveGraphicsState();
                PdfImageDrawer.drawTextStampInputStream(newPagesContentStream, stampImage);
                newPagesContentStream.close();
            }

//FOOTER
            PdfImageDrawer.drawTextStampInputStream(contentStream, stampImage);

            contentStream.close();

drawTextStampInputStream

public static void drawTextStampInputStream(PDPageContentStream contentStream, PDImageXObject stampImage) throws IOException {
        String text1 = "xxxxxxxxxxxxxxxxxxxxxxx";
        String text2 = "xxxxxxxxxxxxxxxxxxxxxxx";
        String text3 = "xxxxxxxxxxxxxxxxxxxxxxx";

//        contentStream.saveGraphicsState();

        PDType1Font HELVETICA = new PDType1Font(Standard14Fonts.FontName.HELVETICA);
        PDExtendedGraphicsState textGraphicsState = new PDExtendedGraphicsState();
        textGraphicsState.setNonStrokingAlphaConstant(0.050f);
        contentStream.setGraphicsStateParameters(textGraphicsState);

        PdfTextWriter.doWrite(contentStream, HELVETICA, 8f,
                (700f) / 3f, 60f, 0, text1);
        PdfTextWriter.doWrite(contentStream, HELVETICA, 8f,
                140f, 50f, 0, text2);
        PdfTextWriter.doWrite(contentStream, HELVETICA, 8f,
                190f, 40f, 0, text3);


        PDExtendedGraphicsState graphicsState = new PDExtendedGraphicsState();
        graphicsState.setNonStrokingAlphaConstant(0.10f);
        contentStream.setGraphicsStateParameters(graphicsState);
        contentStream.drawImage(stampImage, 480F, 10f,
                stampImage.getWidth() / 4F,
                stampImage.getHeight() / 4F);
        contentStream.restoreGraphicsState();

    }

Solution

  • The best option for having a rubber stamp bitmap image on top with the background visible where the rubber stamp does not color, is to have those regions of the bitmap image itself transparent. Then you don't need to apply some global graphics state transparency, the transparency of the stamp image will do the job.

    If you cannot make sure that the stamp bitmap image is transparent where it should be but instead fairly white, you can try to achieve a similar effect using global graphics state transparency.

    Constant alpha transparency which you tried in your code, is not giving the desired result, either the rubber stamp itself becomes to faint or the empty are in-between is not transparent enough.

    But PDF supports a much richer transparency model, there in particular are the different blend modes. For a task like yours the blend modes Darken and Multiply work best.