Search code examples
javaitextitext7

How to add transparent watermark text to each page using iText7 pdfHtml


Currently, I am trying to add a watermark that appears in the background of each page of my pdf using iText7 pdfHtml, but I cannot figure out a solution. For example I would like the text "Confidential" to appear in the background of each page. I have attempted to add it with css like so

@page {
    size: Letter;
    margin: .5in .5in .5in .5in;

    @left-middle {
        content: "Confidential";
        /* z-index: 100; */
        font-size: 80pt;
        font-weight: bold;
        opacity: .2;
        text-align: center;
        text-transform: uppercase;
        transform: translateX(350px) rotate(-54.7deg);
        position: absolute;
        left: 0;
        top: 0;
        width: 100%;
        height: 100%;
        overflow: auto;
        z-index: 0;
    }
}

This almost solves my problem, but the text is not transparent and covers up text behind it. It also does not rotate, but that is not a necessary requirement.

Solutions involving some combination of Java, CSS, or Html are welcome.

Here is an example of my Java code:

        FileInputStream htmlStream = null;
        FileOutputStream pdfStream = null;

        try {
            ConverterProperties converterProperties = new ConverterProperties().setBaseUri(path);
            converterProperties.setMediaDeviceDescription(new MediaDeviceDescription(MediaType.PRINT));
            htmlStream = new FileInputStream(inputPath);
            pdfStream = new FileOutputStream(outputPath);
            HtmlConverter.convertToPdf(htmlStream, pdfStream, converterProperties);

        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } finally {
            if (htmlStream != null) {
                htmlStream.close();
            }
            if (pdfStream != null) {
                pdfStream.close();
            }
        }

Edit

sample html to reproduce:

<!DOCTYPE html>
<html>
<link id="watermark_link" href="watermark.css" rel="stylesheet" type="text/css" />
</head>

<body>
    <h1>My First Heading</h1>
    <p>My first paragraph.</p>
</body>

</html>

Edit 2

After trying out the answer from @BenIngle here is my Java code to get it working with pdfHtml:

    private static void generatePDFFromHTML(String inputPath, String outputPath, String baseUrl) throws IOException {
        FileInputStream htmlStream = null;
        FileOutputStream pdfStream = null;

        try {
            ConverterProperties converterProperties = new ConverterProperties().setBaseUri(baseUrl);
            converterProperties.setMediaDeviceDescription(new MediaDeviceDescription(MediaType.PRINT));
            htmlStream = new FileInputStream(inputPath);
            pdfStream = new FileOutputStream(outputPath);
            PdfWriter writer = new PdfWriter(pdfStream);
            PdfDocument pdfDocument = new PdfDocument(writer);
            Watermark watermark = new Watermark("Confidential");
            pdfDocument.addEventHandler(PdfDocumentEvent.END_PAGE,watermark);
            HtmlConverter.convertToPdf(htmlStream, pdfDocument, converterProperties);
            pdfDocument.close();

        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } finally {
            if (htmlStream != null) {
                htmlStream.close();
            }
            if (pdfStream != null) {
                pdfStream.close();
            }
        }

    }

    protected static class Watermark implements IEventHandler {

        String watermarkText;

        public Watermark(String watermarkText) {
            this.watermarkText = watermarkText;
        }

        @Override
        public void handleEvent(Event event) {
            //Retrieve document and
            PdfDocumentEvent docEvent = (PdfDocumentEvent) event;
            PdfDocument pdf = docEvent.getDocument();
            PdfPage page = docEvent.getPage();
            Rectangle pageSize = page.getPageSize();
            PdfCanvas pdfCanvas = new PdfCanvas(page.getLastContentStream(), page.getResources(), pdf);
            Canvas canvas = new Canvas(pdfCanvas, pdf, pageSize);
            PdfExtGState gstate = new PdfExtGState();
            gstate.setFillOpacity(.2f);
            pdfCanvas.setExtGState(gstate);

            double rotationDeg = -54.7d;
            double rotationRad = Math.toRadians(rotationDeg);
            Paragraph watermarkParagraph = new Paragraph(watermarkText)
                    .setFontSize(80f)
                    .setTextAlignment(TextAlignment.CENTER)
                    .setVerticalAlignment(VerticalAlignment.MIDDLE)
                    .setRotationAngle(rotationRad)
                    .setFixedPosition(100, page.getPageSize().getHeight(), page.getPageSize().getWidth());
            canvas.add(watermarkParagraph);
            canvas.close();

        }

    }

I hope this helps anyone else trying to get started with iText pdfHtml!


Solution

  • Here's a solution to add text "in the background of each page". This will add text behind existing content so that it doesn't cover it up. Note that this does not add transparency. Transparency needs to be added with external graphics state.

    try (PdfDocument doc = new PdfDocument(new PdfReader(in.toFile()), new PdfWriter(out.toFile()))) {
        PdfFont helvetica = PdfFontFactory.createFont();
        for (int pageNum = 1; pageNum <= doc.getNumberOfPages(); pageNum++) {
            PdfPage page = doc.getPage(pageNum);
            // important - add a new content stream in the beginning, to render behind existing text
            PdfCanvas canvas = new PdfCanvas(page.newContentStreamBefore(), page.getResources(), doc);
    
            // option 1 - manual placement
            canvas.saveState();
            canvas.beginText();
            canvas.setFillColor(ColorConstants.GRAY);
            canvas.setFontAndSize(helvetica, 80f);
            canvas.moveText(0f, page.getPageSize().getHeight() - 80f);
            canvas.showText("Confidential1");
            canvas.endText();
            canvas.restoreState();
    
            // option 2 - let iText place it
            try (Canvas canvas1 = new Canvas(canvas, doc, page.getPageSize())) {
                Paragraph watermark = new Paragraph("Confidential2")
                        .setFontColor(ColorConstants.GRAY)
                        .setFont(helvetica)
                        .setFontSize(80f)
                        .setHorizontalAlignment(HorizontalAlignment.LEFT)
                        .setVerticalAlignment(VerticalAlignment.BOTTOM)
                        .setFixedPosition(0f, page.getPageSize().getHeight() - 100f, page.getPageSize().getWidth());
                canvas1.add(watermark);
            }
    
            // option 3 - set opacity and place on top of existing content, plus rotation
            PdfExtGState gstate = new PdfExtGState();
            gstate.setFillOpacity(.2f);
            canvas = new PdfCanvas(page);
            canvas.saveState();
            canvas.setExtGState(gstate);
            try (Canvas canvas2 = new Canvas(canvas, doc, page.getPageSize())) {
                double rotationDeg = -54.7d;
                double rotationRad = Math.toRadians(rotationDeg);
                Paragraph watermark = new Paragraph("Confidential3")
                        .setFont(helvetica)
                        .setFontSize(80f)
                        .setTextAlignment(TextAlignment.CENTER)
                        .setVerticalAlignment(VerticalAlignment.MIDDLE)
                        .setRotationAngle(rotationRad)
                        .setFixedPosition(100, page.getPageSize().getHeight(), page.getPageSize().getWidth());
                canvas2.add(watermark);
            }
            canvas.restoreState();
        }
    }
    

    Edit

    Added a third option which applies transparency and rotation.