zoukankan      html  css  js  c++  java
  • <<iText in Action 2nd>>5.4节(Adding page events to PdfWriter)读书笔记

    前言

    在上一节我们讨论了几种不同页边界的类型后这一节我们继续回到IPdfPageEvent接口中,现在这个接口还剩下以下4个关于文档和页面的方法没有说明:

    • OnOpenDocument----当文档被带打开的时候调用,一般在这个方法中初始化一些需要在整个文档中使用的资源。
    • OnStartPage----当一个新的页面开启时调用,一般使用这个方法初始化一些页面需要的参数,最后要注意不要在这个方法中往文档中添加内容。
    • OnEndPage----在开启新的一页之前或者文档关闭之前调用,这是为文档添加页眉页脚和水印的最佳地方。
    • OnCloseDocument----在文档关闭之前调用,一般清理一些资源。

    现在我们将使用这些方法解决一些经常提到的需求,比如在创建文档的过程中为每一页添加页眉。

    Adding a header and a footer

    现在我们回到第二节中Chapter和Section对象的列子中,我们会进行两个小的修改:定义一个art box并为PdfWriter添加页面事件,具体到代码中就是HeaderFooter实例,我们可以通过此实例为文档添加页眉和页脚,就如下图所示:

    HeaderAndFooter

    在上图中,我们在每个Chapter开始的时候将此Chapter的页码顺序加入到页脚中,而且是居中格式显示。对于页眉则交替显示文本"Movie history"(居右显示)和Chapter的标题(居左显示),下面就是具体的代码:

    listing 5.19 MovieHistory2.cs

    class HeaderFooter : IPdfPageEvent
    {
    /** Alternating phrase for the header. */
    Phrase[] header = new Phrase[2];
    /** Current page number (will be reset for every chapter). */
    int pagenumber;
    
    /// <summary>
    /// Initialize one of the headers, based on the chapter title;
    /// reset the page number.
    /// </summary>
    /// <param name="writer"></param>
    /// <param name="document"></param>
    /// <param name="paragraphPosition"></param>
    /// <param name="title"></param>
    public void OnChapter(PdfWriter writer, Document document, float paragraphPosition, Paragraph title)
    {
        header[1] = new Phrase(title.Content);
        pagenumber = 1;
    }
    
    
    /// <summary>
    /// Adds the header and the footer.
    /// </summary>
    /// <param name="writer"></param>
    /// <param name="document"></param>
    public void OnEndPage(PdfWriter writer, Document document)
    {
        Rectangle rect = writer.GetBoxSize("art");
        switch (writer.PageNumber % 2)
        {
            case 0:
                ColumnText.ShowTextAligned(writer.DirectContent, Element.ALIGN_RIGHT, header[0], rect.Right, rect.Top, 0);
                break;
            case 1:
                ColumnText.ShowTextAligned(writer.DirectContent, Element.ALIGN_LEFT, header[1], rect.Left, rect.Top, 0);
                break;
    
        }
    
        ColumnText.ShowTextAligned(writer.DirectContent, Element.ALIGN_CENTER, new Phrase(string.Format("page {0}", pagenumber)),
            (rect.Left + rect.Right) / 2, rect.Bottom - 18, 0);
    }
    
    /// <summary>
    ///  Initialize one of the headers.
    /// </summary>
    /// <param name="writer"></param>
    /// <param name="document"></param>
    public void OnOpenDocument(PdfWriter writer, Document document)
    {
        header[0] = new Phrase("Movie history");
    }
    
    
    /// <summary>
    /// Increase the page number.
    /// </summary>
    /// <param name="writer"></param>
    /// <param name="document"></param>
    public void OnStartPage(PdfWriter writer, Document document)
    {
        pagenumber++;
    }
    }       

    以上的代码比较好懂,我们在类中定义了两个变量:

    • header----一个包含了两个Pharse对象的数组,一个在OnOpenDocument方法中初始化,这样就可以在整个文档操作过程中使用。一个定义在OnChapter方法中
    • pagenumber----一个定义的页码数,每次创建一个Chapter对象时重置为1。

    在一页完成之前没有内容通过页面事件添加到文档中,我们添加的页眉和页脚都是在OnEndPage方法实现的。这里我们要注意的是通过GetBoxSize方法获取art box,然后使用此crop box来定位页眉和页脚,但我们首先要定义crop box,要不然就会返回null值。在接下来的列子中我们会将页码加到页眉中并显示总的页码数。

    Solving the “page X of Y” problem

    下图就是"page X of Y"的一个具体实例:

    PageXOfY

    获取X的值比较容易,我们可以在OnEndPage方法中获取PdfWriter对象,然后调用其PageNumber即可,但是我们如何获取Y的值呢?在文档还没有构建好的情况下我们是不知道总的页码数,只有在写完最好一页的情况下才可以计算出来。这个问题有两个解决方案,其中一个就是通过两次构建pdf的过程来完成,这个方法在后续章节中会说明,还有一个就是通过PdfTemplate对象和Page Event完成。

    在第三节学习XObject的时候我们知道除非显示的调用ReleaseTemplate方法,否则iText会将此对象一直保存在内存中直到文档关闭。利用这个特性我们可以在每一个页中添加PdfTemplate,然后等待文档的关闭,之后再将页面的总数添加到PdfTemplate中,这样即使第一页的内容已经写入到输出流PdfTemplate中的数据还是会显示在第一页中。

    listing 5.20 MovieCountries1.cs

    public  class TableHeader : IPdfPageEvent
    {
        /** The header text. */
        string header;
    
        public string Header
        {
            get { return header; }
            set { header = value; }
        }
    
        /** The template with the total number of pages. */
        PdfTemplate total;
    
        public TableHeader()
        {
        }
    
        public TableHeader(string header)
        {
            this.header = header;
        }
    
        /// <summary>
        /// Fills out the total number of pages before the document is closed.
        /// </summary>
        /// <param name="writer"></param>
        /// <param name="document"></param>
        public void OnCloseDocument(PdfWriter writer, Document document)
        {
            ColumnText.ShowTextAligned(total, Element.ALIGN_LEFT, new Phrase((writer.PageNumber - 1).ToString()), 2, 2, 0);
        }
    
        /// <summary>
        /// Adds a header to every page
        /// </summary>
        /// <param name="writer"></param>
        /// <param name="document"></param>
        public void OnEndPage(PdfWriter writer, Document document)
        {
            PdfPTable table = new PdfPTable(3);
            try
            {
                table.SetWidths(new int[] { 24, 24, 2 });
                table.TotalWidth = 527;
                table.LockedWidth = true;
                table.DefaultCell.FixedHeight = 20;
                table.DefaultCell.Border = Rectangle.BOTTOM_BORDER;
                table.AddCell(header);
                table.DefaultCell.HorizontalAlignment = Element.ALIGN_RIGHT;
                table.AddCell(string.Format("Page {0} of", writer.PageNumber));
                PdfPCell cell = new PdfPCell(Image.GetInstance(total));
                cell.Border = Rectangle.BOTTOM_BORDER;
                table.AddCell(cell);
                table.WriteSelectedRows(0, -1, 34, 803, writer.DirectContent);
            }
            catch (DocumentException)
            {
    
                throw;
            }
        }
    
        /// <summary>
        /// Creates the PdfTemplate that will hold the total number of pages.
        /// </summary>
        /// <param name="writer"></param>
        /// <param name="document"></param>
        public void OnOpenDocument(PdfWriter writer, Document document)
        {
            total = writer.DirectContent.CreateTemplate(30, 16);
        }           
    }

    在以上代码中:我们先在OnOpenDocument方法中定义一个PdfTemplate并设置大小为30pt*16pt,然后在OnEndPage方法中我们构建一个表格来画页眉,此表格为1行三列。第一个单元格添加的内容为country,第二个单元格添加的内容为"page X of",第三个单元格就比较特殊:我们将PdfTemplate包含在Image中,但这个时候还没有数据添加到PdfTemplate中。最后在OnCloseDocument中往PdfTemplate写入中的页码数,这样所有的页眉都引用了PdfTemplate,总的页码数也随之显示出来。这样还要注意的是当文档被关闭之前,当前页会调用NewPage方法进行一些资源释放的操作,但NewPage方法会将页码增加,所以我们需要减去1获取真正的页码数。在前面的列子我们一般通过ShowTextAligned方法往页眉和页脚写数据,但在这里我们可以通过表格的WriteSelectedRows方法在页眉中添加内容,这是比较好的一个方法,因此通过表格的架构我们可以对线,图和文本进行一些设置。在创建文档的过程中还有一个通常的需求就是添加水印。

    Add a watermark

    接下来的列子是对前一个列子的扩展,主要的区别就是新加了一个功能:水印。具体的效果图见下:

    WaterPrint

    由于是对前面的扩展,代码基本上差不多,只是添加了一个Watermark类来添加水印。

    listing 5.21 MovieCountries2.cs

    class Watermark : IPdfPageEvent
    {
        Font FONT = new Font(Font.FontFamily.HELVETICA, 52, Font.BOLD, new GrayColor(0.75f));
         
        public void OnEndPage(PdfWriter writer, Document document)
        {
            ColumnText.ShowTextAligned(writer.DirectContentUnder, Element.ALIGN_CENTER, new Phrase("FOOBAR FILM FESTIVAL", FONT),
                297.5f, 421, writer.PageNumber % 2 == 1 ? 45 : -45);
        }    
    }

    以上代码中水印是以文本的形式添加的,如果我们的水印是图片则可以有多种选择:通过PdfContentByte.AddImage方法或者将其包裹在ColumnText对象抑或将其放入到表格的单元格中。当我们将页面事件中处理图片时要确认图片只创建了一次,比如在OnOpenDocument方法中创建,如果我们在OnStartPage或者OnEndPage方法中创建则可能将同样的图片添加多次,这不仅会损坏性能而且增加了文档的大小。

    接下来我们会介绍一个功能:文档的每一页自动呈现出来,就如同PPT一样。

    Creating a slideshow

    在我们读文档的时候我们一般按某个按钮,点击鼠标或者直接滚动到下一页,不过我们可以让PDF阅览器在几秒钟之后自动过度到下一页。下面这个列子中我们设置了”Full Screen"(全屏)模式,因为我们将PDF文档当作PPT来使用。

    listing 5.22 MovieSlideShow.cs

    class TransitionDuration:IPdfPageEvent 
    {
        public void OnStartPage(PdfWriter writer, Document document)
        {
            writer.Transition = new PdfTransition(PdfTransition.DISSOLVE, 3);
            writer.Duration = 5;
        }           
    }
    PdfWriter writer = PdfWriter.GetInstance(document, new FileStream(fileName, FileMode.Create));
    writer.PdfVersion = PdfWriter.VERSION_1_5;
    writer.ViewerPreferences = PdfWriter.PageModeFullScreen;
    writer.PageEvent = new TransitionDuration();
    在OnStartPage方法中我们设置了Duration和Transition属性,Duration每一个页面呈现的时间,单位以秒计算。Transition接受一个Transition对象,其构造器有两个参数:一个为类型,一个transition的持续时间,这个时间是页面过度效果的时间和页面呈现的时间不同。iText中有很多Transition类型,具体的大家可以看源代码,每个类型的具体介绍大家就直接看书吧,我这里就不详述了。

    总结

    在这一节中我们通过IPdfPageEvent的4个方法学习如何添加页面页脚和水印,并介绍解决"Page X of Y"问题的方法,到这里整个的第一章就结束了,还有就是这一节的代码下载

    这里我们对整个的第一章总结一下:在第一节我们介绍了基本构建块的使用其中包括:Chunk,Phrase,Paragraphs,List,ListItem,Anchors,Image,Chapter和Section对象,整个第四节中我们介绍了PdfPTable和PdfPCell类。然后在第三节中我们学会了如何使用low-level的方法添加内容(线,图形,图和文本),并学习此节中两个很重要的类:ColumnText和XObject。最后在第五节中我们通过表格事件,单元格事件和页面事件来处理一些比较常见的需求。通过以上五节的学习大家可以从头开始用iText构建pdf文档,后续的章节中我们会学习如何操作已经存在的文档:如何将一个pdf文档中的页面导入到另一个文档中,如何为已经存在的文档添加一些内容,如何将不同的文档组合成一个更大的文档等等。

  • 相关阅读:
    Codeforces Round 546 (Div. 2)
    Codeforces Round 545 (Div. 2)
    Codeforces Round 544(Div. 3)
    牛客小白月赛12
    Codeforces Round 261(Div. 2)
    Codeforces Round 260(Div. 2)
    Codeforces Round 259(Div. 2)
    Codeforces Round 258(Div. 2)
    Codeforces Round 257 (Div. 2)
    《A First Course in Probability》-chaper5-连续型随机变量-随机变量函数的分布
  • 原文地址:https://www.cnblogs.com/julyluo/p/2601641.html
Copyright © 2011-2022 走看看