Technically Impossible

Lets look at the weak link in your statement. Anything "Technically Impossible" basically means we haven't figured out how yet.

特定フォルダ配下のPDFファイルを一括、右綴じ変換 実装編2

f:id:espio999:20170917122716p:plain
いわゆる自炊した文書をスキャンニングしてPDF化した際、その文章フォーマットに関わらず、そのファイルは左綴じの文書として処理されます。見開きで表示すると、左側に奇数ページ、右側に偶数ページの順に配置されます。
日本語のような縦書き文書の場合、右綴じの文書として処理され、見開き表示では右に奇数ページ、左に偶数ページを配置してほしいところです。

単一のファイルを操作する場合、このための設定変更はファイルのプロパティを変更します。Adobe Acrobatの場合でいうと、トップ画像の赤丸の部分を操作することになる。

一度に複数のファイルについて同様の操作を施す場合、何とか効率化したいところです。ここでは次のツールを活用して、対応方法を紹介します。

VB.Net (Visual Studio 2017 Community)
iText 7

長くなるため、次のエントリーに分割して紹介します。

  1. 調査:PDFファイルの変更箇所
  2. 操作:変更操作に対応するiText 7のコード
  3. 実装1:Visual Studio 2017へのiText 7導入
  4. 実装2:Visual Studio 2017での実装

この投稿では、「実装2:Visual Studio 2017での実装」を扱います。「実装1:Visual Studio 2017へのiText 7導入」までの準備が完了している前提で進めます。

コーディングの前にプログラム全体の処理手順を考えます。私が前提としているのは、次の事柄です。

前提1 あるフォルダにpdfを含む、雑多なファイルが格納されている。
前提2 あるフォルダの中にあるpdfだけを処理対象とし、右綴じへ変更する。

これを実現するために、次の処理手順を考えました。

処理1 pdfファイルが格納されているフォルダの選択
Button 1を押下すると、選択されたフォルダがボタンの右に表示される。
処理2 設定変更されたpdfファイルを格納するフォルダの選択
Button 2を押下すると、選択されたフォルダがボタンの右に表示される。

Button 3を押下すると、

処理3 処理1内のフォルダにあるpdfファイルの抽出
処理4 選択されたpdfだけを設定変更する。
処理5 設定変更したpdfファイルを別のフォルダへ格納する。

処理3-5の間、処理対象となったファイル、処理後のファイルをリストボックスに表示します。

フォームの配置と実行後の画面です。実行後の画面では、D:\temp\0917\source配下にあるpdfフォルダが抽出され、D:\temp\0917\destinationへコピーされたことが確認できます。
f:id:espio999:20170917125200p:plain
f:id:espio999:20170917125212p:plain

試みにsource配下にある『影の巡礼者』を開いてみます。ページは左、右の順に配置され、左綴じです。
f:id:espio999:20170917125228p:plain

destination配下にあるファイルを開いてみると、ページは右から左の順に配置され、右綴じに変化していることがわかります。
f:id:espio999:20170917125243p:plain

処理のコードは以下の通りです。必要な方はご参照ください。
GitHubからVB.Netプロジェクトをダウンロードすることもできます。
github.com

Imports iText.Kernel.Pdf
Imports System.IO

Public Class Form1
    Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load

    End Sub

    'Button 1押下
    Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
        Label1.Text = getFolderPath()
    End Sub

    'Button 2:押下
    Private Sub Button2_Click(sender As Object, e As EventArgs) Handles Button2.Click
        Label2.Text = getFolderPath()
    End Sub

    'フォルダの選択
    Private Function GetFolderPath() As String
        If FolderBrowserDialog1.ShowDialog() = DialogResult.OK Then
            GetFolderPath = FolderBrowserDialog1.SelectedPath
        Else
            GetFolderPath = GetFolderPath()
        End If
    End Function

    'Button 3押下
    Private Sub Button3_Click(sender As Object, e As EventArgs) Handles Button3.Click
        Dim files As IEnumerable(Of String)
        Dim result As String

        If IsSameFolder() = False Then
            files = GetPDFarray(Label1.Text)

            For Each f As String In files
                ListBox1.Items.Add(f)
                result = UpdatePDF(f)
                ListBox2.Items.Add(result)
            Next

            MessageBox.Show("complete")
        End If
    End Sub

    Private Function IsSameFolder() As Boolean
        If Label1.Text = Label2.Text Then
            MessageBox.Show("source = destination")
            IsSameFolder = True
        Else
            IsSameFolder = False
        End If
    End Function

    'フォルダ内のPDFファイルを抽出する。
    Private Function GetPDFarray(folder_path As String) As IEnumerable(Of String)
        Dim search_pattern = "*.pdf"
        Dim files As IEnumerable(Of String)

        files = System.IO.Directory.EnumerateFiles(folder_path, search_pattern)
        GetPDFarray = files
    End Function

    'PDFファイルの設定を変更し、目的のフォルダへコピーする。
    Private Function UpdatePDF(source_file As String) As String
        Dim myReader As New PdfReader(source_file)
        Dim destination_file As String = Label2.Text + "\" + Path.GetFileName(source_file)
        Dim myWriter As New PdfWriter(destination_file)
        Dim myPDF As New PdfDocument(myReader, myWriter)
        Dim myPreference As New PdfViewerPreferences

        myPreference.SetDirection(PdfViewerPreferences.PdfViewerPreferencesConstants.RIGHT_TO_LEFT)
        myPDF.GetCatalog().SetViewerPreferences(myPreference)

        myPDF.Close()
        myWriter.Close()
        myReader.Close()
        UpdatePDF = destination_file
    End Function

    Private Sub Button4_Click(sender As Object, e As EventArgs) Handles Button4.Click
        Close()
    End Sub
End Class