簡體   English   中英

使用 TOC 元素合並 PDF 文件

[英]Merge PDF files with TOC element

我正在使用 GemBox.Pdf 合並 PDF 文件, 如下所示 這很好用,我可以輕松添加輪廓。

我以前做過類似的事情,並將 Word 文件與 GemBox.Document 合並, 如下所示

但現在我的問題是 GemBox.Pdf 中沒有 TOC 元素。 我想在將多個 PDF 文件合並為一個時自動獲取目錄。

我是不是遺漏了什么,或者 PDF 真的沒有這樣的元素?
我是否需要重新創建它,如果是,那么我該怎么做?
我可以添加書簽,但我不知道如何添加指向它的鏈接。

PDF文件中沒有這樣的元素,所以我們需要自己創建這個內容。

現在一種方法是創建文本元素、輪廓和鏈接注釋,適當放置它們,並將鏈接目標設置為輪廓。

但是,這可能需要一些工作,因此使用 GemBox.Document 創建所需的 TOC 元素,將其另存為 PDF 文件,然后將其導入生成的 PDF 中可能會更容易。

// Source data for creating TOC entries with specified text and associated PDF files.
var pdfEntries = new[]
{
    new { Title = "First Document Title", Pdf = PdfDocument.Load("input1.pdf") },
    new { Title = "Second Document Title", Pdf = PdfDocument.Load("input2.pdf") },
    new { Title = "Third Document Title", Pdf = PdfDocument.Load("input3.pdf") },
};

/***************************************************************/
/* Create new document with TOC element using GemBox.Document. */
/***************************************************************/

// Create new document.
var tocDocument = new DocumentModel();
var section = new Section(tocDocument);
tocDocument.Sections.Add(section);

// Create and add TOC element.
var toc = new TableOfEntries(tocDocument, FieldType.TOC);
section.Blocks.Add(toc);
section.Blocks.Add(new Paragraph(tocDocument, new SpecialCharacter(tocDocument, SpecialCharacterType.PageBreak)));

// Create heading style.
// By default, when updating TOC element a TOC entry is created for each paragraph that has heading style.
var heading1Style = (ParagraphStyle)tocDocument.Styles.GetOrAdd(StyleTemplateType.Heading1);

// Add heading and empty (placeholder) pages.
// The number of added placeholder pages depend on the number of pages that actual PDF file has so that TOC entries have correct page numbers.
int totalPageCount = 0;
foreach (var pdfEntry in pdfEntries)
{
    section.Blocks.Add(new Paragraph(tocDocument, pdfEntry.Title) { ParagraphFormat = { Style = heading1Style } });
    section.Blocks.Add(new Paragraph(tocDocument, new SpecialCharacter(tocDocument, SpecialCharacterType.PageBreak)));

    int currentPageCount = pdfEntry.Pdf.Pages.Count;
    totalPageCount += currentPageCount;

    while (--currentPageCount > 0)
        section.Blocks.Add(new Paragraph(tocDocument, new SpecialCharacter(tocDocument, SpecialCharacterType.PageBreak)));
}

// Remove last extra-added empty page.
section.Blocks.RemoveAt(section.Blocks.Count - 1);

// Update TOC element and save the document as PDF stream.
toc.Update();
var pdfStream = new MemoryStream();
tocDocument.Save(pdfStream, new GemBox.Document.PdfSaveOptions());

/***************************************************************/
/* Merge PDF files into PDF with TOC element using GemBox.Pdf. */
/***************************************************************/

// Load a PDF stream using GemBox.Pdf.
var pdfDocument = PdfDocument.Load(pdfStream);
var rootDictionary = (PdfDictionary)((PdfIndirectObject)pdfDocument.GetDictionary()[PdfName.Create("Root")]).Value;
var pagesDictionary = (PdfDictionary)((PdfIndirectObject)rootDictionary[PdfName.Create("Pages")]).Value;
var kidsArray = (PdfArray)pagesDictionary[PdfName.Create("Kids")];
var pageIds = kidsArray.Cast<PdfIndirectObject>().Select(obj => obj.Id).ToArray();

// Remove empty (placeholder) pages.
while (totalPageCount-- > 0)
    pdfDocument.Pages.RemoveAt(pdfDocument.Pages.Count - 1);

// Add pages from PDF files.
foreach (var pdfEntry in pdfEntries)
    foreach (var page in pdfEntry.Pdf.Pages)
        pdfDocument.Pages.AddClone(page);

/*****************************************************************************/
/* Update TOC links from placeholder pages to actual pages using GemBox.Pdf. */
/*****************************************************************************/

// Create a mapping from an ID of a empty (placeholder) page indirect object to an actual page indirect object.
var pageCloneMap = new Dictionary<PdfIndirectObjectIdentifier, PdfIndirectObject>();
for (int i = 0; i < kidsArray.Count; ++i)
    pageCloneMap.Add(pageIds[i], (PdfIndirectObject)kidsArray[i]);

foreach (var entry in pageCloneMap)
{
    // If page was updated, it means that we passed TOC pages, so break from the loop.
    if (entry.Key != entry.Value.Id)
        break;

    // For each TOC page, get its 'Annots' entry.
    // For each link annotation from the 'Annots' get the 'Dest' entry.
    // Update the first item in the 'Dest' array so that it no longer points to a removed page.
    if (((PdfDictionary)entry.Value.Value).TryGetValue(PdfName.Create("Annots"), out PdfBasicObject annotsObj))
        foreach (PdfIndirectObject annotObj in (PdfArray)annotsObj)
            if (((PdfDictionary)annotObj.Value).TryGetValue(PdfName.Create("Dest"), out PdfBasicObject destObj))
            {
                var destArray = (PdfArray)destObj;
                destArray[0] = pageCloneMap[((PdfIndirectObject)destArray[0]).Id];
            }
}

// Save resulting PDF file.
pdfDocument.Save("Result.pdf");
pdfDocument.Close();

通過這種方式,您可以使用 TOC 開關和樣式輕松自定義 TOC 元素。 有關更多信息,請參閱 GemBox.Document 中的目錄示例

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM