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文档中的页面导入到另一个文档中,如何为已经存在的文档添加一些内容,如何将不同的文档组合成一个更大的文档等等。

  • 相关阅读:
    python连接SMTP的TLS(587端口)发邮件python发邮件(带认证,587端口)202010
    JAVA抓取通过JS渲染的网站(动态)网页数据
    moviepy音视频剪辑:与大小相关的视频变换函数详解
    区块链知识博文1: 共识算法之争(PBFT,Raft,PoW,PoS,DPoS,Ripple)
    MoviePy v2.0.0.dev1尚不成熟,不建议大家使用
    区块链学习2:一些概念性的基础知识笔记
    老猿学5G:3GPP 5G规范中的URI资源概念
    区块链学习1:Merkle树(默克尔树)和Merkle根
    老猿Python博客文章目录索引
    老猿学5G:融合计费场景的Nchf_ConvergedCharging_Create、Update和Release融合计费消息交互过程
  • 原文地址:https://www.cnblogs.com/julyluo/p/2601641.html
Copyright © 2011-2022 走看看