Unable to add margins in iTextSharp document having images - pdf

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

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

When using iText to generate a PDF, if I need to switch fonts many times the file size becomes too large

I have a section of my PDF in which I need to use one font for its unicode symbol and the rest of the paragraph should be a different font. (It is something like "1. a 2. b 3. c" where "1." is the unicode symbol/font and "a" is another font) I have followed the method Bruno describes here: iText 7: How to build a paragraph mixing different fonts? and it works fine to generate the PDF. The issue is that the file size of the PDF goes from around 20MB to around 100MB compared to using only one font and one Text element. This section is used repeatedly in the document thousands of times. I am wondering if there is a way to reduce the impact of switching fonts or to reduce the file size of the entire document in some way.
Style creation pseudocode:
Style style1 = new Style();
Style style2 = new Style();
PdfFont font1 = PdfFontFactory.createFont(FontProgramFactory.createFont(fontFile1), PdfEncodings.IDENTITY_H, true);
style1.setFont(font1).setFontSize(8f).setFontColor(Color.DARK_GRAY);
PdfFont font2 = PdfFontFactory.createFont(FontProgramFactory.createFont(fontFile2), "", false);
style2.setFont(font2).setFontSize(8f).setFontColor(Color.DARK_GRAY);
Writing text/paragraph pseudocode:
Div div = new Div().setPaddingLeft(3).setMarginBottom(0).setKeepTogether(true);
Paragraph paragraph = new Paragraph();
loop up to 25 times: {
Text unicodeText = new Text(unicodeSymbol + " ").addStyle(style1);
paragraph.add(unicodeText);
Text plainText = new Text(plainText + " ").addStyle(style2);
paragraph.add(plainText);
}
div.add(paragraph);
This writing of text/paragraph is done thousands of times and makes up most of the document. Basically the document consists of thousands of "buildings" that have corresponding codes and the codes have categories. I need to have the index for the category as the unicode symbol and then all of the corresponding codes within the paragraph for the building.
Here is reproducable code:
float offSet = 50;
Integer leading = 10;
DateFormat format = new SimpleDateFormat("yyyy_MM_dd_kkmmss");
String formattedDate = format.format(new Date());
String path = "/tmp/testing_pdf_"+formattedDate + ".pdf";
File targetPdfFile = new File(path);
PdfWriter writer = new PdfWriter(path, new WriterProperties().addXmpMetadata());
PdfDocument pdf = new PdfDocument(writer);
pdf.setTagged();
PageSize pageSize = PageSize.LETTER;
Document document = new Document(pdf, pageSize);
document.setMargins(offSet, offSet, offSet, offSet);
byte[] font1file = IOUtils.toByteArray(FileUtility.getInputStreamFromClassPath("fonts/Garamond-Premier-Pro-Regular.ttf"));
byte[] font2file = IOUtils.toByteArray(FileUtility.getInputStreamFromClassPath("fonts/Quivira.otf"));
PdfFont font1 = PdfFontFactory.createFont(FontProgramFactory.createFont(font1file), "", true);
PdfFont font2 = PdfFontFactory.createFont(FontProgramFactory.createFont(font2file), PdfEncodings.IDENTITY_H, true);
Style style1 = new Style().setFont(font1).setFontSize(8f).setFontColor(Color.DARK_GRAY);
Style style2 = new Style().setFont(font2).setFontSize(8f).setFontColor(Color.DARK_GRAY);
float columnGap = 5;
float columnWidth = (pageSize.getWidth() - offSet * 2 - columnGap * 2) / 3;
float columnHeight = pageSize.getHeight() - offSet * 2;
Rectangle[] columns = {
new Rectangle(offSet, offSet, columnWidth, columnHeight),
new Rectangle(offSet + columnWidth + columnGap, offSet, columnWidth, columnHeight),
new Rectangle(offSet + columnWidth * 2 + columnGap * 2, offSet, columnWidth, columnHeight)};
document.setRenderer(new ColumnDocumentRenderer(document, columns));
for (int j = 0; j < 5000; j++) {
Div div = new Div().setPaddingLeft(3).setMarginBottom(0).setKeepTogether(true);
Paragraph paragraph = new Paragraph().setFixedLeading(leading);
// StringBuilder stringBuilder = new StringBuilder();
for (int i = 0; i < 26; i++) {
paragraph.add(new Text("\u3255 ").addStyle(style2));
paragraph.add(new Text("test ").addStyle(style1));
// stringBuilder.append("\u3255 ").append(" test ");
}
// paragraph.add(stringBuilder.toString()).addStyle(style2);
div.add(paragraph);
document.add(div);
}
document.close();
In creating the reproducible code I have found this this is related to the document being tagged. If you remove the line that marks it as tagged it reduces the file size greatly.
You can also reduce the file size by using the commented out string builder with one font instead of two. (Comment out the two "paragraph.add"s in the for-loop) This mirrors the issue I have in my code.
The problem is not in fonts themselves. The issues comes from the fact that you are creating a tagged PDF. Tagged documents have a lot of PDF objects in them that need a lot of space in the file.
I wasn't able to reproduce your 20MB vs 100MB results. On my machine whether with one font or with two fonts, but with two Text elements, the resultant file size is ~44MB.
To compress file when creating large tagged documents, you should use full compression mode which compresses all PDF objects, not only streams.
To activate full compression mode, create a PdfWriter instance with WriterProperties:
PdfWriter writer = new PdfWriter(outFileName,
new WriterProperties().setFullCompressionMode(true));
This setting reduced the file size for me from >40MB to ~5MB.
Please note that you are using iText 7.0.x while 7.1.x line has already been released and is now the main line of iText, so I recommend that you update to the latest version.

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;

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