Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

how to make my watermark text in any pdf file as non-selectable?

Tags:

java

pdfbox

itext

I have done watermark text in pdf file using itextpdf, but when I copy the actual text of the pdf file it allows us to copy the watermark text too. Is there anyway that we can restrict our watermark text as Non-selectable?

Image watermark_image = Image.getInstance(imageFile.getAbsolutePath());

while (i < num_of_pages) {
    i++;
    //To pass our watermark over text
    add_waterMark = pdfStamper.getOverContent(i);

    //To pass our watermark under text
    //add_waterMark = pdfStamper.getUnderContent(i);

    // watermark_image.
    watermark_image.setAbsolutePosition(0, 0);

    add_waterMark.beginText();
    //add_waterMark.setTextRenderingMode(number_of_pages);

    //watermark_image is png file
    add_waterMark.addImage(watermark_image);

    add_waterMark.endText();
}

i have written code using PdfContentByte, it is hollow and horizontal, but i am able to copy the watermark text here :( i want to replace my code using PdfPatternPainter, if may be possible because PdfPatternPainter inherits all the Fields of PdfContentByte.

here is code using PdfContentByte :

int n = reader.getNumberOfPages();

        PdfContentByte under;
        PdfGState gstate = new PdfGState();
        gstate.setFillOpacity(0.35f);
        gstate.setStrokeOpacity(0.35f);
        BaseFont font = BaseFont.createFont(BaseFont.HELVETICA_BOLD,
                BaseFont.WINANSI, BaseFont.EMBEDDED);

        Rectangle size = reader.getPageSizeWithRotation(1);
//          float angle = (float) ((180 * (Math.asin(size.getHeight()
//                  / Math.sqrt(size.getWidth() * size.getWidth()
//                          + size.getHeight() *       size.getHeight())))) / Math.PI);
        int i = 1;

        while (i < n + 1) {

            under = stamper.getOverContent(i);
            under.setColorStroke(new BaseColor(192, 192, 192));
            i++;
            under.beginText();
                under.setTextRenderingMode(PdfContentByte.TEXT_RENDER_MODE_STROKE);
            under.setLineWidth(0.85f);
            under.setLineDash(0.4f, 0.4f, 0.2f);

            under.showTextAlignedKerned(Element.ALIGN_MIDDLE,
                    "user name must required", 250, 780,
                    MYConstants.WATERMARK_PAGE_ANGLE);
            under.setFontAndSize(font, 42);
            under.showTextAlignedKerned(Element.ALIGN_MIDDLE,
                    "user Company name required", 200, 730,
                    MyConstants.WATERMARK_PAGE_ANGLE);
            under.setFontAndSize(font, 42);
            under.showTextAlignedKerned(Element.ALIGN_MIDDLE,
                    "Plesae enter your email id", 150, 680,
                    MyConstants.WATERMARK_PAGE_ANGLE);


            under.endText();
        }

     stamper.close();
} 
like image 542
KP_JavaDev Avatar asked Dec 03 '13 12:12

KP_JavaDev


People also ask

How do I make a PDF text Not selectable?

1. In a PDF file already created Choose Tools > Protection > Encrypt > Encrypt with Password. (You can also choose File > Properties and select the Security tab.) Ask your PDF Creation questions in Questions & Answers or the Adobe Forums.

How do you make a non removable watermark?

You can use pdfstamp.exe to add watermarks to the PDF pages first, and then use Image to PDF OCR Converter Command Line to convert from stamped PDF files to image based PDF files, if so, the watermarks will become permanent watermarks in the PDF files, these watermarks will impossible to be removed from image based PDF ...


1 Answers

In the comments to the original question, the OP clarified

the only things here needed is, whenever you will try to select entire text of pdf, the watermark text should not be selected. i just want to know is there any approach other then using png image file.

This sounds like using a pattern with text content might be just right for you. Here a proof-of-concept using iText:

void addTextPatternToOverContent(File source, File target) throws IOException, DocumentException
{
    PdfReader reader = new PdfReader(source.getPath());
    OutputStream os = new FileOutputStream(target);
    PdfStamper stamper = new PdfStamper(reader, os);

    PdfPatternPainter painter = stamper.getOverContent(1).createPattern(200, 150);
    painter.setColorFill(BaseColor.BLACK);
    painter.beginText();
    painter.setTextMatrix(AffineTransform.getTranslateInstance(0, 50));
    painter.setFontAndSize(BaseFont.createFont(), 100);
    painter.showText("Test");
    painter.endText();

    for (int i = reader.getNumberOfPages(); i > 0; i--)
    {
        PdfContentByte overContent = stamper.getOverContent(i);
        overContent.setColorFill(new PatternColor(painter));
        overContent.rectangle(200, 300, 200, 150);
        overContent.fill();
    }

    stamper.close();
    os.close();
    reader.close();
}

The text from the pattern is not selectable in Adobe Reader. And @gwillie, no need for heavy lifting here, this is no security feature, the text is easy to find once you know where it's put.

Please be aware that patterns can be fickle. For free positioning of the text, you might want to fill a rectangle with that pattern in a fixed form xobject (thus being on edge with the pattern tiles) and put that xobject wherever you want.

And quite probably you will want to apply transparency or else not fill but only thinly stroke the letters like with this pattern painter:

    PdfPatternPainter painter = stamper.getOverContent(1).createPattern(200, 150);
    painter.setColorStroke(BaseColor.BLACK);
    painter.beginText();
    painter.setTextRenderingMode(PdfPatternPainter.TEXT_RENDER_MODE_STROKE);
    painter.setTextMatrix(AffineTransform.getTranslateInstance(0, 50));
    painter.setFontAndSize(BaseFont.createFont(), 100);
    painter.showText("Test");
    painter.endText();

PS: Taking the OP's new detailed code into account a method adding that as non-copy&paste-able stuff may look like this:

void addUserPatternToOverContent(File source, File target) throws IOException, DocumentException
{
    PdfReader reader = new PdfReader(source.getPath());
    OutputStream os = new FileOutputStream(target);
    PdfStamper stamper = new PdfStamper(reader, os);

    Rectangle pageSize = reader.getPageSize(1);
    final float WATERMARK_PAGE_ANGLE = -72;

    BaseFont font = BaseFont.createFont(BaseFont.HELVETICA_BOLD, BaseFont.WINANSI, BaseFont.NOT_EMBEDDED);

    PdfPatternPainter painter = stamper.getOverContent(1).createPattern(pageSize.getWidth(), pageSize.getHeight());
    painter.setColorStroke(new BaseColor(192, 192, 192));
    painter.setLineWidth(0.85f);
    painter.setLineDash(0.4f, 0.4f, 0.2f);

    painter.beginText();
    painter.setTextRenderingMode(PdfPatternPainter.TEXT_RENDER_MODE_STROKE);
    painter.setFontAndSize(font, 42);
    painter.showTextAlignedKerned(Element.ALIGN_MIDDLE,
            "user name must required", 250, 780,
            WATERMARK_PAGE_ANGLE);
    painter.showTextAlignedKerned(Element.ALIGN_MIDDLE,
            "user Company name required", 200, 730,
            WATERMARK_PAGE_ANGLE);
    painter.showTextAlignedKerned(Element.ALIGN_MIDDLE,
            "Plesae enter your email id", 150, 680,
            WATERMARK_PAGE_ANGLE);
    painter.endText();

    for (int i = reader.getNumberOfPages(); i > 0; i--)
    {
        Rectangle thisPageSize = reader.getPageSize(i);
        PdfContentByte overContent = stamper.getOverContent(i);
        overContent.setColorFill(new PatternColor(painter));
        overContent.rectangle(thisPageSize.getLeft(), thisPageSize.getBottom(), thisPageSize.getWidth(), thisPageSize.getHeight());
        overContent.fill();
    }

    stamper.close();
    os.close();
    reader.close();
}

which shows e.g. as

the water sign applied to a sample page

BTW, I changed the Helvetica Bold to not-embedded as it is one of the standard 14 fonts.

PPS: In the comments the OP wondered furthermore

is there any way that our watermark text go backgroud of actual text and in case of Image it goes foreground. i have tried with getUnderContent() but our Pdf image Hide this text.

Short of sorting the page contents (first images, then the water mark, then the text - not trivial in general) one can try and collect all the areas with images (using the iText parser package classes), then put the water mark to the undercontent, and to the overcontent also put the water mark but only in the combined regions in which you found images. Much easier to implement but a bit dirty. E.g.

void addUserPatternOverAndUnder(File source, File target) throws IOException, DocumentException
{
    PdfReader reader = new PdfReader(source.getPath());
    OutputStream os = new FileOutputStream(target);
    PdfStamper stamper = new PdfStamper(reader, os);

    Rectangle pageSize = reader.getPageSize(1);
    final float WATERMARK_PAGE_ANGLE = -60;

    BaseFont font = BaseFont.createFont(BaseFont.HELVETICA_BOLD, BaseFont.WINANSI, BaseFont.NOT_EMBEDDED);

    PdfPatternPainter painter = stamper.getOverContent(1).createPattern(pageSize.getWidth(),
            pageSize.getHeight());
    painter.setColorStroke(new BaseColor(0, 192, 192));
    painter.setLineWidth(0.85f);
    painter.setLineDash(0.4f, 0.4f, 0.2f);

    painter.beginText();
    painter.setTextRenderingMode(PdfPatternPainter.TEXT_RENDER_MODE_STROKE);
    painter.setFontAndSize(font, 42);
    painter.showTextAlignedKerned(Element.ALIGN_MIDDLE, "user name must required", 150, 780,
            WATERMARK_PAGE_ANGLE);
    painter.showTextAlignedKerned(Element.ALIGN_MIDDLE, "user Company name required", 100, 730,
            WATERMARK_PAGE_ANGLE);
    painter.showTextAlignedKerned(Element.ALIGN_MIDDLE, "Plesae enter your email id", 050, 680,
            WATERMARK_PAGE_ANGLE);
    painter.endText();

    for (int i = reader.getNumberOfPages(); i > 0; i--)
    {
        Rectangle thisPageSize = reader.getPageSize(i);

        PdfContentByte underContent = stamper.getUnderContent(i);
        underContent.setColorFill(new PatternColor(painter));
        underContent.rectangle(thisPageSize.getLeft(), thisPageSize.getBottom(), thisPageSize.getWidth(),
                thisPageSize.getHeight());
        underContent.fill();

        List<Vector> path = getImageBordersPathPoints(reader, i);
        if (path != null && !path.isEmpty())
        {
            PdfContentByte overContent = stamper.getOverContent(i);
            overContent.setColorFill(new PatternColor(painter));

            for (int index = 0; index < path.size(); index++)
            {
                Vector corner = path.get(index);
                if (index % 4 == 0)
                {
                    overContent.moveTo(corner.get(Vector.I1), corner.get(Vector.I2));
                }
                else
                {
                    overContent.lineTo(corner.get(Vector.I1), corner.get(Vector.I2));
                    if (index % 4 == 3)
                    {
                        overContent.closePath();
                    }
                }
            }
            overContent.fill();
        }
    }

    stamper.close();
    os.close();
    reader.close();
}

static Vector A = new Vector(0, 0, 1);
static Vector B = new Vector(1, 0, 1);
static Vector C = new Vector(1, 1, 1);
static Vector D = new Vector(0, 1, 1);
static List<Vector> positive = Arrays.asList(A, B, C, D);
static List<Vector> negative = Arrays.asList(A, D, C, B);

List<Vector> getImageBordersPathPoints(PdfReader reader, int page) throws IOException
{
    final List<Vector> result = new ArrayList<Vector>();
    RenderListener listener = new RenderListener()
    {
        public void renderText(TextRenderInfo renderInfo)
        {
        }

        public void endTextBlock()
        {
        }

        public void beginTextBlock()
        {
        }

        public void renderImage(ImageRenderInfo renderInfo)
        {
            Matrix ctm = renderInfo.getImageCTM();
            List<Vector> unitCorners = ctm.getDeterminant() > 0 ? positive : negative;

            for (Vector corner : unitCorners)
            {
                result.add(corner.cross(ctm));
            }
        }
    };

    PdfReaderContentParser parser = new PdfReaderContentParser(reader);
    parser.processContent(page, listener);
    return result;
}

(Vector is com.itextpdf.text.pdf.parser.Vector)

The result (the Dexx logo is an image, the address is text):

the water sign applied to a sample page the new way

Unfortunately, though, the parsing API does not yet signal vector graphics. Thus, vector graphics (e.g. colored backgrounds which essentially are filled rectangles drawn using vector graphics operations) cover the water mark.

like image 91
mkl Avatar answered Oct 24 '22 17:10

mkl