ok. for watermark, here is the sample pdf. this is the same link i gave to maruan
https://www.dropbox.com/sh/f5zfhh00jhab48n/AABMJ4wpqeyNrmRFhKQPh_Gfa?dl=0 the code i use for watermark is this. i did save the graphic state then restored it. the issue is that the transparent text i created is selectable. PDFont font = PDType1Font.HELVETICA; float fontSize = 80f; PDPageTree pages = pdfDocument.getPages(); PDExtendedGraphicsState gs = new PDExtendedGraphicsState(); gs.setNonStrokingAlphaConstant(0.3f); PDPage page = pages.get(0); try (PDPageContentStream cs = new PDPageContentStream(pdfDocument, page, AppendMode.APPEND, true, true)) { cs.setFont(font, fontSize); cs.setNonStrokingColor(Color.GRAY); cs.setGraphicsStateParameters(gs); for (int w=0; w<watermark.size(); w++) { float stringWidth = font.getStringWidth(watermark.get(w)) / 1000 * fontSize; float fontHeight = 15; cs.saveGraphicsState(); cs.beginText(); if (watermark.size() == 1) cs.transform(Matrix.getRotateInstance(Math.toRadians(270), (page.getCropBox().getWidth() - (fontHeight / 2)) / 2, (page.getCropBox().getHeight() + stringWidth) / 2)); else if (watermark.size() == 2) { cs.transform(Matrix.getRotateInstance(Math.toRadians(270), (page.getCropBox().getWidth() / 2) - (w == 0 ? -(fontHeight / 2) : fontHeight), (page.getCropBox().getHeight() + stringWidth) / 2)); } else { float fontHeightValueToUse = fontHeight / 2; if (w == 0) fontHeightValueToUse = -(fontHeight * 2); else if (w == 2) fontHeightValueToUse = fontHeight * 3; cs.transform(Matrix.getRotateInstance(Math.toRadians(270), (page.getCropBox().getWidth() - fontHeightValueToUse) / 2, (page.getCropBox().getHeight() + stringWidth) / 2)); } cs.showText(watermark.get(w)); cs.endText(); cs.restoreGraphicsState(); } } catch (Exception e) { }

