Nic*_*eve 1 pdf merge itext tableofcontents
我编写了一些代码,将多个 PDF 合并为一个 PDF,然后从 MemoryStream 中显示该 PDF。这很好用。我需要做的是将目录添加到文件末尾,并提供指向每个 PDF 开头的链接。我计划使用 GotoLocalPage 操作来执行此操作,该操作具有页码选项,但它似乎不起作用。如果我将下面代码的操作更改为 PDFAction.FIRSTPAGE 等 Presset 操作之一,它就可以正常工作。这是否不起作用,因为我使用 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());
Run Code Online (Sandbox Code Playgroud)
我想另一种选择是链接到命名元素(而不是页码),但我不知道如何在添加到合并文档之前将“不可见”元素添加到每个文件的开头?
我只需要两张通行证就可以了。在第一遍中,按原样进行合并,但还要记录它应链接到的文件名和页码。在第二遍中,使用 a PdfStamper
,这将使您可以访问 a ColumnText
,您可以使用像 in 一样的一般抽象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());
}
}
Run Code Online (Sandbox Code Playgroud)
下一个块将合并 PDF。这与您的代码基本相同,只是我不是在这里编写目录,而是跟踪我将来想要编写的内容。在我使用的地方,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();
}
Run Code Online (Sandbox Code Playgroud)
最后一个块实际上写入了目录。如果我们使用 aPdfStamper
我们可以创建一个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();
}
Run Code Online (Sandbox Code Playgroud)