将页码和文本作为页脚对齐添加到每个 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);

请记住,在某些特殊情况下,第二个选项可能会导致在文档末尾创建额外的空白页(例如,当您向文档添加如此大的图像时,它甚至无法放入整页),因此请谨慎使用。它不应该对你处理常规的简单内容造成任何问题