Reputation: 167
I'm trying to use iTextSharp in a WPF project to add text to existing PDFs over everything else and at a given point. Basically the digital equivalent of writing on a paper document
I've tried multiple methods and nothing seems to actually write the text to the new PDF. My code is messy from testing various things (left in so I know what I've tried - yes, I'll be clearing it out after resolution), so I'll try to get all of the important lines.
var stream = new FileStream(targetFilePath, FileMode.Create);
var doc = new iTextSharp.text.Document();
var target = new PdfCopy(doc, stream);
doc.Open();
var baseFont = BaseFont.CreateFont(BaseFont.TIMES_ROMAN, BaseFont.CP1252, false);
foreach (var vm in Pages) // Cycles through pages in WPF object...
{
var srcDoc = vm.DocTmpFName;
if (Path.GetExtension(srcDoc).ToUpperInvariant() == ".PDF")
{
var reader = new iTextSharp.text.pdf.PdfReader(srcDoc);
var page = reader.GetPageN(vm.Number);
var stamper = new PdfStamper(reader, stream);
target.AddPage(target.GetImportedPage(reader, vm.Number));
page.Put(PdfName.ROTATE, new PdfNumber(vm.Rotation % 360f));
// Inside this 'foreach' loop is what isn't working. The rest works fine.
foreach(var str in vm.Strings) // str is a container class (just a string and two 'double' vars for positioning)
{
// This isn't writing to new PDF even though it gets run (verified with breakpoints)
var contentByte = stamper.GetOverContent(vm.Number);
contentByte.BeginText();
contentByte.SetTextMatrix((float)str.X, (float)str.Y);
contentByte.SetFontAndSize(baseFont, 72f); //Making it stupid-big so I don't miss it
contentByte.ShowText(str.String);
contentByte.EndText();
}
target.FreeReader(reader);
reader.Close();
}
}
target.Close();
doc.Close();
stream.Close();
If you feel I've missed something let me know. I tried to keep the context of the problem area, so hopefully it's not too horrible of a hack-job.
- Edited to correct code -
Edit
I renamed some variables to make it easier to discern what they're for. Also, I've included the new fixed (albeit buggy) code that adds text at a position - for anyone stumbling across this later. =)
var stream = new FileStream(targetFilePath, FileMode.Create);
var doc = new iTextSharp.text.Document();
var targetPdf = new PdfCopy(doc, stream);
doc.Open();
foreach (ViewModels.PageViewModel vm in document.Pages)
{
var srcDocPath = FileIO.ToTempFileName(vm.DocName);
// Copy pageDict from source...
if (Path.GetExtension(srcDocPath).ToUpperInvariant() == ".PDF")
{
var srcReader = new iTextSharp.text.pdf.PdfReader(srcDocPath);
var pageDict = srcReader.GetPageN(vm.Number);
var importedPage = targetPdf.GetImportedPage(srcReader, vm.Number);
var pageStamp = targetPdf.CreatePageStamp(importedPage);
foreach(var str in vm.Strings)
{
ColumnText.ShowTextAligned(pageStamp.GetOverContent(),
iTextSharp.text.Element.ALIGN_LEFT,
new iTextSharp.text.Phrase(str.String),
(float) str.X,
// pdf-origin is bottom-left, not top-left
(float)(importedPage.Height - str.Y),
0);
}
pageDict.Put(PdfName.ROTATE, new PdfNumber(vm.Rotation % 360f));
pageStamp.AlterContents();
targetPdf.AddPage(importedPage);
targetPdf.FreeReader(srcReader);
srcReader.Close();
}
}
targetPdf.Close();
doc.Close();
stream.Close();
Upvotes: 0
Views: 3142
Reputation: 95918
Your code has a number of issues some of which @Übercoder already addressed in his answer.
Furthermore you have a PdfCopy
and multiple PdfStamper
instances writing into the same stream. This will result in utter chaos in the stream output. If you want to stamp pages you copy, you should use a PdfCopy.PageStamp
, e.g. like this:
using (PdfCopy copy = new PdfCopy(document, OUTPUT_STREAM)) {
document.Open();
PdfReader reader1 = new PdfReader(r1);
int n1 = reader1.NumberOfPages;
PdfImportedPage page;
PdfCopy.PageStamp stamp;
for (int i = 0; i < n1; ) {
page = copy.GetImportedPage(reader1, ++i);
stamp = copy.CreatePageStamp(page);
// CHANGE THE PAGE,
// e.g. by manipulating stamp.GetOverContent()
...
//
stamp.AlterContents();
copy.AddPage(page);
}
}
(excerpt from the official iTextSharp sample ConcatenateStamp.cs)
Upvotes: 3
Reputation: 2940
You chould not close the reader inside the foreach
loop. Nor (I suspect) should you do the target.FreeReader
call:
foreach(var str in vm.Strings) // str is a container class (just a string and two 'double' vars for positioning)
{
// This isn't writing to new PDF even though it gets run (verified with breakpoints)
var contentByte = stamper.GetOverContent(vm.Number);
contentByte.BeginText();
contentByte.SetTextMatrix((float)str.X, (float)str.Y);
contentByte.SetFontAndSize(baseFont, 72f); //Making it stupid-big so I don't miss it
contentByte.ShowText(str.String);
contentByte.EndText();
}
//moved the next two lines out of the foreach loop
target.FreeReader(reader);
reader.Close();
The same aplies for these three lines:
target.Close();
doc.Close();
stream.Close();
They, too should be moved out of the foreach (var vm in Pages)
loop that currently contains them.
Upvotes: 0