zoukankan      html  css  js  c++  java
  • <<iText in Action 2nd>>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 读书笔记。

  • 相关阅读:
    编码实现Spring 利用@Resource注解实现bean的注入,xml实现基本数据类型的注入
    Spring依赖注入
    Spring的DI(Ioc)
    Spring的DI(Ioc)
    Spring的DI(Ioc)
    Spring管理bean的生命周期
    Spring管理Bean的三种创建方式
    简单模拟Spring管理Bean对象
    编写spring配置文件时,不能出现帮助信息
    Spring简介
  • 原文地址:https://www.cnblogs.com/julyluo/p/2568794.html
Copyright © 2011-2022 走看看