diff --git a/Bookmarks/Multi-PDF-merger-with-paginated-TOC/.NET/Multi-PDF-merger-with-paginated-TOC.sln b/Bookmarks/Multi-PDF-merger-with-paginated-TOC/.NET/Multi-PDF-merger-with-paginated-TOC.sln new file mode 100644 index 00000000..48aa73d5 --- /dev/null +++ b/Bookmarks/Multi-PDF-merger-with-paginated-TOC/.NET/Multi-PDF-merger-with-paginated-TOC.sln @@ -0,0 +1,25 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.14.36221.1 d17.14 +MinimumVisualStudioVersion = 10.0.40219.1 +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}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {2DAE4BA6-7373-4CA1-8973-172FAC1350C8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {2DAE4BA6-7373-4CA1-8973-172FAC1350C8}.Debug|Any CPU.Build.0 = Debug|Any CPU + {2DAE4BA6-7373-4CA1-8973-172FAC1350C8}.Release|Any CPU.ActiveCfg = Release|Any CPU + {2DAE4BA6-7373-4CA1-8973-172FAC1350C8}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {83C3C2C7-ADE1-4D8D-9CB9-310464C02705} + EndGlobalSection +EndGlobal diff --git a/Bookmarks/Multi-PDF-merger-with-paginated-TOC/.NET/Multi-PDF-merger-with-paginated-TOC/Data/Fsharp_Succinctly.pdf b/Bookmarks/Multi-PDF-merger-with-paginated-TOC/.NET/Multi-PDF-merger-with-paginated-TOC/Data/Fsharp_Succinctly.pdf new file mode 100644 index 00000000..5b98b839 Binary files /dev/null and b/Bookmarks/Multi-PDF-merger-with-paginated-TOC/.NET/Multi-PDF-merger-with-paginated-TOC/Data/Fsharp_Succinctly.pdf differ diff --git a/Bookmarks/Multi-PDF-merger-with-paginated-TOC/.NET/Multi-PDF-merger-with-paginated-TOC/Data/HTTP_Succinctly.pdf b/Bookmarks/Multi-PDF-merger-with-paginated-TOC/.NET/Multi-PDF-merger-with-paginated-TOC/Data/HTTP_Succinctly.pdf new file mode 100644 index 00000000..5810acec Binary files /dev/null and b/Bookmarks/Multi-PDF-merger-with-paginated-TOC/.NET/Multi-PDF-merger-with-paginated-TOC/Data/HTTP_Succinctly.pdf differ diff --git a/Bookmarks/Multi-PDF-merger-with-paginated-TOC/.NET/Multi-PDF-merger-with-paginated-TOC/Data/WindowsStoreApps_Succinctly.pdf b/Bookmarks/Multi-PDF-merger-with-paginated-TOC/.NET/Multi-PDF-merger-with-paginated-TOC/Data/WindowsStoreApps_Succinctly.pdf new file mode 100644 index 00000000..27f07bf7 Binary files /dev/null and b/Bookmarks/Multi-PDF-merger-with-paginated-TOC/.NET/Multi-PDF-merger-with-paginated-TOC/Data/WindowsStoreApps_Succinctly.pdf differ diff --git a/Bookmarks/Multi-PDF-merger-with-paginated-TOC/.NET/Multi-PDF-merger-with-paginated-TOC/Multi-PDF-merger-with-paginated-TOC.csproj b/Bookmarks/Multi-PDF-merger-with-paginated-TOC/.NET/Multi-PDF-merger-with-paginated-TOC/Multi-PDF-merger-with-paginated-TOC.csproj new file mode 100644 index 00000000..0fd72bd6 --- /dev/null +++ b/Bookmarks/Multi-PDF-merger-with-paginated-TOC/.NET/Multi-PDF-merger-with-paginated-TOC/Multi-PDF-merger-with-paginated-TOC.csproj @@ -0,0 +1,15 @@ + + + + Exe + net8.0 + Multi_PDF_merger_with_paginated_TOC + enable + enable + + + + + + + diff --git a/Bookmarks/Multi-PDF-merger-with-paginated-TOC/.NET/Multi-PDF-merger-with-paginated-TOC/Output/gitkeep.txt b/Bookmarks/Multi-PDF-merger-with-paginated-TOC/.NET/Multi-PDF-merger-with-paginated-TOC/Output/gitkeep.txt new file mode 100644 index 00000000..e69de29b diff --git a/Bookmarks/Multi-PDF-merger-with-paginated-TOC/.NET/Multi-PDF-merger-with-paginated-TOC/Program.cs b/Bookmarks/Multi-PDF-merger-with-paginated-TOC/.NET/Multi-PDF-merger-with-paginated-TOC/Program.cs new file mode 100644 index 00000000..6b512038 --- /dev/null +++ b/Bookmarks/Multi-PDF-merger-with-paginated-TOC/.NET/Multi-PDF-merger-with-paginated-TOC/Program.cs @@ -0,0 +1,205 @@ +using Syncfusion.Drawing; +using Syncfusion.Pdf; +using Syncfusion.Pdf.Graphics; +using Syncfusion.Pdf.Interactive; +using Syncfusion.Pdf.Parsing; +using System.Collections.Generic; + +class Program +{ + public static PdfDocument document; + public static PdfFont font; + + static void Main(string[] args) + { + // Load source PDFs using file streams + using (FileStream fsharpStream = new FileStream(Path.GetFullPath(@"Data/Fsharp_Succinctly.pdf"), FileMode.Open, FileAccess.Read)) + using (PdfLoadedDocument fsharpDoc = new PdfLoadedDocument(fsharpStream)) + + using (FileStream httpStream = new FileStream(Path.GetFullPath(@"Data/HTTP_Succinctly.pdf"), FileMode.Open, FileAccess.Read)) + using (PdfLoadedDocument httpDoc = new PdfLoadedDocument(httpStream)) + + using (FileStream windowsStoreStream = new FileStream(Path.GetFullPath(@"Data/WindowsStoreApps_Succinctly.pdf"), FileMode.Open, FileAccess.Read)) + using (PdfLoadedDocument windowsStoreDoc = new PdfLoadedDocument(windowsStoreStream)) + { + + // Store all loaded documents in an array + PdfLoadedDocument[] docs = { fsharpDoc, httpDoc, windowsStoreDoc }; + + // Create new document + document = new PdfDocument(); + document.PageSettings.Size = fsharpDoc.Pages[0].Size; + + // Estimate TOC pages needed + int totalEntries = 60; + int entriesPerPage = 30; + int tocPages = (int)Math.Ceiling(totalEntries / (double)entriesPerPage); + + // Add TOC pages + List tocPagesList = new List(); + for (int i = 0; i < tocPages; i++) + { + PdfPage tocPage = document.Pages.Add(); + tocPagesList.Add(tocPage); + if (i == 0) + { + font = new PdfStandardFont(PdfFontFamily.Helvetica, 10f); + PdfStringFormat format = new PdfStringFormat(PdfTextAlignment.Center, PdfVerticalAlignment.Middle); + tocPage.Graphics.DrawString("Table Of Contents", font, PdfBrushes.Black, + new RectangleF(PointF.Empty, new SizeF(tocPage.Graphics.ClientSize.Width, 20)), format); + } + } + + // Merge PDFs after TOC pages + PdfDocument.Merge(document, docs); + + // Define TOC entries (title and corresponding page index) + List<(string Title, int PageIndex)> tocEntries = GetTocEntries(); + // Adjust TOC entries + tocEntries = AdjustTOCEntriesWithOffset(tocEntries, tocPages); + // Draw TOC entries + float currentY = 30; + int tocPageIndex = 0; + PdfPage currentTocPage = tocPagesList[tocPageIndex]; + foreach (var (title, pageIndex) in tocEntries) + { + if (currentY > currentTocPage.Graphics.ClientSize.Height - 50) + { + tocPageIndex++; + currentTocPage = tocPagesList[tocPageIndex]; + currentY = 30; + } + + currentY = AddBookmark(document.Pages[pageIndex], currentTocPage, title, currentY); + } + + //Create file stream. + using (FileStream outputFileStream = new FileStream(Path.GetFullPath(@"Output/Output.pdf"), FileMode.Create, FileAccess.ReadWrite)) + { + //Save the PDF document to file stream. + document.Save(outputFileStream); + } + //Close the document + document.Close(true); + } + } + + // Generates a list of Table of Contents (TOC) entries with titles and corresponding page indices. + static List<(string Title, int PageIndex)> GetTocEntries() + { + List<(string Title, int PageIndex)> entries = new List<(string, int)>(); + + for (int i = 1; i <= 60; i++) + { + // Pages 1–20: F# document + if (i <= 20) + { + entries.Add(($"F# - This is first document {i}", i)); + } + // Pages 21–40: HTTP document + else if (i > 20 && i <= 40) + { + entries.Add(($"HTTP - This is second document {i}", i)); + } + // Pages 41–60: Windows Store Apps document + else + { + entries.Add(($"Windows Store Apps - This is third document {i}", i)); + } + } + + return entries; + } + + //Adjusts the page indices of the Table of Contents (TOC) entries by adding an offset. + public static List<(string Title, int PageIndex)> AdjustTOCEntriesWithOffset(List<(string Title, int PageIndex)> tocEntries, int tocPages) + { + List<(string, int)> adjustedList = new List<(string, int)>(); + + // Loop through each TOC entry and add the TOC page count to its page index + foreach (var (title, pageIndex) in tocEntries) + { + adjustedList.Add((title, pageIndex + tocPages)); + } + + return adjustedList; + } + + // Adding bookmarks and drawing TOC entries + private static float AddBookmark(PdfPage page, PdfPage toc, string content, float currentY) + { + // Add a new bookmark to the document with the given title + PdfBookmark bookmark = document.Bookmarks.Add(content); + + // Draw the TOC entry text on the TOC page and get the updated Y-position + float newY = AddTableOfContent(page, toc, content, new PointF(0, currentY)); + + // Create a named destination pointing to the specified page and location + PdfNamedDestination namedDestination = new PdfNamedDestination(content) + { + Destination = new PdfDestination(page, new PointF(0, currentY)) + { + Mode = PdfDestinationMode.FitToPage // Ensures the destination fits the entire page in view + } + }; + + // Add the named destination to the document's collection + document.NamedDestinationCollection.Add(namedDestination); + + // Link the bookmark to the named destination + bookmark.NamedDestination = namedDestination; + + // Return the updated Y-position for the next TOC entry + return newY; + } + + //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. + private static float AddTableOfContent(PdfPage page, PdfPage toc, string content, PointF point) + { + // Define the width available for the TOC entry text + float textWidth = toc.Graphics.ClientSize.Width - 60; + + // Define the bounds where the TOC entry will be drawn + RectangleF textBounds = new RectangleF(point.X, point.Y, textWidth, 100); + + // Create a text element for the TOC entry + PdfTextElement element = new PdfTextElement(content, font, PdfBrushes.Blue); + + // Set layout format to fit within the page and allow pagination if needed + PdfLayoutFormat format = new PdfLayoutFormat + { + Break = PdfLayoutBreakType.FitPage, + Layout = PdfLayoutType.Paginate + }; + + // Draw the TOC entry text and get the layout result + PdfLayoutResult result = element.Draw(toc, textBounds, format); + + // Draw the corresponding page number on the right side of the TOC page + string pageNum = (document.Pages.IndexOf(page) + 1).ToString(); + PdfTextElement pageNumber = new PdfTextElement(pageNum, font, PdfBrushes.Black); + pageNumber.Draw(toc, new PointF(toc.Graphics.ClientSize.Width - 40, point.Y)); + + // Create a clickable link annotation over the TOC entry text + RectangleF bounds = result.Bounds; + bounds.Width = textWidth; + + PdfDocumentLinkAnnotation documentLinkAnnotation = new PdfDocumentLinkAnnotation(bounds) + { + AnnotationFlags = PdfAnnotationFlags.NoRotate, + Text = content, + Color = Color.Transparent, // Invisible clickable area + Destination = new PdfDestination(page), + Border = new PdfAnnotationBorder(0) // No visible border + }; + + // Set the destination location on the target page + documentLinkAnnotation.Destination.Location = point; + + // Add the link annotation to the TOC page + toc.Annotations.Add(documentLinkAnnotation); + + // Return the updated Y-position for the next TOC entry + return result.Bounds.Bottom + 10; + } +}