iText C#: Remove creation date - c#

Is it possible to modify/remove the creation date in metadata? I'm looking to do something similar to this:
Overwrite creationDate in pdf using iText and pdf writer
EDIT:
I have tried the following methods:
writer.Info.Remove(PdfName.CREATIONDATE);
or
writer.Info.Put(PdfName.CREATIONDATE, new PdfDate(new DateTime(2017, 01, 01)));
where writer is a PdfWriter object.
However, that creates a copy of the object (a PdfDictionary) and doesn't modify the PDF I'm creating.
I also can't assign i.e. writer.Info = info
I tried following the advice given in the Java article.
I tried to do this:
var info = writer.Info;
stamper.MoreInfo = info
where stamper is a PdfStamper
But the types are incompatible and I don't think this would work. Does anyone know the actual methods to remove/modify the metadata?
EDIT 2:
Here is the code, I'm creating a new file from an existing PDF.
var filename = #"C:\Users\Someone\Documents\aPdf.pdf";
using( var output = new MemoryStream() )
{
Document document = new Document();
PdfCopy writer = new PdfCopy( document, output );
writer.CloseStream = false;
document.Open();
//read in PDF
PdfReader reader = new PdfReader(filename);
reader.ConsolidateNamedDestinations();
PdfImportedPage page = writer.GetImportedPage(reader, 1);
writer.AddPage(page);
reader.Close();
writer.Close();
document.Close();
return output.ToArray();
}
Now, when I open the file with a text editor this line is inserted (I need it constant/gone):
<</Producer(iTextSharp’ 5.5.12 ©2000-2017 iText Group NV \(AGPL-version\))/CreationDate(D:20180412155130+01'00')/ModDate(D:20180412155130+01'00')>>
The reason why we need to remove/set the date is that we're taking the MD5 hash of the file. Every time a new document is generated, that line changes leading to different MD5 hashes.

As I was trying to get a constant MD5 checksum for the generated file, I had to also set the ID constant, as mentioned by mkl.
My solution was to search byte array produced (i.e. the created PDF), and manually set the values to constants. The text is ASCII chars. I removed the /CreationDate and /ModifiedDated from the PDF entirely, and set the generated ID to a constant arbitrary value.

Related

Xamarin.Forms itext7 - There is no associate PdfWriter for making indirects

I am using itext7 to get the Stream of the file and fill in the form fields.
Here is my code:
using (MemoryStream outFile = new MemoryStream())
{
var streamFile = await App.GraphClient.Me.Drive.Items[item.Id].Content.Request().GetAsync();
using (PdfDocument pdf = new PdfDocument(new PdfReader(streamFile)))
{
PdfAcroForm form = PdfAcroForm.GetAcroForm(pdf, true);
IDictionary<String, PdfFormField> fields = form.GetFormFields();
PdfFormField toSet;
fields.TryGetValue("Full_Names", out toSet);
toSet.SetValue(Customer_Name.Text);
form.FlattenFields();
pdf.Close();
}
}
But I get an error on this line: toSet.SetValue(Customer_Name.Text); when trying to set the value, the error is:
There is no associate PdfWriter for making indirects.
My question is what am I doing wrong and how do I fix this? I am getting the file from OneDrive using Microsoft Graph API. The IDictionary of fields is getting populated and the name of field I am trying to set the value for is accurate.
The problem with your code is that you have dedicated outFile stream for the output result (the document with flattened fields) but you are not telling iText anything about that desired output destination.
PdfDocument has several constructors and the one you are using is PdfDocument(PdfReader) and it is dedicated for reading the document, not changing it (and setting a value is considered changing the document of course). So you should use PdfDocument(PdfReader, PdfWriter) constructor to provide the source document and the target destination to iText.
You should create your PdfDocument as follows:
PdfDocument pdf = new PdfDocument(new PdfReader(streamFile), new PdfWriter(outFile))
At the end of the execution, your outFile stream will contain the bytes of the resultant document.

Insert text and then merge pdf

I'm using IText7 version 7.0.2.2, I'm new with it, I'm trying to merge several pdfs at the same time into one that I'm uploading first, that is working fine, the problem is when I try dynamically to insert some text in one of the pdfs and then merge it, I'm using PdfWriter to write some content into the pdf and then try to merge it, but I'm getting this exception: 'Cannot copy indirect object from the document that is being written.
This is some of the code I'm using:
private byte[] MergePdfForms( HttpPostedFileBase firstPdf, List<SectionAndPdfs> sectionsAndPdf)
{
var dest = new MemoryStream();
PdfDocument pdf = new PdfDocument(new PdfWriter(dest));
PdfMerger merger = new PdfMerger(pdf);
firstSourcePdf = new PdfDocument(new PdfReader(keyValuePair.Value), new PdfWriter(dest));
Document document = new Document(firstSourcePdf);
document.Add(new Paragraph(sectionsAndPdf[i].Key).SetBackgroundColor(iText.Kernel.Colors.Color.GRAY));
merger.Merge(firstSourcePdf, 1, subPages); //I'm getting the exception here..
firstSourcePdf.Close();
}
This is a known bug in the class PdfDestination. It was fixed, and will be present in our next release. At the moment you can of course use the snapshot release, which should solve the problem.

Why is my copied PDF file sized incorrect?

I need to remove the first few pages of a PDF file. Apparently, the easiest way to do that is to create a copy of it and not duplicate the unwanted pages. This works, but they look a lot smaller than they should. Any ideas?
How it should look
How it actually looks
private static void ClipSpecificPDF(string input, string output, int pagesToCut)
{
PdfReader myReader = new PdfReader(input);
using (FileStream fs = new FileStream(output, FileMode.Create, FileAccess.Write, FileShare.None))
{
using (Document doc = new Document())
{
using (PdfWriter myWriter = PdfWriter.GetInstance(doc, fs))
{
//Open the desitination for writing
doc.Open();
//Loop through each page that we want to keep
for (int i = pagesToCut; i < myReader.NumberOfPages; i++)
{
//Add a new blank page to destination document
var PS = myReader.GetPageSizeWithRotation(i);
myWriter.SetPageSize(PS);
doc.NewPage();
//Extract the given page from our reader and add it directly to the destination PDF
myWriter.DirectContent.AddTemplate(myWriter.GetImportedPage(myReader, i + 1), 0, 0);
}
//Close our document
doc.Close();
}
}
}
}
The problem you describe is explained in the FAQ. For instance in the answer to the questions:
How to merge documents correctly?
Why does the function to concatenate / merge PDFs cause issues in some cases?
Using PdfWriter to manipulate PDF documents is a very bad idea. Read chapter 6 of my book to discover why this is a bad idea, and take a look at Table 6.1 to find out which class is a better fit.
In the same chapter, you'll find the SelectPages example. Suppose that you want to create a new PDF containing only page 4 to 8. In that case, you simply use the SelectPages() method and PdfStamper:
PdfReader reader = new PdfReader(src);
reader.SelectPages("4-8");
PdfStamper stamper = new PdfStamper(reader, new FileStream(dest, FileMode.Create, FileAccess.Write));
stamper.Close();
reader.Close();
By using PdfReader, the page size is preserved, as well as any of the interactive features that may be present.
Your approach is bad because you do not respect the original page size: you copy a document with letter (?) format to a document with A4 pages. If the origin of the page doesn't correspond with the lower-left corner, parts of your document will be invisible. If there are interactive features in your PDF, they will be lost. Of all the possible examples you could have followed, you picked the worst one...

Flatten dynamic pdf using iTextSharp

I' m using itextsharp to create pdf from template pdf file. But new pdf file is created like dynamic pdf. I want to convert this file to static pdf file, so i tried to use xfaworker. I get "Signature was corrupted" error from itextsharp.licensekey.dll. How can i use xfaworker or another dll for flatten dynamic pdf?
public string Create(FaxPDFModel model, MemoryStream ms)
{
string codeBase = Assembly.GetExecutingAssembly().CodeBase;
UriBuilder uri = new UriBuilder(codeBase);
string templatePath = Path.GetDirectoryName(Uri.UnescapeDataString(uri.Path));
PdfReader pdfTemplate = new PdfReader(Path.Combine(Path.Combine(templatePath, "Docs"), "fax_template.pdf"));
PdfStamper stamper = new PdfStamper(pdfTemplate, ms);
stamper.Writer.CloseStream = false;
BaseFont bf = BaseFont.CreateFont(BaseFont.TIMES_ROMAN, "ISO-8859-9", BaseFont.EMBEDDED);
var acroFields = stamper.AcroFields;
acroFields.GenerateAppearances = true;
stamper.FormFlattening = true;
acroFields.AddSubstitutionFont(bf);
acroFields.SetField("Name", "Mutabakat test");
acroFields.SetField("Title", "DANIŞMANLIK");
acroFields.SetField("Department", "test");
acroFields.SetField("Phone", "0 (212) 555 55 55");
stamper.Close();
string path = Path.Combine(Path.Combine(templatePath, "Docs"), System.Guid.NewGuid().ToString());
string pdfPath = path + ".pdf";
Document document = new Document();
PdfWriter writer = PdfWriter.GetInstance(document, new FileStream(pdfPath, FileMode.Create));
XFAFlattener xfaf = new XFAFlattener(document, writer);
ms.Position = 0;
xfaf.Flatten(new PdfReader(ms));
document.Close();
return pdfPath;
}
If you want to solve your problem, you will have to start by fixing the following errors:
1. You are using code to fill AcroForms instead of code to fill XFA forms:
If you have a dynamic XFA form, your PDF acts as a container for XML. This form doesn't expect data in the form of key value pairs. This form expects data stored as XML.
You can not use this code:
var acroFields = stamper.AcroFields;
acroFields.AddSubstitutionFont(bf);
acroFields.SetField("Name", "Mutabakat test");
acroFields.SetField("Title", "DANIŞMANLIK");
acroFields.SetField("Department", "test");
acroFields.SetField("Phone", "0 (212) 555 55 55");
This code expects that your form is an AcroForm. You need to fill the form like this:
AcroFields form = stamper.AcroFields;
XfaForm xfa = form.Xfa;
xfa.FillXfaForm(new FileStream(xml, FileMode.Open));
In this snippet xml refers to the data stored as XML.
2. You try to flatten the form before flattening the form:
I see this line in the first part of your code:
stamper.FormFlattening = true;
With this line, you remove all interactivity from your PDF. After closing the stamper object, you no longer have a form, hence the second part of your code will never work.
3. You are trying to embed a Standard Type 1 font:
This line doesn't make sense:
BaseFont bf = BaseFont.CreateFont(BaseFont.TIMES_ROMAN, "ISO-8859-9", BaseFont.EMBEDDED);
Times-Roman is a Standard Type 1 font (in the old days, we called this a Base14 font); iText never embeds any of the 14 Standard Type 1 fonts defined for PDF, hence the parameter BaseFont.EMBEDDED will be ignored.
4. The real error:
XFA Worker is a closed source addon for iTextSharp. It requires a valid license key. When you get an error saying "Signature was corrupted", you are using a license key that is corrupt.
Possible causes:
You are not a customer of iText Group. You are using a key that you found somewhere and you changed some of its contents.
You are a customer of iText Group. You received a key, but somehow it got tampered with. In the past, we've had a similar problem where a customer was reading the key as if it was encoded in EBCDIC. Please contact your account manager at iText Group for more info.

How to create multiple copies documents with iText

I'm using iText to generate a PDF document that consists of several copies of almost the same information.
E.g.: An invoice. One copy is given to the customer, another is filed and a third one is given to an accountant for book-keeping.
All the copies must be exactly the same except for a little piece of text that indicates who is the copy to (Customer, Accounting, File, ...).
There are two possible scenarios (I don't know if the solution is the same for both of them):
a) Each copy goes in a different page.
b) All the copies goes in the same page (the paper will have cutting holes to separete copies).
There will be a wrapper or helper class which uses iText to generate the PDF in order to be able to do something like var pdf = HelperClass.CreateDocument(DocuemntInfo info);. The multiple-copies problem will be solved inside this wrapper/helper.
What does iText provides to accomplish this? Do I need to write each element in the document several times in different positions/pages? Or does iText provide some way to write one copy to the document and then copy it to other position/page?
Note: It's a .Net project, but I tagged the question with both java and c# because this qustion is about how to use iText properly the answer will help both laguage developers.
If each copy goes on a different page, you can create a new document and copy in the page multiple times. Using iText in Java you can do it like this:
// Create output PDF
Document document = new Document(PageSize.A4);
PdfWriter writer = PdfWriter.getInstance(document, outputStream);
document.open();
PdfContentByte cb = writer.getDirectContent();
// Load existing PDF
PdfReader reader = new PdfReader(templateInputStream);
PdfImportedPage page = writer.getImportedPage(reader, 1);
// Copy first page of existing PDF into output PDF
document.newPage();
cb.addTemplate(page, 0, 0);
// Add your first piece of text here
document.add(new Paragraph("Customer"));
// Copy second page of existing PDF into output PDF
document.newPage();
cb.addTemplate(page, 0, 0);
// Add your second piece of text here
document.add(new Paragraph("Accounting"));
// etc...
document.close();
If you want to put all the copies on the same page, the code is similar but instead of using zeroes in addTemplate(page, 0, 0) you'll need to set values for the correct position; the numbers to use depend on the size and shape of your invoice.
See also iText - add content to existing PDF file — the above code is based on the code I wrote in that answer.
Here's how I see this working.
PdfReader reader = new PdfReader( templatePDFPath );
Document doc = new Document();
PdfWriter writer = PdfWriter.createInstance( doc, new FileOutputStream("blah.pdf" ) );
PdfImportedPage inputPage = writer.getImportedPage( reader, 1 );
PdfDirectContent curPageContent = writer.getDirectContent();
String extraStuff[] = getExtraStuff();
for (String stuff : extraStuff) {
curPageContent.saveState();
curPageContent.addTemplate( inputPage /*, x, y*/ );
curPageContent.restoreState();
curPageContent.beginText();
curPageContent.setTextMatrix(x, y);
curPageContent.setFontAndSize( someFont, someSize );
// the actual work:
curPageContent.showText( stuff );
curPageContent.EndText();
// save the contents of curPageContent out to the file and reset it for the next page.
doc.newPage();
}
That's the bare minimum of work on the computer's part. Quite Efficient, and it'll result in a smaller PDF. Rather than having N copies of that page, with tweaks, you have one copy of that page that's reused on N pages, with little tweaks on top.
You could do the same thing, and use the "x,y" parameters in addTemplate to draw them all on the same page. Up to you.
PS: you'll need to figure out the coordinates for setTextMatrix in advance.
You could also use PDfCopy Or PDfSmartCopy to do this.
PdfReader reader = new PdfReader("Path\To\File");
Document doc = new Document();
PdfCopy copier = new PdfCopy(doc, ms1);
//PdfSmartCopy copier = new PdfSmartCopy(doc, ms1);
doc.Open();
copier.CloseStream = false;
PdfImportedPage inputPage = writer.GetImportedPage(reader, 1);
PdfContentByte curPageContent = writer.DirectContent;
for (int i = 0; i < count; i++)
{
copier.AddPage(inputPage);
}
doc.Close();
ms1.Flush();
ms1.Position = 0;
The difference between PdfCopy and PdfSmartCopy is that PdfCopy copies the entire PDF for each page, while PdfSmartCopy outputs a PDF that internally contains only one copy and all pages reference it, resulting in a smaller file and less bandwidth on a network, however it uses more memory on the server and takes longer to process.

Categories

Resources