KTys
KTys

Reputation: 180

iText add Watermark to selected pages

I need to add a watermark to every page that has certain text, such as "PROCEDURE DELETED".

Based on Bruno Lowagie's suggestion in Adding watermark directly to the stream

So far have the PdfWatermark Class with:

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

And this works fine if I add, say, the number 3 to the arrPages ArrayList in my custom detectPages method - it shows the desired watermark on page 3.

What I am having trouble with is how to search through the document for the text string, which I have access to here, only from the PdfWriter writer or the com.itextpdf.text.Document document sent to onEndPage method.

Here is what I have tried, unsuccessfully:

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

Am I on the right track with this as a solution or do I need to back out?

Can anyone supply the missing link needed to scan the doc and pick out "PROCEDURE DELETED" pages?

EDIT: I am using iText 5.0.4 - cannot upgrade to 5.5.X at this time, but could probably upgrade to latest version below that.

EDIT2: More information: This is approach to adding text to the document (doc):

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

        }

That is called in an addText method I control. The template is simply html from a database LOB. The processText checks the html for custom markers contained by curlies as in ${replaceMe}.

This seems to be the place to identify the "PROCEDURE DELETED" string during generation of the document, but I don't see the path to Chunk.setGenericTags().

EDIT3: Table difficulties

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

Commenters mlk and Bruno suggested to detect the "PROCEDURE DELETED" keywords at the time they are added to the doc. However, since the keywords are necessarily inside a table, they have to be detected through PdfPTable rather than the simpler Element.

I could not do it with the code above. Any suggestions exactly how to find text inside a table cell and do a string comparison on it?

EDIT4: Based on some experimentation, I would like to make some assertions and please show me the way through them:

  1. Using Chunk.setGenericTag() is required to trigger the handler onGenericTag
  2. For some reason (PdfPTable) table.getChunks() does not return chunks, at least that my system picks up. This is counterintuitive and possibly there is a setup, version, or code bug causing this behavior.
  3. Therefore, a selection text string inside a table cannot be used to trigger a watermark.

Upvotes: 1

Views: 3156

Answers (1)

KTys
KTys

Reputation: 180

Thanks to all the help from @mkl and @Bruno, I finally got a workable solution. Anybody interested who can give a more elegant approach is welcome - there is certainly room.

Two classes were in play in all the snippets given in the original question. Below is partial code that embodies the working solution.

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

Upvotes: 1

Related Questions