ItextSharp - Acrofields are empty - itext

I have a PDF form with filled out fields. If I try to read the acrofields they are empty. But in the PDF I can change the values and save them.
private static string GetFormFieldNamesWithValues(PdfReader pdfReader)
{
return string.Join("\r\n", pdfReader.AcroFields.Fields
.Select(x => x.Key + "=" +
pdfReader.AcroFields.GetField(x.Key))
.ToArray());
}
var reader = new PdfReader((DataContext as PDFContext).Datei);
AcroFields form = reader.AcroFields;
txt.Text = GetFormFieldNamesWithValues(reader);
How to read the fields?

Clearly your PDF is broken. The fields are defined as widget annotations on the page level, but they aren't referenced in the /AcroForm fields set on the document root level.
You can fix your PDF using the FixBrokenForm code sample:
PdfReader reader = new PdfReader(src);
PdfDictionary root = reader.getCatalog();
PdfDictionary form = root.getAsDict(PdfName.ACROFORM);
PdfArray fields = form.getAsArray(PdfName.FIELDS);
PdfDictionary page;
PdfArray annots;
for (int i = 1; i <= reader.getNumberOfPages(); i++) {
page = reader.getPageN(i);
annots = page.getAsArray(PdfName.ANNOTS);
for (int j = 0; j < annots.size(); j++) {
fields.add(annots.getAsIndirectObject(j));
}
}
PdfStamper stamper = new PdfStamper(reader, new FileOutputStream(dest));
stamper.close();
reader.close();
You should inform the creators of the tool that was used to produce the form that their PDFs aren't compliant with the PDF reference.

Here is my c#-Code:
PdfReader reader = new PdfReader(src);
PdfDictionary root = reader.Catalog;
PdfDictionary form = root.GetAsDict(PdfName.ACROFORM);
PdfArray fields = form.GetAsArray(PdfName.FIELDS);
PdfDictionary page;
PdfArray annots;
for (int i = 1; i <= reader.NumberOfPages; i++)
{
page = reader.GetPageN(i);
annots = page.GetAsArray(PdfName.ANNOTS);
for (int j = 0; j < annots.Size; j++)
{
fields.Add(annots.GetAsIndirectObject(j));
}
}
PdfStamper stamper = new PdfStamper(reader, new FileStream(dest, FileMode.Create));
stamper.Close();
reader.Close();

C# version
public void FixBrokenForm(string src, string dest)
{
PdfReader reader = new PdfReader(src);
PdfDictionary root = reader.Catalog;
PdfDictionary form = root.GetAsDict(PdfName.ACROFORM);
PdfArray fields = form.GetAsArray(PdfName.FIELDS);
PdfDictionary page;
PdfArray annots;
for (int i = 1; i <= reader.NumberOfPages; i++)
{
page = reader.GetPageN(i);
annots = page.GetAsArray(PdfName.ANNOTS);
for (int j = 0; j < annots.Size; j++)
{
fields.Add(annots.GetAsIndirectObject(j));
}
}
PdfStamper stamper = new PdfStamper(reader, new FileStream(dest, FileMode.Create));
stamper.Close();
reader.Close();
}
You will need Itextsharp to make the above code work.

The answers with the FixBrokenForm code are good, but don't forget to call fields.setGenerateAppearances(true) or you'll lose the fields if you call stamper.setFormFlattening(true).
You can do like this:
PdfStamper stamper = new PdfStamper(reader, new FileStream(dest, FileMode.Create));
// Add these 2 lines ***************************
AcroFields acrofields = stamper.getAcroFields();
acrofields.setGenerateAppearances(true);
// *********************************************
stamper.Close();
reader.Close();

Related

How to NOT flatten the image applied in a pdf with Itext in c#?

I need to apply an image on all my pages from my PDF, but without flattening it ( I want to have the ability to move it in my PDF reader afterwards)
My code:
String basePath = "d:\\zPDF\\";
DirectoryInfo d = new DirectoryInfo(basePath);//Assuming Test is your Folder
FileInfo[] Files = d.GetFiles("*.pdf"); //Getting Text files
List<string> listS = new List<string>();
foreach (FileInfo file in Files)
{
listS.Add(file.Name);
}
foreach (string s in listS)
{
using (System.IO.Stream inputPdfStream = new FileStream(basePath + s, FileMode.Open, System.IO.FileAccess.Read, FileShare.ReadWrite))
//using (System.IO.Stream inputImageStream = new FileStream(basePath + "x1.wmf", FileMode.Open, System.IO.FileAccess.Read, FileShare.Read))
using (System.IO.Stream inputImageStream2 = new FileStream(basePath + "x2.wmf", FileMode.Open, System.IO.FileAccess.Read, FileShare.Read))
using (System.IO.Stream outputPdfStream = new FileStream(basePath + "zResult" + s, FileMode.Create, System.IO.FileAccess.Write, FileShare.None))
{
var reader = new PdfReader(inputPdfStream);
var stamper = new PdfStamper(reader, outputPdfStream);
//stamper.FormFlattening = true;
//stamper.FreeTextFlattening = true;
int numberOfPages = reader.NumberOfPages;
Image myImage = Image.GetInstance(inputImageStream2);
float f-Image1, f-Image2;
for (int i = 1; i <= numberOfPages; i++)
{
int nr, plusMinus = 25;
Rectangle mediabox = reader.GetPageSize(i);
int getTOP = (int)mediabox.GetTop(0);
int getRight = (int)mediabox.GetRight(0);
var pdfContentByte = stamper.GetOverContent(i);
nr = getRight - 600;
f-Image1 = row1(nr, nr + plusMinus);
//row1 - generates a random number between those 2 values
nr = 40;
f-Image2 = row1(nr, nr + plusMinus);
//row1 - generates a random number between those 2 values
myImage.SetAbsolutePosition(f-Image1, f-Image2);
myImage.RotationDegrees = row1(-35, 35);
pdfContentByte.AddImage(myImage);
}
stamper.Close();
}
I've tried:
stamper.FormFlattening = false;
stamper.FreeTextFlattening = false;
but no results. The image is still flatten.
I think, after I've read some posts, that I need to set up my PdfStamper in useAppendMode() but I don't know how should I do this and, I don't know if this is this right direction.

How can i get a PdfImportedPage without hidden layer context use Itextsharp

When i generates a PDF file from an existing PDF file with itextsharp,my work code is
The soruce pdf
string sourceFile = "a4.pdf", targetFile = "processed.pdf";
PdfReader reader = new PdfReader(sourceFile);
Document doc = new Document();
PdfWriter writer = PdfWriter.GetInstance(doc, new FileStream(targetFile, FileMode.Create));
doc.Open();
PdfContentByte cb = writer.DirectContent;
PdfImportedPage page;
for (int pageNumber = 1; pageNumber <= reader.NumberOfPages; pageNumber++)
{
doc.SetPageSize(reader.GetPageSizeWithRotation(pageNumber));
doc.NewPage();
page = writer.GetImportedPage(reader, pageNumber);
//Write a PageIndex
ColumnText.ShowTextAligned(cb, PdfContentByte.ALIGN_CENTER, new Phrase(pageNumber.ToString()), 100, 0, 0);
cb.AddTemplate(page, 0, 0);
}
doc.Close();
The problem is, when i get a PdfImportedPage from reader,page = writer.GetImportedPage(reader, pageNumber); the content in sourceFile's hidden layer will display.The processed.pdf has none layer.
How can i get a PdfImportedPage without hidden layer context use Itextsharp.

Add items to existing PDF with iTextSharp

Is the correct way to add items to an existing PDF? The method GetFileBytes
reads a PDF from disk and converts to an array, this works fine, however the
resultant file NP.PDF is exactly the same as the original. I actually want to
add barcodes but thought to try get it working with a simple item first.
Byte[] bytes = GetFileBytes();
Document document = new Document();
MemoryStream ms = new MemoryStream(bytes);
PdfWriter writer = PdfWriter.GetInstance(document, ms);
document.Open();
document.Add(new Paragraph("First Paragraph"));
document.Add(new Paragraph("Second Paragraph"));
//document.Close();
ms.Position = 0;
File.WriteAllBytes(#"e:\NP.pdf", (Byte[])ms.ToArray());
This is how I have done it in the past. You need to use a new stream for the PDF writer, and add your original PDF contents to the new document. This may not be the best or most efficient way of performing this, it's just what I found works.
This is working with my original PDF coming from a Stream, and producing the merged output as another Stream, however you should be able to adapt it to your needs.
var originalPdfStream = GetOriginalPdfStream();
var pdfDocument = Document();
var finalPdfStream = new MemoryStream();
var pdfWriter = PdfWriter.GetInstance(pdfDocument, finalPdfStream);
pdfDocument.Open();
// Add the originalPdfStream into the pdfDocument
var originalPdfReader = new PdfReader(originalPdfStream);
AddPdf(document, writer, reader);
// Code to add other items to the pdfDocument.
pdfWriter.CloseStream = false;
pdfDocument.Close();
finalPdfStream.Position = 0;
return finalPdfStream;
The following method then takes your document and writer, and a reader to the document you want to add.
/// <summary>
/// Merge a PDF file into a PDF document.
/// </summary>
/// <param name="document">The PDF document.</param>
/// <param name="writer">The PDF writer.</param>
/// <param name="reader">A PDF reader.</param>
private static void AddPdf(Document document, PdfWriter writer, PdfReader reader)
{
if (document == null) throw new ArgumentNullException("document");
if (writer == null) throw new ArgumentNullException("writer");
if (reader == null) throw new ArgumentNullException("reader");
var numberOfPages = reader.NumberOfPages;
var originalPageSize = document.PageSize;
// Iterate through all pages
for (var currentPageIndex = 1; currentPageIndex <= numberOfPages; currentPageIndex++)
{
// Determine page size for the current page
document.SetPageSize(reader.GetPageSizeWithRotation(currentPageIndex));
// Create page
document.NewPage();
var importedPage = writer.GetImportedPage(reader, currentPageIndex);
// Determine page orientation and add page.
var pageRotation = reader.GetPageRotation(currentPageIndex);
var pageWidth = reader.GetPageSizeWithRotation(currentPageIndex).Width;
var pageHeight = reader.GetPageSizeWithRotation(currentPageIndex).Height;
switch (pageRotation)
{
case 0:
writer.DirectContent.AddTemplate(importedPage, 1f, 0, 0, 1f, 0, 0);
break;
case 90:
writer.DirectContent.AddTemplate(importedPage, 0, -1f, 1f, 0, 0, pageHeight);
break;
case 180:
writer.DirectContent.AddTemplate(
importedPage, -1f, 0, 0, -1f, pageWidth, pageHeight);
break;
case 270:
writer.DirectContent.AddTemplate(importedPage, 0, 1f, -1f, 0, pageWidth, 0);
break;
default:
throw new Exception("Unexpected page rotation: [{0}].", pageRotation);
}
}
document.SetPageSize(originalPageSize);
}
As long as the added content shall be on new pages only (as your sample code seems to indicate), you can create the additions as if creating a new intermediate PDF (if not too big, it can reside in memory) and then concatenate the PDFs using PdfCopy:
using (MemoryStream ms = new MemoryStream()) {
// step 1
using (Document document = new Document()) {
// step 2
using (PdfCopy copy = new PdfCopy(document, ms)) {
// step 3
document.Open();
// step 4
for (int i = 0; i < pdf.Count; ++i) {
PdfReader reader = ...;// retrieve a PdfReader for the i'th PDF to concatenate
// loop over the pages in that document
int n = reader.NumberOfPages;
for (int page = 0; page < n; ) {
copy.AddPage(copy.GetImportedPage(reader, ++page));
}
}
}
}
byte[] data = ms.ToArray();
...
}
(Shamelessly copied from the Webified iTextSharp Example Concatenate.cs)
If, on the other hand, you want to stamp something on an existing page, you more likely need a PdfStamper:
PdfReader reader = new PdfReader(resource);
using (var ms = new MemoryStream()) {
using (PdfStamper stamper = new PdfStamper(reader, ms)) {
PdfContentByte canvas = stamper.GetOverContent(1);
ColumnText.ShowTextAligned(
canvas,
Element.ALIGN_LEFT,
new Phrase("Hello people!"),
36, 540, 0
);
}
byte[] data = ms.ToArray();
...
}
(Also shamelessly copied from the Webified iTextSharp Example StampText.cs)
For additional background information, read the free sample chapter 6 of iText in Action — 2nd Edition.

iTextSharp - Adding an image and resize pages in PDF

I have a PDF file which I need to insert an image on the 1st page and then resize the original size from A4 to some other size.
I manage to add an image into the PDF but not resizing it with the following code:
string pdfFile = #"C:\Temp\a.pdf";
PdfReader reader = new PdfReader(pdfFile);
PdfStamper stamper = new PdfStamper(reader, new FileStream(#"C:\Temp\out.pdf", FileMode.Create), PdfWriter.VERSION_1_5);
PdfContentByte writer = stamper.GetOverContent(1);
stamper.FormFlattening = true;
stamper.SetFullCompression();
Image topImage = Image.GetInstance(#"C:\Temp\c.jpg");
topImage.ScalePercent(19f);
topImage.SetAbsolutePosition(142, 700);
writer.AddImage(topImage);
Document myPDF = writer.PdfDocument;
myPDF.SetPageSize(PageSize.A3);
stamper.Close();
stamper = null;
Is there anything wrong or missing in my code?
Below is how a PDF can be resized:
private string PdfResize(string tmpPdfFilename, string resultRootName)
{
try
{
string newPdfFilename = resultRootName + ".pdf";
PdfReader resizeReader = new PdfReader(tmpPdfFilename);
Rectangle newRect = new Rectangle(0, 0, Convert.ToSingle(_pdfNewSizeW), Convert.ToSingle(_pdfNewSizeH));
Document doc = new Document(newRect);
Document.Compress = true;
PdfWriter resizeWriter = PdfWriter.GetInstance(doc, new FileStream(newPdfFilename, FileMode.Create));
doc.Open();
PdfContentByte cb = resizeWriter.DirectContent;
for (int pageNumber = 1; pageNumber <= resizeReader.NumberOfPages; pageNumber++)
{
PdfImportedPage page = resizeWriter.GetImportedPage(resizeReader, pageNumber);
cb.AddTemplate(page, newRect.Width / resizeReader.GetPageSize(pageNumber).Width, 0, 0,
newRect.Height / resizeReader.GetPageSize(pageNumber).Height, 0, 0);
doc.NewPage();
}
doc.Close();
doc = null;
return newPdfFilename;
}
catch (Exception exp)
{
return String.Empty;
}
}
Despite being an older thread the original question & thread was very helpful recently.
Here's another version that processes the Pdf input as a byte array instead of file path (more helpful for web app environment), and handles all IDisposable references...
public static byte[] ResizePdfPageSize(byte[] pdfBytes, Rectangle pageSize)
{
Document.Compress = true;
using (var outputMemoryStream = new MemoryStream())
using (var targetDoc = new Document(pageSize))
using (var pdfReader = new PdfReader(pdfBytes))
using (var pdfWriter = PdfWriter.GetInstance(targetDoc, outputMemoryStream))
{
targetDoc.Open();
PdfContentByte pdfContentByte = pdfWriter.DirectContent;
var pageCount = pdfReader.NumberOfPages;
for (int pageNumber = 1; pageNumber <= pageCount; pageNumber++)
{
PdfImportedPage page = pdfWriter.GetImportedPage(pdfReader, pageNumber);
var currentPageSize = pdfReader.GetPageSize(pageNumber);
var scaledPageWidth = pageSize.Width / currentPageSize.Width;
var scaledPageHeight = pageSize.Height / currentPageSize.Height;
pdfContentByte.AddTemplate(
page,
scaledPageWidth,
0, 0,
scaledPageHeight,
0, 0
);
//Move document cursor to next Page!
targetDoc.NewPage();
}
targetDoc.Close();
byte[] finalFileBytes = outputMemoryStream.ToArray();
return finalFileBytes;
}
}
Usage with pre-set PageSizes from iTextSharp is very handy:
var originalPdfBytes = File.ReadAllBytes(fileInfo.FullName);
var resizedBytes = PdfHelper.ResizePdfPageSize(originalPdfBytes, PageSize.A4);
File.WriteAllBytes(fileName, resizedBytes);
EDIT: The above method answers the core question and got me where I needed to get for my issues. However, after working on this and ironing out numerous issues and shortcomings of the above simplified method, I've now put the whole helpful code base on github to share for anyone else interested. The github PdfHelpers project now handle aspect ratio better, page margins, rotation of content for better scaling as landscape, content that's already rotated (in my limited test cases), etc. while also providing other helpful code for simple Pdf tasks.
https://github.com/cajuncoding/PdfHelpers

Using iTextSharp's PdfStamper to fill particular Imported Pages and append to a new Pdf

I'm trying to import pages one by one from a 2-page pdf, fill them up with pre-defined data and then finally save pdf to the disk after form flattening. However, I'm not sure about the usage of PdfStamper in here. Could anyone please help ? Please see the sample code below. Here I'm not sure about lines in bold (between **s).
while (i < n)
{
i++;
document.SetPageSize(reader.GetPageSizeWithRotation(i));
document.NewPage();
page = writer.GetImportedPage(reader, i);
**var ms = new MemoryStream();
PdfReader pr = new PdfReader(page.ToPdf(writer).ToArray());
var stamper = new PdfStamper(pr, ms);
stamper.AcroFields.SetField("payrollNo", "666666");
stamper.Close();**
rotation = reader.GetPageRotation(i);
if (rotation == 90 || rotation == 270)
{
cb.AddTemplate(page, 0, -1f, 1f, 0, 0, reader.GetPageSizeWithRotation(i).Height);
}
else
{
cb.AddTemplate(page, 1f, 0, 0, 1f, 0, 0);
}
}
Thanks ahead !
This will solve your problem:
static PdfReader GetPdf(string filename, bool FillForm)
{
PdfReader reader2 = new PdfReader(filename);
using (MemoryStream ms = new MemoryStream())
{
var stamper = new PdfStamper(reader2, ms);
var form = stamper.AcroFields;
var fieldKeys = form.Fields.Keys;
if (FillForm)
foreach (string fieldKey in fieldKeys)
form.SetField(fieldKey, "REPLACED!");
stamper.Writer.CloseStream = false;
stamper.FormFlattening = true;
stamper.Close();
reader2.Close();
return new PdfReader(ms.ToArray());
}
}