前言
在上一節中,通過listing 4.16產生的表格擁有一個頭,表頭中顯示的為日期。如果我們仔細觀察此PDF的話你會發現基本上每一部電影的信息都會超過一頁,表格中數據被分割的不錯,但是表頭卻消失了。在這一節中我們會fix這個問題,而且還會為表格添加footer。
Repeating headers and footers
好了直接上效果圖:
上圖是具體一天的電影播放信息,日期顯示在第一行,第二行包括一些列的描述信息: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也可以提示我們:表格在前一樣也有數據。
Splitting tables
當表格中的一列不能在一頁中完全填充時就有兩個選擇:在新的一頁開始這一列或者盡可能多在當前頁中的列中添加數據,然后將剩下的數據添加到下一頁中。這兩種方式在iText中都是支持的,具體看下圖:
在上圖的上部大家可以看到,當Terminator2這一列不能在前一頁完全填充時,這一列就會在下一頁中被完全添加,這也是默認的行為。在圖的下部Termianator2的數據被分布在兩頁中,具體的實現代碼很簡單:
listing 4.19 HeaderFooter2.cs
PdfPTable table = GetTable(conn, day); table.SplitLate = true; document.Add(table);
Memory management for LargeElement implementations
在第二節我們學習實現了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 讀書筆記。