Variable data on pdf - c#

I want to add variable text and thumbnail images on specific position.
Text with formatting like fontstyles,rotation etc.
iTextSharp.text.pdf.PdfReader reader = null;
Document document = new Document();
PdfWriter writer;
writer = PdfWriter.GetInstance(document,
new FileStream(temp,
FileMode.Create, FileAccess.Write));
reader = new iTextSharp.text.pdf.PdfReader(file);
size = reader.GetCropBox(1);
PdfContentByte cb = writer.DirectContent;
document.NewPage();
PdfImportedPage page = writer.GetImportedPage(reader,
pageNumber);
cb.AddTemplate(page, 0, 0);
document.Close();
This will copy first page to new document. I want to add text and images on new document. how to do that?

Many people will stop reading your question after your first sentence:
I want to Edit existing PDF
If you read the intro of chapter 6 of my book, you'll understand that the word Edit is not the right word to use.
I did read on, and that I saw that you were using Document and PdfWriter to stamp new content on an existing PDF. If you do the effort of reading chapter 6 of my book, you'll understand that this is wrong. You should use PdfStamper instead.
This is one of the examples provided online:
public void manipulatePdf(String src, String dest) throws IOException, DocumentException {
PdfReader reader = new PdfReader(src);
PdfStamper stamper = new PdfStamper(reader, new FileOutputStream(dest));
Phrase header = new Phrase("Copy", new Font(FontFamily.HELVETICA, 14));
for (int i = 1; i <= reader.getNumberOfPages(); i++) {
float x = reader.getPageSize(i).getWidth() / 2;
float y = reader.getPageSize(i).getTop(20);
ColumnText.showTextAligned(
stamper.getOverContent(i), Element.ALIGN_CENTER,
header, x, y, 0);
}
stamper.close();
reader.close();
}
You can find these examples on the following URLs:
http://itextpdf.com/sandbox/stamper
http://itextpdf.com/book/chapter.php?id=6
If you need the examples from chapter 6 ported to C#, please visit this URL: http://tinyurl.com/itextsharpIIA2C06
Update
Does PdfStamper support:
font embedding: yes, when done correctly, fonts are embedded,
font size: yes, that's obvious,
font family: you provide the font program, the font program knows the font family,
alignments: for single lines: use showTextAligned(); for text blocks use ColumnText (caveat: read about the difference between text mode and composite mode)
rotation: for single lines, this is only a matter of providing a rotation angle; for text blocks, create a Form XObject and add this object using a rotation,
...

Related

Is it possible to change the appearance of the signature within the document after signing it?

Before calculating the hash of the document for signing I am adding the TextField in my document using the below code. as I am following this link
Changing signature appearance after signing pdf file with iTextSharp
Here is a code that adds signature on all pages and adds a text field on the first page.
the purpose of the text field is to extract the "IssuedTo" from the certificate and display it on the signature appearance.
Before esign open pdf in update mode:
XmlNodeList nodeList = xmlDoc.GetElementsByTagName("Signatures");
string signature = nodeList[0].FirstChild.InnerText;
string src = Server.MapPath("~/ESignFiles/" + file_withoutExtn + "_temp.pdf");
string dest = Server.MapPath("~/ESignFiles/" + file_withoutExtn + "_multiple_signed.pdf");
///add text
AddText(src, dest);
///add text
using (PdfReader reader = new PdfReader(src))
{
using (FileStream os = new FileStream(dest, FileMode.Create))
{
byte[] encodedSignature = Convert.FromBase64String(signature);
IExternalSignatureContainer external = new MyExternalSignatureContainer(encodedSignature);
MakeSignature.SignDeferred(reader, "sign1", os, external);
}
}
Code that add text to temp pdf
public void AddText(String src, String dest) {
PdfReader reader = new PdfReader(src);
PdfStamper stamper = new PdfStamper(reader, new FileStream(dest, FileMode.Create), '\0', true);
ColumnText.ShowTextAligned(stamper.GetOverContent(1), Element.ALIGN_LEFT, new Phrase("client name"), 200, 380, 0);
stamper.Close();
}
First of all, as has been discussed in comments to the question and to Bharat's answer:
The need to update signature appearances after applying the signature indicates a bad architecture of the signing solution.
In the case at hand this bad architecture appears to be a result of the requirements ("appearances must include certificate information" in combination with "certificate is not available before signing"). Nonetheless this is a bad architecture and should be improved after reviewing and revising the requirements.
But it indeed is possible under benign circumstances to update signature appearances: If the existing signatures allow for "form fill-ins and annotation changes" and do not completely lock the respective signature fields, the appearances of signatures can be updated in an incremental update without invalidating the signatures (validators may warn about a change, though).
Updating generic PDF signatures
The PDF specification does not specifically define a structure for the appearance of a signature field, a generic solution simply has to replace the appearance stream of each signature field widget annotation with a new one. This can be done like this using iText 5.5.x for .Net:
using (PdfReader pdfReader = new PdfReader(SRC))
using (PdfStamper pdfStamper = new PdfStamper(pdfReader, new FileStream(DEST, FileMode.Create, FileAccess.Write), '\0', true))
{
AcroFields acroFields = pdfStamper.AcroFields;
foreach (String signatureName in acroFields.GetSignatureNames())
{
PdfPKCS7 pkcs7 = acroFields.VerifySignature(signatureName);
X509Certificate signerCert = pkcs7.SigningCertificate;
String signerName = CertificateInfo.GetSubjectFields(signerCert).GetField("CN");
PdfAppearance appearance = PdfAppearance.CreateAppearance(pdfStamper.Writer, 100, 100);
ColumnText columnText = new ColumnText(appearance);
Chunk chunk = new Chunk();
chunk.SetSkew(0, 12);
chunk.Append("Signed by:");
columnText.AddElement(new Paragraph(chunk));
chunk = new Chunk();
chunk.SetTextRenderMode(PdfContentByte.TEXT_RENDER_MODE_FILL_STROKE, 1, BaseColor.BLACK);
chunk.Append(signerName);
columnText.AddElement(new Paragraph(chunk));
columnText.SetSimpleColumn(0, 0, 100, 100);
columnText.Go();
PdfDictionary appDict = new PdfDictionary();
appDict.Put(PdfName.N, appearance.IndirectReference);
AcroFields.Item field = acroFields.GetFieldItem(signatureName);
for (int i = 0; i < field.Size; i++)
{
PdfDictionary widget = field.GetWidget(i);
PdfArray rect = widget.GetAsArray(PdfName.RECT);
float x = Math.Min(rect.GetAsNumber(0).FloatValue, rect.GetAsNumber(0).FloatValue);
float y = Math.Min(rect.GetAsNumber(1).FloatValue, rect.GetAsNumber(3).FloatValue);
widget.Put(PdfName.RECT, new PdfArray(new float[] { x, y, x + 100, y + 100 }));
}
field.WriteToAll(PdfName.AP, appDict, AcroFields.Item.WRITE_WIDGET);
field.MarkUsed(acroFields, AcroFields.Item.WRITE_WIDGET);
}
}
As you can see the code extracts the subject's common name from the signer certificate and writes it (prefixed by a "Signed by:" line) to the new appearance. If you need other data in your your replacement appearance, simply change the data added to the columnText and/or the appearance accordingly.
Furthermore, the code replace all appearances with new ones which are 100×100 in size. You of course can adapt this to your requirements, too.
This essentially is a port of code from this answer to C#.
Updating PDF signatures using the Adobe specific layers
Adobe Acrobat Reader uses a specific scheme to build its signature appearances and even adds a certain functionality to signatures built according to an older version of this scheme. As mentioned above, the PDF specification does not prescribe any such scheme; actually it even forbids such a functionality, cf. this answer.
Nonetheless many stack overflow questions in particular from India seem to indicate that signatures following that obsolete scheme are often required there by clients.
If one follows this scheme, the appearance itself is constructed as a hierarchy of form XObjects, in particular a set of so called "layers" n0 through n4 from which n2 is the layer onto which a signer is expected to apply its identity.
The generic solution above can be adapted as follows to comply with this scheme:
using (PdfReader pdfReader = new PdfReader(SRC))
using (PdfStamper pdfStamper = new PdfStamper(pdfReader, new FileStream(DEST, FileMode.Create, FileAccess.Write), '\0', true))
{
AcroFields acroFields = pdfStamper.AcroFields;
foreach (String signatureName in acroFields.GetSignatureNames())
{
PdfPKCS7 pkcs7 = acroFields.VerifySignature(signatureName);
X509Certificate signerCert = pkcs7.SigningCertificate;
String signerName = CertificateInfo.GetSubjectFields(signerCert).GetField("CN");
AcroFields.Item field = acroFields.GetFieldItem(signatureName);
for (int i = 0; i < field.Size; i++)
{
PdfDictionary widget = field.GetWidget(i);
Rectangle rect = PdfReader.GetNormalizedRectangle(widget.GetAsArray(PdfName.RECT));
PdfAppearance appearance = PdfAppearance.CreateAppearance(pdfStamper.Writer, rect.Width, rect.Height);
ColumnText columnText = new ColumnText(appearance);
Chunk chunk = new Chunk();
chunk.SetSkew(0, 12);
chunk.Append("Signed by:");
columnText.AddElement(new Paragraph(chunk));
chunk = new Chunk();
chunk.SetTextRenderMode(PdfContentByte.TEXT_RENDER_MODE_FILL_STROKE, 1, BaseColor.BLACK);
chunk.Append(signerName);
columnText.AddElement(new Paragraph(chunk));
columnText.SetSimpleColumn(0, 0, rect.Width, rect.Height - 15);
columnText.Go();
PdfDictionary xObjects = GetAsDictAndMarkUsed((PdfStamperImp)pdfStamper.Writer, widget, PdfName.AP, PdfName.N, PdfName.RESOURCES, PdfName.XOBJECT, PdfName.FRM, PdfName.RESOURCES, PdfName.XOBJECT);
xObjects.Put(PdfName.N2, appearance.IndirectReference);
}
}
}
making use of the following helper method:
PdfDictionary GetAsDictAndMarkUsed(PdfStamperImp writer, PdfDictionary dictionary, params PdfName[] names)
{
PRIndirectReference reference = null;
foreach (PdfName name in names)
{
if (dictionary != null)
{
dictionary = dictionary.GetDirectObject(name) as PdfDictionary;
if (dictionary != null)
{
if (dictionary.IndRef != null)
reference = dictionary.IndRef;
}
}
}
if (reference != null)
writer.MarkUsed(reference);
return dictionary;
}
(Beware: This code assumes the signatures to follow the Adobe scheme; if you are not sure that your input does, add some sanity checks and default to the generic solution above.)
Since appearance is part of document while calculating hash to be signed, changing appearance will change hash and invalidate the signature already done.

Write text on existing table/Image using itextsharp

There is an existing table/image. When I write text using pdfcontentbyte, it is written behind this table/image.
I also want to write the text from the right side of this table/column.
The code I'm currently using to produce the image above:
// open the reader
PdfReader reader = new PdfReader(oldFile);
iTextSharp.text.Rectangle size = reader.GetPageSizeWithRotation(1);
Document document = new Document(size);
// open the writer
FileStream fs = new FileStream(newFile, FileMode.Create, FileAccess.Write);
PdfWriter writer = PdfWriter.GetInstance(document, fs);
document.Open();
PdfContentByte cb = writer.DirectContent;
BaseFont bf = BaseFont.CreateFont(BaseFont.COURIER_BOLD,BaseFont.CP1252, BaseFont.EMBEDDED);
string text = "WV0501";
cb.BeginText();
// put the alignment and coordinates here
cb.ShowTextAligned(2, text, 155, 655, 0);
cb.EndText();
// create the new page and add it to the pdf
PdfImportedPage page = writer.GetImportedPage(reader, 1);
cb.AddTemplate(page, 0, 0);
document.Close();
fs.Close();
writer.Close();
You don't show us your code, so we have to guess what you are doing wrong.
The content isn't showing:
You probably have this line in your code:
PdfContentByte canvas = pdfStamper.GetUnderContent(page);
If so, you should replace it with this line:
PdfContentByte canvas = pdfStamper.GetOverContent(page);
Update after you showed us your code:
You want to add content to an existing document, yet you are using a combination of Document and PdfWriter. Why are you doing this? Please read chapter 6 of my book where you'll learn about PdfStamper.
Right now, you are adding the text first and then you are covering it with a page from an existing document. This means that the existing page will cover the text.
You can switch this around like this:
// create the new page and add it to the pdf
PdfImportedPage page = writer.GetImportedPage(reader, 1);
cb.AddTemplate(page, 0, 0);
BaseFont bf = BaseFont.CreateFont(BaseFont.COURIER_BOLD,BaseFont.CP1252, BaseFont.EMBEDDED);
string text = "WV0501";
cb.BeginText();
// put the alignment and coordinates here
cb.ShowTextAligned(2, text, 155, 655, 0);
cb.EndText();
Now the text will cover the page, but that doesn't mean your code is better. You should really use PdfStamper instead of PdfWriter:
PdfReader reader = new PdfReader(oldFile);
FileStream fs = new FileStream(newFile, FileMode.Create, FileAccess.Write);
PdfStamper stamper = new PdfStamper(reader, fs);
PdfContentByte canvas = stamper.GetOverContent(1);
BaseFont bf = BaseFont.CreateFont(BaseFont.COURIER_BOLD,BaseFont.CP1252, BaseFont.NOT_EMBEDDED);
ColumnText.ShowTextAligned(
canvas,
Element.ALIGN_RIGHT,
new Phrase("WV0501", new Font(bf, 9)),
155, 655, 0
);
stamper.Close();
Don't you agree that this is more elegant?
IMPORTANT:
In your code you use:
BaseFont bf = BaseFont.CreateFont(BaseFont.COURIER_BOLD,BaseFont.CP1252, BaseFont.EMBEDDED);
However, that doesn't make much sense: COURIER_BOLD is one of the Standard Type 1 fonts and because of that the embedded parameter is ignored. I changed this into NOT_EMBEDDED because if you use EMBEDDED developers who read your code and who don't know anything about PDF and iText may get confused. They might ask: Why is the font not getting embedded when the parameter says it should be embedded?
BaseFont bf = BaseFont.CreateFont(BaseFont.COURIER_BOLD,BaseFont.CP1252, BaseFont.NOT_EMBEDDED);
Writing from the right side:
You are defining the alignment using a number: 2. I suggest that you don't use a number in your code, but a constant: ALIGN_RIGHT. This way, we see that you want to right align the text.
The text is right aligned with respect to the coordinates you've defined:
x = 155
y = 655
If you are not happy with the position of your text, you should change these hard-coded coordinates. For instance: increase x and decrease y.
You probably want the text to be relative to the border of a table cell or an image. If that is the case, you should not hard-code the coordinates.
Retrieving the coordinates of an image is discussed in another question on SO. Retrieving the coordinates of a table might very well be impossible. It all depends on the nature of the original PDF.

Combine two b5 size pdf to single legal page

is there any way to combine two b5 size pdf into single legal size pdf. after googling i did not find any solution. how to approach this problem. what i can use to do it. i am developing c# desktop application which will combine two b5 size pdf to single legal page. one left size and another is right side
e.g. input
1. b5first.pdf
1234
2. b5second
567
Output should be
3. legal.pdf
1234 567
You can get the text from the second pdf and put it in first, in the place you want to put.
If you are using iTextsharp you can do something like
String text += PdfTextExtractor.GetTextFromPage(reader, pageno ,new LocationTextExtractionStrategy());
Then can put the string wherever you want.
If you are looking for open-source solution then check this MetafileToEPSConverter to convert metafile into EPS and then convert EPS into PDF using epstopdf (included into LyX) or similar tools.
public static void somefunction(string oldFile,string oldFile1,string pathout)
{
// open the reader
PdfReader reader = new PdfReader(oldFile);
PdfReader reader1 = new PdfReader(oldFile1);
Document document = new Document(PageSize.LEGAL.Rotate());
// open the writer
FileStream fs = new FileStream(pathout, FileMode.Create, FileAccess.Write);
PdfWriter writer = PdfWriter.GetInstance(document, fs);
document.Open();
// the pdf content
PdfContentByte cb = writer.DirectContent;
// create the new page and add it to the pdf
PdfImportedPage page = writer.GetImportedPage(reader, 1);
PdfImportedPage page1 = writer.GetImportedPage(reader1, 1);
cb.AddTemplate(page, 0, 0);
cb.AddTemplate(page1, 500, 0);
// close the streams and voilá the file should be changed :)
document.Close();
fs.Close();
writer.Close();
reader.Close();
reader1.Close();
}

Create PDF from HTML with vertical text

I am creating a PDF using iTextSharp 5.4.5 in .NET 4.0 like this:
class Program
{
static void Main(string[] args)
{
string html="<span style='transform: rotate(-90deg)'>Some Text</span>";
byte[] file=PDFGenerator.GeneratePDF(html);
string filename=#"C:\Users\myaccount\Desktop\myfile.pdf";
var v=System.IO.File.Create(filename);
v.Write(file, 0, file.Length);
}
}
public class PDFGenerator
{
public static byte[] GeneratePDF(string html)
{
MemoryStream msOutput = new MemoryStream();
TextReader reader = new StringReader(html);
Document document = new Document(PageSize.A4, 30, 30, 30, 30);
PdfWriter writer = PdfWriter.GetInstance(document, msOutput);
HTMLWorker worker = new HTMLWorker(document);
document.Open();
worker.StartDocument();
worker.Parse(reader);
worker.EndDocument();
worker.Close();
document.Close();
return msOutput.ToArray();
}
}
However, the text in the PDF is not coming out rotated at all. I need the text to be rotated vertically. Is there any CSS or HTML I can enter so that it will rotate?
HTMLWorker has been deprecated for a very long time in favor of XMLWorker and zero work is being done in it. The former doesn't really support CSS while the latter does, too. However, the transform CSS property isn't supported anyway so that doesn't help you. (See this for a list of supported properties.)
Do you have an absolute need for HTML parsing? If not, you can just use the ColumnText class to do what you want. PDFs origins are in the bottom left corner instead of the top left so might need to adjust your math. Also, rotations are in the opposite direction so your -90 becomes just 90.
ColumnText.ShowTextAligned(writer.DirectContent, Element.ALIGN_CENTER, new Phrase("Hello World"), 50, 50, 90);

How can I join two PDF's using iTextSharp?

I tried to do it using this tutorial as a base, but it's throwing a null reference exception at the line specified below. Should I be doing this a different way? If not, why would it throw an null reference exception (both page and cb are NOT null). Code:
string filePath = #"c:\temp\test_new.pdf";
string attachPath = #"c:\temp\test.pdf";
Console.WriteLine("Begin!");
Document d = new Document();
if(File.Exists(filePath)){File.Delete(filePath);}
FileStream fs = new FileStream(filePath, FileMode.Create);
PdfWriter pw = PdfWriter.GetInstance(d, fs);
d.Open();
d.Add(new Paragraph("New document! Now lets add an attachment!"));
PdfReader pRdr = new PdfReader(new FileStream(attachPath,FileMode.Open));
PdfReaderContentParser parser = new PdfReaderContentParser(pRdr);
MemoryStream ms = new MemoryStream();
PdfWriter writer = PdfWriter.GetInstance(d, ms);
writer.Open();
PdfContentByte cb = writer.DirectContent;
PdfImportedPage page;
int rotation;
d.SetPageSize(PageSize.LETTER);
for (int i = 1; i <= pRdr.NumberOfPages; i++)
{
d.NewPage();
page = writer.GetImportedPage(pRdr, i);
rotation = pRdr.GetPageRotation(i);
if (rotation == 90 || rotation == 270)
{
cb.AddTemplate(page, 0, -1.0F, 1.0F, 0, 0, pRdr.GetPageSizeWithRotation(i).Height);
}
else
{
/*NULL EXCEPTION HERE!!!*/cb.AddTemplate(page, 1.0F, 0, 0, 1.0F, 0, 0); //NULL EXCEPTION HERE!!!
}
}
1) Use PdfCopy not PdfWriter. PdfWriter is for writing generated PDFs from a Document. PdfCopy is made for copying pages from A to B.
2) If you're problem is the result of an exception PLEASE post the exception. It'll remove much of the guesswork you see in the comments.
3) PdfImportedPage is just that page's contents and resources. You lose annotations (form fields and the like), bookmarks, and so forth. PdfCopy can help with some of that, but not all.
OK. I might get blasted for not answering your question, but there is a simpler way to merge two PDFs: don't use iTextSharp, use iTextDotNet
I found a post on how to do it: http://alex.buayacorp.com/merge-pdf-files-with-itextdotnet-and-net.html
I remembered this because I had to do this a couple of years ago. It does work, and well.
Check out my simple embPDFUtils library, where you can configure the merging and splitting process through an XML-Configuration file. There are methods to concatenate pdf files, split them and create pdf files from images. It is free. Check it out here: http://blog.mecum.biz/2011/11/how-master-xml-and-mistress-xsd-helped-itextsharp-out-of-the-claws-of-hippi-o-cratic-chaos-huggermugger/

Categories

Resources