首页 > 解决方案 > 在 C# 中,生成 PDF 时,如何以“Page 1 of n”或“Page 1/n”格式获取页脚中的页码

问题描述

在将某些数据发布为 PDF 的 C# 代码中,我正在寻找一种将页码放在页脚(左、中或右索引)中的方法。

格式应如下所示:"Page 1 of n",其中n表示总页数。

PdfWriter writer = PdfWriter.GetInstance(doc, ms);
writer.CloseStream = false;

doc.Open();
//Main pdf publishing code follows 

doc.Close();

标签: c#pdfgithubitextpage-numbering

解决方案


这感觉就像堆栈溢出上的许多问题和答案的重复,但这些问题的大多数答案现在都不是很有帮助,因为它们链接到无法再找到的示例,例如由于 iText 代码库的移动从 sourceforge 到 github 或由于 itext 网站的重新设计。此外,OP 在评论中表示他确实知道一种方法,只是不知道如何将其应用于他的案件。

两种主要方法

首先,有两种主要方法可以将“Page x of y”页眉或页脚添加到您使用 iText 创建的文档的页面中:

  • (一次性方法)您可以使用页面事件(特别是OnEndPage方法)在此页面完成时和下一页开始之前将页眉/页脚/边距材料添加到每个页面。
  • (两遍方法)或者您可以在第一遍中创建没有这些页眉或页脚的 PDF,然后阅读该 PDF 并在第二遍中标记页脚/页眉/matgin 材料。

通常人们更喜欢页眉和页脚材料的页面事件,因为这样就不需要从第一遍临时存储 PDF,而是可以在创建过程中将其流式传输到目标。

但是,“y 页 x of y”页脚/页眉是特殊的,因为在写入所有内容之前不知道最终的页数。因此,对于页面事件方法,必须使用一种技巧并将对画布的引用添加到应出现总页码的每个页面。当所有常规内容都添加到文档中时,最后将总页码写入其中。不幸的是,人们不知道该模板究竟需要多少空间,因此生成的页眉/页脚中的格式最终可能看起来有点尴尬。因此,这里可能首选两次通过的方法。

TwoPassesBruno Lowagie 编写的“iText in Action - 2nd Edition”一书的第 6 章示例中描述了如何使用 iText 5.x 在两次传递中向 PDF 添加“Page x of y”标题。kuujinbo 已将原始 Java 示例移植到 C#。这里是关键代码:

PdfReader reader = new PdfReader(firstPass);
using (MemoryStream ms2 = new MemoryStream()) {
    // Create a stamper
    using (PdfStamper stamper = new PdfStamper(reader, ms2)) {
        // Loop over the pages and add a header to each page
        int n = reader.NumberOfPages;
        for (int i = 1; i <= n; i++) {
            GetHeaderTable(i, n).WriteSelectedRows(0, -1, 34, 803, stamper.GetOverContent(i));
        }
    }
    // write content of ms2 somewhere, e.g. as ms2.ToArray()
}

public static PdfPTable GetHeaderTable(int x, int y) {
    PdfPTable table = new PdfPTable(2);
    table.TotalWidth = 527;
    table.LockedWidth = true;
    table.DefaultCell.FixedHeight = 20;
    table.DefaultCell.Border = Rectangle.BOTTOM_BORDER;
    table.AddCell("FOOBAR FILMFESTIVAL");
    table.DefaultCell.HorizontalAlignment = Element.ALIGN_RIGHT;
    table.AddCell(string.Format("Page {0} of {1}", x, y));
    return table;
}    

(在github / kuujinbo.info上)

适用于您的案例

OP在评论中写道

我一直难以理解两步法,它从哪里开始,如何在我的案例中应用它

好的,您将您的情况描述为:

iTextSharp.text.Document doc = new iTextSharp.text.Document(PageSize.A4.Rotate(), 50f, 50f, 50f, 50f);
{
    try
    {
        //Main pdf publishing code follows 
    }
    catch (Exception ex)
    {
        MessageBox.Show(ex.Message, "Error! try again.", MessageBoxButtons.OK, MessageBoxIcon.Error);
    }
    finally
    {
        doc.Close();
    }
}

首先,我建议您将其更改为:

try
{
    using (iTextSharp.text.Document doc = new iTextSharp.text.Document(PageSize.A4.Rotate(), 50f, 50f, 50f, 50f))
    {
        //Main pdf publishing code follows
    }
}
catch (Exception ex)
{
    MessageBox.Show(ex.Message, "Error! try again.", MessageBoxButtons.OK, MessageBoxIcon.Error);
}

因为您的原始方法错过了关闭文档对象期间引发的异常!该using (Document doc = ...) {...}结构取代了Document doc = ...; try {...} finally {doc.Close();}一个。

现在你//Main pdf publishing code follows特别包括一个PdfWriter实例化,比如

PdfWriter writer = PdfWriter.GetInstance(doc, SOME_STREAM);

对于两通方法,您必须改为将您PdfWriter的目标定位到临时流。然后,您将上面引用的两次密码应用于其中的内容。只有结果才能最终归您所有SOME_STREAM

全部一起:

try
{
    using (MemoryStream ms = new MemoryStream())
    {
        float leftRightMargin = 50f;
        using (iTextSharp.text.Document doc = new iTextSharp.text.Document(PageSize.A4.Rotate(), leftRightMargin, leftRightMargin, 50f, 50f))
        {
            PdfWriter writer = PdfWriter.GetInstance(doc, ms);
            //Main pdf publishing code (except PdfWriter instantiation) follows
        }

        using (PdfReader reader = new PdfReader(ms.ToArray()))
        using (PdfStamper stamper = new PdfStamper(reader, SOME_STREAM))
        {
            int n = reader.NumberOfPages;
            for (int i = 1; i <= n; i++)
            {
                Rectangle cropBox = reader.GetCropBox(i);
                for (int rotation = reader.GetPageRotation(i); rotation > 0; rotation -= 90)
                    cropBox = cropBox.Rotate();
                GetHeaderTable(i, n, cropBox.Width - 2 * leftRightMargin).WriteSelectedRows(0, -1, leftRightMargin, cropBox.GetTop(10), stamper.GetOverContent(i));
            }
        }
    }
}
catch (Exception ex)
{
    MessageBox.Show(ex.Message, "Error! try again.", MessageBoxButtons.OK, MessageBoxIcon.Error);
}

public static PdfPTable GetHeaderTable(int x, int y, float width)
{
    PdfPTable table = new PdfPTable(2);
    table.TotalWidth = width;
    table.LockedWidth = true;
    table.DefaultCell.FixedHeight = 20;
    table.DefaultCell.Border = Rectangle.BOTTOM_BORDER;
    table.AddCell("Header test");
    table.DefaultCell.HorizontalAlignment = Element.ALIGN_RIGHT;
    table.AddCell(string.Format("Page {0} of {1}", x, y));
    return table;
}

推荐阅读