Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
211 views
in Technique[技术] by (71.8m points)

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

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();
} 
See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

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.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...