zoukankan      html  css  js  c++  java
  • <<iText in Action 2nd>>3.4节(Creating reusable content)读书笔记

    前言

    在这一节中我们会讨论两个可重用的对象:Image和PdfTemplate对象。在2.3节我们往文档中添加图片的时候其实就已经接触到Image对象。在一般情况下,图片的字节会被保存在pdf文件中分开的流中,页面如果想要包含这个图片只需引用即可,这种类型的对象也叫做XObject。XObject有很多的类型,不过Image和Form XObject是最重要的。

    Image XObjects

    当我们将图片添加到文档中时其实就已经和image XObject打交道了,我们知道通过Document.Add方法添加的图片都位于文本的下面,但如果我们希望图片在文本的上面那要如何实现呢?

    将图片添加到最上层

    这里直接上图和代码:

    ImageCoverText

    listing 3.23 ImageDirect.cs

    string RESOURCE = ConfigurationManager.AppSettings["ResourceImage"];
    Image img = Image.GetInstance(RESOURCE + "loa.jpg");
    
    img.SetAbsolutePosition((PageSize.POSTCARD.Width - img.ScaledWidth) / 2, (PageSize.POSTCARD.Height - img.ScaledHeight) / 2);
    writer.DirectContent.AddImage(img);
    Paragraph p = new Paragraph("Foobar Film Festival", new Font(Font.FontFamily.HELVETICA, 22));
    p.Alignment = Element.ALIGN_CENTER;
    document.Add(p);

    在代码中我们将包含了文本"Foobar Film Festival"的Paragraph添加到文档中,但是从图上可以看到,其文本被图片覆盖了,而且从图上可以看到我们还是可以选择这段文本的,如果copy出来就会看到文本,而且Adobe Reader还提供了一个选项:Look up "Foobar"(英文的Adobe有这个选项,中文测试没有)。

    如果我们直接用记事本打开pdf文件,可以看到以下的pdf语法片段:

    q
    BT
    30 386 Td
    11.87 -33 Td
    /F1 22 Tf
    (Foobar Film Festival)Tj
    -11.87 0 Td
    ET
    Q
    q 232 0 0 362 25.5 27 cm /img0 Do Q

    在第一个q/Q之间的负责打印文本"Foobar Film Festival",其之间的符号是修改CTM(current transformation matrix)。第二个q/Q就是添加图片,其中Do操作符是将图片以232*362 user space units大小在坐标x=25.5,y=27的地方被添加到文档中,而图片的内容是保存在内容流的外部。

    在pdf内部,每一页都有一个包含了大量键值对的页面字典(page dictionary)。然后我们可以通过key在/Resources下找到对应的值:

    /Resources<</XObject<</img0 1 0 R>> ... >>

    以上就是XObjects的入口,也告诉我们/img0 可以在object 1中找到:包含了图片字节的流。这里其实还有大量其它类型的Resource,如页面引用的文件等。

    在前几节中,我们学会了如何缩放,旋转图片,但是通过以上代码添加的图片还可以有更多的选择。

    倾斜图片

    以下就是具体的代码和效果图:

    ImageSkew

    在我们进行倾斜的操作时其中包含了大量的代数计算,具体的细节大家可以参考14.3节。

    listing 3.24 ImageSkew.cs

     // Add the image to the upper layer
    string resouce = ConfigurationManager.AppSettings["ResourceImage"] + "loa.jpg";
    Image img = Image.GetInstance(resouce);
    writer.DirectContent.AddImage(img, img.Width, 0, 0.35f * img.Height, 0.65f * img.Height, 30, 30);

    以上代码中格外的参数最终生成的PDF语句如下:

    q 232 0 126.7 235.3 30 30 cm /img0 Do Q

    如果我们不希望将图片以XObject添加到文档中,那么可以将图片以内联的方法添加进去。

    图片内联

    图片在内容流中是以字节的方式添加进去的话那么图片就是内联的。

    listing 3.25 ImageInline.cs

    string resouce = ConfigurationManager.AppSettings["ResourceImage"] + "loa.jpg";
    Image img = Image.GetInstance(resouce);
    img.SetAbsolutePosition((PageSize.POSTCARD.Width - img.ScaledWidth) / 2, (PageSize.POSTCARD.Height - img.ScaledHeight) / 2);
    writer.DirectContent.AddImage(img, true);

    现在我们在看pdf的语句时你会在页面的内容流中看到图片的信息,其位于操作符BI(Begin image)和EI(End image)之间,以下为pdf的语句:

    q 232 0 0 362 25.5 27 cm
    BI
    /CS /DeviceRGB
    /BPC 8
    /W 232
    /H 362
    /F /DCTDecode
    ID
    ...
    EI
    Q

    但是如果图片内联的话就不能被重用,所以这不是添加图片的最好方法,添加图片时最好使用image XObject。

    还有一种类型的XObject就是form XObject,其完全的内容流被当作单个的图形对象。

    The PdfTemplate object

    单词form在这个上下文中有点模糊,因为我们讨论的不是一般提到的要填充的表单。为了避免此困扰,iText将form XObject定位为对象PdfTemplate。

    PDFTEMPLATE: ANOTHER NAME FOR FORM XOBJECT

    PdfTemplate是一个可以包含任意序列的图形对象而且是自我描述的Pdf内容流。PdfTemplate对象继承于PdfContentByte对象,因此继承了其父类的所有方法。PdfTemplate一个格外的有自定义尺寸的模板层(template layer),我们可以使用其完成一些不同的任务。

    假设你想将数据中所有电影的封面全部在一页中打印出来,为了更好的好看,我们还希望画一些长条让电影的方面看起来就像镜框一样,具体的效果看下图:

    TemplateWithStrip

    数据库中有120部电影,所以在一行中容纳12部电影,我们需要画10行,但我们只需创建一次就好了。

    listing 3.26 MoviePosters.cs

    // Create the XObject
    PdfTemplate celluloid = canvas.CreateTemplate(595, 84.2f);
    celluloid.Rectangle(8, 8, 579, 68);
    
    for (float f = 8.25f; f < 581; f += 6.5f)
    {
        celluloid.RoundRectangle(f, 8.5f, 6, 3, 1.5f);
        celluloid.RoundRectangle(f, 72.5f, 6, 3, 1.5f);
    }
    
    celluloid.SetGrayFill(0.1f);
    celluloid.EoFill();
    // Write the XObject to the OutputStream
    writer.ReleaseTemplate(celluloid);

    代码中通过特定的层来创建PdfTemplate,并且传入自定义的长宽,这里我们定义的长度和页面一样,但高度只有页面的十分之一。然后再里面画了一个大的矩形,矩形里面有很多一系列小的圆矩形。下面的代码比较的特殊:

    你可能认为的是调用Fill方法,但是这个方法也会将里面的圆矩形也填充,因此使用EoFill方法来实现,图像就会使用奇偶规则(even odd rule)。如果大家对这种规则不熟悉的话没有关系,具体的解释会在14.2节中展开。

    对于XObject流,最重要的要知道他们是被存储在单独的对象中:

    1 0 obj
    <</Length 153/Filter/FlateDecode>>stream
    8 8 579 68 re
    ...
    581.75 72.5 m
    584.75 72.5 l
    585.58 72.5 586.25 73.17 586.25 74 c
    586.25 74 l
    586.25 74.83 585.58 75.5 584.75 75.5 c
    581.75 75.5 l
    580.92 75.5 580.25 74.83 580.25 74 c
    580.25 74 l
    580.25 73.17 580.92 72.5 581.75 72.5 c
    0.1 g
    f*
    endstream

    完整的流比以上的片段要长很多哦,如果你仔细观察文件,你会发现在文件中XObject在逻辑上和物理上都(逻辑上:对象的数字是1,物理上:对象在第十五个字节被添加)是第一个被添加到文档中的。但这不是iText中的标准行为,一般情况下PdfTemplate对象会被一直保存在内存中直到我们调用Document.Add方法,或者显示的调用Writer.ReleaseTemplate方法。这样的行为是有目的的:我们会在第五节的时候发现将XObject保存在内存中的好处。

    ADDING PDFTEMPLATE OBJECTS

    因为是可重用的内容,因此我们不需要重复的将那一大串的PDF语句添加10次到页面的内容流中,相反它是按照以下方式被引用的:

    q 1 0 0 1 0 0 cm /Xf1 Do Q
    q 1 0 0 1 0 84.2 cm /Xf1 Do Q
    q 1 0 0 1 0 168.4 cm /Xf1 Do Q
    q 1 0 0 1 0 252.6 cm /Xf1 Do Q
    q 1 0 0 1 0 336.8 cm /Xf1 Do Q
    q 1 0 0 1 0 421 cm /Xf1 Do Q
    q 1 0 0 1 0 505.2 cm /Xf1 Do Q
    q 1 0 0 1 0 589.4 cm /Xf1 Do Q
    q 1 0 0 1 0 673.6 cm /Xf1 Do Q
    q 1 0 0 1 0 757.8 cm /Xf1 Do Q

    以上的pdf语句的片段也很好解释:form XObject /xf1 按照原有大小在坐标(0,y)(其中y是从0到757.8之间的数值,并且有一个84.2递增)被添加进去。

    以下的代码是两次将PdfTemplate对象按照特定的x,y坐标添加进去。

    listing 3.27 MoviePosters.cs(continued)

    // Add the XObject 10 times
    for (int i = 0; i < 10; i++)
    {
        canvas.AddTemplate(celluloid, 0, i * 84.2f);
    }
    
    List<Movie> movies = PojoFactory.GetMovies(conn);
    Image img;
    float x = 11.5f;
    float y = 769.7f;
    
    // Loop over the movies and add images
    foreach (Movie movie in movies)
    {
        string RESOURCE = ConfigurationManager.AppSettings["ResourcePosters"];
        img = Image.GetInstance(string.Format(RESOURCE, movie.IMDB));
        img.ScaleToFit(1000, 60);
        img.SetAbsolutePosition(x + (45 - img.ScaledWidth) / 2, y);
        canvas.AddImage(img);
        x += 48;
        if (x > 578)
        {
            x = 11.5f;
            y -= 84.2f;
        }
    }

    下图展示的是通过AddTemplate方法的其他版本:

    PdfTemplateCTM

    在上图中,电影的线条被添加了四次,但是被转动,缩放,倾斜和旋转。

    listing 3.28 MoviePosters.cs (continued)

    // Add the template using a different CTM
    canvas.AddTemplate(celluloid, 0.8f, 0, 0.35f, 0.65f, 0, 600);
    // Wrap the XObject in an Image object
    Image tmpImage = Image.GetInstance(celluloid);
    tmpImage.SetAbsolutePosition(0, 480);
    document.Add(tmpImage);
    
    // Perform transformations on the image
    tmpImage.RotationDegrees = 30;
    tmpImage.ScalePercent(80);
    tmpImage.SetAbsolutePosition(30, 500);
    document.Add(tmpImage);
    // More transformations
    tmpImage.Rotation = ((float) Math.PI/2);
    tmpImage.SetAbsolutePosition(200, 300);
    document.Add(tmpImage);

    方法AddTemplate中格外的参数是CTM的设置,我们可以使用其完成一些更加复杂的转换。但如果我们只需要移动,缩放或者旋转这个模板,我们可以将PdfTemplate包裹在Iamge中从而改善代码的可读性。

    WRAPPING A PDFTEMPLATE INSIDE AN IMAGE

    通过AddTemplate方法转换的语法如下:

    q 0.8 0 0.35 0.65 0 600 cm /Xf1 Do Q

    将其包裹在Image中设置的语法如下:

    q 1 0 0 1 0 480 cm /Xf1 Do Q
    q 0.69282 0.4 -0.4 0.69282 63.68 500 cm /Xf1 Do Q
    q 0 0.8 -0.8 0 267.36 300 cm /Xf1 Do Q

    大家可以看到其还是被当作XObject对象处理而不是被转换为image XObject。通过这种包裹的方法可以避免对一些CTM的计算,应该算是一种封装吧。最后我们通过PdfTemplate将前几节中创建的Film Festival的页面大小减少。

    ADAPTING THE TIMETABLE EXAMPLE

    在3.1节中,我们在文档的每一页中都画了两个Grid。但将此两个Grid添加到PdfTemplate应该是个重用的好例子。

    listing 3.29 MovieTemplates.cs

    PdfContentByte over = writer.DirectContent;
    PdfContentByte under = writer.DirectContentUnder;
    
    locations = PojoFactory.GetLocations(conn);
    PdfTemplate t_under = under.CreateTemplate(PageSize.A4.Height, PageSize.A4.Width);
    DrawTimeTable(t_under);
    
    PdfTemplate t_over = over.CreateTemplate(PageSize.A4.Height, PageSize.A4.Width);
    DrawTimeSlots(t_over);
    
    DrawInfo(t_over);
    
    List<DateTime> days = PojoFactory.GetDays(conn);
    List<Screening> screenings;
    
    int d = 1;
    foreach (DateTime day in days)
    {
        over.AddTemplate(t_over, 0, 0);
        under.AddTemplate(t_under, 0, 0);
        DrawDateInfo(day, d++, over);
        screenings = PojoFactory.GetScreenings(conn, day);
        foreach (Screening screening in screenings)
        {
            DrawBlock(screening, under, over);
            DrawMovieInfo(screening, over);
        }
    
        document.NewPage();
    }

    因为MovieTemplates继续MovieCalander,所以DrawTimeTable和DrawTimeSlots方法可以被重用。最后大家可以比较一下文件的大小:通过MovieCalendar生成的Pdf的大小是18.82kb,而通过MovieTemplates生成的pdf的大小是14.29kb,这也意味着我们通过XObject的使用可以节省大概25%的空间。

    总结:

    对XObject的使用主要目的是减少文件的大小,iText对Image对象有了内置的支持。对于PdfTemplate则需要自己创建,但我们可以设置PdfTemplate的CTM来实现很复杂的功能,如果只是简单的旋转,移动和缩放则可以将其包裹在Image中,然后调用Image对应的方法即可。在接下来的章节中我们会学到iText中最重要的俩个类:PdfPTable和PdfPCell,我们还会学到在PdfPCell对象的内部使用了ColumnText对象来处理每个单元格的文本。最后就是代码下载

    同步

    此文章已同步到目录索引:iText in Action 2nd 读书笔记。

  • 相关阅读:
    洛谷P3959 宝藏(状压dp)
    洛谷P3645 [APIO2015]雅加达的摩天楼(最短路+分块)
    洛谷P3646 [APIO2015]巴厘岛的雕塑(数位dp)
    洛谷P4770 [NOI2018]你的名字(后缀自动机+线段树)
    洛谷P4768 [NOI2018]归程(克鲁斯卡尔重构树+最短路)
    hive3.1.1 hive-site.xml
    mysql 远程连接数据库的二种方法
    linux彻底干干净净完全卸载 mysql
    jdk环境变量配置
    Eclipse常用快捷键
  • 原文地址:https://www.cnblogs.com/julyluo/p/2571501.html
Copyright © 2011-2022 走看看