Reputation: 53
I added a sample project to reproduce the problem to the GitHub repo, I hope this helps: Click to go to the Github repo
I need to add text and a number of page to a PDF, this PDF is from an invoice.
I already tried using events (but the examples are for java, and are incomplete, or I think they are), and it's not working, I'm getting the same error, and in that way text cannot be aligned.
As I said, I've trying to achieve this using a table and a canvas, the code works fine if the PDF has only one page.
But with more than one page I get this error:
This exception was originally thrown at this call stack: KernelExtensions.Get<TKey,
TValue>(System.Collections.Generic.IDictionary, TKey) iText.Kernel.Pdf.PdfDictionary.Get(iText.Kernel.Pdf.PdfName, bool) iText.Kernel.Pdf.PdfDictionary.GetAsNumber(iText.Kernel.Pdf.PdfName) iText.Kernel.Pdf.PdfPage.GetRotation() iText.Kernel.Pdf.Canvas.PdfCanvas.PdfCanvas(iText.Kernel.Pdf.PdfPage) BoarGiveMeMoar.Invoices.InvoiceToPdf.AddFooter(iText.Layout.Document) in InvoiceToPdf.cs BoarGiveMeMoar.Invoices.InvoiceToPdf.FillPdf.AnonymousMethod__4() in InvoiceToPdf.cs System.Threading.Tasks.Task.InnerInvoke() in Task.cs System.Threading.Tasks.Task..cctor.AnonymousMethod__274_0(object) in Task.cs System.Threading.ExecutionContext.RunFromThreadPoolDispatchLoop(System.Threading.Thread, System.Threading.ExecutionContext, System.Threading.ContextCallback, object) in ExecutionContext.cs ... [Call Stack Truncated]
This is my code, but I get the error from above:
private readonly int smallFontSize = 6;
private readonly int stdFontSize = 7;
public void FillPdf(string filename)
{
using var pdfWriter = new PdfWriter(filename);
using var pdfDoc = new PdfDocument(pdfWriter);
using var doc = new Document(pdfDoc, PageSize.LETTER);
doc.SetMargins(12, 12, 36, 12);
AddData(doc);
AddFooter(doc);
doc.Close();
}
private void AddData(Document doc)
{
Table table = new Table(UnitValue.CreatePercentArray(5)).UseAllAvailableWidth();
PdfFont bold = PdfFontFactory.CreateFont(StandardFonts.HELVETICA_BOLD);
for (int i = 0; i < 250; i++)
{
var cell = new Cell(1, 5)
.Add(new Paragraph($"My Favorite animals are boars and hippos")
.SetFontSize(stdFontSize).SetFont(bold));
cell.SetBorder(Border.NO_BORDER);
cell.SetPadding(0);
table.AddCell(cell);
}
doc.Add(table);
}
private void AddFooter(Document doc)
{
if (doc is null)
return;
Table table = new Table(UnitValue.CreatePercentArray(60)).UseAllAvailableWidth();
int numberOfPages = doc.GetPdfDocument().GetNumberOfPages();
for (int i = 1; i <= numberOfPages; i++)
{
PdfPage page = doc.GetPdfDocument().GetPage(i);
PdfCanvas pdfCanvas = new PdfCanvas(page);
Rectangle rectangle = new Rectangle(
0,
0,
page.GetPageSize().GetWidth(),
15);
Canvas canvas = new Canvas(pdfCanvas, doc.GetPdfDocument(), rectangle);
var cell = new Cell(1, 20).SetFontSize(smallFontSize);
cell.SetBorder(Border.NO_BORDER);
cell.SetPadding(0);
table.AddCell(cell);
cell = new Cell(1, 20).Add(new Paragraph("This document is an invoice")
.SetTextAlignment(TextAlignment.CENTER)).SetFontSize(smallFontSize);
cell.SetBorder(Border.NO_BORDER);
cell.SetPadding(0);
table.AddCell(cell);
cell = new Cell(1, 10).SetFontSize(smallFontSize);
cell.SetBorder(Border.NO_BORDER);
cell.SetPadding(0);
table.AddCell(cell);
cell = new Cell(1, 7)
.Add(new Paragraph($"Page {string.Format(CultureInfo.InvariantCulture, "{0:#,0}", i)} of {string.Format(CultureInfo.InvariantCulture, "{0:#,0}", numberOfPages)} ")
.SetTextAlignment(TextAlignment.RIGHT)).SetFontSize(smallFontSize);
cell.SetBorder(Border.NO_BORDER);
cell.SetPadding(0);
table.AddCell(cell);
cell = new Cell(1, 3).SetFontSize(smallFontSize);
cell.SetBorder(Border.NO_BORDER);
cell.SetPadding(0);
table.AddCell(cell);
canvas.Add(table).SetFontSize(smallFontSize);
canvas.Close();
}
}
Example way to call the code:
new InvoiceToPdf()
.FillPdf(@$"{Environment.GetFolderPath(Environment.SpecialFolder.Desktop)}\StackOverflow Invoice Test.pdf");
I get an error on this line that creates a new PdfCanvas
PdfCanvas pdfCanvas = new PdfCanvas(page);
So, anyone of you has a solution?
Upvotes: 2
Views: 673
Reputation: 12312
By default to decrease memory consumption a bit the pages of the PDF document you are creating are flushed as they fill up.
It means that when the content is written e.g. on page 3 of the document, the content of the page 1 is already written to the disk and it's too late to add more content there.
One option is to first create a document and close it, then open it with reader and writer, i.e.: new PdfDocument(PdfReader, PdfWriter)
, create Document
around PdfDocument
again and append content to the document as you do now:
public void FillPdf(string filename)
{
{
using var pdfWriter = new PdfWriter(tempFilename);
using var pdfDoc = new PdfDocument(pdfWriter);
using var doc = new Document(pdfDoc, PageSize.LETTER);
doc.SetMargins(12, 12, 36, 12);
AddData(doc);
doc.Close();
}
{
using var pdfWriter = new PdfWriter(filename);
using var pdfReader = new PdfReader(tempFilename);
using var pdfDoc = new PdfDocument(pdfReader, pdfWriter);
using var doc = new Document(pdfDoc, PageSize.LETTER);
doc.SetMargins(12, 12, 36, 12);
AddFooter(doc);
doc.Close();
}
}
Second option is not to flush the content as soon as possible and keep it in memory instead. You can do that by only making a slight modification in your code: pass third parameter to Document
constructor
using var doc = new Document(pdfDoc, PageSize.LETTER, false);
Please keep in mind that second option might result in additional empty page being created at the end of your document in some corner cases (e.g. when you add such a large image to your document that it doesn't even fit into a full page), so use it carefully. It should not create any problems with you are dealing with regular simple content though
Upvotes: 1