首页 > 解决方案 > 为每个 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);

那么,你们中的任何人都有解决方案吗?

标签: c#pdfpdf-generationitext7

解决方案


默认情况下,为了减少内存消耗,您正在创建的 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);

请记住,在某些极端情况下,第二个选项可能会导致在文档末尾创建额外的空白页面(例如,当您将如此大的图像添加到文档中以至于它甚至无法放入整页时),所以小心使用它。不过,它不应该对您处理常规的简单内容造成任何问题


推荐阅读