7

I have a bunch of PDF documents in a folder and I want to augment them with a watermark. What are my options from a Java serverside context?

Preferably the watermark will support transparency. Both vector and raster is desirable.

Bruno Lowagie
  • 75,994
  • 9
  • 109
  • 165
Mr. Developerdude
  • 9,118
  • 10
  • 57
  • 95
  • Essentially you are asking for recommendations concerning PDF libraries (one shouldn't try to manipulate pdfs without such a library), but library recommendations are off topic here. – mkl Apr 12 '15 at 22:23
  • I am sorry about that. I do find these kind of questions on SO to be very valuable and its a shame when they are put down by righteous people following rules. Also sorry that I did not mention that I was looking for a solution that would not cost money. iText looks awesome but for this simple use we simply can't afford it. – Mr. Developerdude Apr 13 '15 at 10:04
  • There is no need to be sorry about that, it is just off-topic, no matter how you feel about it or if you think differently. You are free to ask these questions anyway, but don't be upset when they are closed ... . – martin Apr 14 '15 at 06:54

2 Answers2

7

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:

public void manipulatePdf(String src, String dest) throws IOException, DocumentException {
    PdfReader reader = new PdfReader(src);
    int n = reader.getNumberOfPages();
    PdfStamper stamper = new PdfStamper(reader, new FileOutputStream(dest));
    // text watermark
    Font f = new Font(FontFamily.HELVETICA, 30);
    Phrase p = new Phrase("My watermark (text)", f);
    // image watermark
    Image img = Image.getInstance(IMG);
    float w = img.getScaledWidth();
    float h = img.getScaledHeight();
    // transparency
    PdfGState gs1 = new PdfGState();
    gs1.setFillOpacity(0.5f);
    // properties
    PdfContentByte over;
    Rectangle pagesize;
    float x, y;
    // loop over every page
    for (int i = 1; i <= n; i++) {
        pagesize = reader.getPageSizeWithRotation(i);
        x = (pagesize.getLeft() + pagesize.getRight()) / 2;
        y = (pagesize.getTop() + pagesize.getBottom()) / 2;
        over = stamper.getOverContent(i);
        over.saveState();
        over.setGState(gs1);
        if (i % 2 == 1)
            ColumnText.showTextAligned(over, Element.ALIGN_CENTER, p, x, y, 0);
        else
            over.addImage(img, w, 0, 0, h, x - (w / 2), y - (h / 2));
        over.restoreState();
    }
    stamper.close();
    reader.close();
}

As you can see, we create a Phrase object for the text and an Image object for the image. We also create a PdfGState 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 PdfReader object to get information about the existing document, for instance the dimensions of every page. We use the PdfStamper object when we want to stamp extra content on the existing document, for instance adding a watermark on top of each single page.

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 setGState() method and depending on whether the page is an odd page or an even page, we add the text (using ColumnText 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 stamper and the reader.

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.

Take a look at TransparentWatermark3:

public void manipulatePdf(String src, String dest) throws IOException, DocumentException {
    PdfReader reader = new PdfReader(src);
    int n = reader.getNumberOfPages();
    PdfStamper stamper = new PdfStamper(reader, new FileOutputStream(dest));
    stamper.setRotateContents(false);
    // text watermark
    Font f = new Font(FontFamily.HELVETICA, 30);
    Phrase p = new Phrase("My watermark (text)", f);
    // image watermark
    Image img = Image.getInstance(IMG);
    float w = img.getScaledWidth();
    float h = img.getScaledHeight();
    // transparency
    PdfGState gs1 = new PdfGState();
    gs1.setFillOpacity(0.5f);
    // properties
    PdfContentByte over;
    Rectangle pagesize;
    float x, y;
    // loop over every page
    for (int i = 1; i <= n; i++) {
        pagesize = reader.getPageSize(i);
        x = (pagesize.getLeft() + pagesize.getRight()) / 2;
        y = (pagesize.getTop() + pagesize.getBottom()) / 2;
        over = stamper.getOverContent(i);
        over.saveState();
        over.setGState(gs1);
        if (i % 2 == 1)
            ColumnText.showTextAligned(over, Element.ALIGN_CENTER, p, x, y, 0);
        else
            over.addImage(img, w, 0, 0, h, x - (w / 2), y - (h / 2));
        over.restoreState();
    }
    stamper.close();
    reader.close();
}

In this case, we don't use getPageSizeWithRotation() but simply getPageSize(). We also tell the stamper not to compensate for the existing page rotation: stamper.setRotateContents(false);

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.

enter image description here

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.

enter image description here

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

If you want to read this answer in French, please read Comment créer un filigrane transparent en PDF?

Bruno Lowagie
  • 75,994
  • 9
  • 109
  • 165
  • How could we rotate the watermark `x` degrees. i.e.: 45 degrees? I asked it here: https://stackoverflow.com/q/52748248/5640649 in case you have some spare time and can help with this. Thanks in advance! – lealceldeiro Oct 10 '18 at 20:41
1

Best option is iText. Check a watermark demo here

Important part of the code (where the watermar is inserted) is this:

public class Watermark extends PdfPageEventHelper {

        @Override
        public void onEndPage(PdfWriter writer, Document document) {
             // insert here your watermark
        }

Read carefully the example.

onEndPage() method will be something like (in my logo-watermarks I use com.itextpdf.text.Image;):

Image image = Image.getInstance(this.getClass().getResource("/path/to/image.png"));

// set transparency
image.setTransparency(transparency);     

// set position
image.setAbsolutePosition(absoluteX, absoluteY);

// put into document
document.add(image);
Jordi Castilla
  • 26,609
  • 8
  • 70
  • 109
  • 3
    This doesn't really help the OP, but adapt your answer so that it uses `PdfStamper` and loops over the pages to add a watermark to the `getOverContent()` based on this example, and you deserve an up-vote: http://itextpdf.com/sandbox/stamper/TransparentWatermark – Bruno Lowagie Apr 10 '15 at 13:58
  • 2
    OK, I upvoted your answer because you are trying to answer several iText questions in the past. I appreciate the effort and I appreciate the fact that this makes me feel less lonely (sometimes it's as if iText questions are only answer by me, mkl and Chris Haas). I would like to welcome you as a 4th contributor of iText answers. However, you should compare your answer to mine. You'll understand that you should try to avoid copy/pasting examples if do not address a new question adequately. – Bruno Lowagie Apr 12 '15 at 07:40
  • Are there solutions to this that will not incur licensing fees? – Mr. Developerdude Apr 12 '15 at 08:25
  • 1
    @Bruno thanks for that, reading carefully your examples is much easier the way you do, specially for this case with existing pdf files... Just trying to help and learn to improve myself and SO level, but i'm really glad of listening my answers (or at least the efforts) are appreciated by you ;) – Jordi Castilla Apr 13 '15 at 07:42
  • 1
    Even answers that aren't 100% correct often direct people in the right direction. At the same time, you get feedback that is useful for future answers. – Bruno Lowagie Apr 13 '15 at 07:44