PDFs generated with iTextSharp generated watermark giving error - pdf

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.

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

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();

How to make PdfLayer.SetPrint work with PdfStamper?

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();

Customize PDFStamper using iTextSharp

I'm using iTextSharp to sign PDFs. When signing, it stamps the document with 4 fields: who signed, when, reason and location. What I meant to do is to add a field below (or above, that doesn't matter) with custom information.
Any Idea?
Here's my code that is generating the stamp:
PdfStamper stp = PdfStamper.CreateSignature(reader, memoryOut, '\0');
PdfSignatureAppearance sap = stp.SignatureAppearance;
iTextSharp.text.Rectangle rectangle = new iTextSharp.text.Rectangle(100, 100, 500, 200);
sap.SetVisibleSignature(rectangle, stp.Reader.NumberOfPages, null);
sap.SignDate = DateTime.Now;
sap.SetCrypto(null, chain, null, null);
sap.Reason = ssReason;
sap.Contact = ssContact;
sap.Location = ssLocation;
sap.Acro6Layers = true;
//sap.SignatureGraphic = iTextSharp.text.Image.GetInstance(ssImageUrl);
//sap.SignatureGraphic.ScaleToFit(131, 45);
sap.Render = PdfSignatureAppearance.SignatureRender.Description;
Several options:
modify one of the PdfTemplates from sap.getLayer(int).
call sap.setLayer2Text() to include your extra information.
Use a graphic. You can wrap a PdfTemplate in an Image, so you can draw anything you want. Then
sap.Render = PdfSignatureAppearance.SignatureRender.GraphicAndDescription
Hack The Source.

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