How to make PdfLayer.SetPrint work with PdfStamper? - pdf

I'm adding watermarks on existing PDF using the iText PdfStamper class. And I want these watermarks to be switched to on or off, so I'm using the class PdfLayer.
But I also want these watermarks to be always visible when the file is printed : I'm using the function PdfLayer.setPrint() then.
This is this last step that unfortunately doesn't work as expected.
Here's my code :
PdfReader reader = new PdfReader("C:/Temp/input.pdf");
PdfStamper stamp = new PdfStamper(reader, new FileOutputStream("C:/Temp/output.pdf"));
PdfWriter writer = stamp.getWriter();
PdfLayer layer = new PdfLayer("Watermarks", writer);
layer.setOn(true);
layer.setPrint("Watermarks", true);
BaseFont bf = BaseFont.createFont();
PdfContentByte cb = stamp.getOverContent(1);
cb.beginText();
cb.setFontAndSize(bf, 18);
cb.beginLayer(layer);
cb.showTextAligned(Element.ALIGN_LEFT, "Watermark line 1", 50, 55, 0);
cb.showTextAligned(Element.ALIGN_LEFT, "Watermark line 2", 50, 40, 0);
cb.endLayer();
cb.endText();
stamp.close();
reader.close();
When I check the layer properties from Adobe Reader (version 10), I see that the "Initial State : Print" property stays at "Prints When Visible" while it should be "Always Print".
I also tried creating layers on a new PDF document and there the setPrint() works.
What am I doing wrong ?

I have the same issue. My code want to add a image as watermark on every page of original pdf. And the watermark can only be viewed, not allow to print. I use PdfStamper and PdfLayer.setPrint() too. But it did not work. I read the itext java source and found a way to make it work. Here is code :
PdfWriter writer = stamp.getWriter();
PdfLayer layer = new PdfLayer("Watermarks", writer);
layer.setOn(true);
layer.setOnPanel(false);
layer.setPrint("watermark", false);
writer.addToBody(layer.getPdfObject(), layer.getRef());
It call addToBody after setPrint. This works well.

I have the same problem. As a workaround, you can use new Document and getImportedPage instead of pdfStamper.
Unfortunately, you loose the hyperlink because all pages are converted to images. I tried to use PdfCopy but I reproduced the same issue. I am really interested in a solution allowing me to add a watermark without changing the source file.
Degraded sample solution :
PdfReader pdfReaderS = new PdfReader(filepathS);
Document document = new Document(pdfReaderS.getPageSizeWithRotation(1));
PdfWriter pdfWriterD = PdfWriter.getInstance(document, new FileOutputStream(filepathD));
document.open();
PdfContentByte pdfContentByteD = pdfWriterD.getDirectContent();
BaseFont baseFont = BaseFont.createFont(BaseFont.HELVETICA, BaseFont.WINANSI, BaseFont.EMBEDDED);
int n = pdfReaderS.getNumberOfPages();
PdfLayer pdfLayer = new PdfLayer("Watermark", pdfWriterD);
pdfLayer.setPrint("Print", true);
pdfLayer.setView(visibleScreen);
for (int i = 1; i <= n; i++) {
Rectangle pageSizeS =pdfReaderS.getPageSizeWithRotation(i);
float pageWidth = pageSizeS.getWidth() / 2;
float pageheight = pageSizeS.getHeight() / 2;
float degree = (float)(Math.toDegrees(Math.atan(pageSizeS.getHeight()/pageSizeS.getWidth())));
document.setPageSize(pageSizeS);
document.newPage();
PdfImportedPage pdfImportedPage = pdfWriterD.getImportedPage(pdfReaderS, i);
int rotation = pdfReaderS.getPageRotation(i); //This value can be 0, 90, 180 or 270.
if (rotation == 0)
pdfContentByteD.addTemplate(page, 1, 0, 0, 1, 0, 0);
else if (rotation == 90)
pdfContentByteD.addTemplate(page, 0, -1, 1, 0, 0, pageSizeS.getHeight());
else if (rotation == 180)
pdfContentByteD.addTemplate(page, -1, 0, 0, -1, pageSizeS.getHeight(), pageSizeS.getWidth());
else if (rotation == 270)
pdfContentByteD.addTemplate(page, 0, 1, -1, 0, pageSizeS.getWidth(), 0);
pdfContentByteD.beginLayer(pdfLayer);
pdfContentByteD.beginText();
pdfContentByteD.setFontAndSize(baseFont, policeSize);
pdfContentByteD.setColorFill(col);
pdfContentByteD.showTextAligned(PdfContentByte.ALIGN_CENTER, text, pageWidth, pageheight, degree);
pdfContentByteD.endText();
pdfContentByteD.endLayer();
}
document.close();
pdfReaderS.close();

Related

How to extract a portion of a page and write to a new PDF file in itext7?

I want to divide a PDF page in to 4 quadrants. Then write each quadrant in to separate PDF page (or a document). I don't want to crop the existing page, but extract the contents of each quadrant and write it in to a new PDF file. Is there a way to do this using itext7?
I want to mention that the documentation for itextsharp and itext7 is bad and lacking in many ways - the book "iText in Action 2nd Edition" is the only help, if you are willing to read a book, and the examples are only in Java and some of the code is implemented in a different way in C#, not to mention that this is only on itextsharp 5.
For future reference - assuming you need equal parts split, here is what will do the trick( this is for 4x4 - that is 16 parts):
public void manipulatePdf(string src, string dest)
{
PdfReader reader = new PdfReader(src);
iTextSharp.text.Rectangle pagesize = reader.GetPageSizeWithRotation(1);
Document document = new Document(pagesize);
PdfWriter writer = PdfWriter.GetInstance(document,
new FileStream(dest, FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.ReadWrite));
document.Open();
PdfContentByte content = writer.DirectContent;
PdfImportedPage page = writer.GetImportedPage(reader, 1);
float x, y;
for (int i = 0; i< 16; i++)
{
x = -pagesize.Width * (i % 4);
y = pagesize.Height * (i / 4 - 3);
content.AddTemplate(page, 4, 0, 0, 4, x, y);
document.NewPage();
}
document.Close();
}

PDFs generated with iTextSharp generated watermark giving error

We are applying a watermark using iTextSharp to PDF documents before passing them to client. On some machines (all using v.11 of PDF viewer), the following error is being displayed.
An error exists on this page. Acrobat may not display the page correctly. Please contact the person who created the PDF Document to correct the problem.
The watermarking code is as follows:
protected static byte[] GetStampedDocument(byte[] content, string mark, string heading)
{
PdfReader reader = new PdfReader(content);
using (MemoryStream stream = new MemoryStream())
{
PdfStamper pdfStamper = new PdfStamper(reader, stream);
for (int i = 1; i <= reader.NumberOfPages; i++)
{
iTextSharp.text.Rectangle pageSize = reader.GetPageSizeWithRotation(i);
PdfContentByte pdfPageContents = pdfStamper.GetOverContent(i);
pdfPageContents.BeginText();
PdfGState gstate = new PdfGState();
gstate.FillOpacity = 0.2f;
gstate.StrokeOpacity = 0.3f;
pdfPageContents.SaveState();
pdfPageContents.SetGState(gstate);
BaseFont baseFont = BaseFont.CreateFont(BaseFont.HELVETICA_BOLD, Encoding.ASCII.EncodingName, false);
pdfPageContents.SetFontAndSize(baseFont, 46);
pdfPageContents.SetRGBColorFill(32, 32, 32);
pdfPageContents.ShowTextAligned(PdfContentByte.ALIGN_CENTER, mark, pageSize.Width / 2, pageSize.Height / 2, 66);
if (heading != null && heading.Length > 0)
{
pdfPageContents.SetFontAndSize(baseFont, 12);
pdfPageContents.SetRGBColorFill(32, 32, 32);
pdfPageContents.ShowTextAligned(PdfContentByte.ALIGN_LEFT, heading, 5, pageSize.Height - 15, 0);
}
pdfPageContents.EndText();
pdfPageContents.RestoreState();
}
pdfStamper.FormFlattening = true;
pdfStamper.FreeTextFlattening = true;
pdfStamper.Close();
return stream.ToArray();
}
}
I cannot recreate this on any machine I have tried so there is an environmental element to this as well I expect.
Any ideas?
You save the graphics state inside a text object:
pdfPageContents.BeginText();
[...]
pdfPageContents.SaveState();
[...]
pdfPageContents.EndText();
pdfPageContents.RestoreState();
This is not allowed, cf. Figure 9 — Graphics objects — in ISO 32000-2, special graphics state operators (like saving or restoring the graphics state) may not be used inside a text object.
To prevent this invalid syntax, move pdfPageContents.SaveState() before pdfPageContents.BeginText(). This furthermore makes the nesting of saving/restoring the state and beginning and ending the text object more natural.

How to make stamped image uneditable using iTextSharp?

My goal is stamp an image on a 3D PDF that behaves like a watermark (end-user cannot select, edit, resize, or delete the image).
I tried making an Annotation as shown below, but the image ("ClassificationBlock.png" in Resources) can be resized and deleted on the output PDF. Is that an inherent behavior of "PdfAnnotation" rectangles or is there a property I can define that will keep the image essentially read-only?
using (PdfStamper stamp = new PdfStamper(reader, fs))
.
.
.
Rectangle stampRect2 = null;
System.Drawing.Image imageBTM2 = System.Drawing.Image.FromHbitmap(Properties.Resources.ClassificationBlock.GetHbitmap());
Image stampImage2 = iTextSharp.text.Image.GetInstance(imageBTM2, System.Drawing.Imaging.ImageFormat.Png);
Rectangle location2 = new Rectangle(0, 0, stampImage2.Width, stampImage2.Height);
PdfAnnotation pdfStamp2 = PdfAnnotation.CreateStamp(stamp.Writer, location2, null, "ImageText");
stampImage2.SetAbsolutePosition(0, 0);
PdfAppearance app2 = stamp.GetOverContent(1).CreateAppearance(stampImage2.Width, stampImage2.Height);
app2.AddImage(stampImage2);
pdfStamp2.SetAppearance(PdfName.N, app2);
pdfStamp2.SetPage();
stamp.AddAnnotation(pdfStamp2, 1);
stampRect2 = location2;
stamp.FormFlattening = true;
stamp.Close();
reader.Close();
fs.Close();
I've also tried it by mimicking another user's attempt at watermark text via pdfContentBytes, but I can't get the image to even display on the PDF.
stamp.FormFlattening = false;
iTextSharp.text.Rectangle pageRectangle = reader.GetPageSizeWithRotation(1);
PdfContentByte pdfData = stamp.GetOverContent(1);
pdfData.SetFontAndSize(BaseFont.CreateFont(BaseFont.HELVETICA_BOLD, BaseFont.CP1252, BaseFont.NOT_EMBEDDED), 10);
PdfGState graphicsState = new PdfGState();
graphicsState.FillOpacity = 0.5F;
pdfData.SetGState(graphicsState);
pdfData.BeginText();
System.Drawing.Image imageBTM2 = System.Drawing.Image.FromHbitmap(Properties.Resources.TEKLAPDF_InstructionBlock.GetHbitmap());
iTextSharp.text.Image stampImage2 = iTextSharp.text.Image.GetInstance(imageBTM2, System.Drawing.Imaging.ImageFormat.Png);
float width = pageRectangle.Width;
float height = pageRectangle.Height;
stampImage2.ScaleToFit(width, height);
stampImage2.SetAbsolutePosition(width / 2 - stampImage2.Width / 2, height / 2 - stampImage2.Height / 2);
stampImage2.SetAbsolutePosition(50, 50);
stampImage2.Rotation = 0;
pdfData.AddImage(stampImage2);
pdfData.EndText();
Any ideas on how best to accomplish this? This is driving me crazy.
EDIT*****************************
These are the current avenues I've pursued. Any ideas on how to "watermark" the 3D PDF?
//Stamp Image Method (works on 2D PDF and 3D PDF BUT results in EDITABLE stamp)
System.Drawing.Image imageBTM2 = System.Drawing.Image.FromHbitmap(Properties.Resources.ClassificationBlock.GetHbitmap());
Image stampImage2 = iTextSharp.text.Image.GetInstance(imageBTM2, System.Drawing.Imaging.ImageFormat.Png);
Rectangle stampRect2 = null;
Rectangle location2 = new Rectangle(0, 0, stampImage2.Width, stampImage2.Height);
PdfAnnotation pdfStamp2 = PdfAnnotation.CreateStamp(stamp.Writer, location2, null, "ImageText");
stampImage2.SetAbsolutePosition(0, 0);
PdfAppearance app2 = stamp.GetUnderContent(1).CreateAppearance(stampImage2.Width, stampImage2.Height);
app2.AddImage(stampImage2);
pdfStamp2.SetAppearance(PdfName.N, app2);
pdfStamp2.SetPage();
stamp.AddAnnotation(pdfStamp2, 1);
stampRect2 = location2;
//Watermark Layering Method (works only on 2D PDF)
var layers = stamp.GetPdfLayers();
var imgLayer = new PdfLayer("StackoverflowImage", stamp.Writer);
PdfContentByte cb = stamp.GetUnderContent(1);
cb.BeginLayer(imgLayer);
stampImage2.ScalePercent(100f);
stampImage2.SetAbsolutePosition(pageWidth/2, pageHeight/2);
cb.AddImage(stampImage2);
cb.EndLayer();
//Jan's Watermark method (works only on 2D PDF)
PdfContentByte over = stamp.GetOverContent(1);
stampImage2.SetAbsolutePosition(pageWidth / 2, pageHeight / 2);
PdfLayer imgLayer = new PdfLayer("StackoverflowImage", stamp.Writer);
imgLayer.OnPanel = false;
over.BeginLayer(imgLayer);
over.AddImage(stampImage2);
over.EndLayer();
stamp.Close();
reader.Close();
First off: You most likely cannot prevent selection of the image.
Second: I do itext in Java, so you'll probably end up uppercasing first character of method names...
For the remainder or your question you could try to add this image to a layer:
PdfContentByte over = stamp.getOverContent(1)
Image img = ...//code to get your image;
img.setAbsolutePosition(x, y); //at your postion
PdfLayer imgLayer = new PdfLayer("StackoverflowImage", stamper.getWriter());
imgLayer.setOnPanel(false);
over.beginLayer(imgLayer);
over.addImage(img);
over.endLayer();
SOLVED! Using the "Stamp Image Method" as described above, I just needed to change the properties of the stamp itself (changing FLAGS to LOCKED and READ-ONLY). This results in a stamp that is above the 3D PDF layer AND cannot be resized, edited, or deleted. So the code is now:
//Stamp Image Method
System.Drawing.Image imageBTM2 = System.Drawing.Image.FromHbitmap(Properties.Resources.ClassificationBlock.GetHbitmap());
Image stampImage2 = iTextSharp.text.Image.GetInstance(imageBTM2, System.Drawing.Imaging.ImageFormat.Png);
Rectangle stampRect2 = null;
Rectangle location2 = new Rectangle(0, 0, stampImage2.Width, stampImage2.Height);
PdfAnnotation pdfStamp2 = PdfAnnotation.CreateStamp(stamp.Writer, location2, null, "ImageText");
pdfStamp2.Flags = iTextSharp.text.pdf.PdfAnnotation.FLAGS_LOCKED;
pdfStamp2.Flags = iTextSharp.text.pdf.PdfAnnotation.FLAGS_READONLY;
stampImage2.SetAbsolutePosition(0, 0);
PdfAppearance app2 = stamp.GetUnderContent(1).CreateAppearance(stampImage2.Width, stampImage2.Height);
app2.AddImage(stampImage2);
pdfStamp2.SetAppearance(PdfName.N, app2);
pdfStamp2.SetPage();
stamp.AddAnnotation(pdfStamp2, 1);
stampRect2 = location2;

Unable to add margins in iTextSharp document having images

Requirement:
A large image (dynamic) needs to be split and shown in PDF pages. If image can't be accomodated in one page then we need to add another page and try to fit the remaining portion and so on.
So far I am able to split the image in multiple pages, however it appears that they are completely ignoring the margin values and so images are shown without any margins.
Please see below code:
string fileStringReplace = imageByteArray.Replace("data:image/jpeg;base64,", "");
Byte[] imageByte = Convert.FromBase64String(fileStringReplace);
iTextSharp.text.Image image = iTextSharp.text.Image.GetInstance(imageByte);
float w = image.ScaledWidth;
float h = image.ScaledHeight;
float cropHeight = 1500f;
iTextSharp.text.Rectangle page = new iTextSharp.text.Rectangle(1150f, cropHeight);
var x = page.Height;
Byte[] created;
iTextSharp.text.Document document = new iTextSharp.text.Document(page, 20f, 20f, 20f, 40f); --This has no impact
using (var outputMemoryStream = new MemoryStream())
{
PdfWriter writer = PdfWriter.GetInstance(document, outputMemoryStream);
writer.CloseStream = false;
document.Open();
PdfContentByte canvas = writer.DirectContentUnder;
float usedHeights = h;
while (usedHeights >= 0)
{
usedHeights -= cropHeight;
document.SetPageSize(new iTextSharp.text.Rectangle(1150f, cropHeight));
canvas.AddImage(image, w, 0, 0, h, 0, -usedHeights);
document.NewPage();
}
document.Close();
created = outputMemoryStream.ToArray();
outputMemoryStream.Write(created, 0, created.Length);
outputMemoryStream.Position = 0;
}
return created;
I also tried to set margin in the loop by document.SetMargins() - but that's not working.
You are mixing different things.
When you create margins, be it while constructing the Document instance or by using the setMargins() method, you create margins for when you let iText(Sharp) decide on the layout. That is: the margins will be respected when you do something like document.Add(image).
However, you do not allow iText to create the layout. You create a PdfContentByte named canvas and you decide to add the image to that canvas using a transformation matrix. This means that you will calculate the a, b, c, d, e, and f value needed for the AddImage() method.
You are supposed to do that Math. If you want to see a margin, then the values w, 0, 0, h, 0, and -usedHeights are wrong, and you shouldn't blame iTextSharp, you should blame your lack of insight in analytical geometrics (that's the stuff you learn in high school at the age of 16).
This might be easier for you:
iTextSharp.text.Image image = iTextSharp.text.Image.GetInstance(imageByte);
float w = image.ScaledWidth;
float h = image.ScaledHeight;
// For the sake of simplicity, I don't crop the image, I just add 20 user units
iTextSharp.text.Rectangle page = new iTextSharp.text.Rectangle(w + 20, h + 20);
iTextSharp.text.Document document = new iTextSharp.text.Document(page);
PdfWriter writer = PdfWriter.GetInstance(document, outputMemoryStream);
// Please drop the line that prevents closing the output stream!
// Why are so many people making this mistake?
// Who told you you shouldn't close the output stream???
document.Open();
// We define an absolute position for the image
// it will leave a margin of 10 to the left and to the bottom
// as we created a page that is 20 user units to wide and to high,
// we will also have a margin of 10 to the right and to the top
img.SetAbsolutePosition(10, 10);
document.Add(Image);
document.Close();
Note that SetAbsolutePosition() also lets you take control, regardless of the margins, as an alternative, you could use:
iTextSharp.text.Image image = iTextSharp.text.Image.GetInstance(imageByte);
float w = image.ScaledWidth;
float h = image.ScaledHeight;
// For the sake of simplicity, I don't crop the image, I just add 20 user units
iTextSharp.text.Rectangle page = new iTextSharp.text.Rectangle(w + 20, h + 20);
iTextSharp.text.Document document = new iTextSharp.text.Document(page, 10, 10, 10, 10);
PdfWriter writer = PdfWriter.GetInstance(document, outputMemoryStream);
// Please drop the line that prevents closing the output stream!
// Why are so many people making this mistake?
// Who told you you shouldn't close the output stream???
document.Open();
// We add the image to the document, and we let iTextSharp decide where to put it
// As there is just sufficient space to fit the image inside the page, it should fit,
// But be aware of the existence of a leading; that could create side-effects
// such as forwarding the image to the next page because it doesn't fit vertically
document.Add(Image);
document.Close();

PDF Watermark for printing only, programmatically

I can watermark any PDF already, and the images inside, everything ok, but now I need the watermark only showing up when the PDF is printed... Is this possible? How?
I need to do this programmatically of course.
For future readers, this is possible to do by wrapping the watermark in a PDF layer (Optional Content Group), then configuring the Usage attribute of this layer as Print-Only. See the PDF Reference Document, Chapter 4-Graphics, part 4.10-Optional Content for more details.
Specifically, using itextsharp, I was able to get it working with the following, specifically - pdf version 1.7, and SetPrint("Watermark",true)
string oldfile = #"c:\temp\oldfile.pdf";
string newFile = #"c:\temp\newfile.pdf";
PdfReader pdfReaderS = new PdfReader(oldfile);
Document document = new Document(pdfReaderS.GetPageSizeWithRotation(1));
PdfWriter pdfWriterD = PdfWriter.GetInstance(document, new FileStream(newFile, FileMode.Create, FileAccess.Write));
pdfWriterD.SetPdfVersion(PdfWriter.PDF_VERSION_1_7);
document.Open();
PdfContentByte pdfContentByteD = pdfWriterD.DirectContent;
BaseFont bf = BaseFont.CreateFont(BaseFont.HELVETICA, BaseFont.CP1252, BaseFont.NOT_EMBEDDED);
int n = pdfReaderS.NumberOfPages;
string text = "UNCONTROLLED";
for (int i = 1; i <= n; i++)
{
iTextSharp.text.Rectangle pageSizeS = pdfReaderS.GetPageSizeWithRotation(i);
float pageWidth = pageSizeS.Width / 2;
float pageheight = pageSizeS.Height / 2;
document.SetPageSize(pageSizeS);
document.NewPage();
PdfImportedPage pdfImportedPage = pdfWriterD.GetImportedPage(pdfReaderS, i);
PdfLayer layer1 = new PdfLayer("Watermark", pdfWriterD);
layer1.SetPrint("Watermark", true);
layer1.View = false;
layer1.On = false;
layer1.OnPanel = false;
pdfContentByteD.BeginLayer(layer1);
pdfContentByteD.SetColorFill(BaseColor.RED);
pdfContentByteD.SetFontAndSize(bf, 30);
ColumnText.ShowTextAligned(pdfContentByteD, Element.ALIGN_CENTER, new Phrase(text), 300, 700, 0);
pdfContentByteD.EndLayer();
pdfContentByteD.AddTemplate(pdfImportedPage, 0, 0);//, 0, 1, 0, 0);
}
document.Close();
pdfReaderS.Close();
You should probably make use of the fact that the screen uses RGB and the printer CMYK. You should be able to create two colors in CMYK that map to the same RGB value. This is of course not enough against a determined specialist.
The bOnScreen parameter determines whether the watermark will be displayed when the PDF is viewed on the computer screen, and bOnPrint determines whether it will be displayed when the PDF is printed.
-- https://acrobatusers.com/tutorials/watermarking-a-pdf-with-javascript