How to watermark PDFs using text or images?

Tags: watermarkpage rotationpage orientationiText 7
Watermark without rotation
Watermark without rotation

What are my options from a Java serverside context? Preferably the watermark will support transparency. Both vector and raster is desirable.

Posted on StackOverflow on Apr 10, 2015 by Lennart Rolland

Please take a look at the TransparentWatermark2 example. It adds transparent text on each odd page and a transparent image on each even page of an existing PDF document.

This is how it's done:

protected void manipulatePdf(String src, String dest) throws Exception {
    PdfDocument pdfDoc = new PdfDocument(new PdfReader(src), new PdfWriter(dest));
    Document doc = new Document(pdfDoc);
    int n = pdfDoc.getNumberOfPages();
    PdfFont font = PdfFontFactory.createFont(FontConstants.HELVETICA);
    Paragraph p = new Paragraph("My watermark (text)").setFont(font).setFontSize(30);
    // image watermark
    ImageData img = ImageDataFactory.create(IMG);
    //  Implement transformation matrix usage in order to scale image
    float w = img.getWidth();
    float h = img.getHeight();
    // transparency
    PdfExtGState gs1 = new PdfExtGState();
    gs1.setFillOpacity(0.5f);
    // properties
    PdfCanvas over;
    Rectangle pagesize;
    float x, y;
    // loop over every page
    for (int i = 1; i <= n; i++) {
        PdfPage pdfPage = pdfDoc.getPage(i);
        pagesize = pdfPage.getPageSizeWithRotation();
        pdfPage.setIgnorePageRotationForContent(true);

        x = (pagesize.getLeft() + pagesize.getRight()) / 2;
        y = (pagesize.getTop() + pagesize.getBottom()) / 2;
        over = new PdfCanvas(pdfDoc.getPage(i));
        over.saveState();
        over.setExtGState(gs1);
        if (i % 2 == 1) {
            doc.showTextAligned(p, x, y, i, TextAlignment.CENTER, VerticalAlignment.TOP, 0);
        } else {
            over.addImage(img, w, 0, 0, h, x - (w / 2), y - (h / 2), false);
        }
        over.restoreState();
    }
    doc.close();
}

As you can see, we create a Paragraph object for the text and an ImageData object for the image. We also create a PdfExtGState object for the transparency. In our case, we go for 50% opacity (change the 0.5f into something else to experiment).

Once we have these objects, we loop over every page. We use the PdfDocument object to get information about the existing document, for instance the dimensions of every page. We create a new PdfCanvas object from the page we are interested in, when we want to add extra content on the existing document.

When changing the graphics state, it is always safe to perform a saveState() before you start and to restoreState() once you're finished. You code will probably also work if you don't do this, but believe me: it can save you plenty of debugging time if you adopt the discipline to do this as you can get really strange effects if the graphics state is out of balance.

We apply the transparency using the setExtGState() method and depending on whether the page is an odd page or an even page, we add the text (using showTextAligned() and an (x, y) coordinate calculated so that the text is added in the middle of each page) or the image (using the addImage() method and the appropriate parameters for the transformation matrix).

Once you've done this for every page in the document, you have to close() the document.

Caveat:

You'll notice that pages 3 and 4 are in landscape, yet there is a difference between those two pages that isn't visible to the naked eye. Page 3 is actually a page of which the size is defined as if it were a page in portrait, but it is rotated by 90 degrees. Page 4 is a page of which the size is defined in such a way that the width > the height.

This can have an impact on the way you add a watermark, but if you use getPageSizeWithRotation(), iText will adapt. This may not be what you want: maybe you want the watermark to be added differently.

Look how the loop changes in TransparentWatermark3:

for (int i = 1; i <= n; i++) {
    pagesize = pdfDoc.getPage(i).getPageSize();
    x = (pagesize.getLeft() + pagesize.getRight()) / 2;
    y = (pagesize.getTop() + pagesize.getBottom()) / 2;
    over = new PdfCanvas(pdfDoc.getPage(i));
    over.saveState();
    over.setExtGState(gs1);
    if (i % 2 == 1)
        doc.showTextAligned(p, x, y, i, TextAlignment.CENTER, VerticalAlignment.TOP, 0);
    else
        over.addImage(img, w, 0, 0, h, x - (w / 2), y - (h / 2), true);
    over.restoreState();
}

In this case, we don't use getPageSizeWithRotation() but simply getPageSize(). We also removed pdfPage.setIgnorePageRotationForContent(true); so it won’t compensate for the existing page rotation.

Take a look at the difference in the resulting PDFs:

In the first screen shot (showing page 3 and 4 of the resulting PDF of TransparentWatermark2), the page to the left is actually a page in portrait rotated by 90 degrees. iText however, treats it as if it were a page in landscape just like the page to the right.

Watermark without rotation
Watermark without rotation

In the second screen shot (showing page 3 and 4 of the resulting PDF of TransparentWatermark3), the page to the left is a page in portrait rotated by 90 degrees and we add the watermark as if the page is in portrait. As a result, the watermark is also rotated by 90 degrees. This doesn't happen with the page to the right, because that page has a rotation of 0 degrees.

Watermark with rotation
Watermark with rotation

This is a subtle difference, but I thought you'd want to know.

Click this link if you want to see how to answer this question in iText 5.