c# - 为每个 pdf 页面添加页码和作为页脚对齐的文本
问题描述
我添加了一个示例项目来重现该问题到 GitHub 存储库,我希望这会有所帮助: 单击转到 Github 存储库
我需要在 PDF 中添加文本和页数,此 PDF 来自发票。
我已经尝试过使用事件(但这些示例是针对 java 的,并且不完整,或者我认为它们是),但它不起作用,我遇到了同样的错误,这样文本就无法对齐。
正如我所说,我试图使用表格和画布来实现这一点,如果 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) InvoiceToPdf.cs 中的 BoarGiveMeMoar.Invoices.InvoiceToPdf.AddFooter(iText.Layout.Document) BoarGiveMeMoar.Invoices.InvoiceToPdf.FillPdf.AnonymousMethod__4() 在 InvoiceToPdf.cs System.Threading.Tasks.Task.InnerInvoke() 在 Task.cs System.Threading.Tasks.Task..cctor.AnonymousMethod__274_0(object) 在 Task.cs 系统.Threading.ExecutionContext.RunFromThreadPoolDispatchLoop(System.Threading.Thread, System.Threading.ExecutionContext, System.Threading.ContextCallback, object) 在 ExecutionContext.cs ...[调用堆栈被截断]
这是我的代码,但我从上面得到错误:
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)}\StackOverflow Invoice Test.pdf");
我在创建新 PdfCanvas 的这一行上收到错误
PdfCanvas pdfCanvas = new PdfCanvas(page);
那么,你们中的任何人都有解决方案吗?
解决方案
默认情况下,为了减少内存消耗,您正在创建的 PDF 文档的页面在填满时会被刷新。
这意味着当内容被写入文档的第 3 页时,第 1 页的内容已经写入磁盘,现在再添加更多内容为时已晚。
一种选择是首先创建一个文档并关闭它,然后使用读取器和写入器打开它,即: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
构造函数
using var doc = new Document(pdfDoc, PageSize.LETTER, false);
请记住,在某些极端情况下,第二个选项可能会导致在文档末尾创建额外的空白页面(例如,当您将如此大的图像添加到文档中以至于它甚至无法放入整页时),所以小心使用它。不过,它不应该对您处理常规的简单内容造成任何问题
推荐阅读
- go - Get frame from video bytes
- mysql - 我想替换 sql 中的文本 | 匹配的行:1 更改:0
- c# - How do I get data from a POST request on Nancy 2.0?
- java - 错误:类 X 是公共的,应在名为 X 的文件中声明
- javascript - 函数初始化位置之间的性能差异 [JavaScript]
- javascript - Javascript getter/setter 范围访问
- python - 如何在 Python 中使用并行处理过滤器?
- azure - Azure 命令行工具,无法导入 `cygrpc`
- excel - INDEX returns wrong value and no value
- javascript - setLocation should take two parameters, singlePerson and city. It should set a location property on the person to the city