iTextSharp Signature Image not showing in PDF Signature Field - itext

I have researched this issue for most of the day. I have an existing application that uses iTextSharp to sign PDF documents. The digital signature exists and the text representation shows within the Signature Field on the PDF but the Image of the signature does not show.
The following is the code in question. I appreciate any help/advice you can offer.
if (signatureBitmap != null)
{
if (reader == null) reader = new PdfReader(PDFByteArray);
pdfStamper = PdfStamper.CreateSignature(reader, outputStream, PDFVersion, null, true);
PdfSignatureAppearance signatureAppearance = pdfStamper.SignatureAppearance;
//Add digital certificate to field
signatureAppearance.SetCrypto(Key, Certificate, null, PdfSignatureAppearance.SELF_SIGNED);
signatureAppearance.Reason = Reason;
signatureAppearance.Location = Location;
signatureAppearance.Render = PdfSignatureAppearance.SignatureRender.GraphicAndDescription;
if (signatureBitmap != null)
{
iTextSharp.text.Image signatureFieldImage = iTextSharp.text.Image.GetInstance(signatureBitmap, System.Drawing.Imaging.ImageFormat.Bmp);
signatureAppearance.SignatureGraphic = signatureFieldImage;
}
signatureAppearance.Acro6Layers = acroLayer;
signatureAppearance.SetVisibleSignature(acrobatSignatureFieldName);
pdfStamper.Close();
reader.Close();
reader = null;
}

you need the position about your signature, try with this:
Rectangle rSignature= new Rectangle(100,100);
signatureAppearance.setVisibleSignature(rSignature, 1, "a name");

Related

Field validation in editable pdf while filling data

I have editable pdf with fields name, roll number , DOB and city, I want to validate the fields like, for example when I enter the value in roll number field, if name is empty then it should alert "name is empty" and similar to roll number and DOB and city.
DOB - Need to validate mm/dd/yyyy format.
I am able to get fields and values using below iTextSharp code, but how can validate and and show alert to user.
iTextSharp.text.pdf.PdfReader reader = new iTextSharp.text.pdf.PdfReader(filename);
using (MemoryStream outputStream = new MemoryStream())
{
//PdfStamper stamper = new PdfStamper(reader, );
using (PdfStamper stamper = new PdfStamper(reader, outputStream, '\0', true))
{
PdfWriter writer = stamper.Writer;
AcroFields acroFields = reader.AcroFields;
AcroFields.Item dateField = acroFields.GetFieldItem("D");
iTextSharp.text.pdf.PdfAction pdfAction = iTextSharp.text.pdf.PdfAction.JavaScript("app.alert('hello');", writer);
iTextSharp.text.pdf.PdfDictionary widgetRefDict = (iTextSharp.text.pdf.PdfDictionary)iTextSharp.text.pdf.PdfReader.GetPdfObject(dateField.GetWidgetRef(0));
iTextSharp.text.pdf.PdfDictionary actionDict = widgetRefDict.GetAsDict(iTextSharp.text.pdf.PdfName.AA);
if (actionDict == null)
{
actionDict = new iTextSharp.text.pdf.PdfDictionary();
// add the newly created action dict
widgetRefDict.Put(iTextSharp.text.pdf.PdfName.AA, actionDict);
}
actionDict.Put(iTextSharp.text.pdf.PdfName.V, pdfAction);
stamper.Close();
reader.Close();
}
byte[] content = outputStream.ToArray();
// Write out PDF from memory stream.
using (FileStream fs = File.Create("d:\\Page1-output.pdf"))
{
fs.Write(content, 0, (int)content.Length);
}
}

How to place the Same Digital signatures to Multiple places in PDF using itextsharp.net

I have implemented Digital Signature using iTextSharp Dll to sign PDF files with a single signature creating empty signature fields and update the signature field with signed hash working fine. Now, I want to place the same digital signature in every page of pdf. It's my client requirement.
I’m using the following code:
public class MyExternalSignatureContainer : IExternalSignatureContainer
{
private readonly byte[] signedBytes;
public MyExternalSignatureContainer(byte[] signedBytes)
{
this.signedBytes = signedBytes;
}
public byte[] Sign(Stream data)
{
return signedBytes;
}
public void ModifySigningDictionary(PdfDictionary signDic)
{
}
}
Below code used in program
PdfReader reader = new PdfReader(unsignedPdf);
FileStream os = File.OpenWrite(tempPdf);
PdfStamper stamper = PdfStamper.CreateSignature(reader, os, '\0');
PdfSignatureAppearance appearance = stamper.SignatureAppearance;
appearance.Reason = "Reason1";
appearance.Contact = "";
appearance.Location = "Location1";
appearance.Acro6Layers = false;
appearance.Image = null;
appearance.SignatureRenderingMode = PdfSignatureAppearance.RenderingMode.DESCRIPTION;
appearance.SetVisibleSignature(new iTextSharp.text.Rectangle(36, 748, 144, 780), 1, null);
for (int i = 1; i < 8; i++)
{
var signatureField = PdfFormField.CreateSignature(stamper.Writer);
var signatureRect = new Rectangle(200, 200, 100, 100);
signatureField.Put(PdfName.T, new PdfString("ClientSignature_"+i.ToString()));
PdfIndirectReference PRef = stamper.Writer.PdfIndirectReference;
signatureField.Put(PdfName.V, PRef);
signatureField.Put(PdfName.F, new PdfNumber("132"));
signatureField.SetWidget(signatureRect, null);
signatureField.Put(PdfName.SUBTYPE, PdfName.WIDGET);
PdfDictionary xobject1 = new PdfDictionary();
PdfDictionary xobject2 = new PdfDictionary();
xobject1.Put(PdfName.N, appearance.GetAppearance().IndirectReference);
xobject2.Put(PdfName.AP, xobject1);
signatureField.Put(PdfName.AP, xobject1);
signatureField.SetPage();
PdfDictionary xobject3 = new PdfDictionary();
PdfDictionary xobject4 = new PdfDictionary();
xobject4.Put(PdfName.FRM, appearance.GetAppearance().IndirectReference);
xobject3.Put(PdfName.XOBJECT, xobject4);
signatureField.Put(PdfName.DR, xobject3);
stamper.AddAnnotation(signatureField, i);
}
IExternalSignatureContainer external = new ExternalBlankSignatureContainer(PdfName.ADOBE_PPKMS, PdfName.ADBE_PKCS7_DETACHED);
MakeSignature.SignExternalContainer(appearance, external, 8192);
stamper.Close();
byte[] SignedHash = DoEsign(SHA256Managed.Create().ComputeHash(appearance.GetRangeStream());
os.close();
reader.close();
reader = new PdfReader(tempPdf))
os = File.OpenWrite(signedPdf)
IExternalSignatureContainer external1 = new MyExternalSignatureContainer(SignedHash);
MakeSignature.SignDeferred(reader, signatureFieldName, os, external1);
os.close();
reader.close();
Please suggest me to complete the task
To give all signature fields the same single value wrapping the newly created signature container, they must all reference the same indirect object as value. Unfortunately iText creates the indirect object for the signature value only after the application code had the chance to add its additional fields which in turn require a reference to that signature value object. Thus, the application code has to anticipate the object number that indirect object will have.
This anticipation or prediction of the object number is very delicate, it depends on the exact same use case and can also become incorrect as the result of minor changes in the iTextSharp library
To make this easier, the application code should add those signature fields with their signature value references as late as possible, so there are as few other new indirect objects created as possible until iText creates the value indirect object.
As it turns out, the ModifySigningDictionary method of an IExternalSignatureContainer is a good position for that.
As soon as one adds one's code there, another issue pops up: There is no means to set the anticipated object number in a PdfIndirectReference instance externally. One way to get around this is to mimic such a reference using a PdfLiteral. (Well, probably one could also use reflection for this.)
Furthermore it turns out that one best creates the appearance streams to use by all one's additional signature fields before building that PdfLiteral mimicking a PdfIndirectReference as this simplifies the calculation of the object number iText will use for the actual value object.
With this in mind, here a proof-of concept. This proof of concept makes use of an IExternalSignature instance for actually signing. This is not a necessary precondition, one can also use an IExternalSignatureContainer instead with only a few changes, even an ExternalBlankSignatureContainer as in the question to later finalize the signature using MakeSignature.SignDeferred.
So given cipher parameters cp (private key material, e.g. pk.Key for an Org.BouncyCastle.Pkcs.AsymmetricKeyEntry pk) and a certificate chain chain, one would use
PdfReader reader = new PdfReader(SRC);
FileStream os = new FileStream(DEST, FileMode.Create, FileAccess.Write);
PdfStamper stamper = PdfStamper.CreateSignature(reader, os, '\0');
PdfSignatureAppearance appearance = stamper.SignatureAppearance;
appearance.Reason = "Reason1";
appearance.Contact = "";
appearance.Location = "Location1";
appearance.Acro6Layers = false;
appearance.Image = null;
appearance.SignatureRenderingMode = PdfSignatureAppearance.RenderingMode.DESCRIPTION;
appearance.SetVisibleSignature(new iTextSharp.text.Rectangle(10, 10, 100, 100), reader.NumberOfPages, null);
IExternalSignature externalSignature = new PrivateKeySignature(cp, "SHA-256");
AllPagesSignatureContainer allPagesContainer = new AllPagesSignatureContainer(appearance, externalSignature, chain);
MakeSignature.SignExternalContainer(appearance, allPagesContainer, 8192);
with this external signature container class
public class AllPagesSignatureContainer : IExternalSignatureContainer
{
public AllPagesSignatureContainer(PdfSignatureAppearance appearance, IExternalSignature externalSignature, ICollection<X509Certificate> chain)
{
this.appearance = appearance;
this.chain = chain;
this.externalSignature = externalSignature;
}
public void ModifySigningDictionary(PdfDictionary signDic)
{
signDic.Put(PdfName.FILTER, PdfName.ADOBE_PPKMS);
signDic.Put(PdfName.SUBFILTER, PdfName.ADBE_PKCS7_DETACHED);
PdfStamper stamper = appearance.Stamper;
PdfReader reader = stamper.Reader;
PdfDictionary xobject1 = new PdfDictionary();
PdfDictionary xobject2 = new PdfDictionary();
xobject1.Put(PdfName.N, appearance.GetAppearance().IndirectReference);
xobject2.Put(PdfName.AP, xobject1);
PdfIndirectReference PRef = stamper.Writer.PdfIndirectReference;
PdfLiteral PRefLiteral = new PdfLiteral((PRef.Number + 1 + 2*(reader.NumberOfPages - 1)) + " 0 R");
for (int i = 1; i < reader.NumberOfPages; i++)
{
var signatureField = PdfFormField.CreateSignature(stamper.Writer);
signatureField.Put(PdfName.T, new PdfString("ClientSignature_" + i.ToString()));
signatureField.Put(PdfName.V, PRefLiteral);
signatureField.Put(PdfName.F, new PdfNumber("132"));
signatureField.SetWidget(appearance.Rect, null);
signatureField.Put(PdfName.SUBTYPE, PdfName.WIDGET);
signatureField.Put(PdfName.AP, xobject1);
signatureField.SetPage();
Console.WriteLine(signatureField);
stamper.AddAnnotation(signatureField, i);
}
}
public byte[] Sign(Stream data)
{
String hashAlgorithm = externalSignature.GetHashAlgorithm();
PdfPKCS7 sgn = new PdfPKCS7(null, chain, hashAlgorithm, false);
IDigest messageDigest = DigestUtilities.GetDigest(hashAlgorithm);
byte[] hash = DigestAlgorithms.Digest(data, hashAlgorithm);
byte[] sh = sgn.getAuthenticatedAttributeBytes(hash, null, null, CryptoStandard.CMS);
byte[] extSignature = externalSignature.Sign(sh);
sgn.SetExternalDigest(extSignature, null, externalSignature.GetEncryptionAlgorithm());
return sgn.GetEncodedPKCS7(hash, null, null, null, CryptoStandard.CMS);
}
PdfSignatureAppearance appearance;
ICollection<X509Certificate> chain;
IExternalSignature externalSignature;
}
The predicted indirect object number of the signature value in the line
PdfIndirectReference PRef = stamper.Writer.PdfIndirectReference;
PdfLiteral PRefLiteral = new PdfLiteral((PRef.Number + 1 + 2*(reader.NumberOfPages - 1)) + " 0 R");
strictly depends upon the use case being "exactly one signature field per page". For different use cases the estimate the prediction would differ.
I stress this here once again because e.g. the OP of this question did not take this into account when trying "to place multiple signatures on single page".
Another strict requirement for the object number prediction above is that the PdfStamper is created as above, i.e. not in append mode. If the signature is applied as an incremental update, i.e. in append mode, the lines above have to be replaced by
stamper.Writer.AddToBody(new PdfNull(), stamper.Writer.PdfIndirectReference, true);
PdfIndirectReference PRef = stamper.Writer.PdfIndirectReference;
PdfLiteral PRefLiteral = new PdfLiteral((PRef.Number + reader.NumberOfPages) + " 0 R");
This made a difference in the context of this question; the first line, adding an indirect null object to the PDF, is necessary to make sure that in case of PDFs with object streams the object stream object number has already been determined and does not slip between the next objects, resulting in an off-by-one error for our prediction.
Beware: While this procedure creates something which does not violate the letter of the PDF specifications (which only forbid the cases where the same field object is referenced from multiple pages, be it via the same or via distinct widgets), it clearly does violate its intent, its spirit. Thus, this procedure might also become forbidden as part of a Corrigenda document for the specification.

how to change code so it works with itext 7

The following code works nicely with iText 5:
LEVEL = PdfAConformanceLevel.PDF_A_3A
writer = PdfAWriter.getInstance(document, baos, LEVEL)
What is the correct syntax for iText 7?
Where can I find examples and documentation about iText 7?
why so negative? Seems like you want to put extra pressure on me to write documentation. That's kind of frustrating. I think that encouragement works better. Nevertheless, your claim that there's no documentation is wrong.
See Chapter 7: Creating PDF/UA and PDF/A documents of the iText 7: Jump-Start Tutorial
You overlooked the UnitedStates_PDFA_3a example in which we have a CSV file that we convert to a PDF/A-3 document adding that CSV file as attachment.
public void createPdf(String dest) throws IOException, XMPException {
PdfADocument pdf = new PdfADocument(new PdfWriter(dest),
PdfAConformanceLevel.PDF_A_3A,
new PdfOutputIntent("Custom", "", "http://www.color.org",
"sRGB IEC61966-2.1", new FileInputStream(INTENT)));
Document document = new Document(pdf, PageSize.A4.rotate());
document.setMargins(20, 20, 20, 20);
//Setting some required parameters
pdf.setTagged();
pdf.getCatalog().setLang(new PdfString("en-US"));
pdf.getCatalog().setViewerPreferences(
new PdfViewerPreferences().setDisplayDocTitle(true));
PdfDocumentInfo info = pdf.getDocumentInfo();
info.setTitle("iText7 PDF/A-3 example");
//Add attachment
PdfDictionary parameters = new PdfDictionary();
parameters.put(PdfName.ModDate, new PdfDate().getPdfObject());
PdfFileSpec fileSpec = PdfFileSpec.createEmbeddedFileSpec(
pdf, Files.readAllBytes(Paths.get(DATA)), "united_states.csv",
"united_states.csv", new PdfName("text/csv"), parameters,
PdfName.Data, false);
fileSpec.put(new PdfName("AFRelationship"), new PdfName("Data"));
pdf.addFileAttachment("united_states.csv", fileSpec);
PdfArray array = new PdfArray();
array.add(fileSpec.getPdfObject().getIndirectReference());
pdf.getCatalog().put(new PdfName("AF"), array);
//Embed fonts
PdfFont font = PdfFontFactory.createFont(FONT, true);
PdfFont bold = PdfFontFactory.createFont(BOLD_FONT, true);
// Create content
Table table = new Table(new float[]{4, 1, 3, 4, 3, 3, 3, 3, 1});
table.setWidthPercent(100);
BufferedReader br = new BufferedReader(new FileReader(DATA));
String line = br.readLine();
process(table, line, bold, true);
while ((line = br.readLine()) != null) {
process(table, line, font, false);
}
br.close();
document.add(table);
//Close document
document.close();
}
public void process(Table table, String line, PdfFont font, boolean isHeader) {
StringTokenizer tokenizer = new StringTokenizer(line, ";");
while (tokenizer.hasMoreTokens()) {
if (isHeader) {
table.addHeaderCell(new Cell().setHorizontalAlignment(HorizontalAlignment.CENTER).add(new Paragraph(tokenizer.nextToken()).setHorizontalAlignment(HorizontalAlignment.CENTER).setFont(font)));
} else {
table.addCell(new Cell().setHorizontalAlignment(HorizontalAlignment.CENTER).add(new Paragraph(tokenizer.nextToken()).setHorizontalAlignment(HorizontalAlignment.CENTER).setFont(font)));
}
}
}

LibTiff.net isn't setting the Subject tag

I'm trying to mimic some production code to generate Tiffs with a subject for testing purposes (IE in windows, right click, go to properties and the details tab there is a subject). We place some text we need to reference later in the subject field. The field we use is 0x9c9f which as far as I can find is (Subject tag used by Windows, encoded in UCS2)
Here's the code I'm using to generate the tag
public static void TagExtender(Tiff tif)
{
TiffFieldInfo[] tiffFieldInfo =
{
new TiffFieldInfo(TIFFTAG_SUBJECT, 256, 256, TiffType.BYTE, FieldBit.Custom, true, false, "XPSubject"),
};
tif.MergeFieldInfo(tiffFieldInfo, tiffFieldInfo.Length);
//if (m_parentExtender != null)
// m_parentExtender(tif);
}
public static void GenerateTiff(string filename, int pages = 1, bool encrypt = false, string tag = null)
{
// Register the custom tag handler
if (m_parentExtender == null)
{
Tiff.TiffExtendProc extender = TagExtender;
m_parentExtender = Tiff.SetTagExtender(extender);
}
// Open the output image
using (Tiff output = Tiff.Open(filename, "w"))
{
//...other code to generate tiff
if (tag != null)
{
byte[] bytes = UnicodeStr2HexStr(tag);
output.SetField(TIFFTAG_SUBJECT, bytes.Length-1, bytes);
}
// Code to actually write the image ....
output.WriteDirectory();
}
output.Close();
}
Basically, the tag (code wise) appears to be in the tiff but the windows properties dialog never shows it. Anything special needed to get this in place?
You are passing a bytecount, but set the passCount flag to false.
If you want to pass the count, use these lines at their correct positions:
// Your FieldInfo
new TiffFieldInfo((TiffTag)40095, -1, -1, TiffType.BYTE, FieldBit.Custom, true, true, "XPSubject")
// Your Input
byte[] test = Encoding.Unicode.GetBytes("Test3");
tiff.SetField((TiffTag)40095, test.Length, test);

Syncfusion DocIO -- how to insert image (local file) at bookmark using BookmarksNavigator

I have been using Syncfusion DocIO for generating MS Word documents from my .net applications (winforms). So far I have dealt with plain text and it is fairly straightforward to insert text in a word document template where bookmarks serve as reference points for text insertion.
I am navigating the bookmarks using BookmarksNavigator.MoveToBookmark() . Now I need to insert an image at a bookmark but I am at a loss at how to go about it.
Please help...
Thanks.
Specifically for adding it to a bookmark :
//Move to the specified bookmark
bk.MoveToBookmark(bookmark);
//Insert the picture into the specified bookmark location
bk.DeleteBookmarkContent(true);
// we assume the text is a full pathname for an image file
// get the image file
System.Drawing.Image image = System.Drawing.Image.FromFile(sText);
IWParagraph paragraph = new WParagraph(document);
paragraph.AppendPicture(image);
bk.InsertParagraph(paragraph);
private System.Drawing.Image LoadSignature(string sFileName)
{
string sImagePath = sFileName;
System.Drawing.Image image = System.Drawing.Image.FromFile(sImagePath);
return image;
}
private void MergeSignature(WordDocument doc, string sFile, string sBalise)
{
System.Drawing.Image iSignature = LoadSignature(sFile);
WordDocument ImgDoc = new WordDocument();
ImgDoc.AddSection();
ImgDoc.Sections[0].AddParagraph().AppendPicture(iSignature);
if (iSignature != null)
{
TextSelection ts = null ;
Regex pattern = new Regex(sBalise);
ts = doc.Find(pattern);
if (ts != null)
{
doc.ReplaceFirst = true;
doc.Replace(pattern, ImgDoc, false);
}
}
iSignature.Dispose();
}
See here: https://help.syncfusion.com/file-formats/docio/working-with-mailmerge
1) You should create docx file with name "Template.docx". This file will use as template.
In your docx file create Field of type MergeField.
2) Create MergeFiled with name Image:Krishna
3)
using Syncfusion.DocIO.DLS;
using System.Drawing;
public class Source
{
public Image Krishna { get; set; } = Image.FromFile(#"C:\1.png");
}
and generating code:
public static void Generate()
{
WordDocument doc = new WordDocument("Template.docx");
Source data = new Source();
var dataTable = new MailMergeDataTable("", new Source[] { data });
doc.MailMerge.ExecuteGroup(dataTable);
doc.Save("result.docx");
doc.Close();
}