Set Metadata in iTextSharp - c#

I am developing an application and i use the iTextSharp library.
I am also reading the iText in action from Manning so i can get references.
In Chapter 12 it has the following code to change the metadata in Java.
PdfReader reader = new PdfReader(src);
PdfStamper stamper =
new PdfStamper(reader, new FileOutputStream(dest));
HashMap<String, String> info = reader.getInfo();
info.put("Title", "Hello World stamped");
info.put("Subject", "Hello World with changed metadata");
info.put("Keywords", "iText in Action, PdfStamper");
info.put("Creator", "Silly standalone example");
info.put("Author", "Also Bruno Lowagie");
stamper.setMoreInfo(info);
stamper.close();
How can i do the same in C#?

Conversion from Java to C# is usually pretty straightforward. By convention, Java properties use get and set prefixes so to convert to C# you just need to drop the prefix and turn it into a .Net getter/setter call. getInfo() becomes Info and setMoreInfo(info) becomes MoreInfo = info. Then you just need to convert the native Java types to their equivalent C# types. In this case the Java FileOutputStream becomes a .Net FileStream and the HashMap<String, String> becomes a Dictionary<String, String>.
Lastly, I've updated the code to reflect recent changes to iTextSharp that now (as of 5.1.1.0) implement IDisposable now.
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Windows.Forms;
using System.IO;
using iTextSharp.text;
using iTextSharp.text.pdf;
namespace WindowsFormsApplication1
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
string workingFolder = Environment.GetFolderPath(Environment.SpecialFolder.Desktop);
string inputFile = Path.Combine(workingFolder, "Input.pdf");
string outputFile = Path.Combine(workingFolder, "Output.pdf");
PdfReader reader = new PdfReader(inputFile);
using(FileStream fs = new FileStream(outputFile, FileMode.Create, FileAccess.Write, FileShare.None)){
using (PdfStamper stamper = new PdfStamper(reader, fs))
{
Dictionary<String, String> info = reader.Info;
info.Add("Title", "Hello World stamped");
info.Add("Subject", "Hello World with changed metadata");
info.Add("Keywords", "iText in Action, PdfStamper");
info.Add("Creator", "Silly standalone example");
info.Add("Author", "Also Bruno Lowagie");
stamper.MoreInfo = info;
stamper.Close();
}
}
this.Close();
}
}
}

I just made this one after searching the right place in the watch window of the PdfWriter object, it changes the "PDF Creator" in the PDF as it is not accessible by default:
private static void ReplacePDFCreator(PdfWriter writer)
{
Type writerType = writer.GetType();
PropertyInfo writerProperty = writerType.GetProperties(BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.Instance).Where(p => p.PropertyType == typeof(PdfDocument)).FirstOrDefault();
PdfDocument pd = (PdfDocument)writerProperty.GetValue(writer);
Type pdType = pd.GetType();
FieldInfo infoProperty = pdType.GetFields(BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.Instance).Where(p => p.Name == "info").FirstOrDefault();
PdfDocument.PdfInfo pdfInfo = (PdfDocument.PdfInfo)infoProperty.GetValue(pd);
PdfString str = new PdfString("YOUR NEW PDF CREATOR HERE");
pdfInfo.Remove(new PdfName("Producer"));
pdfInfo.Put(new PdfName("Producer"), str);
}
I got a suggestion from "#yannic-donot-text" and it is way much cleaner!:
private static void ReplacePDFCreator(PdfWriter writer)
{
writer.Info.Put(new PdfName("Producer"), new PdfString("YOUR NEW PDF CREATOR HERE"));
}
I tought it was only archievable by reflection but I appreciate the collaboration of more educated persons :)
Thx!

public void pdfproperties()
{
string inputFile = #"D:\1.pdf";
string outputFile = #"D:\48.pdf";
PdfReader reader = new PdfReader(inputFile);
foreach (KeyValuePair<string, string> KV in reader.Info)
{
reader.Info.Remove(KV.Key);
}
using (FileStream FS = new FileStream(outputFile, FileMode.Create, FileAccess.Write, FileShare.None))
{
using (Document Doc = new Document())
{
using (PdfCopy writer = new PdfCopy(Doc, FS))
{
Doc.Open();
Doc.AddTitle("Add Title");
Doc.AddSubject("Add Subject");
Doc.AddKeywords("Add Keywords");
Doc.AddCreator("Application Creator");
Doc.AddAuthor("Add Author");
for (int i = 1; i <= reader.NumberOfPages; i++)
{
writer.AddPage(writer.GetImportedPage(reader, i));
}
writer.Info.Put(new PdfName("Producer"), new PdfString("Producer Name"));
Doc.Close();
}
}
}
}

Related

How can I create named destinations with iTextSharp?

I am trying to convert PDF bookmarks into named destinations with C# and iTextSharp 5 library. Unfortunately iTextSharp seems not to write named destinations into the target PDF file.
using System;
using System.Collections.Generic;
using iTextSharp.text.pdf;
using iTextSharp.text;
using System.IO;
namespace PDFConvert
{
class Program
{
static void Main(string[] args)
{
String InputPdf = #"test.pdf";
String OutputPdf = "out.pdf";
PdfReader reader = new PdfReader(InputPdf);
var fileStream = new FileStream(OutputPdf, FileMode.Create, FileAccess.Write, FileShare.None);
var list = SimpleBookmark.GetBookmark(reader);
PdfStamper stamper = new PdfStamper(reader, fileStream);
foreach (Dictionary<string, object> entry in list)
{
object o;
entry.TryGetValue("Title", out o);
String title = o.ToString();
entry.TryGetValue("Page", out o);
String location = o.ToString();
String[] aLoc = location.Split(' ');
int page = int.Parse(aLoc[0]);
PdfDestination dest = new PdfDestination(PdfDestination.XYZ, float.Parse(aLoc[2]), float.Parse(aLoc[3]), float.Parse(aLoc[4]));
stamper.Writer.AddNamedDestination(title, page, dest);
// stamper.Writer.AddNamedDestinations(SimpleNamedDestination.GetNamedDestination(reader, false), reader.NumberOfPages);
}
stamper.Close();
reader.Close();
}
}
}
I already tried to use PdfWriter instead of PdfStamper, with the same result. I have definitely calls of stamper.Writer.AddNamedDestination(title, page, dest); but no sign of NamedDestinations in my target file.
I have found a solution using iText 7 instead of 5. Unfortunately the syntax is completely different. In my code below I only consider the second level Bookmarks ("Outline") of my PDF.
using iText.Kernel.Pdf;
using iText.Kernel.Pdf.Navigation;
using System;
namespace PDFConvert
{
class Program
{
static void Main(string[] args)
{
String InputPdf = #"test.pdf";
String OutputPdf = "out.pdf";
PdfDocument pdfDoc = new PdfDocument(new PdfReader(InputPdf), new PdfWriter(OutputPdf));
PdfOutline outlines = pdfDoc.GetOutlines(false);
// first level
foreach (var outline in outlines.GetAllChildren())
{
// second level
foreach (var second in outline.GetAllChildren())
{
String title = second.GetTitle();
PdfDestination dest = second.GetDestination();
pdfDoc.AddNamedDestination(title, dest.GetPdfObject());
}
}
pdfDoc.Close();
}
}
}

How to create a copy of a PDF file in ASP.NET MVC

I'm reading a PDF file for writing a string on it like this :
public ActionResult Index(HttpPostedFileBase file)
{
byte[] pdfbytes = null;
BinaryReader rdr = new BinaryReader(file.InputStream);
pdfbytes = rdr.ReadBytes((int)file.ContentLength);
PdfReader myReader = new PdfReader(pdfbytes);
and I'm trying to pass a new file to FileStream like this :
FileStream fs = new FileStream(newFile, FileMode.Create, FileAccess.Write);
But I don't know how to pass the copied new file to fs object. Can you help me with that? Thanks.
If you have access to updated byte array pass it to File.WriteAllBytes. Or you might have an instance of PdfDocument or PdfWriter which usually allow saving the document to file on disk too. Hope it helps!
Here is example which is reading existing pdf file, copying it to new one and adding new string line:
using iTextSharp.text;
using iTextSharp.text.pdf;
using System.IO;
namespace ConsoleApp1
{
class Program
{
static void Main(string[] args)
{
string originalFile = "c:\\Users\\Admin\\Desktop\\receipt mod 3.pdf";
string copyOfOriginal = "c:\\Users\\Admin\\Desktop\\newFile.pdf";
using (var reader = new PdfReader(originalFile))
{
using (var fileStream = new FileStream(copyOfOriginal, FileMode.Create, FileAccess.Write))
{
var document = new Document(reader.GetPageSizeWithRotation(1));
var writer = PdfWriter.GetInstance(document, fileStream);
document.Open();
for (var i = 1; i <= reader.NumberOfPages; i++)
{
document.NewPage();
var baseFont = BaseFont.CreateFont(BaseFont.HELVETICA_BOLD, BaseFont.CP1252, BaseFont.NOT_EMBEDDED);
var importedPage = writer.GetImportedPage(reader, i);
var contentByte = writer.DirectContent;
contentByte.BeginText();
contentByte.SetFontAndSize(baseFont, 12);
var LineString = "Hello World!";
contentByte.ShowTextAligned(10,LineString,50,50,0);
contentByte.EndText();
contentByte.AddTemplate(importedPage, 0, 0);
}
document.Close();
writer.Close();
}
}
}
}
}
Try this.
This program copies all pdf files from one location to another.
protected void Button1_Click(object sender, EventArgs e)
{
string sourceDirectory = #"D:\project training\source";
string targetDirectory = #"D:\project training\destiny";
Copy(sourceDirectory, targetDirectory);
}
public static void Copy(string sourceDirectory, string targetDirectory)
{
DirectoryInfo diSource = new DirectoryInfo(sourceDirectory);
DirectoryInfo diTarget = new DirectoryInfo(targetDirectory);
CopyAll(diSource, diTarget);
}
public static void CopyAll(DirectoryInfo source, DirectoryInfo target)
{
Directory.CreateDirectory(target.FullName);
foreach (FileInfo fi in source.GetFiles())
{
if (fi.Extension.Equals(".pdf"))
{
fi.CopyTo(Path.Combine(target.FullName, fi.Name), true);
}
}
foreach (DirectoryInfo diSourceSubDir in source.GetDirectories())
{
DirectoryInfo nextTargetSubDir =
target.CreateSubdirectory(diSourceSubDir.Name);
CopyAll(diSourceSubDir, nextTargetSubDir);
}
}

itext sharp merge pdfs with acrofields - fields go missing when merging

I have tried this now and its not working. form.GenerateAppearances = true; I merge my 2 documents and then save it. Then I open it again to populate all the fields. It says all the Acrofields keys are gone but when I open it in Nitro pro its there. Why can't I see them in code? Do I have to add something before I save?
private static void CombineAndSavePdf1(string savePath, List<string> lstPdfFiles)
{
using (Stream outputPdfStream = new FileStream(savePath, FileMode.Create, FileAccess.Write, FileShare.None))
{
Document document = new Document();
PdfSmartCopy copy = new PdfSmartCopy(document, outputPdfStream);
document.Open();
PdfReader reader;
int totalPageCnt;
PdfStamper stamper;
string[] fieldNames;
foreach (string file in lstPdfFiles)
{
reader = new PdfReader(file);
totalPageCnt = reader.NumberOfPages;
for (int pageCnt = 0; pageCnt < totalPageCnt; )
{
//have to create new reader for each page or PdfStamper will throw error
reader = new PdfReader(file);
stamper = new PdfStamper(reader, outputPdfStream);
fieldNames = new string[stamper.AcroFields.Fields.Keys.Count];
stamper.AcroFields.Fields.Keys.CopyTo(fieldNames, 0);
foreach (string name in fieldNames)
{
stamper.AcroFields.RenameField(name, name);
}
copy.AddPage(copy.GetImportedPage(reader, ++pageCnt));
}
copy.FreeReader(reader);
}
}
}
You are merging the documents the wrong way. See MergeForms to find out how to do it correctly. The key line that is missing in your code, is:
copy.setMergeFields();
Without it, the fields disappear (as you have noticed).
There's also a MergeForms2 example that explains how to merge two identical forms. In this case, you need to rename the fields, because each field needs to have a unique name. I'm adding a reference to this second example, because I see that you also try renaming the fields. There is, however, a serious flaw in your code: you create a stamper object, but you never do stamper.close(). Your use of the reader object is also problematic. All in all, it would be best to throw away your code, and to start anew using the two examples from the official iText web site.
Update: I've added the tags itext and itextsharp to your question. Only then I noticed that you're using iTextSharp instead of iText. Porting the Java code to C# should be easy for a C# developer, but I've never written a C# program, so please use the JAVA examples as if you were using pseudo-code. The code in C# won't be that different.
public void manipulatePdf(String src, String dest) throws IOException, DocumentException {
Document document = new Document();
PdfCopy copy = new PdfSmartCopy(document, new FileOutputStream(dest));
copy.setMergeFields();
document.open();
List<PdfReader> readers = new ArrayList<PdfReader>();
for (int i = 0; i < 3; ) {
PdfReader reader = new PdfReader(renameFields(src, ++i));
readers.add(reader);
copy.addDocument(reader);
}
document.close();
for (PdfReader reader : readers) {
reader.close();
}
}
public byte[] renameFields(String src, int i) throws IOException, DocumentException {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
PdfReader reader = new PdfReader(src);
PdfStamper stamper = new PdfStamper(reader, baos);
AcroFields form = stamper.getAcroFields();
Set<String> keys = new HashSet<String>(form.getFields().keySet());
for (String key : keys) {
form.renameField(key, String.format("%s_%d", key, i));
}
stamper.close();
reader.close();
return baos.toByteArray();
}

How can i set an image to a pdf field in existing pdf file?

How can i set an image to a pdf field in existing pdf file?
I'm using the iTextSharp object.
Setting the text field is working fine. No problem in it.
pdfFormFields.SetField("Firstname", "Mujeeb");
Please help.
Remove the Text field and replace it with a Pushbutton field of the same size and position. If you set the Pushbutton to READ_ONLY then it can't be pressed and it will look like a static image. This keeps the image you're trying to add as a field annotation instead of adding it to the page content.
void ConvertTextFieldToImage(string inputFile, string fieldName, string imageFile, string outputFile)
{
using (PdfStamper stamper = new PdfStamper(new PdfReader(inputFile), File.Create(outputFile)))
{
AcroFields.FieldPosition fieldPosition = stamper.AcroFields.GetFieldPositions(fieldName)[0];
PushbuttonField imageField = new PushbuttonField(stamper.Writer, fieldPosition.position, fieldName);
imageField.Layout = PushbuttonField.LAYOUT_ICON_ONLY;
imageField.Image = iTextSharp.text.Image.GetInstance(imageFile);
imageField.ScaleIcon = PushbuttonField.SCALE_ICON_ALWAYS;
imageField.ProportionalIcon = false;
imageField.Options = BaseField.READ_ONLY;
stamper.AcroFields.RemoveField(fieldName);
stamper.AddAnnotation(imageField.Field, fieldPosition.page);
stamper.Close();
}
}
To the best of my knowledge you can't technically set a standard PDF field as an image (although you might be able to do this with XFA).
The workaround, however, is to just create a standard iTextSharp image and scale it to the form field's dimensions and place it where the field is.
Below is a full working C# 2010 WinForms app targeting iTextSharp 5.1.1.0 that shows how to do this. It starts by creating a very simple PDF with a single form field on it called "firstName". The second part of the program then gets the position and dimensions of that field and places an image there scaled appropriately. See the comments in the code for further details.
using System;
using System.ComponentModel;
using System.Text;
using System.Windows.Forms;
using System.IO;
using iTextSharp.text;
using iTextSharp.text.pdf;
namespace WindowsFormsApplication1
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
string baseFile = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Desktop), "StartFile.pdf");
string secondFile = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Desktop), "SecondFile.pdf");
string TestImage = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Desktop), "Test.jpg");
//Create a very simple PDF with a single form field called "firstName"
using (FileStream fs = new FileStream(baseFile, FileMode.Create, FileAccess.Write, FileShare.None))
{
using (Document doc = new Document(PageSize.LETTER))
{
using (PdfWriter writer = PdfWriter.GetInstance(doc, fs))
{
doc.Open();
writer.AddAnnotation(new TextField(writer, new iTextSharp.text.Rectangle(0, 0, 100, 100), "firstName").GetTextField());
doc.Close();
}
}
}
//Create a second file "filling out" the form above
using (FileStream fs = new FileStream(secondFile, FileMode.Create, FileAccess.Write, FileShare.None))
{
using (PdfStamper stamper = new PdfStamper(new PdfReader(baseFile), fs))
{
//GetFieldPositions returns an array of field positions if you are using 5.0 or greater
//This line does a lot and should really be broken up for null-checking
iTextSharp.text.Rectangle rect = stamper.AcroFields.GetFieldPositions("firstName")[0].position;
//Create an image
iTextSharp.text.Image img = iTextSharp.text.Image.GetInstance(TestImage);
//Scale it
img.ScaleAbsolute(rect.Width, rect.Height);
//Position it
img.SetAbsolutePosition(rect.Left, rect.Bottom);
//Add it to page 1 of the document
stamper.GetOverContent(1).AddImage(img);
stamper.Close();
}
}
this.Close();
}
}
}
This is the answer that works for placing an image in a specific location. `
using (PdfStamper stamper = new PdfStamper(new PdfReader(fromFilePath), File.Create("toFilePath")))
{
AcroFields.FieldPosition fieldPosition = stamper.AcroFields.GetFieldPositions("btn1")[0];
PushbuttonField imageField = new PushbuttonField(stamper.Writer, fieldPosition.position, "btn1Replaced");
imageField.Layout = PushbuttonField.LAYOUT_ICON_ONLY;
imageField.Image = iTextSharp.text.Image.GetInstance(ImageLocationPath);
imageField.ScaleIcon = PushbuttonField.SCALE_ICON_ALWAYS;
imageField.ProportionalIcon = false;
imageField.Options = BaseField.READ_ONLY;
stamper.AcroFields.RemoveField("btn1");
stamper.AddAnnotation(imageField.Field, fieldPosition.page);
stamper.Close();
}

Using PdfStamper to add a rectangle

Hi I have a pdf I created using itextsharp.
Using pdfreader I am reading the created pdf into a pdfstamper.
Now I am trying to use the pdfstamper to add a black rectangle the size of the page on all pages. How do i do this?
Also I cannot use document to add the rectangle because the stream is close!
MemoryStream stream = new MemoryStream();
PdfReader pdfReader = new PdfReader(output.ToArray());
PdfStamper stamper = new PdfStamper(pdfReader, stream);
for (int x = 0; x < stamper.Reader.NumberOfPages; x++)
{
Rectangle rectangle = document.PageSize;
rectangle.BackgroundColor = new BaseColor(0, 0, 0);
//stamper.Writer.AcroForm.
//document.Add(rectangle);
}
output.Close();
pdfReader.Close();
stamper.Close();
If you want to draw using the PdfStamper then you need to use the PdfContentByte which you can get by calling stamper.GetOverContent(pageNum). There's a specific command on that object called Rectangle which does exactly what you want it to do. Also, remember that pages within a PDF start numbering at one and not zero.
Below is a full working C# 2010 WinForm app targeting iTextSharp 5.1.1.0 that should do what you're looking for, I think. You'll need to modify it to support the MemoryStream but that should be pretty easy.
using System;
using System.Text;
using System.Windows.Forms;
using System.IO;
using iTextSharp.text;
using iTextSharp.text.pdf;
namespace WindowsFormsApplication1
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
string inputFile = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Desktop), "input.pdf");
string outputFile = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Desktop), "output.pdf");
PdfReader pdfReader = new PdfReader(inputFile);
using (FileStream fs = new FileStream(outputFile, FileMode.Create, FileAccess.Write, FileShare.None))
{
using (PdfStamper stamper = new PdfStamper(pdfReader, fs))
{
int PageCount = pdfReader.NumberOfPages;
for (int x = 1; x <= PageCount; x++)
{
PdfContentByte cb = stamper.GetOverContent(x);
iTextSharp.text.Rectangle rectangle = pdfReader.GetPageSizeWithRotation(x);
rectangle.BackgroundColor = BaseColor.BLACK;
cb.Rectangle(rectangle);
}
}
}
this.Close();
}
}
}

Categories