问题描述
在将某些数据发布为PDF的C#代码中,我正在寻找一种在页脚(左,中或右索引)中放置页码的方法。
格式应如下所示:“第1页,共n页” ,其中 n 表示总页数。
PdfWriter writer = PdfWriter.GetInstance(doc,ms);
writer.CloseStream = false;
doc.open();
//Main pdf publishing code follows
doc.Close();
解决方法
这看起来像是堆栈溢出时许多问题和答案的重复,但是如今这些问题的大多数答案都变得不是很有帮助,因为它们链接到了无法再找到的示例,例如由于将iText代码库从sourceforge迁移到github或由于itext网站的重新设计。此外,OP在评论中指出他确实知道一种方法,只是不知道如何将其应用于他的案子。
两种主要方法
首先,有两种主要方法可将“ y的第x页”页眉或页脚添加到使用iText创建的文档页面中:
- (单遍方法)您可以使用页面事件(特别是
OnEndPage
方法)在此页面结束时以及在下一页开始之前向每个页面添加页眉/页脚/边距材料。 - (两遍方法)或者您可以在第一遍中创建没有那些页眉或页脚的PDF,然后阅读该PDF并在第二遍中标记页脚/页眉/空白材料。
通常,人们更喜欢用页事件作为页眉和页脚材料,因为这样一来,无需从第一遍临时存储PDF,而是可以将其流式传输到目标,而仍处于创建过程中。
“ y的第x页”页脚/页眉很特殊,因为在写入所有内容之前,尚不知道最终的页数。因此,对于页面事件方法,必须使用一种技巧,并将对画布的引用添加到应显示总页面编号的每个页面。当所有常规内容都添加到文档中后,最后将总页码写入其中。不幸的是,人们不知道该模板到底需要多少空间,因此最终的页眉/页脚中的格式可能看起来有些尴尬。因此,这里最好采用两次通过方法。
Bruno撰写的《 iText in Action-2nd Edition》一书第6章的示例TwoPasses
中介绍了如何使用iText 5.x在两遍中将“ y的第x页”标题添加到PDF。洛瓦吉。原始Java示例已由kuujinbo移植到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);
{
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))
{
//Main pdf publishing code follows
}
}
catch (Exception ex)
{
MessageBox.Show(ex.Message,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,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,cropBox.GetTop(10),stamper.GetOverContent(i));
}
}
}
}
catch (Exception ex)
{
MessageBox.Show(ex.Message,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}",y));
return table;
}