Unable to make itext5 pdf watermark non removable in VMware Workspace ONE Boxer email
Asked Answered
S

3

5

I am using itext5 to create pdf files with painted non-removable watermarks as follows:

public class TestWatermark {

    public static String resourcesPath = "C:\\Users\\java\\Desktop\\TestWaterMark\\";
    public static String FILE_NAME = resourcesPath + "test.pdf";

    public static void main(String[] args) throws IOException {
        System.out.println("########## STARTED ADDING WATERMARK ###########");
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        try {
            byte[] byteArray = Files.readAllBytes(Paths.get(FILE_NAME));
            String watermarkText = "confidential";
            String fontPath = resourcesPath + "myCustomFont.ttf";
            Font arabicFont = FontFactory.getFont(fontPath, BaseFont.IDENTITY_H, 16);

            BaseFont baseFont = arabicFont.getBaseFont();
            PdfReader reader = new PdfReader(byteArray);
            PdfStamper stamper = new PdfStamper(reader, baos);

            int numberOfPages = reader.getNumberOfPages();

            float height = baseFont.getAscentPoint(watermarkText, 24) + baseFont.getDescentPoint(watermarkText, 24);

            for (int i = 1; i <= numberOfPages; i++) {

                Rectangle pageSize = reader.getPageSizeWithRotation(i);
                PdfContentByte overContent = stamper.getOverContent(i);

                PdfPatternPainter bodyPainter = stamper.getOverContent(i).createPattern(pageSize.getWidth(),
                        pageSize.getHeight());
                BaseColor baseColor = new BaseColor(10, 10, 10);
                bodyPainter.setColorStroke(baseColor);
                bodyPainter.setColorFill(baseColor);
                bodyPainter.setLineWidth(0.85f);
                bodyPainter.setLineDash(0.2f, 0.2f, 0.2f);

                PdfGState state = new PdfGState();
                state.setFillOpacity(0.3f);
                overContent.saveState();
                overContent.setGState(state);

                for (float x = 70f; x < pageSize.getWidth(); x += height + 100) {
                    for (float y = 90; y < pageSize.getHeight(); y += height + 100) {

                        bodyPainter.beginText();
                        bodyPainter.setTextRenderingMode(PdfPatternPainter.TEXT_RENDER_MODE_FILL);
                        bodyPainter.setFontAndSize(baseFont, 13);
                        bodyPainter.showTextAlignedKerned(Element.ALIGN_MIDDLE, watermarkText, x, y, 45f);
                        bodyPainter.endText();

                        overContent.setColorFill(new PatternColor(bodyPainter));
                        overContent.rectangle(pageSize.getLeft(), pageSize.getBottom(), pageSize.getWidth(),
                                pageSize.getHeight());
                        overContent.fill();

                    }
                }

                overContent.restoreState();

            }

            stamper.close();
            reader.close();
            byteArray = baos.toByteArray();
            File outputFile = new File(resourcesPath + "output.pdf");
            if (outputFile.exists()) {
                outputFile.delete();
            }
            Files.write(outputFile.toPath(), byteArray);

            System.out.println("########## FINISHED ADDING WATERMARK ###########");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

the above code makes the watermark non-selectable and non-removable in the Adobe Pro editing function but the issue is when opening this pdf file from the VMware Workspace ONE Boxer email, the watermark is not displayed!

Any advice on how to fix this issue?

UPDATE: the following code works fine in Boxer PDF Viewer and the watermark is showing fine, but the issue is that this watermark is selectable and removable by adobe pro:

public class TestWatermark2 {

    public static String resourcesPath = "C:\\Users\\java\\Desktop\\TestWaterMark\\";
    public static String FILE_NAME = resourcesPath + "test.pdf";

    public static void main(String[] args) throws IOException {
        System.out.println("########## STARTED ADDING WATERMARK ###########");
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        try {
            byte[] byteArray = Files.readAllBytes(Paths.get(FILE_NAME));
            String watermarkText = "confidential";
            String fontPath = resourcesPath + "myCustomFont.ttf";
            Font arabicFont = FontFactory.getFont(fontPath, BaseFont.IDENTITY_H, 16);

            BaseFont baseFont = arabicFont.getBaseFont();
            PdfReader reader = new PdfReader(byteArray);
            PdfStamper stamper = new PdfStamper(reader, baos);
            Phrase watermarkPhrase = new Phrase(watermarkText, arabicFont);

            int numberOfPages = reader.getNumberOfPages();

            float height = baseFont.getAscentPoint(watermarkText, 24) + baseFont.getDescentPoint(watermarkText, 24);

            for (int i = 1; i <= numberOfPages; i++) {

                Rectangle pageSize = reader.getPageSizeWithRotation(i);
                PdfContentByte overContent = stamper.getOverContent(i);

                PdfGState state = new PdfGState();
                state.setFillOpacity(0.3f);
                overContent.saveState();
                overContent.setGState(state);

                for (float x = 70f; x < pageSize.getWidth(); x += height + 100) {
                    for (float y = 90; y < pageSize.getHeight(); y += height + 100) {
                        ColumnText.showTextAligned(overContent, Element.ALIGN_CENTER, watermarkPhrase, x, y, 45f,
                                PdfWriter.RUN_DIRECTION_RTL, ColumnText.DIGITS_AN2EN);
                    }
                }

                overContent.restoreState();

            }

            stamper.close();
            reader.close();
            byteArray = baos.toByteArray();
            File outputFile = new File(resourcesPath + "output.pdf");
            if (outputFile.exists()) {
                outputFile.delete();
            }
            Files.write(outputFile.toPath(), byteArray);

            System.out.println("########## FINISHED ADDING WATERMARK ###########");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

UPDATE2: I tried mkl solution and it is working very fine but it has one small issue if the watermark text is arabic it is getting displayed incorrect LTR as in the attached image:

enter image description here

Stereoisomerism answered 10/3, 2024 at 9:21 Comment(15)
Is it really getting removed? Or does that program simply not show the watermark?Such
@Such How to know the answer for sure? From my test when forwarding the same pdf email from Boxer to my Gmail, the pdf is forwarded with the watermark, and when downloading the file on the mobile it is downloaded with the watermarkStereoisomerism
@mkl,Also how to prevent the painted watermark in above code from being hidden or removed?Stereoisomerism
@Mahmoud "How to know the answer for sure? From my test when forwarding the same pdf email from Boxer to my Gmail, the pdf is forwarded with the watermark, and when downloading the file on the mobile it is downloaded with the watermark" - For me that sounds like Boxer does not remove it but simply does not display it, possibly because it doesn't support transparency (at least not with .01 opacity) or patterns. There are many PDF processors out there that don't support the whole PDF specification.Such
@Mahmoud "Also how to prevent the painted watermark in above code from being hidden or removed?" - You cannot. Of course you can create watermarks that are more and more difficult to remove, in particular by generic software. But as long the watermark does not completely hide content and remove that content's drawing instructions, it is undoable.Such
Ah, I just realize that you use a custom font but not embed it. Maybe your custom font simply is not registered in the Boxer PDF viewer. As a consequence, Boxer would not be able to display your watermark.Such
@mkl, the issue is with neither the opacity nor the font, because I increased the opacity with no luck and also changed the font with no luck, the issue is with exactly the PdfPatternPainter because when using ColumnText.showTextAligned it appears in the Boxer PDF Viewer with no issues, see my updated codeStereoisomerism
Ok, so apparently that viewer does not (properly) support patterns. You may want to tell the Boxer support of this issue.Such
By the way, you may want to move the overContent.setColorFill ... overContent.fill(); after the double loop. That loop after all constructs the pattern, so it may be better to use the pattern only after the loop. I assume, though, that this is unrelated to the Boxer issue.Such
And another remark on the side: "the above code makes the watermark non-selectable and non-removable in the Adobe Pro editing function" - I don't know which Acrobat version you tested with, but with the version 2023.008.20555 I have here Acrobat can remove the watermark. I simply use the "Edit a PDF" tool, select the page-covering object, and delete it. Ok, I have to do so some 35 times because you fill the overcontent that many times with the pattern (see my previous comment), but thereafter the mark is gone.Such
@mkl, you are correct, I tried it and it can be removed, so there's no 100% solution to make the watermark non removable from pdf files ?Stereoisomerism
There may be some other techniques which currently are not editable by Acrobat. But Acrobat is also being improved over time, so what currently is not editable may become editable the next monthSuch
What you currently can do is also putting the actual content into the pattern together with the watermark: Currently Adobe only allows to remove the whole filled rectangle, so if the actual content was also in the pattern, someone removing the rectangle would also remove the content. The downside: text in patterns cannot be copied&pasted. Furthermore, text in patterns is not accessible, screen readers etc. won't read it.Such
@mkl, can you please provide a sample code for this proposed solution?Stereoisomerism
"can you please provide a sample code for this proposed solution" - See my answer.Such
S
4

After finding out in the comments that the original watermark actually could be selected and deleted in the current Adobe Acrobat, I there mentioned the following option:

What you currently can do is also putting the actual content into the pattern together with the watermark: Currently Adobe only allows to remove the whole filled rectangle, so if the actual content was also in the pattern, someone removing the rectangle would also remove the content. The downside: text in patterns cannot be copied&pasted. Furthermore, text in patterns is not accessible, screen readers etc. won't read it.

You responded by asking for sample code for this proposed solution. This answer is focusing on quick & dirty example code for it.

The following is a quick proof-of-concept for the proposed solution. In particular it may have to be adjusted for pages whose lower left corner (crop box) is not the coordinate origin (0, 0) or which have page rotation applied.

Furthermore, in the course of testing the code it turned out that Acrobat suddenly allows editing the pattern content if there is no other content in the page than the pattern.

Thus, the code also adds a short pseudo content, a string of spaces. Interestingly this suffices to make the pattern again selectable only as a whole, and at the same time, because it's merely a string of spaces, Acrobat does not allow to select this pseudo content, either...

Following are screenshots in the Acrobat Edit tool and example files without and with pseudo content respectively:

Result file without pseudo content Result file with pseudo content
screen shot screen shot
example PDF example PDF

Beware, chances are that Acrobat will eventually also allow to edit this kind of watermarking. Maybe it even now is possible, merely not as obvious as before.

ByteArrayOutputStream baos = new ByteArrayOutputStream();

PdfReader reader = new PdfReader(byteArray);
PdfStamper stamper = new PdfStamper(reader, baos);

BaseFont baseFont = BaseFont.createFont(BaseFont.HELVETICA_OBLIQUE, BaseFont.WINANSI, false);
String watermarkText = "confidential";

int numberOfPages = reader.getNumberOfPages();
for (int i = 1; i <= numberOfPages; i++) {
    Rectangle pageSize = reader.getPageSizeWithRotation(i);

    // get handle for existing page content
    PdfImportedPage pageContent = stamper.getImportedPage(reader, i);
    // store that content as form XObject
    stamper.getWriter().addToBody(pageContent.getFormXObject(stamper.getWriter().getCompressionLevel()), pageContent.getIndirectReference());
    pageContent.setCopied();
    // reset page content
    reader.getPageN(i).put(PdfName.CONTENTS, null);

    // create pattern with former page content
    PdfPatternPainter bodyPainter = stamper.getOverContent(i).createPattern(pageSize.getWidth(),
            pageSize.getHeight());
    bodyPainter.addTemplate(pageContent, 0, 0);

    // add watermark to pattern
    PdfGState state = new PdfGState();
    state.setFillOpacity(0.3f);
    bodyPainter.saveState();
    bodyPainter.setGState(state);
    for (float x = 70f; x < pageSize.getWidth(); x += 100) {
        for (float y = 90; y < pageSize.getHeight(); y += 100) {
            bodyPainter.beginText();
            bodyPainter.setTextRenderingMode(PdfPatternPainter.TEXT_RENDER_MODE_FILL);
            bodyPainter.setFontAndSize(baseFont, 13);
            bodyPainter.showTextAlignedKerned(Element.ALIGN_MIDDLE, watermarkText, x, y, 45f);
            bodyPainter.endText();
        }
    }
    bodyPainter.restoreState();

    // create new page content
    PdfContentByte canvas = stamper.getUnderContent(i);
    // add pseudo-content
    canvas.beginText();
    canvas.setFontAndSize(baseFont, 13);
    canvas.showTextAlignedKerned(Element.ALIGN_MIDDLE, "        ", 0, 0, 45f);
    canvas.endText();
    // fill with pattern holding former page content
    canvas.setColorFill(new PatternColor(bodyPainter));
    canvas.rectangle(pageSize.getLeft(), pageSize.getBottom(), pageSize.getWidth(),
            pageSize.getHeight());
    canvas.fill();
}

stamper.close();
reader.close();
byteArray = baos.toByteArray();

(AddWatermark test testWatermarkAllInPattern)

Such answered 20/3, 2024 at 9:31 Comment(8)
@KJ "always up for a challenge so I would like to see your output" - I added example screen shots and file links to my answer. But don't get me wrong, I don't think this approach will necessarily fool many tools, but it currently seems to work against Acrobat.Such
@KJ "1st attempt see" - You can of course always render as image and do the standard tricks. It clearly is lossy in quality. Which may or may not disturb the person removing the mark. "To defeat thresholding" - Mahmoud may also consider this tipp. It's easy to change the code in my answer to do that.Such
Of course you cannot defeat a determined user, in particular one who either knows their way around in PDF internals or who has no problem with the quality losses associated with rendering as image. Fortunately the OP merely is looking for a solution that defeats simple editing attempts using the current Acrobat Edit tool.Such
@mkl, I tried your solution and it is working fine, but can you please help in the case with Arabic water mark text since the text is added left to right and it should be right to left, please see my updated question I attached an imageStereoisomerism
@mkl, FYI the sample code you provided is unremovable but unfortunately the whole PDF content is hidden in Boxer PDF ViewerStereoisomerism
@mkl, can you please assist with the RTL issue ?Stereoisomerism
"FYI the sample code you provided is unremovable but unfortunately the whole PDF content is hidden in Boxer PDF Viewer" - yes, considering your earlier observations that was too be expected. Boxer appears not to support patterns. That obviously makes it easy to use to show people incorrect contents. As a consequence I'd not use boxer in that version.Such
"in the case with Arabic water mark text since the text is added left to right and it should be right to left" - iText 5 has a limited RTL support only. In particular there is support only in a few contexts, in particular in table cells. I'm afraid, though, that I don't know the details as I never needed RTL support . There should be multiple questions and answers here, though, on that topic.Such
B
2

You can use a PDF file as a watermark instead of creating it inside your code.

Also, consider creating a separate file instead of transforming the original file and reusing the same file, then create a new one with the sheets updated with the watermark.

This is how I used it and I certainly never faced any issues. You can read the sample I created from my Github: https://github.com/web20opensource/stamper/blob/master/README.md

I would say the difference from your code, mainly is that the pdf watermark is created separately into a pdf and then using it for stamping the pdf You want to have the pdf watermark.

On the resource You can find the tutorial I used at that time ( some years ago) from Lars Vogel a very transparent, clear, and great resource.

And I was using this same version of the itext library (itextpdf-5.4.2.jar), probably major updates today, please check the site for the latest version.

Bordelon answered 19/3, 2024 at 14:27 Comment(0)
D
1

I came across this issue not same but similar, using canvas helped me to achieve.

PdfContentByte canvas = writer.getDirectContext();

//adding the content

canvas.addTemplate(writer.getImportedPage(reader, pageNum), 0,0);

//adding the watermark

canvas.beginText();canvas.setFontandSize(BaseFont.createFont(),40);canvas.showTextAligned(Element.ALIGN_CENTER, "CONFIDENTIAL", 300, 400, 45);canvas.endText();

}

Diatomaceous answered 20/3, 2024 at 8:20 Comment(0)

© 2022 - 2025 — McMap. All rights reserved.