Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / Languages / VB

PDF Merge

4.87/5 (40 votes)
26 Oct 2014CPOL 1   9.6K  
This Windows application lets you merge image and PDF files in a given folder into one PDF file.

Image 1

Introduction

This complete Windows application lets you merge image and PDF files in a given folder into one PDF file. It also lets you password protect the PDF file. It uses free iTextSharp library.

Using the code

To use this program, simply select a folder and click Process. The program will create a PDF file within each folder and subfolder. The file will have the same name as the folder plus the PDF extension.

Here is the code:

VB
Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
    cbFileType.SelectedIndex = 0
End Sub

Private Sub btnFrom_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnFrom.Click
    fldFrom.SelectedPath = txtFrom.Text
    fldFrom.ShowDialog()
    txtFrom.Text = fldFrom.SelectedPath
End Sub

Private Sub btnProcess_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnProcess.Click

    btnProcess.Enabled = False

    Dim sFromPath As String = txtFrom.Text
    If Not Directory.Exists(sFromPath) Then
        btnProcess.Enabled = True
        MsgBox("Folder does not exist")
        Exit Sub
    End If

    txtOutput.Text = ""
    txtOutput.Text += "Starting..." & vbCrLf
    ProccessFolder(sFromPath)
    txtOutput.Text += "Done!"

    btnProcess.Enabled = True

End Sub

Private Function PadExt(ByVal s As String) As String
    s = UCase(s)
    If s.Length > 3 Then
        s = s.Substring(1, 3)
    End If
    Return s
End Function

Function GetPageCount(ByVal sFolderPath As String) As Integer
    Dim iRet As Integer = 0
    Dim oFiles As String() = Directory.GetFiles(sFolderPath)

    For i As Integer = 0 To oFiles.Length - 1
        Dim sFromFilePath As String = oFiles(i)
        Dim oFileInfo As New FileInfo(sFromFilePath)
        Dim sFileType As String = cbFileType.SelectedItem
        Dim sExt As String = PadExt(oFileInfo.Extension)

        Select Case sFileType
            Case "All"
                If sExt = "PDF" Then
                    iRet += 1
                ElseIf sExt = "JPG" Or sExt = "TIF" Then
                    iRet += 1
                End If

            Case "PDF"
                If sExt = "PDF" Then
                    iRet += 1
                End If

            Case "JPG", "TIF"
                If sExt = "JPG" Or sExt = "TIF" Then
                    iRet += 1
                End If
        End Select
    Next

    Return iRet
End Function

Sub ProccessFolder(ByVal sFolderPath As String)

    Dim bOutputfileAlreadyExists As Boolean = False
    Dim oFolderInfo As New System.IO.DirectoryInfo(sFolderPath)
    Dim sOutFilePath As String = sFolderPath & "\" & oFolderInfo.Name & ".pdf"

    If chkCreateInParentFolder.Checked Then
        sOutFilePath = oFolderInfo.Parent.FullName & "\" & oFolderInfo.Name & ".pdf"
    End If

    If IO.File.Exists(sOutFilePath) Then
        Try
            IO.File.Delete(sOutFilePath)
        Catch ex As Exception
            txtOutput.Text += "Output file already exists: " & sOutFilePath & _
                             " and could not be deleted." & vbTab & vbCrLf
            bOutputfileAlreadyExists = True
        End Try
    End If

    Dim iPageCount As Integer = GetPageCount(sFolderPath)
    If iPageCount > 0 And bOutputfileAlreadyExists = False Then
        txtOutput.Text += "Processing folder: " & sFolderPath & " - " & iPageCount & " files." & vbCrLf

        Dim oFiles As String() = Directory.GetFiles(sFolderPath)
        ProgressBar1.Maximum = oFiles.Length

        Dim oPdfDoc As New iTextSharp.text.Document()
        Dim oPdfWriter As PdfWriter = PdfWriter.GetInstance(oPdfDoc, New FileStream(sOutFilePath, FileMode.Create))
        If txtPassword.Text <> "" Then
            oPdfWriter.SetEncryption(PdfWriter.STRENGTH40BITS, txtPassword.Text, txtPassword.Text, PdfWriter.AllowCopy)
        End If
        oPdfDoc.Open()

        System.Array.Sort(Of String)(oFiles)

        For i As Integer = 0 To oFiles.Length - 1
            Dim sFromFilePath As String = oFiles(i)
            Dim oFileInfo As New FileInfo(sFromFilePath)
            Dim sFileType As String = cbFileType.SelectedItem
            Dim sExt As String = PadExt(oFileInfo.Extension)

            Try
                Select Case sFileType
                    Case "All"
                        If sExt = "PDF" Then
                            AddPdf(sFromFilePath, oPdfDoc, oPdfWriter)
                        ElseIf sExt = "JPG" Or sExt = "TIF" Then
                            AddImage(sFromFilePath, oPdfDoc, oPdfWriter, sExt)
                        End If

                    Case "PDF"
                        If sExt = "PDF" Then
                            AddPdf(sFromFilePath, oPdfDoc, oPdfWriter)
                        End If

                    Case "JPG", "TIF"
                        If sExt = "JPG" Or sExt = "TIF" Then
                            AddImage(sFromFilePath, oPdfDoc, oPdfWriter, sExt)
                        End If

                End Select

            Catch ex As Exception
                txtOutput.Text += sFromFilePath & vbTab & ex.Message & vbCrLf
            End Try

            If chkDeleteSourceFiles.Checked And IO.File.Exists(sFromFilePath) Then
                Try
                    IO.File.Delete(sFromFilePath)
                Catch ex As Exception
                    txtOutput.Text += "Could not delete " & sFromFilePath & _
                     ", " & ex.Message & vbCrLf
                End Try
            End If

            ProgressBar1.Value = i
        Next

        Try
            oPdfDoc.Close()
            oPdfWriter.Close()
        Catch ex As Exception
            txtOutput.Text += ex.Message & vbCrLf
            Try
                IO.File.Delete(sOutFilePath)
            Catch ex2 As Exception
            End Try
        End Try

        ProgressBar1.Value = 0
    End If

    Dim oFolders As String() = Directory.GetDirectories(sFolderPath)
    For i As Integer = 0 To oFolders.Length - 1
        Dim sChildFolder As String = oFolders(i)
        Dim iPos As Integer = sChildFolder.LastIndexOf("\")
        Dim sFolderName As String = sChildFolder.Substring(iPos + 1)
        ProccessFolder(sChildFolder)
    Next

End Sub

Sub AddBookmark(ByRef oPdfDoc As iTextSharp.text.Document, ByVal sFromFilePath As String)
    If chkBookmarks.Checked = False Then
        Exit Sub
    End If

    Dim oChapter As New iTextSharp.text.Chapter("", 0)
    oChapter.NumberDepth = 0
    Dim oFileInfo As New FileInfo(sFromFilePath)
    oChapter.BookmarkTitle = oFileInfo.Name
    oPdfDoc.Add(oChapter)
End Sub

Sub AddPdf(ByVal sInFilePath As String, ByRef oPdfDoc As iTextSharp.text.Document, ByRef oPdfWriter As PdfWriter)

    AddBookmark(oPdfDoc, sInFilePath)

    Dim oDirectContent As iTextSharp.text.pdf.PdfContentByte = oPdfWriter.DirectContent
    Dim oPdfReader As iTextSharp.text.pdf.PdfReader = New iTextSharp.text.pdf.PdfReader(sInFilePath)
    Dim iNumberOfPages As Integer = oPdfReader.NumberOfPages
    Dim iPage As Integer = 0

    Do While (iPage < iNumberOfPages)
        iPage += 1

        Dim iRotation As Integer = oPdfReader.GetPageRotation(iPage)
        Dim oPdfImportedPage As iTextSharp.text.pdf.PdfImportedPage = oPdfWriter.GetImportedPage(oPdfReader, iPage)

        If chkResize.Checked Then
            If (oPdfImportedPage.Width <= oPdfImportedPage.Height) Then
                oPdfDoc.SetPageSize(iTextSharp.text.PageSize.LETTER)
            Else
                oPdfDoc.SetPageSize(iTextSharp.text.PageSize.LETTER.Rotate())
            End If

            oPdfDoc.NewPage()

            Dim iWidthFactor As Single = oPdfDoc.PageSize.Width / oPdfReader.GetPageSize(iPage).Width
            Dim iHeightFactor As Single = oPdfDoc.PageSize.Height / oPdfReader.GetPageSize(iPage).Height
            Dim iFactor As Single = Math.Min(iWidthFactor, iHeightFactor)

            Dim iOffsetX As Single = (oPdfDoc.PageSize.Width - (oPdfImportedPage.Width * iFactor)) / 2
            Dim iOffsetY As Single = (oPdfDoc.PageSize.Height - (oPdfImportedPage.Height * iFactor)) / 2

            oDirectContent.AddTemplate(oPdfImportedPage, iFactor, 0, 0, iFactor, iOffsetX, iOffsetY)

        Else
            oPdfDoc.SetPageSize(oPdfReader.GetPageSizeWithRotation(iPage))
            oPdfDoc.NewPage()

            If (iRotation = 90) Or (iRotation = 270) Then
                oDirectContent.AddTemplate(oPdfImportedPage, 0, -1.0F, 1.0F, 0, 0, oPdfReader.GetPageSizeWithRotation(iPage).Height)
            Else
                oDirectContent.AddTemplate(oPdfImportedPage, 1.0F, 0, 0, 1.0F, 0, 0)
            End If
        End If
    Loop

End Sub

Sub AddImage(ByVal sInFilePath As String, ByRef oPdfDoc As iTextSharp.text.Document, ByRef oPdfWriter As PdfWriter, ByVal sExt As String)

    AddBookmark(oPdfDoc, sInFilePath)

    If chkResize.Checked = False Then
        Dim oDirectContent As iTextSharp.text.pdf.PdfContentByte = oPdfWriter.DirectContent
        Dim oPdfImage As iTextSharp.text.Image
        oPdfImage = iTextSharp.text.Image.GetInstance(sInFilePath)
        oPdfImage.SetAbsolutePosition(1, 1)
        oPdfDoc.SetPageSize(New iTextSharp.text.Rectangle(oPdfImage.Width, oPdfImage.Height))
        oPdfDoc.NewPage()
        oDirectContent.AddImage(oPdfImage)
        Exit Sub
    End If

    Dim oImage As System.Drawing.Image = System.Drawing.Image.FromFile(sInFilePath)

    'Multi-Page Tiff
    If sExt = "TIF" Then
        Dim iPageCount As Integer = oImage.GetFrameCount(Imaging.FrameDimension.Page)
        If iPageCount > 1 Then
            For iPage As Integer = 0 To iPageCount - 1
                oImage.SelectActiveFrame(Imaging.FrameDimension.Page, iPage)
                Dim oMemoryStream As New IO.MemoryStream()
                oImage.Save(oMemoryStream, System.Drawing.Imaging.ImageFormat.Png)
                Dim oImage2 As System.Drawing.Image = System.Drawing.Image.FromStream(oMemoryStream)
                AddImage2(oImage2, oPdfDoc, oPdfWriter)
                oMemoryStream.Close()
            Next
            Exit Sub
        End If
    End If

    AddImage2(oImage, oPdfDoc, oPdfWriter)
    oImage.Dispose()

End Sub


Sub AddImage2(ByRef oImage As System.Drawing.Image, ByRef oPdfDoc As iTextSharp.text.Document, ByRef oPdfWriter As PdfWriter)

    Dim oDirectContent As iTextSharp.text.pdf.PdfContentByte = oPdfWriter.DirectContent
    Dim oPdfImage As iTextSharp.text.Image
    Dim iWidth As Single = oImage.Width
    Dim iHeight As Single = oImage.Height
    Dim iAspectRatio As Double = iWidth / iHeight

    Dim iWidthPage As Single = 0
    Dim iHeightPage As Single = 0

    If iAspectRatio < 1 Then
        'Landscape image
        iWidthPage = iTextSharp.text.PageSize.LETTER.Width
        iHeightPage = iTextSharp.text.PageSize.LETTER.Height
    Else
        iHeightPage = iTextSharp.text.PageSize.LETTER.Width
        iWidthPage = iTextSharp.text.PageSize.LETTER.Height
    End If

    Dim iPageAspectRatio As Double = iWidthPage / iHeightPage

    Dim iWidthGoal As Single = 0
    Dim iHeightGoal As Single = 0
    Dim bFitsWithin As Boolean = False

    If iWidth < iWidthPage And iHeight < iHeightPage Then
        'Image fits within the page
        bFitsWithin = True
        iWidthGoal = iWidth
        iHeightGoal = iHeight

    ElseIf iAspectRatio > iPageAspectRatio Then
        'Width is too big
        iWidthGoal = iWidthPage
        iHeightGoal = iWidthPage * (iHeight / iWidth)

    Else
        'Height is too big
        iWidthGoal = iHeightPage * (iWidth / iHeight)
        iHeightGoal = iHeightPage
    End If

    If bFitsWithin = False Then
        oImage = FixedSize(oImage, iWidthGoal, iHeightGoal)
        'oImage.Save("C:\temp\folder1\Lilly_copy.jpg")
    End If

    oPdfImage = iTextSharp.text.Image.GetInstance(oImage, System.Drawing.Imaging.ImageFormat.Png)
    oPdfImage.SetAbsolutePosition(1, 1)

    If iAspectRatio < 1 Then
        'Landscape image
        oPdfDoc.SetPageSize(iTextSharp.text.PageSize.LETTER)
    Else
        oPdfDoc.SetPageSize(iTextSharp.text.PageSize.LETTER.Rotate())
    End If

    oPdfDoc.NewPage()
    oPdfImage.ScaleAbsolute(iWidthGoal, iHeightGoal)
    oDirectContent.AddImage(oPdfImage)

End Sub

Private Function FixedSize(ByVal imgPhoto As System.Drawing.Image, _
ByVal Width As Integer, ByVal Height As Integer) As System.Drawing.Image

    If txtResizeResolution.Text = "" OrElse IsNumeric(txtResizeResolution.Text) = False Then
        txtOutput.Text += "Resize Resolution is not a number."
        Return imgPhoto
    End If

    Dim iResizeResolution As Double = CDbl(txtResizeResolution.Text) / 100
    Width = Width * iResizeResolution
    Height = Height * iResizeResolution

    Dim sourceWidth As Integer = imgPhoto.Width
    Dim sourceHeight As Integer = imgPhoto.Height
    Dim sourceX As Integer = 0
    Dim sourceY As Integer = 0
    Dim destX As Integer = 0
    Dim destY As Integer = 0

    Dim nPercent As Single = 0
    Dim nPercentW As Single = 0
    Dim nPercentH As Single = 0

    nPercentW = (CSng(Width) / CSng(sourceWidth))
    nPercentH = (CSng(Height) / CSng(sourceHeight))

    If nPercentH < nPercentW Then
        nPercent = nPercentH
        destX = CInt(((Width - (sourceWidth * nPercent)) / 2))
    Else
        nPercent = nPercentW
        destY = CInt(((Height - (sourceHeight * nPercent)) / 2))
    End If

    Dim destWidth As Integer = CInt((sourceWidth * nPercent))
    Dim destHeight As Integer = CInt((sourceHeight * nPercent))

    Dim bmPhoto As New Bitmap(Width, Height, System.Drawing.Imaging.PixelFormat.Format24bppRgb)
    bmPhoto.SetResolution(imgPhoto.HorizontalResolution, imgPhoto.VerticalResolution)

    Dim grPhoto As Graphics = Graphics.FromImage(bmPhoto)
    grPhoto.Clear(Color.White)
    grPhoto.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.HighQualityBicubic

    grPhoto.DrawImage(imgPhoto, New Rectangle(destX, destY, destWidth, destHeight), _
      New Rectangle(sourceX, sourceY, sourceWidth, sourceHeight), GraphicsUnit.Pixel)

    grPhoto.Dispose()
    imgPhoto.Dispose()

    Return bmPhoto
End Function

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)