将页码和文本作为页脚对齐添加到每个 pdf 页面
Add a page number and text aligned as footer to every pdf page
我添加了一个示例项目来将问题重现到 GitHub 存储库中,希望这对您有所帮助:
Click to go to the Github repo
我需要向 PDF 添加文本和页码,此 PDF 来自发票。
我已经尝试过使用事件(但这些示例是针对 java 的,并且不完整,或者我认为它们是完整的),但它不起作用,我遇到了同样的错误,而且是这样的文本无法对齐。
如我所说,我尝试使用 table 和 canvas 来实现此目的,如果 PDF 只有一页,代码也能正常工作。
但是超过一页我得到这个错误:
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]
这是我的代码,但我从上面得到错误:
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();
}
}
调用代码的示例方法:
new InvoiceToPdf()
.FillPdf(@$"{Environment.GetFolderPath(Environment.SpecialFolder.Desktop)}\Whosebug Invoice Test.pdf");
我在这行创建新的 PdfCanvas 时遇到错误
PdfCanvas pdfCanvas = new PdfCanvas(page);
所以,你们有解决方案吗?
默认情况下,为了减少内存消耗,您正在创建的 PDF 文档的页面在填满时会被刷新。
这意味着当内容写成例如在文档的第 3 页,第 1 页的内容已经写入磁盘,现在添加更多内容已经来不及了。
一个选择是先创建一个文档并关闭它,然后用reader和writer打开它,即:new PdfDocument(PdfReader, PdfWriter)
,创建Document
大约 PdfDocument
并像现在一样将内容附加到文档中:
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();
}
}
第二个选项是不尽快刷新内容,而是保留在内存中。您只需对代码稍作修改即可做到这一点:将第三个参数传递给 Document
constructor
using var doc = new Document(pdfDoc, PageSize.LETTER, false);
请记住,在某些特殊情况下,第二个选项可能会导致在文档末尾创建额外的空白页(例如,当您向文档添加如此大的图像时,它甚至无法放入整页),因此请谨慎使用。它不应该对你处理常规的简单内容造成任何问题
我添加了一个示例项目来将问题重现到 GitHub 存储库中,希望这对您有所帮助: Click to go to the Github repo
我需要向 PDF 添加文本和页码,此 PDF 来自发票。
我已经尝试过使用事件(但这些示例是针对 java 的,并且不完整,或者我认为它们是完整的),但它不起作用,我遇到了同样的错误,而且是这样的文本无法对齐。
如我所说,我尝试使用 table 和 canvas 来实现此目的,如果 PDF 只有一页,代码也能正常工作。
但是超过一页我得到这个错误:
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]
这是我的代码,但我从上面得到错误:
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();
}
}
调用代码的示例方法:
new InvoiceToPdf()
.FillPdf(@$"{Environment.GetFolderPath(Environment.SpecialFolder.Desktop)}\Whosebug Invoice Test.pdf");
我在这行创建新的 PdfCanvas 时遇到错误
PdfCanvas pdfCanvas = new PdfCanvas(page);
所以,你们有解决方案吗?
默认情况下,为了减少内存消耗,您正在创建的 PDF 文档的页面在填满时会被刷新。
这意味着当内容写成例如在文档的第 3 页,第 1 页的内容已经写入磁盘,现在添加更多内容已经来不及了。
一个选择是先创建一个文档并关闭它,然后用reader和writer打开它,即:new PdfDocument(PdfReader, PdfWriter)
,创建Document
大约 PdfDocument
并像现在一样将内容附加到文档中:
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();
}
}
第二个选项是不尽快刷新内容,而是保留在内存中。您只需对代码稍作修改即可做到这一点:将第三个参数传递给 Document
constructor
using var doc = new Document(pdfDoc, PageSize.LETTER, false);
请记住,在某些特殊情况下,第二个选项可能会导致在文档末尾创建额外的空白页(例如,当您向文档添加如此大的图像时,它甚至无法放入整页),因此请谨慎使用。它不应该对你处理常规的简单内容造成任何问题