【问题标题】:How to watermark PDFs using text or images?如何使用文本或图像为 PDF 添加水印?
【发布时间】:2018-12-02 21:25:50
【问题描述】:

我在一个文件夹中有一堆 PDF 文档,我想用水印来增加它们。 Java 服务器端上下文有哪些选择?

最好水印支持透明度。矢量和光栅都是可取的。

【问题讨论】:

  • 本质上您是在寻求有关 PDF 库的建议(不应该尝试在没有这样的库的情况下操作 pdf),但库建议在这里是题外话。
  • 对此我很抱歉。我确实发现这些关于 SO 的问题非常有价值,当它们被遵循规则的正义人士放下时,这是一种耻辱。也很抱歉我没有提到我正在寻找一种不花钱的解决方案。 iText 看起来很棒,但对于这种简单的用途,我们根本买不起。
  • 没有必要为此感到抱歉,这只是题外话,无论您对此有何看法或想法不同。无论如何你都可以问这些问题,但是当它们关闭时不要难过...... .

标签: java pdf


【解决方案1】:

请查看TransparentWatermark2 示例。它在现有 PDF 文档的每个奇数页上添加透明文本,并在每个偶数页上添加透明图像。

它是这样完成的:

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

如您所见,我们为文本创建了一个Phrase 对象,为图像创建了一个Image 对象。我们还为透明度创建了一个PdfGState 对象。在我们的例子中,我们选择 50% 的不透明度(将 0.5f 更改为其他内容以进行实验)。

一旦我们有了这些对象,我们就会遍历每一页。我们使用PdfReader 对象来获取有关现有文档的信息,例如每个页面的尺寸。当我们想要在现有文档上标记额外内容时,我们使用PdfStamper 对象,例如在每个单页顶部添加水印。

更改图形状态时,在开始之前执行saveState() 并在完成后执行restoreState() 始终是安全的。如果您不这样做,您的代码可能也可以工作,但请相信我:如果您采用纪律来执行此操作,它可以为您节省大量调试时间,因为如果图形状态不平衡,您会得到非常奇怪的效果。

我们使用setGState() 方法应用透明度,并根据页面是奇数页还是偶数页,添加文本(使用ColumnText 和计算出的(x, y) 坐标以便添加文本在每页的中间)或图像(使用addImage() 方法和转换矩阵的适当参数)。

为文档中的每一页完成此操作后,您必须close()stamperreader

警告:

您会注意到第 3 页和第 4 页是横向的,但这两个页面之间存在肉眼不可见的差异。第 3 页实际上是一个页面,其大小定义为就好像它是纵向页面一样,但它被旋转了 90 度。第 4 页是按宽度 > 高度定义大小的页面。

这可能会影响您添加水印的方式,但如果您使用getPageSizeWithRotation(),iText 会适应。这可能不是您想要的:也许您希望添加不同的水印。

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

在这种情况下,我们不使用getPageSizeWithRotation(),而只是使用getPageSize()。我们还告诉stamper 来补偿现有的页面旋转:stamper.setRotateContents(false);

查看生成的 PDF 的不同之处:

在第一个屏幕截图中(显示TransparentWatermark2 生成的 PDF 的第 3 页和第 4 页),左侧的页面实际上是纵向旋转 90 度的页面。然而,iText 将其视为横向页面,就像右侧页面一样。

在第二个屏幕截图中(显示TransparentWatermark3 生成的 PDF 的第 3 页和第 4 页),左侧页面是旋转 90 度的纵向页面,我们添加水印,就好像页面是纵向的一样.结果,水印也被旋转了90度。右侧的页面不会发生这种情况,因为该页面的旋转角度为 0 度。

这是一个微妙的区别,但我想你会想知道的。

如果您想阅读此法文答案,请阅读Comment créer un filigrane transparent en PDF?

【讨论】:

【解决方案2】:

最佳选择是iText。检查watermark demo here

代码的重要部分(插入水印的地方)是这样的:

public class Watermark extends PdfPageEventHelper {

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

仔细阅读示例。

onEndPage() 方法类似于(在我的徽标水印中,我使用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);

【讨论】:

  • 这对 OP 并没有真正的帮助,但会调整您的答案,以便它使用 PdfStamper 并循环页面以根据此示例向 getOverContent() 添加水印,您值得拥有赞成票:itextpdf.com/sandbox/stamper/TransparentWatermark
  • 好的,我赞成您的回答,因为您过去曾尝试回答几个 iText 问题。我很感激我的努力,也很感激这让我感觉不那么孤独(有时就好像 iText 问题只有我、mkl 和 Chris Haas 才能回答)。我想欢迎您成为 iText 答案的第四位贡献者。但是,您应该将您的答案与我的答案进行比较。你会明白,如果没有充分解决新问题,你应该尽量避免复制/粘贴示例。
  • 是否有解决方案不会产生许可费用?
  • @Bruno 谢谢你,仔细阅读你的例子会更容易,特别是对于现有 pdf 文件的这种情况......只是试图帮助和学习提高自己和 SO 水平,但我真的很高兴听到我的回答(或至少是努力)得到你的赞赏;)
  • 即使不是 100% 正确的答案也常常将人们引向正确的方向。同时,您会获得对未来答案有用的反馈。
猜你喜欢
  • 2017-07-24
  • 1970-01-01
  • 2018-11-11
  • 2016-04-11
  • 2020-03-02
  • 1970-01-01
  • 2021-10-05
  • 1970-01-01
相关资源
最近更新 更多