使用PDFBox加水印

时间:2012-01-19 16:50:25

标签: java image pdf watermark pdfbox

我正在尝试使用PDFBox专门为PDF添加水印。我已经能够让图像显示在每个页面上,但它会失去背景透明度,因为它看起来好像PDJpeg将其转换为JPG。也许有一种方法可以使用PDXObjectImage。

这是我到目前为止所写的内容:

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

7 个答案:

答案 0 :(得分:29)

更新的答案(更好的版本,简单的水印方式,感谢下面的评论员和@okok提供输入的答案)

如果您使用的是PDFBox 1.8.10或更高版本,则可以轻松地为PDF文档添加水印,从而更好地控制需要加水印的页面。假设您有一个带有水印图像的单页PDF文档,您可以将其叠加在要添加水印的文档上,如下所示。

使用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.

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

如果要将新包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.");

OLD ANSWER 效率不高,不推荐。

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

警告:您应该确保匹配两个文档中的页数。否则,您最终会得到一个页面数量与页面数量最少的页面相匹配的文档。您可以操作水印文档并复制页面以匹配您的文档。

希望这有帮助。!

答案 1 :(得分:10)

刚刚制作了这段代码,将(透明)图像(jpg,png,gif)添加到带pdfbox的pdf页面中:

/**
 * 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。

答案 2 :(得分: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.");

由于我没有足够的声誉来添加评论,我认为将其添加到新答案中是合适的。

答案 3 :(得分:0)

util包中还有另一个Overlay类,它可以帮助您避免创建与源文档具有相同页数的pdf,然后进行覆盖。

要了解其用法,请查看pdfbox源代码,特别是OverlayPDF类。

答案 4 :(得分:0)

这是我能够使用C#中的PdfBox 2.0.x添加带有日期的文本水印的方式。它将水印置于页面顶部的中心。

<ListView x:Name="ItemsCollectionView" ItemsSource="{Binding TableDataGroup}"
      IsGroupingEnabled="true" HasUnevenRows="True">
        <ListView.GroupHeaderTemplate>
            <DataTemplate>
                <ViewCell>
                        <ViewCell.View>
                            <StackLayout BackgroundColor="DarkSlateGray" Orientation="Horizontal">
                                <StackLayout Padding="2" VerticalOptions="CenterAndExpand">
                                <Image Source="{Binding ImagePath}" HeightRequest="15" WidthRequest="15"/>
                                </StackLayout>
                                <StackLayout Padding="2" VerticalOptions="CenterAndExpand">
                                <Label Text="{Binding Championship}" FontSize="15" TextColor="White"/>
                                </StackLayout>
                                <StackLayout Margin="0,0,10,0" VerticalOptions="CenterAndExpand" HorizontalOptions="EndAndExpand">
                                    <Label Text="TIP" FontSize="15" TextColor="White"/>
                                </StackLayout>
                            </StackLayout>
                        </ViewCell.View>
                </ViewCell>
            </DataTemplate>
        </ListView.GroupHeaderTemplate>

        <ListView.ItemTemplate>
            <DataTemplate>
                <ViewCell>
                    <ViewCell.View>
                        <Grid Padding="10,5,5,10" BackgroundColor="Gray">
                            <Grid.RowDefinitions>
                                <RowDefinition Height="25" />
                            </Grid.RowDefinitions>
                            <Grid.ColumnDefinitions>
                                <ColumnDefinition Width="35"/>
                                <ColumnDefinition Width="120"/>
                                <ColumnDefinition Width="25"/>
                                <ColumnDefinition Width="25"/>
                                <ColumnDefinition Width="120"/>
                                <ColumnDefinition Width="45"/>
                            </Grid.ColumnDefinitions>
                            <Label Text="{Binding Time}" Grid.Column="0" FontSize="13" TextColor="Black" VerticalTextAlignment="Center" HorizontalOptions="StartAndExpand"></Label>
                            <Label Text="{Binding TeamOne}" FontSize="13" TextColor="Black" Grid.Column="1" VerticalTextAlignment="Center" HorizontalOptions="End"></Label>
                            <Label Text="{Binding ScoreTeamOne}" FontSize="13" Grid.Column="2" BackgroundColor="Gray" TextColor="White" VerticalTextAlignment="Center" HorizontalTextAlignment="Center" WidthRequest="25" MinimumWidthRequest="25"></Label>
                            <Label Text="{Binding ScoreTeamTwo}" FontSize="13" Grid.Column="3" BackgroundColor="Gray" TextColor="White" VerticalTextAlignment="Center" HorizontalTextAlignment="Center" WidthRequest="25" MinimumWidthRequest="25"></Label>
                            <Label Text="{Binding TeamTwo}" FontSize="13" TextColor="Black" Grid.Column="4" VerticalTextAlignment="Center"></Label>
                            <Label Text="{Binding Tip}" FontSize="13" TextColor="White" Grid.Column="5" BackgroundColor="DarkSlateGray" HorizontalTextAlignment="Center" VerticalTextAlignment="Center" HorizontalOptions="Fill"></Label>
                        </Grid>
                    </ViewCell.View>
                </ViewCell>
                <!--<TextCell Text="{Binding Name}" Detail="{Binding Description}"></TextCell>-->
            </DataTemplate>
        </ListView.ItemTemplate>
    </ListView>

答案 5 :(得分:0)

通过@Droidman添加到答案如果您使用的是版本 2.0.21 及更高版本,则可以直接覆盖在PDDocument上。示例代码如下。

string

答案 6 :(得分:-3)

看看这个方法,使用PDFBOX库在pdf源中添加水印图像

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