Watermarking with PDFBox

2019-01-06 18:12发布

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();
    }
}

5条回答
该账号已被封号
2楼-- · 2019-01-06 18:42

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.

查看更多
别忘想泡老子
3楼-- · 2019-01-06 18:52

@Androidman : Addition to https://stackoverflow.com/a/9382212/7802973

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.

查看更多
够拽才男人
4楼-- · 2019-01-06 18:54

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.!

查看更多
女痞
5楼-- · 2019-01-06 18:58

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();
    }
查看更多
我只想做你的唯一
6楼-- · 2019-01-06 19:02

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.

查看更多
登录 后发表回答