Stack overflow exception on itextsharp document.close - c#

I am merging multiple PDF into one single file.
here is my code:
string[] files = Directory.GetFiles(FolderName);
using (FileStream fs = new FileStream(PdfOutputFile, FileMode.Create))
{
Document doc = new Document();
PdfWriter writer = PdfWriter.GetInstance(doc, fs);
doc.Open();
foreach (string percorsoFile in files)
{
PdfContentByte cb = writer.DirectContent;
PdfReader reader = new PdfReader(percorsoFile);
int numpagine = reader.NumberOfPages - PageAddedByWord;
for (int pagina = 1; pagina <= numpagine; pagina++)
{
int rotazione = reader.GetPageSizeWithRotation(pagina).Rotation;
PdfImportedPage page = writer.GetImportedPage(reader, pagina);
doc.NewPage();
if (rotazione == 90 || rotazione == 270)
cb.AddTemplate(page, 0, -1f, 1f, 0, 0, reader.GetPageSizeWithRotation(pagina).Height);
else
cb.AddTemplate(page, 0f, 0f);
}
reader.Close();
}
doc.Close();
}
And I get stack overflow exception on the doc.Close() line, but I can't understand why. I close every reader as soon as I am done with that file. I get this exception only with large sets of pdf (like 2000).
thank's for the help.

SOLVED, I used PdfSmartCopy instead of pdfwriter. Now the code looks like this
Document doc = new Document();
PdfSmartCopy pdfCopy = new PdfSmartCopy(doc, ms);
doc.Open();
foreach (var percorsoFilePdf in files)
{
PdfReader reader = new PdfReader(percorsoFilePdf);
int numpagine = reader.NumberOfPages;
for (int I = 1; I <= numpagine-PageAddedByWord; I++)
{
doc.SetPageSize(reader.GetPageSizeWithRotation(1));
PdfImportedPage page = pdfCopy.GetImportedPage(reader, I);
pdfCopy.AddPage(page);
}
//Clean up
//pdfCopy.FreeReader(reader);
reader.Close();
}
//Clean up
doc.Close();
SharedMethods.MemoryStreamToFile(ms, PdfOutputFile);
also using a memorystream instead of a file stream is much faster, and the process doesn't get "too large".

Related

iTextSharp : create one more page copying last page

I have a PDF file with headers, footers etc on every page. I need to extend count of pages, if text is very long (I have variable text). For example:
if (countOfRecords > 120)
{
// add one more page like second page of template (which does not have content, only header/footer
}
Is it possible?
I implemented it the following way: created PDF file with "empty" page (means only with necessary headers/footers) and then add it to main PDF if necessary how many times as necessary:
PdfReader pdfReader = new PdfReader(model.Input);
Document document = new Document(pdfReader.GetPageSizeWithRotation(1));
using (MemoryStream ms = new MemoryStream())
{
PdfWriter writer = PdfWriter.GetInstance(document, ms);
document.Open();
PdfContentByte cb = writer.DirectContent;
PdfImportedPage page = writer.GetImportedPage(pdfReader, 1);
cb.AddTemplate(page, 0, 0);
int countOfPages = (int)Math.Ceiling(Convert.ToDecimal(model.ActiveDriverList.Count - countDriversOnFirstPage) / countDriversOnEmptyPage);
for (int i = 0; i < countOfPages; i++)
{
PdfReader readerPage = new PdfReader(model.EmptyPage);
readerPage.ConsolidateNamedDestinations();
document.SetPageSize(pdfReader.GetPageSizeWithRotation(1));
document.NewPage();
PdfImportedPage importedPage = writer.GetImportedPage(readerPage, 1);
cb = AddTextDriversNextPage(cb, model.ActiveDriverList, i + 1);
cb.AddTemplate(importedPage, 0, 0);
}
document.Close();
writer.Close();
return ms.ToArray();
}

Convert html to pdf and merge it with existing pdfs

I have a System.Net.Mail.MailMessage which shall have it's html body and pdf attachments converted into one single pdf.
Converting the html body to pdf works for me with this answer
Converting the pdf attachments into one pdf works for me with this answer
However after ~10 hours of trying I can not come up with a combined solution which does both. All I'm getting are NullReferenceExceptions somewhere in IText source, "the document is not open", etc...
For example, this will throw no error but the resulting pdf will only contain the attachments but not the html email body:
Document document = new Document();
StringReader sr = new StringReader(mail.Body);
HTMLWorker htmlparser = new HTMLWorker(document);
using (FileStream fs = new FileStream(targetPath, FileMode.Create))
{
PdfCopy writer = new PdfCopy(document, fs);
document.Open();
htmlparser.Parse(sr);
foreach (string fileName in pdfList)
{
PdfReader reader = new PdfReader(fileName);
reader.ConsolidateNamedDestinations();
for (int i = 1; i <= reader.NumberOfPages; i++)
{
PdfImportedPage page = writer.GetImportedPage(reader, i);
writer.AddPage(page);
}
PRAcroForm form = reader.AcroForm;
if (form != null)
{
writer.CopyAcroForm(reader);
}
reader.Close();
}
writer.Close();
document.Close();
}
I'm using the LGPL licensed ITextSharp 4.1.6
From v4.1.6 fanboy to v4.1.6 fanboy :D
Looks like the HTMLWorker is closing the documents stream right after parsing. So as a workaround, you could create a pdf from your mailbody in memory. And then add this one together with the attachment to your final pdf.
Here is some code, that should do the trick:
StringReader htmlStringReader = new StringReader("<html><body>Hello World!!!!!!</body></html>");
byte[] htmlResult;
using (MemoryStream htmlStream = new MemoryStream())
{
Document htmlDoc = new Document();
PdfWriter htmlWriter = PdfWriter.GetInstance(htmlDoc, htmlStream);
htmlDoc.Open();
HTMLWorker htmlWorker = new HTMLWorker(htmlDoc);
htmlWorker.Parse(htmlStringReader);
htmlDoc.Close();
htmlResult = htmlStream.ToArray();
}
byte[] pdfResult;
using (MemoryStream pdfStream = new MemoryStream())
{
Document doc = new Document();
PdfCopy copyWriter = new PdfCopy(doc, pdfStream);
doc.Open();
PdfReader htmlPdfReader = new PdfReader(htmlResult);
AppendPdf(copyWriter, htmlPdfReader); // your foreach pdf code here
htmlPdfReader.Close();
PdfReader attachmentReader = new PdfReader("C:\\temp\\test.pdf");
AppendPdf(copyWriter, attachmentReader);
attachmentReader.Close();
doc.Close();
pdfResult = pdfStream.ToArray();
}
using (FileStream fs = new FileStream("C:\\temp\\test2.pdf", FileMode.Create, FileAccess.Write))
{
fs.Write(pdfResult, 0, pdfResult.Length);
}
private void AppendPdf(PdfCopy writer, PdfReader reader)
{
for (int i = 1; i <= reader.NumberOfPages; i++)
{
PdfImportedPage page = writer.GetImportedPage(reader, i);
writer.AddPage(page);
}
}
Ofc you could directly use a FileStream for the final document instead of a MemoryStream as well.

how to add pagenumbers to every pdf page using itextsharp

here is What i want i want to add page numbers to every pdf page that i generated on the fly.
i used on end page method but it did not worked out even when i added the doc bottom margin.
I decided to add the page numbers after the pdf is generated from the file path.
here is my code for generating pdf:
Document doc = new Document(iTextSharp.text.PageSize.LETTER, 10, 10, 42, 35);
PdfWriter wri = PdfWriter.GetInstance(doc, new FileStream("t5.pdf", FileMode.Create));
doc.Open();//Open Document to write
iTextSharp.text.Font font8 = FontFactory.GetFont("ARIAL", 7);
Paragraph paragraph = new Paragraph("Some content");
doc.Add(paragraph);
doc.Add(paragraph);// add paragraph to the document
doc.Close();
FileStream stream = File.OpenRead("t5.pdf");
byte[] fileBytes = new byte[stream.Length];
stream.Read(fileBytes, 0, fileBytes.Length);
stream.Close();
AddPageNumbers(fileBytes);
using (Stream file = File.OpenWrite("t5.pdf"))
{
file.Write(fileBytes, 0, fileBytes.Length);
}
}
and her is my add pagenumbers method:
MemoryStream ms = new MemoryStream();
PdfReader reader = new PdfReader(pdf);
int n = reader.NumberOfPages;
iTextSharp.text.Rectangle psize = reader.GetPageSize(1);
Document document = new Document(psize, 50, 50, 50, 50);
PdfWriter writer = PdfWriter.GetInstance(document, ms);
document.Open();
PdfContentByte cb = writer.DirectContent;
int p = 0;
for (int page = 1; page <= reader.NumberOfPages; page++)
{
document.NewPage();
p++;
PdfImportedPage importedPage = writer.GetImportedPage(reader, page);
cb.AddTemplate(importedPage, 0, 0);
BaseFont bf = BaseFont.CreateFont(BaseFont.HELVETICA, BaseFont.CP1252, BaseFont.NOT_EMBEDDED);
cb.BeginText();
cb.SetFontAndSize(bf, 10);
cb.ShowTextAligned(PdfContentByte.ALIGN_CENTER, +p + "/" + n, 100, 450, 0);
cb.EndText();
}
document.Close();
return ms.ToArray();
how ever it does not add the page numbers to the pdf document so what is the alternatives here? what can i do.
When posting a question here, please only post the smallest amount of code possible. Your "create a sample PDF with multiple pages" is 116 lines long. Inside of it you've got complicated PdfPTable and DataTable logic that is 100% unrelated to the problem. Instead, the following 13 lines is enough to make a multiple page PDF:
//Create a sample multiple page PDF and place it on the desktop
var outputFile = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Desktop), "t5.pdf");
using (var fs = new FileStream(outputFile, FileMode.Create, FileAccess.Write, FileShare.None)) {
using (var doc = new Document()) {
using (var writer = PdfWriter.GetInstance(doc, fs)) {
doc.Open();
for (var i = 0; i < 1000; i++) {
doc.Add(new Paragraph(String.Format("This is paragraph #{0}", i)));
}
doc.Close();
}
}
}
Second, get rid of try/catch. Those are great for production (sometimes) but at the development level that's why we have IDEs and compilers, they'll tell us specifically what's wrong.
Now on to the bigger problem, you need to keep these two processes separate from each other. Every single brace and object from part part #1 must be closed, done and accounted for. Part #2 then needs to be fed a completely valid PDF but neither of the two parts should be "aware" of each other or depend on each other.
Since you just borrowed some code that wasn't intended for what you're trying to do I'm going to also ignore that and use some code that I know specifically will work. Also, since you're open to using a MemoryStream in the first place I'm just going to avoid writing to disk until I need to. Below is a full working sample that creates a multiple page and then adds page numbers in a second pass.
//Will hold our PDF as a byte array
Byte[] bytes;
//Create a sample multiple page PDF, nothing special here
using (var ms = new MemoryStream()) {
using (var doc = new Document()) {
using (var writer = PdfWriter.GetInstance(doc, ms)) {
doc.Open();
for (var i = 0; i < 1000; i++) {
doc.Add(new Paragraph(String.Format("This is paragraph #{0}", i)));
}
doc.Close();
}
}
//Store our bytes before
bytes = ms.ToArray();
}
//Read our sample PDF and apply page numbers
using (var reader = new PdfReader(bytes)) {
using (var ms = new MemoryStream()) {
using (var stamper = new PdfStamper(reader, ms)) {
int PageCount = reader.NumberOfPages;
for (int i = 1; i <= PageCount; i++) {
ColumnText.ShowTextAligned(stamper.GetOverContent(i), Element.ALIGN_CENTER, new Phrase(String.Format("Page {0} of {1}", i, PageCount)), 100, 10 , 0);
}
}
bytes = ms.ToArray();
}
}
var outputFile = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Desktop), "t5.pdf");
System.IO.File.WriteAllBytes(outputFile, bytes);

ItextSharp changing Userunit

If I change the userunit to 10 the size shown in acrobat is correct but when I import the pdf in CorelDrawX3 and X7 or in Rasterlink Pro5 the size get changed back to before Userunit was changed.
I have to change the userunit because the pdf is to big.
Here is my coding.
public void PDFUserUnit(string FileIn, string FileOut)
{
using (var doc = new Document(new Rectangle(10000f, 16000f)))
{
using (var fs = new FileStream(FileOut, FileMode.Create, FileAccess.Write, FileShare.None))
{
using (var writer = PdfWriter.GetInstance(doc, fs))
{
iTextSharp.text.pdf.ByteBuffer.HIGH_PRECISION = true;
writer.SetPdfVersion(PdfWriter.PDF_VERSION_1_6);
writer.Userunit = 10f;
doc.Open();
doc.NewPage();
PdfReader reader = new PdfReader(FileIn);
PdfContentByte canvas = writer.DirectContent;
PdfTemplate tmp = writer.GetImportedPage(reader, 1);
canvas.AddTemplate(tmp, 1f, 0, 0, 1f, 0, 0);
doc.Close();
}
}
}
}

Adding text to existing multipage PDF document in memorystream using iTextSharp

I am trying to add text to an existing PDF file using iTextSharp. I have been reading many posts, including the popular thread here.
I have some differences:
My PDF are X pages long
I want to keep everything in memory, and never have a file stored on my filesystem
So I tried to modify the code, so it takes in a byte array and returns a byte array. I have come this far:
The code compiles and runs
My out byte array has a different length than my in byte array
My problem:
I cannot see my added text when i later store the modified byte array and open it in my PDF reader
I don't get why. From every StackOverflow post I have seen, I do the same. using the DirectContent, I use BeginText and write a text. However, i cannot see it, no matter how I move the position around.
Any idea what is missing from my code?
public static byte[] WriteIdOnPdf(byte[] inPDF, string str)
{
byte[] finalBytes;
// open the reader
using (PdfReader reader = new PdfReader(inPDF))
{
Rectangle size = reader.GetPageSizeWithRotation(1);
using (Document document = new Document(size))
{
// open the writer
using (MemoryStream ms = new MemoryStream())
{
using (PdfWriter writer = PdfWriter.GetInstance(document, ms))
{
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, 18);
var multiLineString = "Hello,\r\nWorld!";
contentByte.ShowTextAligned(PdfContentByte.ALIGN_LEFT, multiLineString,100, 200, 0);
contentByte.EndText();
contentByte.AddTemplate(importedPage, 0, 0);
}
document.Close();
ms.Close();
writer.Close();
reader.Close();
}
finalBytes = ms.ToArray();
}
}
}
return finalBytes;
}
The code below shows off a full-working example of creating a PDF in memory and then performing a second pass, also in memory. It does what #mkl says and closes all iText parts before trying to grab the raw bytes from the stream. It also uses GetOverContent() to draw "on top" of the previous pdf. See the code comments for more details.
//Bytes will hold our final PDFs
byte[] bytes;
//Create an in-memory PDF
using (var ms = new MemoryStream()) {
using (var doc = new Document()) {
using (var writer = PdfWriter.GetInstance(doc, ms)) {
doc.Open();
//Create a bunch of pages and add text, nothing special here
for (var i = 1; i <= 10; i++) {
doc.NewPage();
doc.Add(new Paragraph(String.Format("First Pass - Page {0}", i)));
}
doc.Close();
}
}
//Right before disposing of the MemoryStream grab all of the bytes
bytes = ms.ToArray();
}
//Another in-memory PDF
using (var ms = new MemoryStream()) {
//Bind a reader to the bytes that we created above
using (var reader = new PdfReader(bytes)) {
//Store our page count
var pageCount = reader.NumberOfPages;
//Bind a stamper to our reader
using (var stamper = new PdfStamper(reader, ms)) {
//Setup a font to use
var baseFont = BaseFont.CreateFont(BaseFont.HELVETICA_BOLD, BaseFont.CP1252, BaseFont.NOT_EMBEDDED);
//Loop through each page
for (var i = 1; i <= pageCount; i++) {
//Get the raw PDF stream "on top" of the existing content
var cb = stamper.GetOverContent(i);
//Draw some text
cb.BeginText();
cb.SetFontAndSize(baseFont, 18);
cb.ShowText(String.Format("Second Pass - Page {0}", i));
cb.EndText();
}
}
}
//Once again, grab the bytes before closing things out
bytes = ms.ToArray();
}
//Just to see the final results I'm writing these bytes to disk but you could do whatever
var testFile = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Desktop), "test.pdf");
System.IO.File.WriteAllBytes(testFile, bytes);

Categories