iText 为选定页面添加水印

iText add Watermark to selected pages

我需要为每个包含特定文本的页面添加水印,例如"PROCEDURE DELETED"。

根据 Bruno Lowagie 在 Adding watermark directly to the stream

中的建议

到目前为止,PdfWatermark Class 具有:

protected Phrase watermark = new Phrase("DELETED", new Font(FontFamily.HELVETICA, 60, Font.NORMAL, BaseColor.PINK));
ArrayList<Integer> arrPages = new ArrayList<Integer>();
boolean pdfChecked = false;

@Override
public void onEndPage(PdfWriter writer, Document document) {

    if(pdfChecked == false) {
        detectPages(writer, document);
        pdfChecked = true;
    } 
    int pageNum = writer.getPageNumber();

    if(arrPages.contains(pageNum)) {
        PdfContentByte canvas = writer.getDirectContentUnder();
        ColumnText.showTextAligned(canvas, Element.ALIGN_CENTER, watermark, 298, 421, 45);
    }
}

如果我在我的自定义 detectPages 方法中将数字 3 添加到 arrPages ArrayList,则效果很好 - 它在第 3 页上显示所需的水印。

我遇到的问题是如何在文档中搜索我有权访问的文本字符串,只能从 PdfWriter 编写器或发送到 onEndPage 方法的 com.itextpdf.text.Document 文档中搜索。

这是我尝试过的,但没有成功:

 private void detectPages(PdfWriter writer, Document document) {
    try {

        //arrPages.add(3);

        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
        PdfWriter.getInstance(document, byteArrayOutputStream);
        //following code no work
        PdfReader reader = new PdfReader(writer.getDirectContent().getPdfDocument());
        PdfContentByte canvas = writer.getDirectContent();
        PdfImportedPage page;

        for (int i = 0; i < reader.getNumberOfPages(); ) {    
            page = writer.getImportedPage(reader, ++i);
            canvas.addTemplate(page, 1f, 0, 0.4f, 0.4f, 72, 50 * i);
            canvas.beginText();
            canvas.showTextAligned(Element.ALIGN_CENTER,

                    //search String
                    String.valueOf((char)(181 + i)), 496, 150 + 50 * i, 0);

                    //if(detected) arrPages.add(i);

            canvas.endText();
        }

我是否在正确的轨道上将此作为解决方案,还是我需要退出?

任何人都可以提供扫描文档并挑选 "PROCEDURE DELETED" 页所需的缺失 link 吗?

编辑:我正在使用 iText 5.0.4 - 目前无法升级到 5。5.X,但可能会升级到低于该版本的最新版本。

EDIT2:更多信息:这是向文档添​​加文本的方法 (doc):

        String processed = processText(template);
        List<Element> objects = HTMLWorker.parseToListLog(new StringReader(processed),
                styles, interfaceProps, errors);
        for (Element elem : objects) {
            doc.add(elem);

        }

这是在我控制的 addText 方法中调用的。 template 只是来自数据库 LOB 的 html。 processText 检查 html 是否包含卷曲的自定义标记,如 ${replaceMe}.

这似乎是在生成文档期间识别 "PROCEDURE DELETED" 字符串的地方,但我没有看到 Chunk.setGenericTags() 的路径。

EDIT3:Table 困难

        List<Element> objects = HTMLWorker.parseToListLog(new StringReader(processed),
                styles, interfaceProps, errors);
        for (Element elem : objects) { 
            //Code below no work
            if (elem instanceof PdfPTable) {
                PdfPTable table = (PdfPTable) elem;
                ArrayList<Chunk> chks = table.getChunks();
                for(Chunk chk : chks){
                    if(chk.toString().contains("TEST DELETED")) {
                        chk.setGenericTag("delete_tag");
                    }
                }                       
            }

            doc.add(elem);
        }

评论者 mlk 和 Bruno 建议在将 "PROCEDURE DELETED" 关键字添加到文档时检测它们。但是,由于关键字必须在 table 内,因此必须通过 PdfPTable 而不是更简单的 Element 来检测它们。

我用上面的代码做不到。关于如何在 table 单元格中查找文本并对其进行字符串比较,有什么建议吗?

EDIT4:基于一些实验,我想做一些断言,请告诉我如何通过它们:

  1. 需要使用 Chunk.setGenericTag() 来触发处理程序 onGenericTag
  2. 出于某种原因 (PdfPTable) table.getChunks() 没有 return 块,至少我的系统可以接受。这是违反直觉的,可能存在导致此行为的设置、版本或代码错误。
  3. 因此,table 中的选择文本字符串不能用于触发水印。

感谢@mkl 和@Bruno 的帮助,我终于得到了一个可行的解决方案。欢迎任何有兴趣的人提供更优雅的方法 - 当然有空间。

两个 类 在原始问题中给出的所有片段中都在起作用。以下是体现工作解决方案的部分代码。

public class PdfExporter
    //a custom class to build content

    //create some content to add to pdf
    String text = "<div>Lots of important content here</div>";

    //assume the section is known to be deleted
    if(section_deleted) {
        //add a special marker to the text, in white-on-white
        text = "<span style=\"color:white;\">.!^DEL^?.</span>" + text + "</span>";
    } 

    //based on some indicator, we know we want to force a new page for a new section
    boolean ensureNewPage = true;

    //use an instance of PdfDocHandler to add the content
    pdfDocHandler.addText(text, ensureNewPage); 




public class PdfDocHandler extends PdfPageEventHelper
    private boolean isDEL;

    public boolean addText(String text, boolean ensureNewPage)

       if (ensureNewPage) {     
           //turn isDEL off: a forced pagebreak indicates a new section
           isDEL = false;
       }

        //attempt to find the special DELETE marker in first chunk
        //NOTE: this can be done several ways
        ArrayList<Chunk> chks = elem.getChunks();
        if(chks.size()>0) {
            if(chks.get(0).getContent().contains(".!^DEL^?.")) {
                //special delete marker found in text
                isDEL = true;
            }
        }

        //doc is an iText Document
        doc.add(elem);


    public void onEndPage(PdfWriter writer, Document document) {

        if(isDEL) {
            //set the watermark
            Phrase watermark = new Phrase("DELETED", new Font(Font.FontFamily.HELVETICA, 60, Font.NORMAL, BaseColor.PINK));
            PdfContentByte canvas = writer.getDirectContentUnder();
            ColumnText.showTextAligned(canvas, Element.ALIGN_CENTER, watermark, 298, 421, 45);   
        }
    }