2012-01-19 144 views
14

我想用PDFBox專門爲PDF添加水印。我已經能夠讓圖像出現在每個頁面上,但是它丟失了背景透明度,因爲它好像PDJpeg將它轉換爲JPG一樣。也許有一種使用PDXObjectImage的方法。帶PDFBox的水印

這是我迄今寫的:

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

以下答案的問題是,如果頁面的尺寸不同,定位將無法正常工作(左上對齊)。我需要在PDF文檔的每個頁面的頂部添加文本水印,而上述解決方案正是我所需要的,所以顛覆​​了這個解決方案。 –

回答

26

修訂ANSWER(更好的版本加水印圖像用下面的評論員和@okok誰提供了他的答案輸入)簡單的方式來水印,

如果您使用的是PDFBox 1.8.10或更高版本,您可以輕鬆地在PDF文檔中添加水印,並更好地控制哪些頁面需要加水印。假設您擁有一張帶有水印圖像的PDF頁面文檔,您可以按如下方式將其覆蓋在要加水印的文檔上。使用PDFBox的2.0.0發佈候選

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

OLD ANSWER低效的方式使用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. 

樣品

示例代碼,不建議使用。

那麼,OP問如何在PDFBox中做到這一點,第一個答案看起來像使用iText的例子。在PDFBox中創建水印非常簡單。訣竅是,你應該有一個空的PDF文件與水印圖像。然後,您只需在要添加水印的文檔上覆蓋此水印文檔。

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"); 

注意:你應該確保你匹配的兩個document..Otherwise的頁數,你最終會與匹配具有至少頁數的一個頁數的文檔。您可以操作水印文檔並複製頁面以匹配您的文檔。

希望這會有所幫助。

+0

對不起,延遲迴復(項目暫停)。我試着實現這個代碼,但我得到兩個問題之一: (1)我使用了1頁的PDF和1頁的水印PDF,但水印是唯一顯示在最終文檔上的東西(可能是某種類型的透明度問題?) (2)我嘗試了一個2頁的PDF與2頁的水印PDF,並保持「目前不支持COSArray佈局頁面。」我找不到任何文檔或如何將多頁文檔更改爲更合適的格式。 – aosmith

+0

在玩過這些之後,它似乎運作良好,但只在特定場景下。如果它不是文本以外的任何其他東西,水印似乎並不能很好地工作(無法獲得透明度)。覆蓋層也不能包含COSArray(我仍不確定它來自哪裏,可能與書籤或文本有關)。 – aosmith

+0

嗨 - 是否有解決方案來PDF上的特定頁面而不是所有的水印? –

-3

看看這個方法,whitch在PDF源使用PDFBOX庫

/** 
    * 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(); 
    } 
+2

這是用於iText的 –

+0

你如何用Rails做這件事? –

9

剛做這一塊的代碼(透明)圖片(JPG,PNG,GIF等)添加到PDF頁面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(); 
} 

例子:

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

這爲我工作使用jdk 1.7和bcmail-jdk16-140.jar,bcprov-jdk16-140.jar,commons-logging-1.1.3.jar,fontbox-1.8.3.jar,jempbox-1.8.3.jar和pdfbox-1.8。 3.jar。

+0

如果使用的Android不支持BufferedImage,而PdfBox-Android版本1.8.9.1還沒有PDPixelMap,那麼PDXObjectImage可以從位圖創建:PDImageXObject ximage = LosslessFactory.createFromImage(document,bitmap); – jk7

0

util包中還有另一個Overlay類,它可以避免創建一個與源文檔具有相同頁面數的pdf,然後執行疊加。

要了解其用法,請查看pdfbox源代碼,特別是OverlayPDF類。

2

@Androidman:加成https://stackoverflow.com/a/9382212/7802973

好像很多方法與PDFBox的每個版本中刪除。所以這段代碼在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") 

相反,使用void org.apache.pdfbox.pdmodel.PDDocument.save(String fileName),我想:

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

編輯: 我使用org.apache.pdfbox.tools.OverlayPDF用於覆蓋目前和它工作得很好。代碼如下所示:

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

由於我沒有足夠的聲譽添加評論,我想那會appropiate在一個新的答案添加此。