在 C# 中,生成 PDF 时,如何以 "Page 1 of n" 或 "Page 1/n" 格式获取页脚中的页码
In C#, while generating a PDF, how to get page number in footer in the format "Page 1 of n" or "Page 1/n"
在我将某些数据发布为 PDF 的 C# 代码中,我正在寻找一种将页码放在页脚中的方法(左、中或右索引)。
格式应如下所示:“第 1 页,共 n”,其中 n 表示总页数。
PdfWriter writer = PdfWriter.GetInstance(doc, ms);
writer.CloseStream = false;
doc.Open();
//Main pdf publishing code follows
doc.Close();
这感觉像是堆栈溢出上的许多问题和答案的重复,但如今这些问题的大多数答案都不是很有用,因为它们 link 无法提供示例找不到了,例如因为 iText 代码库从 sourceforge 移动到 github 或者因为 itext 网站的重新设计。此外,OP 在评论中表示他确实知道一种方法,只是不知道如何将其应用于他的案例。
两种主要方法
首先,有两种主要方法可以将“第 x 页,共 y 页”header 或页脚添加到您使用 iText 创建的文档的页面:
- (one-pass 方法) 您可以使用页面事件(特别是
OnEndPage
方法)在该页面完成时立即向每个页面添加 header/footer/margin material在下一页开始之前。
- (two-passes 方法)或者您可以在第一遍创建没有那些 header 或页脚的 PDF,然后阅读该 PDF 并标记 footer/header/matgin material 在第二遍中。
通常人们更喜欢 header 和页脚 material 的页面事件,因为这样就不需要临时存储第一遍的 PDF,而是可以将它流式传输到目标,同时仍然正在创建它。
“第 x 页,共 y 页”footers/headers 很特别,因为在所有内容都写完之前,人们不知道最后页数。因此,对于页面事件方法,必须使用一种技巧,并向应显示总页码的每个页面添加对 canvas 的引用。当所有常规内容都添加到文档中后,最后将总页码写入其中。不幸的是,人们并不知道该模板到底需要多少 space,因此最终 header / 页脚中的格式可能看起来有点尴尬。因此,two-pass 方法在这里可能是首选。
如何使用 iText 5.x 将“第 x 页,共 y 页”headers 添加到 PDF 的两遍中 5.x 在“iText”一书第 6 章的示例 TwoPasses
中进行了描述in Action - 第 2 版”,作者 Bruno Lowagie。 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 / on kuujinbo.info 上)
申请您的案例
OP 在评论中写道
I have been facing difficulty understanding the two step method, where it starts, how to apply it in my case
好的,你描述你的情况是这样的:
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);
}
因为您原来的方法遗漏了关闭文档时抛出的异常 object! using (Document doc = ...) {...}
结构替换了 Document doc = ...; try {...} finally {doc.Close();}
结构。
现在您的 //Main pdf publishing code follows
特别包含一个 PdfWriter
实例化,例如
PdfWriter writer = PdfWriter.GetInstance(doc, SOME_STREAM);
对于两次通过的方法,您必须将 PdfWriter
定位到临时流。然后对其内容应用上面引用的 two-pass 代码。只有这样的结果才能最终到达你的 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;
}
在我将某些数据发布为 PDF 的 C# 代码中,我正在寻找一种将页码放在页脚中的方法(左、中或右索引)。
格式应如下所示:“第 1 页,共 n”,其中 n 表示总页数。
PdfWriter writer = PdfWriter.GetInstance(doc, ms);
writer.CloseStream = false;
doc.Open();
//Main pdf publishing code follows
doc.Close();
这感觉像是堆栈溢出上的许多问题和答案的重复,但如今这些问题的大多数答案都不是很有用,因为它们 link 无法提供示例找不到了,例如因为 iText 代码库从 sourceforge 移动到 github 或者因为 itext 网站的重新设计。此外,OP 在评论中表示他确实知道一种方法,只是不知道如何将其应用于他的案例。
两种主要方法
首先,有两种主要方法可以将“第 x 页,共 y 页”header 或页脚添加到您使用 iText 创建的文档的页面:
- (one-pass 方法) 您可以使用页面事件(特别是
OnEndPage
方法)在该页面完成时立即向每个页面添加 header/footer/margin material在下一页开始之前。 - (two-passes 方法)或者您可以在第一遍创建没有那些 header 或页脚的 PDF,然后阅读该 PDF 并标记 footer/header/matgin material 在第二遍中。
通常人们更喜欢 header 和页脚 material 的页面事件,因为这样就不需要临时存储第一遍的 PDF,而是可以将它流式传输到目标,同时仍然正在创建它。
“第 x 页,共 y 页”footers/headers 很特别,因为在所有内容都写完之前,人们不知道最后页数。因此,对于页面事件方法,必须使用一种技巧,并向应显示总页码的每个页面添加对 canvas 的引用。当所有常规内容都添加到文档中后,最后将总页码写入其中。不幸的是,人们并不知道该模板到底需要多少 space,因此最终 header / 页脚中的格式可能看起来有点尴尬。因此,two-pass 方法在这里可能是首选。
如何使用 iText 5.x 将“第 x 页,共 y 页”headers 添加到 PDF 的两遍中 5.x 在“iText”一书第 6 章的示例 TwoPasses
中进行了描述in Action - 第 2 版”,作者 Bruno Lowagie。 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 / on kuujinbo.info 上)
申请您的案例
OP 在评论中写道
I have been facing difficulty understanding the two step method, where it starts, how to apply it in my case
好的,你描述你的情况是这样的:
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);
}
因为您原来的方法遗漏了关闭文档时抛出的异常 object! using (Document doc = ...) {...}
结构替换了 Document doc = ...; try {...} finally {doc.Close();}
结构。
现在您的 //Main pdf publishing code follows
特别包含一个 PdfWriter
实例化,例如
PdfWriter writer = PdfWriter.GetInstance(doc, SOME_STREAM);
对于两次通过的方法,您必须将 PdfWriter
定位到临时流。然后对其内容应用上面引用的 two-pass 代码。只有这样的结果才能最终到达你的 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;
}