iTextSharp - 在合并PDF中使用PDFAction.GotoLocalPage

时间:2014-08-14 15:32:14

标签: pdf merge itextsharp itext tableofcontents

我编写了一些代码,将多个PDF合并为一个PDF,然后我从MemoryStream中显示。这非常有效。我需要做的是在文件的末尾添加一个目录,其中包含指向每个PDF的开头的链接。我计划使用GotoLocalPage操作执行此操作,该操作具有页码选项,但似乎不起作用。如果我将对下面代码的操作更改为像PDFAction.FIRSTPAGE这样的预设代码,它可以正常工作。这不起作用,因为我使用PDFCopy对象作为GotoLocalPage的writer参数吗?

Document mergedDoc = new Document();
MemoryStream ms = new MemoryStream();
PdfCopy copy = new PdfCopy(mergedDoc, ms);
mergedDoc.Open();

MemoryStream tocMS = new MemoryStream();
Document tocDoc = null;
PdfWriter tocWriter = null;

for (int i = 0; i < filesToMerge.Length; i++)
{
     string filename = filesToMerge[i];

     PdfReader reader = new PdfReader(filename);
     copy.AddDocument(reader);

     // Initialise TOC document based off first file
     if (i == 0)
     {
          tocDoc = new Document(reader.GetPageSizeWithRotation(1));
          tocWriter = PdfWriter.GetInstance(tocDoc, tocMS);
          tocDoc.Open();
     }

     // Create link for TOC, added random number of 3 for now
     Chunk link = new Chunk(filename);
     PdfAction action = PdfAction.GotoLocalPage(3, new PdfDestination(PdfDestination.FIT), copy);
     link.SetAction(action);
     tocDoc.Add(new Paragraph(link));
}

// Add TOC to end of merged PDF
tocDoc.Close();
PdfReader tocReader = new PdfReader(tocMS.ToArray());
copy.AddDocument(tocReader);

copy.Close();

displayPDF(ms.ToArray());

我想另一种方法是链接到一个命名元素(而不是页码),但是在添加到合并文档之前,我看不到如何在每个文件的开头添加一个“不可见”元素?

1 个答案:

答案 0 :(得分:3)

我会选择两次传球。在第一次传递中,按原样执行合并,但也记录它应链接到的文件名和页码。在第二次传递中,使用PdfStamper可以访问ColumnText,您可以使用Paragraph等常规抽象。下面是一个显示此内容的示例:

由于我没有您的文件,以下代码会创建10个文档,每个文档的随机页数仅用于测试目的。 (你显然不需要做这个部分。)它还创建了一个简单的字典,其中伪文件名作为键,PDF中的原始字节作为值。您有一个真正的文件集合可以使用,但您应该能够适应该部分。

//Create a bunch of files, nothing special here
//files will be a dictionary of names and the raw PDF bytes
Dictionary<string, byte[]> Files = new Dictionary<string, byte[]>();
var r = new Random();
for (var i = 1; i <= 10; i++) {
    using (var ms = new MemoryStream()) {
        using (var doc = new Document()) {
            using (var writer = PdfWriter.GetInstance(doc, ms)) {
                doc.Open();

                //Create a random number of pages
                for (var j = 1; j <= r.Next(1, 5); j++) {
                    doc.NewPage();
                    doc.Add(new Paragraph(String.Format("Hello from document {0} page {1}", i, j)));
                }
                doc.Close();
            }
        }
        Files.Add("File " + i.ToString(), ms.ToArray());
    }
}

下一个块合并PDF。这与你的代码大致相同,除了不是在这里写一个TOC,我只是跟踪我将来要写的东西。在我使用file.value的位置,您使用完整的文件路径以及我使用file.key的位置您将使用您的文件名称。

//Dictionary of file names (for display purposes) and their page numbers
var pages = new Dictionary<string, int>();

//PDFs start at page 1
var lastPageNumber = 1;

//Will hold the final merged PDF bytes
byte[] mergedBytes;

//Most everything else below is standard
using (var ms = new MemoryStream()) {
    using (var document = new Document()) {
        using (var writer = new PdfCopy(document, ms)) {
            document.Open();

            foreach (var file in Files) {

                //Add the current page at the previous page number
                pages.Add(file.Key, lastPageNumber);

                using (var reader = new PdfReader(file.Value)) {
                    writer.AddDocument(reader);

                    //Increment our current page index
                    lastPageNumber += reader.NumberOfPages;
                }
            }
        }
    }
    mergedBytes = ms.ToArray();
}

这最后一个块实际上写了TOC。如果我们使用PdfStamper,我们可以创建ColumnText,以便我们使用Paragraphs

//Will hold the final PDF
byte[] finalBytes;
using (var ms = new MemoryStream()) {
    using (var reader = new PdfReader(mergedBytes)) {
        using (var stamper = new PdfStamper(reader, ms)) {

            //The page number to insert our TOC into
            var tocPageNum = reader.NumberOfPages + 1;

            //Arbitrarily pick one page to use as the size of the PDF
            //Additional logic could be added or this could just be set to something like PageSize.LETTER
            var tocPageSize = reader.GetPageSize(1);

            //Arbitrary margin for the page
            var tocMargin = 20;

            //Create our new page
            stamper.InsertPage(tocPageNum, tocPageSize);

            //Create a ColumnText object so that we can use abstractions like Paragraph
            var ct = new ColumnText(stamper.GetOverContent(tocPageNum));

            //Set the working area
            ct.SetSimpleColumn(tocPageSize.GetLeft(tocMargin), tocPageSize.GetBottom(tocMargin), tocPageSize.GetRight(tocMargin), tocPageSize.GetTop(tocMargin));

            //Loop through each page
            foreach (var page in pages) {
                var link = new Chunk(page.Key);
                var action = PdfAction.GotoLocalPage(page.Value, new PdfDestination(PdfDestination.FIT), stamper.Writer);
                link.SetAction(action);
                ct.AddElement(new Paragraph(link));
            }

            ct.Go();
        }
    }
    finalBytes = ms.ToArray();
}