前言
在這一節中我們將會學習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的代碼下載。
同步
此文章已同步到目錄索引:iText in Action 2nd 讀書筆記。