iTextSharp-在合并的PDF中使用pdprotion.GotoLocalPage

iTextSharp-在合并的PDF中使用pdprotion.GotoLocalPage,pdf,merge,itextsharp,itext,tableofcontents,Pdf,Merge,Itextsharp,Itext,Tableofcontents,我编写了一些代码,将多个PDF合并成一个PDF,然后从MemoryStream中显示。这很有效。我需要做的是在文件末尾添加一个目录,其中包含指向每个PDF开头的链接。我计划使用GotoLocalPage操作来实现这一点,该操作有一个页码选项,但似乎不起作用。如果我将下面代码中的操作更改为一个按按钮的操作,如pdprotion.FIRSTPAGE,则可以正常工作。因为我正在对GotoLocalPage的writer参数使用PDFCopy对象,所以这样做不起作用吗 Document mergedDo

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

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());

我想另一种方法是链接到命名元素而不是页码,但在添加到合并文档之前,我看不出如何在每个文件的开头添加“不可见”元素。在第一个过程中,按原样进行合并,但还要记录文件名和它应该链接到的页码。在第二步中,使用PdfStamper,它将允许您访问可以使用一般抽象(如中的段落)的ColumnText。下面是一个示例,展示了这一点:

由于我没有您的文档,下面的代码创建了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());
    }
}
最后一个块实际上写入了TOC。如果我们使用PdfStamper,我们可以创建一个允许我们使用段落的ColumnText


我只要两次传球就行了。在第一个过程中,按原样进行合并,但还要记录文件名和它应该链接到的页码。在第二步中,使用PdfStamper,它将允许您访问可以使用一般抽象(如中的段落)的ColumnText。下面是一个示例,展示了这一点:

由于我没有您的文档,下面的代码创建了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());
    }
}
最后一个块实际上写入了TOC。如果我们使用PdfStamper,我们可以创建一个允许我们使用段落的ColumnText


布鲁诺的可能复制品:有没有一种方法可以在没有toc.pdf的情况下做到这一点?另外,有没有一个原因,为什么它不工作的页码?使用.AddDocument会很好,而不必在页面中循环。在我看来,很明显,它不适用于页码,但我不知道如何解释为什么它是显而易见的……这可能有助于理解PDF的一些内容。首先,PDF使用文档唯一标识符来引用内容。第二,PDF没有页码,这只是iText为方便您而做的事情。当您创建转到第3页链接时,您会传递一个PdfWriter实例,iText会将您的第3页转换为这个特定编写器的第三页,可能类似于5 0 R对象5。但是,您试图从一个作者副本中获取引用,并将其与另一个作者一起使用,而另一个作者不会。Bruno的可能副本:在没有toc.pdf的情况下,有没有办法做到这一点?另外,有没有一个原因,为什么它不工作的页码?使用.AddDocument会很好,而不必在页面中循环。在我看来,很明显,它不适用于页码,但我不知道如何解释为什么它是显而易见的……这可能有助于理解PDF的一些内容。首先,PDF使用文档唯一标识符来引用内容。第二,PDF没有页码,这只是iText为方便您而做的事情。当您创建转到第3页链接时,您会传递一个PdfWriter实例,iText会将您的第3页转换为这个特定编写器的第三页,可能类似于5 0 R对象5。然而,您正试图从一个作者副本中获取引用,并将它们与另一个作者一起使用,而另一个作者不会这样做。
//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();
}