<>6.2节(Copying pages from existing PDF documents)读书笔记

前言

大家可能还记得我们在第五节中创建d的超人pdf文档,那个文档是从包含了Pdf语法的文本文件中构建起来的,图片看起来蛮cool,但那不是标准的构建方式。如果你希望可以重用现有文档内容,使用第五节中那些方法就比较危险。其实有更加安全的方法从现有文档中导入内容,这也是我们这一节要介绍的内容。

Importing pages

现在我们对第三节中创建的TimeTable文档进行一些操作。假设我们希望重用TimeTable文档中的内容,而且将其的每一页当作一个图片处理。下图就是如果用PdfPTable处理导出页面的效果。

<<iText in Action 2nd>>6.2节(Copying pages from existing PDF documents)读书笔记_第1张图片

代码如下:

listing 6.4 ImportingPages1.cs

document.Open();

PdfPTable table = new PdfPTable(2);
PdfReader reader = new PdfReader(new MovieTemplates().ContructFile());
int n = reader.NumberOfPages;
PdfImportedPage page;
for (int i = 1; i <= n; i++)
{
    page = writer.GetImportedPage(reader, i);
    table.AddCell(Image.GetInstance(page));
}
document.Add(table);

在以上代码中我们会发现使用了五步来构建PDF文档的过程。只是在这里我们通过PdfReader对象来循环现有文档的所有页面,然后通过GetImportedPage方法获取一个PdfImportedPage对象实例。但这些方法具体的含义是什么呢?

PAGE CONTENT AND RESOURCES

如果你浏览pdfReader类的API文档就会发现有一个GetPageContent方法,这个方法会返回一个页面的内容流。这个内容流和构建超人图片的hero.txt文件的内容很类似。一般情况下,这种内容流会包含一些外部的引用如图片和字体。

在3.4节中,画一个像素图的PDF语法如下:

q 232 0 0 362 25.5 27 cm /img0 Do Q

在上面的片段中/img0引用的是页面/Resources字典中的一个key,其对应的值就是包含图片的二进制内容。如果没有这些二进制内容,/img0这个pdf语法就没有意义了。

但在以前的超人列子有些特殊:画图片的语法都是自我描述和自我包含,这是很不寻常的情况。对其他的文档来说只要涉及到文本,那么我们至少有一个字体的引用。如果我们没有将字体copy过去,那么就会得到类似这样的警告:"Could not find a font in the Resoucres dictionary"。这样也是我们为什么从不用PdfReader类直接抽取页面的原因。相反我们应该将此PdfReader类传给PdfWriter类,然后通过PdfWriter(注意不是reader)去导出一个页面,这样就返回一个PdfImportedPage对象,在这种情况下所有必要的资源(比如图片和字体)都会被获取和copy过去。

使用PdfImportedPagel类时大家会发现页面所有的链接(links)全部都没有了。在这里我们要理解页面内容呈现需要的资源和页面交互特性之间的区别。这些特性一般也叫做注释(annotations),其中包括链接(links),文本注释(text annotations)和表单字段(form field)。大家要注意的是这些特性不属于页面内容流,他们没有包含在页面的资源字典中,而是在页面的注释字典中。这些交互的特性在使用PdfImportedPage对象时是不会被复制的。

PdfImportedPage对象继承于PdfTemplate,但是我们不能往其添加内容,这是一个只读XObject,我们只能通过AddTemplate方法去重用或者将其包裹在Image中,这些技巧都在3.4节中已经学习过。每一个导出页面的原始尺寸和页面初始media box一致,但PdfImportedPage对象会将其缩放以便添加到表格中。这里要注意的是原始页面的旋转通过PdfImportedPage对象时也是没有修改的,如果页面的旋转有问题我们还需要设置旋转相关的属性,代码如下:

listing 6.5 ImportingPages2.cs

document.Open();

PdfReader reader = new PdfReader(new MovieTemplates().ContructFile());
int n = reader.NumberOfPages;
PdfImportedPage page;
PdfPTable table = new PdfPTable(2);
for (int i = 1; i <= n; i++)
{
    page = writer.GetImportedPage(reader, i);
    table.DefaultCell.Rotation = -reader.GetPageRotation(i);
    table.AddCell(Image.GetInstance(page));
}
document.Add(table);

以上代码就是将现有文档的页面旋转了,注意旋转的方向为逆时针,接下来我们会学习更多的转换。

Scaling and superimposing pages

在iText中我们可以将页面转换就像前几节中转换图片一样。下图的左边是我们学习内容层用的文档,一共有四页,现在我们会将其导出并整合成一页显示,效果在下图的右边:

<<iText in Action 2nd>>6.2节(Copying pages from existing PDF documents)读书笔记_第2张图片

listing 6.6  Layers.cs

PdfContentByte canvas = writer.DirectContent;

PdfImportedPage page;
BaseFont bf = BaseFont.CreateFont(BaseFont.ZAPFDINGBATS, "", BaseFont.EMBEDDED);
for (int i = 0; i < reader .NumberOfPages ; )
{
    page = writer.GetImportedPage(reader, ++i);
    canvas.AddTemplate(page, 1f, 0, 0.4f, 0.4f, 72, 50 * i);
    canvas.BeginText();
    canvas.SetFontAndSize(bf, 20);
    canvas.ShowTextAligned(Element.ALIGN_CENTER, ((char)(181 + i)).ToString(), 496, 150 + 50 * i, 0);
    canvas.EndText();
}

在PDF文件中有一个比较常用的技巧:叠加(superimposing)。

SUPERIMPOSING PDF PAGES

叠加的意思在一页的pdf中我们将页面依次叠加起来,上一步中我们有4个页面,下图就是其4个页面叠加的效果图:

<<iText in Action 2nd>>6.2节(Copying pages from existing PDF documents)读书笔记_第3张图片

listing 6.7 Superimposing.cs

using (document)
{
    // step 3
    document.Open();
    // step 4
    PdfContentByte canvas = writer.DirectContent;

    PdfImportedPage page;
    for (int i = 1; i <= reader.NumberOfPages; i++)
    {
        page = writer.GetImportedPage(reader, i);
        canvas.AddTemplate(page, 1f, 0, 0, 1, 0, 0);

    }
}

叠加一般用来创建一个有标准页眉和页脚的文档。

IMPORTING COMPANY STATIONERY

有些公司会预印一些纸张,其中包含了公司的名称和logo或者还有水印,然后具体打印文档的时候就直接使用这些纸张打进行印。我们也可以通过PDF完成类似的效果:

<<iText in Action 2nd>>6.2节(Copying pages from existing PDF documents)读书笔记_第4张图片

上图的左边就是预印的页面,右边呈现的为添加了预印页面作为背景的一个新文档,添加的方法就是通过页面事件,具体代码如下:

listing 6.8 Stationery.cs

public void UseStationary(PdfWriter writer)
{
    writer.PageEvent = this;
    PdfReader reader = new PdfReader(stationery);
    page = writer.GetImportedPage(reader, 1);
}
public void OnEndPage(PdfWriter writer, iTextSharp.text.Document document)
{
    writer.DirectContentUnder.AddTemplate(page, 0, 0);
}

接下里我们会通过几个列子总结一下PdfImportedPage类的用法。

N-up copying and tiling PDF documents

当我们在google一些pdf工具时,你会发现有大量满足特定需求的小工具,比如创建一个N-UP布局的pdf文档。N-UP布局的意思就是将N页组合到一页中,下图就是2-UP,4-UP,8-UP,16-UP的效果图:

<<iText in Action 2nd>>6.2节(Copying pages from existing PDF documents)读书笔记_第5张图片

网上的这些小工具一般包含了iText类库,具体的代码如下:

listing 6.9 NUp.cs

public void ManipulatePdf(string src, string dest, int pow)
{
    // reader for the src file
    PdfReader reader = new PdfReader(src);
    // initializations
    Rectangle pageSize = reader.GetPageSize(1);
    Rectangle newSize = (pow % 2) == 0 ? new Rectangle(pageSize.Width, pageSize.Height) : new Rectangle(pageSize.Height, pageSize.Width);
    Rectangle unitSize = new Rectangle(pageSize.Width, pageSize.Height);
    for (int i = 0; i < pow; i++)
    {
        unitSize = new Rectangle(unitSize.Height / 2, unitSize.Width);
    }
    int n = (int)Math.Pow(2, pow);
    int r = (int)Math.Pow(2, pow / 2);
    int c = n / r;


    // step 1
    Document document = new Document(newSize, 0, 0, 0, 0);
    // step 2
    PdfWriter writer = PdfWriter.GetInstance(document, new FileStream(string.Format(dest, pow), FileMode.Create));

    using (document)
    {
        // step 3
        document.Open();
        // step 4
        PdfContentByte cb = writer.DirectContent;
        PdfImportedPage page;
        Rectangle currentSize;
        float offsetX, offsetY, factor;
        int total = reader.NumberOfPages;
        for (int i = 0; i < total; )
        {
            if (i % n == 0)
            {
                document.NewPage();
            }

            currentSize = reader.GetPageSize(++i);
            factor = Math.Min(unitSize.Width / currentSize.Width, unitSize.Height / currentSize.Height);
            offsetX = unitSize.Width * ((i % n) % c) + (unitSize.Width - (currentSize.Width * factor)) / 2f;
            offsetY = newSize.Height - (unitSize.Height * (((i % n) / c) + 1)) + (unitSize.Height - (currentSize.Height * factor)) / 2f;
            page = writer.GetImportedPage(reader, i);
            cb.AddTemplate(page, factor, 0, 0, factor, offsetX, offsetY);
        }

    }
}

和N-up布局相反的就是将一页分成多页,英文叫做TillPage,效果图如下:

<<iText in Action 2nd>>6.2节(Copying pages from existing PDF documents)读书笔记_第6张图片

这里列子我们在第五节的时候已经碰到了,这里我们使用PdfImportedPage类重新实现,具体代码如下:

listing 6.10 TilingHero.cs

PdfReader reader = new PdfReader(resouce);
Rectangle pagesize = reader.GetPageSizeWithRotation(1);

// step 1
Document document = new Document(pagesize);
// step 2
PdfWriter writer = PdfWriter.GetInstance(document, new FileStream(fileName, FileMode.Create));

using (document)
{
    // step 3
    document.Open();
    // step 4
    PdfContentByte content = writer.DirectContent;
    PdfImportedPage page = writer.GetImportedPage(reader, 1);

    // adding the same page 16 times with a different offset
    float x, y;

    for (int i = 0; i < 16; i++)
    {
        x = -pagesize.Width * (i % 4);
        y = pagesize.Height * (i / 4 - 3);
        content.AddTemplate(page, 4, 0, 0, 4, x, y);
        document.NewPage();
    }

}

总结

在这一节中我们学会从现有文档中重用内容,其中介绍了PdfImportedPage类,并学习如何缩放和转换页面,还介绍了N-up布局和TillPage的方法,这些知识点在代码中都统一在AddTemplate方法中,此方法有大量的参数,但作者在这一节中对其梅雨哦说明,直到书的14节(坐标系统转换)时才会对此方法详细介绍。这里我们知道有这些功能并能依葫芦画瓢应该就可以了,最后就是代码下载

同步

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

你可能感兴趣的:(document)