< >3.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();
}

最終的效果圖如下:

MovieCalander

上圖有點模糊,具體的效果大家可以自己打印出來看。現在我們的Film Festival已經完成了,但對ColumnText對象的使用還沒有結束。在以上的例子中因為我們可以將內容填充在矩形中,所以是沒什么問題,但如果文本不能完全填充在矩形中呢?還有在以上代碼中我們只是將Phrase對象加入到column並將其絕對定位,那我們能不能添加其他的對象如Paragraph,List或者Image到ColumnText對象中呢?第一個問題會在后續中解釋,第二個問題的回答是“yes”,但不是出於文本模式,而是組合模式。

在文本模式使用ColumnText對象

在2.3節中,我們將電影的信息已Paragraph對象的形式組合並打印在文檔上。假設你想重復這一過程,但是以column的形式打印出來,就如下圖所示:

MovieCOlumn1

除了使用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

以上的代碼列的實現是通過一個設置兩個矩形來完成的,但我們還可以將其設置為不規則的列,以下的代碼就是將每一列設置為一個多邊形,結果就是文本流動在圖中的方框之間。

MovieColumn4

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對象加入到里面。

ColumnMovie1

以下是實現的代碼:

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對象就被居中,左對其和兩段對其。

MovieColumn2

以上的圖我們可以重用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 讀書筆記。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM