Following the pattern in this example https://kb.itextpdf.com/home/it7kb/examples/toc-as-first-page I implemented the table of contents (toc) within my own code. Everything works fine, with the following exception:
My toc has 3 pages and could grow
I can move the last two pages of pdf (2 pages of toc) to the top of the document but when I try to move the first page of the toc to the top I get a null exception
int tocPageNumber = pdfDoc.GetNumberOfPages();
pdfDoc.MovePage(tocPageNumber, 1);
pdfDoc.MovePage(tocPageNumber, 1);
pdfDoc.MovePage(tocPageNumber, 1); // Null Exception here
doc.Close();
This code is proof of concept. I'd have logic to determine how many pages there are in the toc and move them in a loop.
Screen shot:
Stack Trace:
at KernelExtensions.Get[TKey,TValue](IDictionary`2 col, TKey key)
at iText.Kernel.Pdf.PdfDictionary.Put(PdfName key, PdfObject value)
at iText.Kernel.Pdf.PdfPages.AddPage(Int32 index, PdfPage pdfPage)
at iText.Kernel.Pdf.PdfPagesTree.AddPage(Int32 index, PdfPage pdfPage)
at iText.Kernel.Pdf.PdfDocument.MovePage(Int32 pageNumber, Int32 insertBefore)
at BlueCoatExtractor.PDFHelper.buildPDFNatively1(String header, String dtStamp, Dictionary`2 bcPoliciesDict, String dest) in C:\Users\xyz\source\repos\bbb\bbb\PDFHelper.cs:line 216
Full Sample Code to recreate the issue:
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using iText.IO.Font.Constants;
using iText.Kernel.Font;
using iText.Kernel.Pdf;
using iText.Kernel.Pdf.Action;
using iText.Kernel.Pdf.Canvas.Draw;
using iText.Layout;
using iText.Layout.Element;
using iText.Layout.Hyphenation;
using iText.Layout.Layout;
using iText.Layout.Properties;
using iText.Layout.Renderer;
using iText.Kernel.Utils;
namespace BlueCoatExtractor
{
public class C06E04_TOC_GoToNamed
{
public const String SRC = "Logs/jekyll_hyde.txt";
public const String DEST = "Output/jekyll_hyde_toc2.pdf";
internal static void initialize()
{
FileInfo file = new FileInfo(DEST);
file.Directory.Create();
//new C06E04_TOC_GoToNamed().CreatePdf(DEST);
}
public static void CreatePdf()
{
//Initialize PDF document
PdfDocument pdf = new PdfDocument(new PdfWriter(DEST));
// Initialize document
Document document = new Document(pdf);
PdfFont font = PdfFontFactory.CreateFont(StandardFonts.TIMES_ROMAN);
PdfFont bold = PdfFontFactory.CreateFont(StandardFonts.HELVETICA_BOLD);
document.SetTextAlignment(TextAlignment.JUSTIFIED).SetHyphenation(new HyphenationConfig("en", "uk", 3, 3))
.SetFont(font).SetFontSize(11);
StreamReader sr = File.OpenText(SRC);
String name;
String line;
Paragraph p;
bool title = true;
int counter = 0;
IList<KeyValuePair<String, KeyValuePair<String, int>>> toc = new List<KeyValuePair
<String, KeyValuePair<String, int>>>();
while ((line = sr.ReadLine()) != null)
{
p = new Paragraph(line);
p.SetKeepTogether(true);
if (title)
{
name = String.Format("title{0:00}", counter++);
KeyValuePair<String, int> titlePage = new KeyValuePair<string, int>(line, pdf.GetNumberOfPages());
p.SetFont(bold).SetFontSize(12).SetKeepWithNext(true).SetDestination(name).SetNextRenderer(new UpdatePageRenderer(p, titlePage));
title = false;
document.Add(p);
toc.Add(new KeyValuePair<string, KeyValuePair<string, int>>(name, titlePage));
}
else
{
p.SetFirstLineIndent(36);
if (String.IsNullOrEmpty(line))
{
p.SetMarginBottom(12);
title = true;
}
else
{
p.SetMarginBottom(0);
}
document.Add(p);
}
}
document.Add(new AreaBreak(AreaBreakType.NEXT_PAGE));
p = new Paragraph().SetFont(bold).Add("Table of Contents").SetDestination("toc");
document.Add(p);
toc.RemoveAt(0);
IList<TabStop> tabstops = new List<TabStop>();
tabstops.Add(new TabStop(580, TabAlignment.RIGHT, new DottedLine()));
foreach (KeyValuePair<String, KeyValuePair<String, int>> entry in toc)
{
KeyValuePair<String, int> text = entry.Value;
p = new Paragraph().AddTabStops(tabstops).Add(text.Key).Add(new Tab()).Add(text.Value.ToString()).SetAction
(PdfAction.CreateGoTo(entry.Key));
document.Add(p);
}
//Close document
int tocPageNumber = pdf.GetNumberOfPages();
pdf.MovePage(tocPageNumber, 1);
pdf.MovePage(tocPageNumber, 1);
pdf.MovePage(tocPageNumber, 1); // null exception
document.Close();
}
protected internal class UpdatePageRenderer : ParagraphRenderer
{
protected internal KeyValuePair<String, int> entry;
public UpdatePageRenderer(Paragraph modelElement, KeyValuePair
<String, int> entry)
: base(modelElement)
{
this.entry = entry;
}
public override LayoutResult Layout(LayoutContext layoutContext)
{
LayoutResult result = base.Layout(layoutContext);
int pageNumber = layoutContext.GetArea().GetPageNumber();
entry = new KeyValuePair<string, int>(entry.Key, pageNumber);
return result;
}
}
}
}
Use the following text file: https://github.com/itext/i7js-highlevel/blob/develop/src/main/resources/txt/jekyll_hyde.txt
Based on the pattern in this code: https://github.com/itext/i7ns-samples/blob/develop/itext/itext.samples/itext/samples/sandbox/stamper/ReorderPages.cs
I created a new document and copied the content from the original into it. The table of content links still functioned as expected.
fs = File.Create("Output/resultDoc.pdf");
fs.Dispose();
PdfDocument srcDoc = new PdfDocument(new PdfReader(dest));
var srcTotalPages = srcDoc.GetNumberOfPages();
PdfDocument resultDoc = new PdfDocument(new PdfWriter("Output/resultDoc.pdf"));
resultDoc.InitializeOutlines();
IList<int> pages = new List<int>();
pages.Add(101);
pages.Add(102);
pages.Add(103);
for (int i = 1; i <= 100; i++)
{
pages.Add(i);
}
srcDoc.CopyPagesTo(pages, resultDoc);
resultDoc.Close();
srcDoc.Close();