Skip to content

Commit 5b55e36

Browse files
committed
972107: Added Proper code for paginated TOC in PDF document
1 parent 0bc58de commit 5b55e36

File tree

7 files changed

+247
-0
lines changed

7 files changed

+247
-0
lines changed
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
2+
Microsoft Visual Studio Solution File, Format Version 12.00
3+
# Visual Studio Version 17
4+
VisualStudioVersion = 17.14.36221.1 d17.14
5+
MinimumVisualStudioVersion = 10.0.40219.1
6+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Multi-PDF-merger-with-paginated-TOC", "Multi-PDF-merger-with-paginated-TOC\Multi-PDF-merger-with-paginated-TOC.csproj", "{2DAE4BA6-7373-4CA1-8973-172FAC1350C8}"
7+
EndProject
8+
Global
9+
GlobalSection(SolutionConfigurationPlatforms) = preSolution
10+
Debug|Any CPU = Debug|Any CPU
11+
Release|Any CPU = Release|Any CPU
12+
EndGlobalSection
13+
GlobalSection(ProjectConfigurationPlatforms) = postSolution
14+
{2DAE4BA6-7373-4CA1-8973-172FAC1350C8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
15+
{2DAE4BA6-7373-4CA1-8973-172FAC1350C8}.Debug|Any CPU.Build.0 = Debug|Any CPU
16+
{2DAE4BA6-7373-4CA1-8973-172FAC1350C8}.Release|Any CPU.ActiveCfg = Release|Any CPU
17+
{2DAE4BA6-7373-4CA1-8973-172FAC1350C8}.Release|Any CPU.Build.0 = Release|Any CPU
18+
EndGlobalSection
19+
GlobalSection(SolutionProperties) = preSolution
20+
HideSolutionNode = FALSE
21+
EndGlobalSection
22+
GlobalSection(ExtensibilityGlobals) = postSolution
23+
SolutionGuid = {83C3C2C7-ADE1-4D8D-9CB9-310464C02705}
24+
EndGlobalSection
25+
EndGlobal
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
3+
<PropertyGroup>
4+
<OutputType>Exe</OutputType>
5+
<TargetFramework>net8.0</TargetFramework>
6+
<RootNamespace>Multi_PDF_merger_with_paginated_TOC</RootNamespace>
7+
<ImplicitUsings>enable</ImplicitUsings>
8+
<Nullable>enable</Nullable>
9+
</PropertyGroup>
10+
11+
<ItemGroup>
12+
<PackageReference Include="Syncfusion.Pdf.Net.Core" Version="*" />
13+
</ItemGroup>
14+
15+
</Project>

Bookmarks/Multi-PDF-merger-with-paginated-TOC/.NET/Multi-PDF-merger-with-paginated-TOC/Output/gitkeep.txt

Whitespace-only changes.
Lines changed: 207 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,207 @@
1+
using Syncfusion.Drawing;
2+
using Syncfusion.Pdf;
3+
using Syncfusion.Pdf.Graphics;
4+
using Syncfusion.Pdf.Interactive;
5+
using Syncfusion.Pdf.Parsing;
6+
using System.Collections.Generic;
7+
8+
class Program
9+
{
10+
public static PdfDocument document;
11+
public static PdfFont font;
12+
13+
static void Main(string[] args)
14+
{
15+
// Load source PDFs using file streams
16+
using (FileStream fsharpStream = new FileStream(Path.GetFullPath(@"Data/Fsharp_Succinctly.pdf"), FileMode.Open, FileAccess.Read))
17+
using (PdfLoadedDocument fsharpDoc = new PdfLoadedDocument(fsharpStream))
18+
19+
using (FileStream httpStream = new FileStream(Path.GetFullPath(@"Data/HTTP_Succinctly.pdf"), FileMode.Open, FileAccess.Read))
20+
using (PdfLoadedDocument httpDoc = new PdfLoadedDocument(httpStream))
21+
22+
using (FileStream windowsStoreStream = new FileStream(Path.GetFullPath(@"Data/WindowsStoreApps_Succinctly.pdf"), FileMode.Open, FileAccess.Read))
23+
using (PdfLoadedDocument windowsStoreDoc = new PdfLoadedDocument(windowsStoreStream))
24+
{
25+
26+
// Store all loaded documents in an array
27+
PdfLoadedDocument[] docs = { fsharpDoc, httpDoc, windowsStoreDoc };
28+
29+
30+
// Create new document
31+
document = new PdfDocument();
32+
document.PageSettings.Size = fsharpDoc.Pages[0].Size;
33+
34+
// Estimate TOC pages needed
35+
int totalEntries = 60;
36+
int entriesPerPage = 30;
37+
int tocPages = (int)Math.Ceiling(totalEntries / (double)entriesPerPage);
38+
39+
// Add TOC pages
40+
List<PdfPage> tocPagesList = new List<PdfPage>();
41+
for (int i = 0; i < tocPages; i++)
42+
{
43+
PdfPage tocPage = document.Pages.Add();
44+
tocPagesList.Add(tocPage);
45+
if (i == 0)
46+
{
47+
font = new PdfStandardFont(PdfFontFamily.Helvetica, 10f);
48+
PdfStringFormat format = new PdfStringFormat(PdfTextAlignment.Center, PdfVerticalAlignment.Middle);
49+
tocPage.Graphics.DrawString("Table Of Contents", font, PdfBrushes.Black,
50+
new RectangleF(PointF.Empty, new SizeF(tocPage.Graphics.ClientSize.Width, 20)), format);
51+
}
52+
}
53+
54+
// Merge PDFs after TOC pages
55+
PdfDocument.Merge(document, docs);
56+
57+
// Define TOC entries (title and corresponding page index)
58+
List<(string Title, int PageIndex)> tocEntries = GetTocEntries();
59+
60+
// Adjust TOC entries
61+
tocEntries = AdjustTOCEntriesWithOffset(tocEntries, tocPages);
62+
// Draw TOC entries
63+
float currentY = 30;
64+
int tocPageIndex = 0;
65+
PdfPage currentTocPage = tocPagesList[tocPageIndex];
66+
foreach (var (title, pageIndex) in tocEntries)
67+
{
68+
if (currentY > currentTocPage.Graphics.ClientSize.Height - 50)
69+
{
70+
tocPageIndex++;
71+
currentTocPage = tocPagesList[tocPageIndex];
72+
currentY = 30;
73+
}
74+
75+
currentY = AddBookmark(document.Pages[pageIndex], currentTocPage, title, currentY);
76+
}
77+
78+
//Create file stream.
79+
using (FileStream outputFileStream = new FileStream(Path.GetFullPath(@"Output/Output.pdf"), FileMode.Create, FileAccess.ReadWrite))
80+
{
81+
//Save the PDF document to file stream.
82+
document.Save(outputFileStream);
83+
}
84+
//Close the document
85+
document.Close(true);
86+
}
87+
}
88+
89+
// Generates a list of Table of Contents (TOC) entries with titles and corresponding page indices.
90+
static List<(string Title, int PageIndex)> GetTocEntries()
91+
{
92+
List<(string Title, int PageIndex)> entries = new List<(string, int)>();
93+
94+
for (int i = 1; i <= 60; i++)
95+
{
96+
// Pages 1–20: F# document
97+
if (i <= 20)
98+
{
99+
entries.Add(($"F# - This is first document {i}", i));
100+
}
101+
// Pages 21–40: HTTP document
102+
else if (i > 20 && i <= 40)
103+
{
104+
entries.Add(($"HTTP - This is second document {i}", i));
105+
}
106+
// Pages 41–60: Windows Store Apps document
107+
else
108+
{
109+
entries.Add(($"Windows Store Apps - This is third document {i}", i));
110+
}
111+
}
112+
113+
return entries;
114+
}
115+
116+
//Adjusts the page indices of the Table of Contents (TOC) entries by adding an offset.
117+
public static List<(string Title, int PageIndex)> AdjustTOCEntriesWithOffset(List<(string Title, int PageIndex)> tocEntries, int tocPages)
118+
{
119+
List<(string, int)> adjustedList = new List<(string, int)>();
120+
121+
// Loop through each TOC entry and add the TOC page count to its page index
122+
foreach (var (title, pageIndex) in tocEntries)
123+
{
124+
adjustedList.Add((title, pageIndex + tocPages));
125+
}
126+
127+
return adjustedList;
128+
}
129+
130+
// Adding bookmarks and drawing TOC entries
131+
private static float AddBookmark(PdfPage page, PdfPage toc, string content, float currentY)
132+
{
133+
// Add a new bookmark to the document with the given title
134+
PdfBookmark bookmark = document.Bookmarks.Add(content);
135+
136+
// Draw the TOC entry text on the TOC page and get the updated Y-position
137+
float newY = AddTableOfContent(page, toc, content, new PointF(0, currentY));
138+
139+
// Create a named destination pointing to the specified page and location
140+
PdfNamedDestination namedDestination = new PdfNamedDestination(content)
141+
{
142+
Destination = new PdfDestination(page, new PointF(0, currentY))
143+
{
144+
Mode = PdfDestinationMode.FitToPage // Ensures the destination fits the entire page in view
145+
}
146+
};
147+
148+
// Add the named destination to the document's collection
149+
document.NamedDestinationCollection.Add(namedDestination);
150+
151+
// Link the bookmark to the named destination
152+
bookmark.NamedDestination = namedDestination;
153+
154+
// Return the updated Y-position for the next TOC entry
155+
return newY;
156+
}
157+
158+
//Draws a TOC entry on the specified page, adds the corresponding page number, and creates a clickable link that navigates to the target content page.
159+
private static float AddTableOfContent(PdfPage page, PdfPage toc, string content, PointF point)
160+
{
161+
// Define the width available for the TOC entry text
162+
float textWidth = toc.Graphics.ClientSize.Width - 60;
163+
164+
// Define the bounds where the TOC entry will be drawn
165+
RectangleF textBounds = new RectangleF(point.X, point.Y, textWidth, 100);
166+
167+
// Create a text element for the TOC entry
168+
PdfTextElement element = new PdfTextElement(content, font, PdfBrushes.Blue);
169+
170+
// Set layout format to fit within the page and allow pagination if needed
171+
PdfLayoutFormat format = new PdfLayoutFormat
172+
{
173+
Break = PdfLayoutBreakType.FitPage,
174+
Layout = PdfLayoutType.Paginate
175+
};
176+
177+
// Draw the TOC entry text and get the layout result
178+
PdfLayoutResult result = element.Draw(toc, textBounds, format);
179+
180+
// Draw the corresponding page number on the right side of the TOC page
181+
string pageNum = (document.Pages.IndexOf(page) + 1).ToString();
182+
PdfTextElement pageNumber = new PdfTextElement(pageNum, font, PdfBrushes.Black);
183+
pageNumber.Draw(toc, new PointF(toc.Graphics.ClientSize.Width - 40, point.Y));
184+
185+
// Create a clickable link annotation over the TOC entry text
186+
RectangleF bounds = result.Bounds;
187+
bounds.Width = textWidth;
188+
189+
PdfDocumentLinkAnnotation documentLinkAnnotation = new PdfDocumentLinkAnnotation(bounds)
190+
{
191+
AnnotationFlags = PdfAnnotationFlags.NoRotate,
192+
Text = content,
193+
Color = Color.Transparent, // Invisible clickable area
194+
Destination = new PdfDestination(page),
195+
Border = new PdfAnnotationBorder(0) // No visible border
196+
};
197+
198+
// Set the destination location on the target page
199+
documentLinkAnnotation.Destination.Location = point;
200+
201+
// Add the link annotation to the TOC page
202+
toc.Annotations.Add(documentLinkAnnotation);
203+
204+
// Return the updated Y-position for the next TOC entry
205+
return result.Bounds.Bottom + 10;
206+
}
207+
}

0 commit comments

Comments
 (0)