Imports System.IO
Imports org.pdfbox.pdmodel
Imports org.pdfbox.util
Imports org.pdfbox.pdmodel.interactive.documentnavigation.destination
Imports org.pdfbox.pdmodel.interactive.documentnavigation.outline
Imports java.util

Module Module1

    Sub Main()

        'Create a pdf file list and add files to it
        Dim pdfList As New List(Of String)
        pdfList.Add("C:\reports\pdfFile1.pdf")
        pdfList.Add("C:\reports\pdfFile2.pdf")

        'Declare a output file path for the temp file.
        'The final output file will be saved in the save location with the
        'phrase "temp_" removed from the file name... So the final output file will
        'be "C:\MergedPdf\myMergedPdf.pdf"
        Dim outFile As String = "C:\MergedPdf\temp_myMergedPdf.pdf"

        'Try to merge the pdf files
        If MergePdfFiles(pdfList, outFile) Then
            'If successful, create bookmark data
            Dim bookmarkData As DataTable = CreateBookmarkDataTable(pdfList)
            'If successful, try to add bookmarks to the merged file
            If Not IsNothing(bookmarkData) Then
                If Not AddBookMarks(outFile, bookmarkData) Then
                    Console.WriteLine("It blew....")
                    Console.ReadLine()
                Else
                    'Add bookmarks OKed, delete the temp file
                    File.Delete(outFile)
                End If
            End If
        End If
    End Sub

    Private Function MergePdfFiles(ByVal pdfFileList As List(Of String), _
                                   ByVal outputFileFullName As String) As Boolean
        Dim result As Boolean = False
        Dim pdfMerger As PDFMergerUtility = Nothing
        Dim fileCount As Integer = pdfFileList.Count
        If fileCount > 1 Then
            Try
                'Instantiate an instance of Pdf Merger Utility
                pdfMerger = New PDFMergerUtility()
                With pdfMerger
                    'Set output destination
                    .setDestinationFileName(outputFileFullName)
                    'Looping thru the file list and add source to the merger
                    For i As Integer = 0 To fileCount - 1 Step 1
                        .addSource(pdfFileList(i))
                    Next i
                    'Merge the documents
                    pdfMerger.mergeDocuments()
                    result = True
                End With
            Catch ex As Exception
                WriteToLog("MergePDFFile(" & outputFileFullName & "): " & ex.Message)
                Return False
            End Try
        End If
        Return result
    End Function

    Private Function CreateBookmarkDataTable(ByVal pdfFileList As List(Of String)) As DataTable
        Dim bookmarkData As New DataTable
        Dim row As DataRow = Nothing
        Dim bookmarkTitle As String = String.Empty
        Dim pageNumber As Integer = 0
        Try
            bookmarkData.Columns.Add("BookmarkTitle", GetType(String))
            bookmarkData.Columns.Add("PageNumber", GetType(Integer))
            Dim count As Integer = pdfFileList.Count
            If count > 0 Then
                For i As Integer = 0 To count - 1 Step 1
                    bookmarkTitle = Path.GetFileNameWithoutExtension(pdfFileList(i))
                    row = bookmarkData.NewRow()
                    row.Item("BookmarkTitle") = bookmarkTitle
                    row.Item("PageNumber") = pageNumber
                    bookmarkData.Rows.Add(row)
                    pageNumber += GetPageCount(pdfFileList(i))
                Next
            End If
        Catch ex As Exception
            WriteToLog("CreateBookmarkDataTable(): " & ex.Message)
            Return Nothing
        End Try
        Return bookmarkData
    End Function

    Private Function GetPageCount(ByVal pdfFile As String) As Integer
        Dim pageCount As Integer
        Dim pdfDoc As PDDocument = Nothing
        Try
            pdfDoc = PDDocument.load(pdfFile)
            pageCount = pdfDoc.getNumberOfPages
        Catch ex As Exception
            WriteToLog("GetPageCount(" & pdfFile & "): " & ex.Message)
            Return 0
        Finally
            If Not pdfDoc Is Nothing Then
                pdfDoc.close()
            End If
        End Try
        Return pageCount
    End Function

    Private Function AddBookMarks(ByVal pdfFile As String, _
                                  ByVal bookmarkTable As DataTable) As Boolean
        Dim result As Boolean = False
        Dim PdfDoc As PDDocument = Nothing
        Dim outFile As String = String.Empty
        Dim rowCount As Integer = bookmarkTable.Rows.Count
        Try
            If rowCount > 0 Then
                'Set the output file full path
                outFile = pdfFile.Replace("temp_", "")
                'Load the input pdf file
                PdfDoc = PDDocument.load(pdfFile)
                If Not PdfDoc.isEncrypted() Then
                    'Create new document outline and assign it to the pdf document
                    Dim outline As PDDocumentOutline = New PDDocumentOutline()
                    PdfDoc.getDocumentCatalog().setDocumentOutline(outline)

                    'Create new outline item for the document outline
                    Dim pagesOutline As PDOutlineItem = New PDOutlineItem()
                    pagesOutline.setTitle("All Pages")
                    outline.appendChild(pagesOutline)

                    'Get the list of pages in the document
                    Dim pages As List = PdfDoc.getDocumentCatalog().getAllPages()

                    Dim i, pageNumber As Integer
                    Dim row As DataRow = Nothing
                    Dim bookmarkTitle As String = String.Empty
                    'loop thru the bookmark datatable and add bookmarks to the document accordingly
                    For i = 0 To rowCount - 1 Step 1
                        'Read the row's data
                        row = bookmarkTable.Rows(i)
                        pageNumber = CInt(row.Item("PageNumber"))
                        bookmarkTitle = CStr(row.Item("BookmarkTitle"))
                        'Get the page at pageNumber from pages list
                        Dim page As PDPage = CType(pages.get(pageNumber), PDPage)
                        Dim dest As PDPageFitWidthDestination = New PDPageFitWidthDestination()
                        dest.setPage(page)
                        'Then set bookmark to it
                        Dim bookmark As PDOutlineItem = New PDOutlineItem()
                        bookmark.setDestination(dest)
                        bookmark.setTitle(bookmarkTitle)
                        'Add this bookmark to the document's outline
                        pagesOutline.appendChild(bookmark)
                    Next i
                    'Expand the bookmark tree
                    pagesOutline.openNode()
                    outline.openNode()
                    'Save the the document to a file
                    PdfDoc.save(outFile)
                    result = True
                Else
                    WriteToLog("Can't add bookmarks to <" & pdfFile & "> because the document is encrypted.")
                End If
            Else
                WriteToLog("Can't add bookmarks to <" & pdfFile & "> because BookmarkTable has no data.")
            End If
        Catch ex As Exception
            WriteToLog("AddBookmarks(" & pdfFile & "): " & ex.Message)
            Return False
        Finally
            If Not PdfDoc Is Nothing Then
                PdfDoc.close()
            End If
        End Try
        Return result
    End Function

    Private Sub WriteToLog(ByVal txt As String)
        Dim logPath As String = Environment.CurrentDirectory & "\Log"
        Try
            If Not Directory.Exists(logPath) Then
                Directory.CreateDirectory(logPath)
            End If
            Dim logFile As String = logPath & "\PdfMerger.log"
            Dim prefix As String = Date.Now.ToString("yyyy/MM/dd HH:mm:ss - ")
            Using writer As New StreamWriter(logFile, True)
                writer.WriteLine(prefix & txt)
            End Using
        Catch ex As Exception
            Console.Write(ex.Message)
        End Try
        
    End Sub

End Module
