Watermarking with PDFBox
Asked Answered
I

7

26

I am trying to add a watermark to a PDF specifically with PDFBox. I've been able to get the image to appear on each page, but it loses the background transparency because it appears as though PDJpeg converts it to a JPG. Perhaps there's a way to do it using PDXObjectImage.

Here is what I have written thus far:

public static void watermarkPDF(PDDocument pdf) throws IOException
{
    // Load watermark
    BufferedImage buffered = ImageIO.read(new File("C:\\PDF_Test\\watermark.png"));
    PDJpeg watermark = new PDJpeg(pdf, buffered);

    // Loop through pages in PDF
    List pages = pdf.getDocumentCatalog().getAllPages();
    Iterator iter = pages.iterator();
    while(iter.hasNext())
    {
        PDPage page = (PDPage)iter.next();

        // Add watermark to individual page
        PDPageContentStream stream = new PDPageContentStream(pdf, page, true, false);
        stream.drawImage(watermark, 100, 0);
        stream.close();
    }

    try 
    {
        pdf.save("C:\\PDF_Test\\watermarktest.pdf");
    } 
    catch (COSVisitorException e) 
    {
        e.printStackTrace();
    }
}
Indigent answered 19/1, 2012 at 16:50 Comment(1)
Problem with the below answer is that the positioning doesn't work as I would expect (top-left justified) if the pages are different dimensions. I needed to add a text watermark to the top of every page in a PDF document, and the above solution was exactly what I needed, so bumping this solution up.Revival
H
41

UPDATED ANSWER (Better version with easy way to watermark, thanks to the commentators below and @okok who provided input with his answer)

If you are using PDFBox 1.8.10 or above, you can add watermark to your PDF document easily with better control over what pages needs to be watermarked. Assuming you have a one page PDF document that has the watermark image, you can overlay this on the document you want to watermark as follows.

Sample Code using 1.8.10

import java.util.HashMap;
import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.util.Overlay;

public class TestPDF {
    public static void main(String[] args) throws Exception{
            PDDocument realDoc = PDDocument.load("originaldocument.pdf"); 
            //the above is the document you want to watermark                   

            //for all the pages, you can add overlay guide, indicating watermark the original pages with the watermark document.
            HashMap<Integer, String> overlayGuide = new HashMap<Integer, String>();
            for(int i=0; i<realDoc.getPageCount(); i++){
                overlayGuide.put(i+1, "watermark.pdf");
                //watermark.pdf is the document which is a one page PDF with your watermark image in it. Notice here that you can skip pages from being watermarked.
            }
            Overlay overlay = new Overlay();
            overlay.setInputPDF(realDoc);
            overlay.setOutputFile("final.pdf");
            overlay.setOverlayPosition(Overlay.Position.BACKGROUND);
            overlay.overlay(overlayGuide,false);
           //final.pdf will have the original PDF with watermarks.

Sample using PDFBox 2.0.0 Release candidate

import java.io.File;
import java.util.HashMap;
import org.apache.pdfbox.multipdf.Overlay;
import org.apache.pdfbox.pdmodel.PDDocument;

public class TestPDF {

    public static void main(String[] args) throws Exception{        
        PDDocument realDoc = PDDocument.load(new File("originaldocument.pdf"));
        //the above is the document you want to watermark
        //for all the pages, you can add overlay guide, indicating watermark the original pages with the watermark document.

        HashMap<Integer, String> overlayGuide = new HashMap<Integer, String>();
        for(int i=0; i<realDoc.getNumberOfPages(); i++){
            overlayGuide.put(i+1, "watermark.pdf");
            //watermark.pdf is the document which is a one page PDF with your watermark image in it. 
            //Notice here, you can skip pages from being watermarked.
        }
        Overlay overlay = new Overlay();
        overlay.setInputPDF(realDoc);
        overlay.setOutputFile("final.pdf");
        overlay.setOverlayPosition(Overlay.Position.BACKGROUND);
        overlay.overlay(overlayGuide);      
    }
}

If you want to use the new package org.apache.pdfbox.tools.OverlayPDF for overlays you can do this way. (Thanks the poster below)

String[] overlayArgs = {"C:/Examples/foreground.pdf", "C:/Examples/background.pdf", "C:/Examples/resulting.pdf"};
OverlayPDF.main(overlayArgs);
System.out.println("Overlay finished.");

OLD ANSWER Inefficient way, not recommended.

Well, OP asked how to do it in PDFBox, the first answer looks like an example using iText. Creating a watermark in PDFBox is really simple. The trick is, you should have an empty PDF document with the watermark image. Then all you have to do is Overlay this watermark document on the document that you want to add the watermark to.

PDDocument watermarkDoc = PDDocument.load("watermark.pdf");
//Assuming your empty document with watermark image in it.

PDDocument realDoc = PDDocument.load("document-to-be-watermarked.pdf");
//Let's say this is your document that you want to watermark. For example sake, I am opening a new one, you would already have a reference to PDDocument if you are creating one

Overlay overlay = new Overlay();
overlay.overlay(realDoc,watermarkDoc);
watermarkDoc.save("document-now-watermarked.pdf");

Caution: You should make sure you match the number of pages in both document..Otherwise, you would end up with a document with number of pages matching the one which has least number of pages. You can manipulate the watermark document and duplicate the pages to match your document.

Hope this helps.!

Hexagram answered 21/2, 2012 at 17:30 Comment(8)
Sorry for the late reply (project was put on hold). I tried implementing this code, but I get one of two issues: (1) I used a 1 page PDF and a 1 page watermark PDF, but the watermark was the only thing to show up on the final document (maybe some sort of transparency issue?) (2) I tried a 2 page PDF with a 2 page watermark PDF and kept getting "Layout pages with COSArray currently not supported." I couldn't find any documentation on it or how to change the multi-page document into a more suitable format.Indigent
After playing around with this some more, it seems to work well, but under particular scenarios only. The watermark doesn't seem to work well if it is anything other than text (couldn't get transparency to work either). The overlay also cannot have COSArray in it (which I'm still not sure where it comes from, perhaps related to bookmarks or text).Indigent
Hi - Is there a solution to watermark specific pages on the PDF and not all of them?Revisal
@Joey Ezekiel, I am not sure if there is an easier way. Probably when you create watermark document that will be overlaid on the final document, you can make sure you have matching empty pages on watermark document for those pages you don't want to watermark. Not tried it, just a thought.Hexagram
Don't confuse the packages org.apache.pdfbox.util.Overlay and org.apache.pdfbox.Overlay. The latter is the correct one.Reborn
The above comment was for the old answer and it is changed now. So the correct one to be used is org.apache.pdfbox.util.Overlay.Hexagram
In my case only overlay.setOverlayPosition(Overlay.Position.FOREGROUND); worked because my original PDF documents were build from TIFF images which covered the watermark in the background.Multilateral
it worked like charm..all the basic pdf task can be performed with PDFbox easilyAttempt
M
10

Just made this piece of code to add (transparent) images (jpg, png, gif) to a pdf page with pdfbox:

/**
 * Draw an image to the specified coordinates onto a single page. <br>
 * Also scaled the image with the specified factor.
 * 
 * @author Nick Russler
 * @param document PDF document the image should be written to.
 * @param pdfpage Page number of the page in which the image should be written to.
 * @param x X coordinate on the page where the left bottom corner of the image should be located. Regard that 0 is the left bottom of the pdf page.
 * @param y Y coordinate on the page where the left bottom corner of the image should be located.
 * @param scale Factor used to resize the image.
 * @param imageFilePath Filepath of the image that is written to the PDF.
 * @throws IOException
 */
public static void addImageToPage(PDDocument document, int pdfpage, int x, int y, float scale, String imageFilePath) throws IOException {   
    // Convert the image to TYPE_4BYTE_ABGR so PDFBox won't throw exceptions (e.g. for transparent png's).
    BufferedImage tmp_image = ImageIO.read(new File(imageFilePath));
    BufferedImage image = new BufferedImage(tmp_image.getWidth(), tmp_image.getHeight(), BufferedImage.TYPE_4BYTE_ABGR);        
    image.createGraphics().drawRenderedImage(tmp_image, null);

    PDXObjectImage ximage = new PDPixelMap(document, image);

    PDPage page = (PDPage)document.getDocumentCatalog().getAllPages().get(pdfpage);

    PDPageContentStream contentStream = new PDPageContentStream(document, page, true, true);
    contentStream.drawXObject(ximage, x, y, ximage.getWidth()*scale, ximage.getHeight()*scale);
    contentStream.close();
}

Example:

public static void main(String[] args) throws Exception {
    String pdfFilePath = "C:/Users/Nick/Desktop/pdf-test.pdf";
    String signatureImagePath = "C:/Users/Nick/Desktop/signature.png";
    int page = 0;

    PDDocument document = PDDocument.load(pdfFilePath);

    addImageToPage(document, page, 0, 0, 0.5f, signatureImagePath);

    document.save("C:/Users/Nick/Desktop/pdf-test-neu.pdf");
}

This worked for me with jdk 1.7 and bcmail-jdk16-140.jar, bcprov-jdk16-140.jar, commons-logging-1.1.3.jar, fontbox-1.8.3.jar, jempbox-1.8.3.jar and pdfbox-1.8.3.jar.

Markel answered 16/12, 2013 at 18:30 Comment(1)
If using Android which doesn't support BufferedImage, and PdfBox-Android version 1.8.9.1 which doesn't have PDPixelMap yet, the PDXObjectImage can be created from a bitmap: PDImageXObject ximage = LosslessFactory.createFromImage(document, bitmap);Leg
P
5

@Androidman : Addition to https://mcmap.net/q/281727/-watermarking-with-pdfbox

It seems like many methods are removed with each version of PDFBox. So that code will not work on PDFBox 2.0.7.

Overlay overlay = new Overlay();
overlay.setInputPDF(realDoc);
// ** The method setOutputFile(String) is undefined for the type Overlay ** 
overlay.setOutputFile("final.pdf")

Instead, use void org.apache.pdfbox.pdmodel.PDDocument.save(String fileName), I think:

PDDocument realDoc = PDDocument.load(new File("originaldocument.pdf"));
    //the above is the document you want to watermark
    //for all the pages, you can add overlay guide, indicating watermark the original pages with the watermark document.

HashMap<Integer, String> overlayGuide = new HashMap<Integer, String>();
    for(int i=0; i<realDoc.getNumberOfPages(); i++){
        overlayGuide.put(i+1, "watermark.pdf");
        //watermark.pdf is the document which is a one page PDF with your watermark image in it. 
        //Notice here, you can skip pages from being watermarked.
    }
Overlay overlay = new Overlay();
overlay.setInputPDF(realDoc);
overlay.overlay(overlayGuide).save("final.pdf");
overlay.close();

Edit: I am using org.apache.pdfbox.tools.OverlayPDF for overlays now and it works just fine. The code looks like this:

String[] overlayArgs = {"C:/Examples/foreground.pdf", "C:/Examples/background.pdf", "C:/Examples/resulting.pdf"};
OverlayPDF.main(overlayArgs);
System.out.println("Overlay finished.");

As I had not enough reputation to add a comment, I thought it'd be appropiate to add this in a new answer.

Parkins answered 11/9, 2017 at 18:57 Comment(0)
B
0

There is another Overlay class within util package, that saves you from creating a pdf with same number of pages as the source document and then doing the overlay.

To understand its usage, take a look at pdfbox source code, specifically the OverlayPDF class.

Barkeeper answered 10/11, 2015 at 13:49 Comment(0)
A
0

This is how I was able to add a text watermark with the date using PdfBox 2.0.x in C#. It centres the watermark at the top of the page.

public static void AddWatermark(string fileName)
        {
            StringBuilder sb = new StringBuilder();
            sb.Append("watermark_text  ");
            sb.Append(DateTime.Now.ToString());

            string waterMarkText = sb.ToString();

            PDDocument origDoc = PDDocument.load(new java.io.File(fileName));
            PDPageTree allPages = origDoc.getPages();
            PDFont font = PDType1Font.HELVETICA_BOLD;
            for (int i = 0, len = allPages.getCount(); i < len; ++i)
            {
                PDPage pg = (PDPage)allPages.get(i);

                AddWatermarkText(origDoc, pg, font, waterMarkText);
            }

            origDoc.save(fileName);
            origDoc.close();
        }

static void AddWatermarkText(PDDocument doc, PDPage page, PDFont font, string text)
        {
            using (PDPageContentStream cs = new PDPageContentStream(doc, page, PDPageContentStream.AppendMode.APPEND, true, true))
            {
                float fontHeight = 30;
                float width = page.getMediaBox().getWidth();
                float height = page.getMediaBox().getHeight();
                float stringWidth = font.getStringWidth(text) / 1000 * fontHeight;

                float x = (width / 2) - (stringWidth / 2);
                float y = height - 25;

                cs.setFont(font, fontHeight);

                PDExtendedGraphicsState gs = new PDExtendedGraphicsState();
                gs.setNonStrokingAlphaConstant(new java.lang.Float(0.2f));
                gs.setStrokingAlphaConstant(new java.lang.Float(0.2f));
                gs.setBlendMode(BlendMode.MULTIPLY);
                gs.setLineWidth(new java.lang.Float(3f));
                cs.setGraphicsStateParameters(gs);

                cs.setNonStrokingColor(Color.red);
                cs.setStrokingColor(Color.red);

                cs.beginText();
                cs.newLineAtOffset(x, y);
                cs.showText(text);
                cs.endText();
            }
        }

Alcides answered 23/6, 2020 at 22:35 Comment(0)
S
0

Adding to the answer by @Droidman If your using version 2.0.21 and above you can directly overlay on PDDocument. Sample code as follows.

PDDocument realDoc = pdfGenerator.getDocument(); 
HashMap<Integer, PDDocument> overlayGuide = new HashMap<>(); 
for (int i = 0; i < realDoc.getNumberOfPages(); i++) { 
   overlayGuide.put(i + 1, document); 
} 
Overlay overlay = new Overlay(); 
overlay.setInputPDF(realDoc); 
overlay.setOverlayPosition(Overlay.Position.BACKGROUND); 
overlay.overlayDocuments(overlayGuide);
Slash answered 8/12, 2020 at 13:53 Comment(0)
K
-3

Look at this method, whitch add a watermark image in pdf sources using PDFBOX library

/**
     * Coloca una imagen como marca de agua en un pdf en una posición especifica
     * 
     * @param buffer
     *            flujo de bytes que contiene el pdf original
     * 
     * @param imageFileName
     *            nombre del archivo de la imagen a colocar
     * 
     * @param x
     *            posición x de la imagen en el pdf
     * 
     * @param y
     *            posición y de la imagen en el pdf
     * 
     * @param under 
     * determina si la marca se pone encima o por debajo de la factura
     * 
     * @return flujo de bytes resultante que contiene el pdf modificado
     * 
     * @throws IOException
     * @throws DocumentException
     */
    public static byte[] addWaterMarkImageToPDF(byte[] buffer,
            String imageFileName, int x, int y, boolean under) throws IOException,
            DocumentException {
        logger.debug("Agregando marca de agua:"+imageFileName);
        PdfReader reader = new PdfReader(buffer);
        ByteArrayOutputStream arrayOutput = new ByteArrayOutputStream();
        OutputStream output = new BufferedOutputStream(arrayOutput);
        PdfStamper stamper = new PdfStamper(reader, output);
        com.lowagie.text.Image img = com.lowagie.text.Image
                .getInstance(imageFileName);
        img.setAbsolutePosition(x, y);
        img.scalePercent(SCALE_PER);
        PdfContentByte pdfContent;
        int total = reader.getNumberOfPages() + 1;
        for (int i = 1; i < total; i++) {
            pdfContent = (under)?stamper.getUnderContent(i):
                stamper.getOverContent(i);
            pdfContent.addImage(img);
        }
        stamper.close();
        output.flush();
        output.close();
        return arrayOutput.toByteArray();
    }
Kimberli answered 15/2, 2012 at 21:55 Comment(1)
How would you do this with Rails?Amido

© 2022 - 2025 — McMap. All rights reserved.