Search code examples
javaitextvmwarewatermarkvmware-boxer

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


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


Solution

  • 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)