iText in Action 2nd3.3节(Working with the ColumnText object)读书笔记
前言
在这一节中我们将会学习ColumnText对象的使用:如果只是往ColumnText中加入Chunk或者Phrase对象,那么我们就处于文本模式(text mode);如果加入其它高层次的对象那么就处于组合模式(composite mode)。
在listing3.13种我们调用方法ShowMovieInfo来文档上打印一个大大的P字符。我们希望通过相同的方法往矩形中添加电影的标题,但是ShowTextAligned方法不能包裹文本,而且在这个方法中我们不能再string或者Chunk对象中使用换行。现在我们使用ColumnText提供的SetSimpleColumn方法来完成前一节的Film Festival,代码如下:
listing 3.15 MovieCalendar.cs
protected override void DrawMovieInfo(Screening screening, PdfContentByte directcontent){ base.DrawMovieInfo(screening, directcontent); Rectangle rect = GetPosition(screening ); ColumnText column = new ColumnText(directcontent); column.SetSimpleColumn(new Phrase(screening.Movie.Title), rect.Left, rect.Bottom, rect.Right, rect.Top, 18, Element.ALIGN_CENTER); column.Go();}
最终的效果图如下:
上图有点模糊,具体的效果大家可以自己打印出来看。现在我们的Film Festival已经完成了,但对ColumnText对象的使用还没有结束。在以上的例子中因为我们可以将内容填充在矩形中,所以是没什么问题,但如果文本不能完全填充在矩形中呢?还有在以上代码中我们只是将Phrase对象加入到column并将其绝对定位,那我们能不能添加其他的对象如Paragraph,List或者Image到ColumnText对象中呢?第一个问题会在后续中解释,第二个问题的回答是“yes”,但不是出于文本模式,而是组合模式。
在文本模式使用ColumnText对象
在2.3节中,我们将电影的信息已Paragraph对象的形式组合并打印在文档上。假设你想重复这一过程,但是以column的形式打印出来,就如下图所示:
除了使用SetSimpleColumn方法将Phrase对象添加进去之外,我们还可以使用AddText方法将一系列的Phrase或者Chunk对象添加进去。
使用AddText方法添加文本
以下的代码就是将电影的信息以列的形式打印在文档上:
listing 3.16 MovieColumns1.cs
List<Movie> movies = PojoFactory.GetMovies(conn);ColumnText ct = new ColumnText(writer.DirectContent);foreach (Movie movie in movies){ ct.AddText(CreateMovieInformation(movie)); ct.AddText(Chunk.NEWLINE);}ct.Alignment = Element.ALIGN_JUSTIFIED;ct.ExtraParagraphSpace = 6;ct.SetLeading(0, 1.2f);ct.FollowingIndent = 27;int linesWritten = 0;int column = 0;int status = ColumnText.NO_MORE_COLUMN;while (ColumnText.HasMoreText(status)){ ct.SetSimpleColumn(COLUMNS[column, 0], COLUMNS[column, 1], COLUMNS[column, 2], COLUMNS[column, 3]); ct.YLine = COLUMNS[column, 3]; status = ct.Go(); linesWritten += ct.LinesWritten; column = Math.Abs(column - 1); if (column == 0) { document.NewPage(); }}ct.AddText(new Phrase("Line written: " + linesWritten));ct.Go();
如同在代码Listing3.15中一样,我们传入一个PdfContentByte对象的实例构建一个ColumnText对象,然后通过AddText方法将Chunk或者Phrase对象添加进去。接下来我们设置了文本的很多属性:文本的对其,行高,段落之间的距离已经特殊的缩减。代码中我们还定义了一些变量来保存状态,如lineWritten保存的就是已经打印的行数,但是变量column(保存的是列的号码)和变量satus更加的重要。因为我们希望将所有的信息全部打印出来,所以在一个while循环语句中我们不断地通过Go方法来输出文本。代码中大家要注意的是我们定义了COLUMNS数组,其就是将页面设置为两列的形式,还有ColumnText的YLine属性,这是文本在一列中被打印出来的y座标。
当我们调用Go方法时,ColumnText里面的内容就会被打印出来,如果文本不能完全填充当前的列,那剩下的文本还是保留在ColumnText对象中。当一列被完全填充后我们需要换到其他的列继续往里面填充内容,最后当前页中没有其他的列的话就需要新的一页。
以上的代码我们还通过Alignment属性设置了文本的对其方式,这个和Paragraph对象的属性是同一个名词,作用也一样。SetLeadingf方法是设置行高,有两个参数,第一个是决定的行高0,第二个是相对的行高1.2,最后的行高就是:0+1.2*12(字体大小)=14.4pt。
COLUMNTEXT的属性
以上的代码中,虽然没有用到Paragraph对象,但通过设置ColumnText的ExtraParagraphSpace属性,这样就为每一段添加一些格外的空间,其他的如FollowingIndent属性可以设置缩减。在2.2节的时候我们学会如何在PdfWriter层次上改变字符空间比率,但这会影响所有的高层次的对象。在ColumnText中也有SpaceCharRatio属性来修改,这样的话就不至于影响所有的高层次对象。
ADDING CONTENT IN SMALL PORTIONS
在代码listing 3.16种,我们将数据库中所有的电影信息一次性全部填充在ColumnText对象中,然后再通过Go方法将其打印出来。但数据库中有120部电影,对应的ColumnText中就会保存120个Phrase对象的信息。所以竟可能早的频繁调用Go方法可以避免内存的消耗,就如以下代码:
listing 3.17 MovieColumns2.cs
List<Movie> movies = PojoFactory.GetMovies(conn);ColumnText ct = new ColumnText(writer.DirectContent);ct.Alignment = Element.ALIGN_JUSTIFIED;ct.ExtraParagraphSpace = 6;ct.Leading = 14;ct.Indent = 10;ct.RightIndent = 3;ct.SpaceCharRatio = PdfWriter.NO_SPACE_CHAR_RATIO;int column = 0;int status = ColumnText.NO_MORE_COLUMN;ct.SetSimpleColumn(COLUMNS[column, 0], COLUMNS[column, 1], COLUMNS[column, 2], COLUMNS[column, 3]);foreach (Movie movie in movies){ ct.AddText(CreateMovieInformation(movie)); status = ct.Go(); if (ColumnText.HasMoreText(status)) { column = Math.Abs(column - 1); if (column == 0) { document.NewPage(); } ct.SetSimpleColumn(COLUMNS[column, 0], COLUMNS[column, 1], COLUMNS[column, 2], COLUMNS[column, 3]); ct.YLine = COLUMNS[column, 3]; status = ct.Go(); }}
以上代码中,但我们往ColumnText对象中添加内容时就调用Go方法,这样ColumnText中保存的内容就随之减少,但效果是一样的。不过这里有一个问题:如果我们希望将一部电影的信息放在一起,而不是被分隔为两列,那要如何处理?
ADDING CONTENT IN SIMULATION MODE
以下的代码就是解决刚刚提到的问题,这里我们使用了一个特殊的Go方法:
listing 3.18 MovieColumns3.cs
List<Movie> movies = PojoFactory.GetMovies(conn);ColumnText ct = new ColumnText(writer.DirectContent);int column = 0;ct.SetSimpleColumn(COLUMNS[column, 0], COLUMNS[column, 1], COLUMNS[column, 2], COLUMNS[column, 3]);int status = ColumnText.NO_MORE_COLUMN;Phrase p;float y;foreach (Movie movie in movies){ y = ct.YLine; p = CreateMovieInformation(movie); ct.AddText(p); status = ct.Go(true); if (ColumnText.HasMoreText(status)) { column = Math.Abs(column - 1); if (column == 0) { document.NewPage(); } ct.SetSimpleColumn(COLUMNS[column, 0], COLUMNS[column, 1], COLUMNS[column, 2], COLUMNS[column, 3]); y = COLUMNS[column, 3]; } ct.YLine = y; ct.SetText(p); status = ct.Go(false);}
代码3.18和3.17几乎是相同的,但在3.18种我们调用了两次Go方法:第一次的Go方法是模拟打印内容,实际上没有内容被打印出来。这个时候再去判断内容是否可以在一列中完全填充完,如果不可以就去下一列,当前页中没有其他列就新开一页。然后再设置ColumnText对象的YLine属性使之从刚刚模拟的地方再次添加,不过这里要调用的是SetText方法。SetText方法将在ColumnText对象中已经被打印但又可能还保留的内容清除,这样可以避免内容便添加了两次。最后就在此调用Go方法即可。
IRREGULAR COLUMNS
以上的代码列的实现是通过一个设置两个矩形来完成的,但我们还可以将其设置为不规则的列,以下的代码就是将每一列设置为一个多边形,结果就是文本流动在图中的方框之间。
listing 3.19 MovieColumns4.cs
List<Movie> movies = PojoFactory.GetMovies(conn);ColumnText ct = new ColumnText(canvas);ct.Alignment = Element.ALIGN_JUSTIFIED;ct.Leading = 14;int column = 0;ct.SetColumns(LEFT[column], RIGHT[column]);int status = ColumnText.NO_MORE_COLUMN;Phrase p;float y;foreach (Movie movie in movies){ y = ct.YLine; p = CreateMovieInformation(movie); ct.AddText(p); status = ct.Go(true); if (ColumnText.HasMoreText(status)) { column = Math.Abs(column - 1); if (column == 0) { document.NewPage(); DrawRectangle(canvas); } ct.SetColumns(LEFT[column], RIGHT[column]); y = 806; } ct.YLine = y; ct.SetText(p); status = ct.Go();}
这里的代码和前面的基本相同,只不同调用了SetColumns方法而不是SetSimpleColumn方法。SetColumns方法的参数如下:
但不规则的列在组合模式中是不容许的。
在组合模式下使用ColumnText对象
目前为止,我们只是调用了COlumnText的AddText和SetText方法,下面我们会用到AddElement方法,当使用这个方法时,ColumnText会自动的从文本模式转换为组合模式。
ADDING CONTENT WITH ADDELEMENT()
下图显示的是四列的一个文档,而且我们将Image,Paragraph,List和Chunk对象加入到里面。
以下是实现的代码:
listing 3.20 ColumnsMovies1.cs
public void AddContent(ColumnText ct, Movie movie, Image img){ ct.AddElement(img); ct.AddElement(new Paragraph(movie.Title, FilmFonts.BOLD)); if (movie.OriginalTitle != null) { ct.AddElement(new Paragraph(movie.OriginalTitle, FilmFonts.ITALIC)); } ct.AddElement(PojoToElementFactory.GetDirectorList(movie)); ct.AddElement(PojoToElementFactory.GetYearPhrase(movie)); ct.AddElement(PojoToElementFactory.GetDurationPhrase(movie)); ct.AddElement(Chunk.NEWLINE);}
这个AddContent方法和我们前面碰到的基本上差不多。
listing 3.21 ColumnsMovies1.cs (continued)
List<Movie> movies = PojoFactory.GetMovies(conn);ColumnText ct = new ColumnText(writer.DirectContent);int column = 0;ct.SetSimpleColumn(COLUMNS[column][0], COLUMNS[column][1], COLUMNS[column][2], COLUMNS[column][3]);int status = ColumnText.NO_MORE_COLUMN;float y;Image img;foreach (Movie movie in movies){ y = ct.YLine; img = Image.GetInstance(string.Format(RESOURCE, movie.IMDB)); img.ScaleToFit(80, 1000); AddContent(ct, movie, img); status = ct.Go(true); if (ColumnText.HasMoreText(status)) { column = (column + 1) % 4; if (column == 0) { document.NewPage(); } ct.SetSimpleColumn(COLUMNS[column][0], COLUMNS[column][1], COLUMNS[column][2], COLUMNS[column][3]); y = COLUMNS[column][3]; } ct.YLine = y; ct.SetText(null); AddContent(ct, movie, img); status = ct.Go();}
这里我们又调用了两次的Go方法:一次是模拟添加,一次是实际的添加。这样就可以确保一部电影的信息是在同一列中。这里特别要注意的是以下代码:
ct.SetText(null);
如果我们注释这一行的话你会发现文档中的部分内容被添加了两次。
PROPERTIES OF THE COLUMNTEXT OBJECT VERSUS ELEMENT PROPERTIES
一旦我们调用了AddElement方法,在前面提到的一些ColumnText的属性如行高,对其等就不会起作用了,相反通过AddElement添加的元素自身的属性就会起作用了。如下图中的Paragraph对象就被居中,左对其和两段对其。
以上的图我们可以重用listing 3.21的代码,并只要修改方法AddContent即可,修改的代码如下:
listing 3.21 ColumnsMovies2.cs
public void AddContent(ColumnText ct, Movie movie){ Paragraph p; p = new Paragraph(new Paragraph(movie.Title, FilmFonts.BOLD)); p.Alignment = Element.ALIGN_CENTER; p.SpacingBefore = 16; ct.AddElement(p); if (movie.OriginalTitle != null) { p = new Paragraph(movie.OriginalTitle, FilmFonts.ITALIC); p.Alignment = Element.ALIGN_RIGHT; ct.AddElement(p); } p = new Paragraph(); p.Add(PojoToElementFactory.GetYearPhrase(movie)); p.Add(" "); p.Add(PojoToElementFactory.GetDurationPhrase(movie)); p.Alignment = Element.ALIGN_JUSTIFIED; ct.AddElement(p); p = new Paragraph(new Chunk(new StarSeparator())); p.SpacingAfter = 12; ct.AddElement(p);}
总结
ColumnText的文本模式和组合模式在我们创建PdfPCell的时候还会再次提到,但现在我们还要对已经创建好的Film Festival进行一些处理。最终打印的效果是完全一样的,不过我们接下来会学会如何通过重用数据来减少文档的大小。最后是本次demo的代码下载。
TAG: