在第二节中我们创建了大量high-level的对象,iText会自动对其布局。在第三节的时候我们学会了writing to direct content,然后使用ColumnText对象将high-levl和lowel-level对象组合起来使用。目前为止我们将PdfPTable对象当作high-level对象使用,因此在其被添加到Document时:单元格的文本内容会被添加到文本层(text layer),其它所有的边框,背景色,图片都添加到文本层的下面。但是我们也可以将PdfPTable添加到文本层的上面抑或是图形层的下面。
下图是2011年的日历,背景是一张图片,而在背景前面是一个绝对定位的表格。
listing 4.21 PdfCalendar.cs
PdfPTable table; Calendar calendar = CultureInfo.GetCultureInfo(LANGUAGE).Calendar; PdfContentByte canvas = writer.DirectContent; for (int month = 1; month <= 12; month++) { // draw the background DrawImageAndText(canvas, month.ToString().PadLeft(2, '0')); // create a table with 7 columns table = new PdfPTable(7); table.TotalWidth = 504; // add the name of the month table.DefaultCell.BackgroundColor = BaseColor.WHITE; DateTime dt = new DateTime(YEAR, month, 1); table.AddCell(GetMonthCell(dt)); int daysInMonth = DateTime.DaysInMonth(YEAR, month); int day = 0; int position = (int)calendar.GetDayOfWeek(dt); // add empty cells for (int j = 0; j < position; j++) { table.AddCell(""); } // add cells for each day do { DateTime oneDt = dt.AddDays(day); day++; table.AddCell(GetDayCell(calendar, oneDt)); } while (day < daysInMonth); // complete the table table.CompleteRow(); // write the table to an absolute position table.WriteSelectedRows(0, -1, 169, table.TotalHeight + 18, canvas); document.NewPage(); }
在我们将table绝对定位时一定要设置表格总的宽度(TotalWidth),而且我们也不需要将宽度锁定,因为iText会忽视WidthPercentage的属性(这个属性只有在通过Documnet.Add方法添加时才有效)。表格的绝对定位是通过WriteSelectedRows方法实现的,现在我们讨论下此方法的参数。
方法的前两个参数是表格的起始和结束列,0到-1表示添加所有的列,其中-1表示所有剩余的列。接下来的两个参数是表格绝对定位的左上角座标,因为希望表格不要离页面的下边距太近,我们在表格的高度上添加了18pt。最后一个参数就是要添加的PdfContentByte对象。
除了单个的PdfContentByte对象,WriteSelectedRows方法还接受4个PdfContentByte对象组合的一个数组,其中每个PdfContentByte对象都有一个特定的名称和目的:
如果只是传入一个PdfContentByte对象,则文本位于线上面,线位于背景色上面,背景色位于BaseCanvas上面。
假设一个表格的列太多了导致一页不能完全显示,这个时候我们就可以将表格在水平面上分隔,就如下图:
上图是一个表格,其内容是张艺谋导演的电影,表格总的宽度设置为600pt,但页面可用的宽度只有595pt。因此使用了两次WriteSelectedRows方法来实现,具体参考以下代码:
listing 4.22 Zhang.cs
// Create a table and fill it with movies List<Movie> movies = PojoFactory.GetMovies(conn, 3); PdfPTable table = new PdfPTable(new float[] { 1, 5, 5, 1 }); foreach (Movie movie in movies) { table.AddCell(movie.Year.ToString()); table.AddCell(movie.Title); table.AddCell(movie.OriginalTitle); table.AddCell(movie.Duration.ToString()); } // set the total width of the table table.TotalWidth = 600; PdfContentByte canvas = writer.DirectContent; // draw the first three columns on one page table.WriteSelectedRows(0, 2, 0, -1, 236, 806, canvas); document.NewPage(); // draw the next three columns on the next page table.WriteSelectedRows(2, -1, 0, -1, 36, 806, canvas);
在第一次调用WriteSelectedRows方法时我们将前两列添加到一页中,其它的列添加到下一页中。通过WriteSelectedRows方法可以将表格绝对定位,但还有一种绝对定位的方法:将PdfPTable对象包裹在ColumnText对象中。
好了直接上图和代码:
listing 4.23 ColumnTable.cs
ColumnText column = new ColumnText(writer.DirectContent); List<DateTime> days = PojoFactory.GetDays(conn); float[,] x ={ { document .Left ,document .Left +380}, { document .Right -380,document .Right }}; foreach (DateTime day in days) { // add content to the column column.AddElement(GetTable(conn, day)); int count = 0; float height = 0; int status = ColumnText.NO_MORE_COLUMN; // render the column as long as it has content while (ColumnText.HasMoreText(status)) { // add the top-level header to each new page if (count == 0) { height = AddHeaderTable(document, day, writer.PageNumber); } // set the dimensions of the current column column.SetSimpleColumn(x[count, 0], document.Bottom, x[count, 1], document.Top - height - 10); // render as much content as possible status = column.Go(); // go to a new page if you've reached the last column if (++count > 1) { count = 0; document.NewPage(); } } document.NewPage(); }
这里使用的是ColumnText对象的一些属性,比较简单就不详细解释。
表格的绝对定位这里使用了两种方式:WriteSelectedRows方法和ColumnText对象,这两种方式都设计一些low-level的操作。这里第四节的内容就完全结束了,但对于PdfPTable和PdfPCell的讨论还要继续,下一节中我们会使用表格和单元格的事件进行更加完美的布局,最后是代码下载。
此文章已同步到目录索引:iText in Action 2nd 读书笔记。