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();
}
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
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):
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.