在上一节中,通过listing 4.16产生的表格拥有一个头,表头中显示的为日期。如果我们仔细观察此PDF的话你会发现基本上每一部电影的信息都会超过一页,表格中数据被分割的不错,但是表头却消失了。在这一节中我们会fix这个问题,而且还会为表格添加footer。
好了直接上效果图:
上图是具体一天的电影播放信息,日期显示在第一行,第二行包括一些列的描述信息:Location,Time,Run Length等,同样的信息还被加入到footer中。为了实现上图的效果我们需要添加三列:背景为黑色且有日期的一列,背景为灰色的二列(一列添加到header,一列添加到footer)。具体参考以下代码:
listing 4.18 HeaderFooter1.cs
PdfPCell cell = new PdfPCell(new Phrase(day.ToString("yyyy-MM-dd"), f)); cell.BackgroundColor = BaseColor.BLACK; cell.HorizontalAlignment = Element.ALIGN_CENTER; cell.Colspan = 7; table.AddCell(cell); // Add the second header row twice table.DefaultCell.BackgroundColor = BaseColor.LIGHT_GRAY; for (int i = 0; i < 2; i++) { table.AddCell("Location"); table.AddCell("Time"); table.AddCell("Run Length"); table.AddCell("Title"); table.AddCell("Year"); table.AddCell("Directors"); table.AddCell("Countries"); } table.DefaultCell.BackgroundColor = null; // There are three special rows table.HeaderRows = 3; // One of them is a footer table.FooterRows = 1;
以上代码看起来有点奇怪:我们在还没有添加内容时就将footer添加进去了。不过这是iText内部设置的:首先通过HeaderRows属性告诉iText有三个特殊的列,然后通过FooterRows属性说明其中一个为footer列。这样前两列就被先添加进去,然后是具体的内容,但需要新的一页或者没有数据时,第三列也就是footer列就会被添加。表格跨页显示时表头也会被重复,footer也会在表格的末端重复,但如果设置SkipLastFooter为true就footer列就不会被添加。重复的footer列有一个好处就是可以提示我们:表格会在下一页继续显示。和SkipLastFooter属性对应的为SkipFirstHeader,将其设置为true也可以提示我们:表格在前一样也有数据。
当表格中的一列不能在一页中完全填充时就有两个选择:在新的一页开始这一列或者尽可能多在当前页中的列中添加数据,然后将剩下的数据添加到下一页中。这两种方式在iText中都是支持的,具体看下图:
在上图的上部大家可以看到,当Terminator2这一列不能在前一页完全填充时,这一列就会在下一页中被完全添加,这也是默认的行为。在图的下部Termianator2的数据被分布在两页中,具体的实现代码很简单:
listing 4.19 HeaderFooter2.cs
PdfPTable table = GetTable(conn, day); table.SplitLate = true; document.Add(table);
在第二节我们学习实现了ILargElement的Chapter和Section对象,这一节中PdfPTable也实现了此接口。这些对象会在被添加到文档之前,我们一般会往其添加大量的内容,相应也会消耗大量内存。一般当我们将一些对象添加到Document时,这些对象就可以被GC回收,但类似PdfPTable和Chapter对象我们只能在完成之后才能将其添加到Document中。因此我们希望有一个完善的解决方案:在这些对象没有被添加到Document之前我们可以将部分内容写入到PdfWriter和相应的输出流中以便减少内存消耗,而且我们希望这个过程没有副作用(side effects),不会影响到PdfPTable的header,footer以及Chapter对象的标题,缩进等。ILargeElement就是为了解决这个问题而创建的。以下为ILargeElement接口:
public interface ILargeElement : IElement { /** * If you invoke setCompleted(false), you indicate that the content * of the object isn't complete yet; it can be added to the document * partially, but more will follow. If you invoke setCompleted(true), * you indicate that you won't add any more data to the object. * @since iText 2.0.8 * @param complete false if you'll be adding more data after * adding the object to the document. */ bool ElementComplete { get; set; } /** * Flushes the content that has been added. */ void FlushContent(); }
其中FlushContent方法是有内部管理,我们只需要设置ElementComplete属性,具体代码如下:
listing 4.20 MemoryTests.cs
// Create a table with 2 columns PdfPTable table = new PdfPTable(new float[] {1, 7}); // Mark the table as not complete if(test) { table.ElementComplete = false; } … // add information about a movie foreach (var movie in movies) { ……// insert a checkpoint every 10 movies if(count ++%10==0 ) { // add the incomplete table to the document if(test) { document.Add(table); } } } // Mark the table as complete if(test) { table.ElementComplete = true; } // add the table to the document document.Add(table);
在以上代码中,实现设置表格的ElementComplete属性为false,然后就可以在表格还没有完全构建完毕之前将其添加到文档中,最后表格构建完毕时设置ElementComplete属性为true,最后再次添加到文档中即可。书中的列子还详细的写了使用ILargeElement接口和不使用的内存使用,这里我就没有写了,各位那个老鸟就帮忙写个。
这一节主要是对大数据量表格的处理,如重复的header和footer,最后一列的处理方式,最后就是内存的使用。看起来比较复杂但iText已经封装的很好,写代码时只要设置几个属性即可。最后是代码下载。
此文章已同步到目录索引:iText in Action 2nd 读书笔记。