自炊3年目突入(自炊したPDFファイルの一覧作成処理について)

 一昨年の2月に始めた自炊だけど、ぼちぼち3年目に突入した。
 自炊を始めた頃のPDFファイルのタイムスタンプが2011年2月2日ということで間違いはないようだ。
 この間、1000冊を超えたのが2012/1/29で本日今までで1625冊ということに相成った。たまにバースト的に大量の本処理を行うこともあるけど少し飽きが来たのか、それとも自炊してない本の中に読みたい本がなくなってきたのか2年目は1年目の6割ぐらいのペースにまで落ちてしまった。
 それでも、本の減少ははっきりわかる。以前はちきれんばかりに本が入っていた本棚を別なものが占拠し始めている。床に散乱している物の数も減った。減った?減ったかなぁ?まぁ、前よりマシな気がする。

 当初は収拾がつかなくなっていた本の整理が目的だった。溢れかえった物理的な本を何とかするためには廃棄するかどうするかというところまで追い詰められていた。これまで大規模な廃棄は2回、小規模な廃棄は幾度と無く行ってきた。しかし、今となっては廃棄したことを後悔する本が幾冊もあり、そういった時に自炊という手段は渡りに船だった。
 リーダーにiPad2を導入したことにより新規購入本もまず自炊をしてiPadに喰わせて読むというパターンが定着した。iPad(と言うかリーダー)は一台のハードに何百冊も本が入る。まぁ、ある意味本棚を持ち歩いているようでまとめ買いした本を全部放り込んでその時の気分で読むとか、お気に入りの再読しそうな本を入れておいて新刊に飽きたらそちらを読むとか、自分的には自由気ままで大変によい。
 今ではiPad2がiPadminiに代わり読書時の腕に掛かる負担はかなり減ったのでいよいよ快適である。

 ただ、気を使うのが自炊した本の保存、PDFファイルの保存で、まとめてハードディスクに入れているがそれが壊れてしまったらこれまで買った本が全滅してしまう。
 バックアップ環境として、保存はRaid1のネットワークドライブに入れ、別に50GByte毎にBlu-rayに焼いている。Blu-rayに焼かれる前のものはPCのハードディスクにも入れてまぁ取り敢えず2重に持つようにはしている。
 今のところファイルが紛失したことは無い。

 ネットワークドライブ上は分かりやすいように分類ごとにフォルダ分けをしている。
 具合的にはネットワーク上のZドライブにbookというフォルダを切りその中に「ノンフィクション」「マンガ」「科学」「小説」「心理学」等のフォルダで分け、その中は作家や作品のシリーズで分けている。

 自炊本のリストだけど自炊した時に確実に記録していれば間違いないんだけど、その日によって手順が違ったり眠かったりして記録忘れがあるとそこからポロポロ漏れてゆく。以前はGoogleドキュメントのCalcを使っていたけど記録忘れがあったのと、記録をネットワーク上に置いておくことの不安、間違えて公開してしまう不安があった。あと、PDFファイルのページ数を記録していたんだけどそれが面倒で何か一発で出来ないものかと考えたりもした。
 自炊本のPDFファイルは一番最初からフォーマットを決めていて概ねのところは揺らがなかったので、Excelのマクロを使ったれということで「分類されたフォルダに入っているPDFファイルのファイル名を取得してPDFファイルのページ数を取得するマクロ」を作ってみた。
(ファイルフォーマットは、「本のタイトル-作者-出版社-ISBN.PDF」である。)
 使用したExcelは2010。Acrobatは9でReaderはダメ。
 まぁ、応用すれば別にPDFだけじゃないファイルの一覧の作成もできるだろうし、PDFのページ数取得の方法も入っているので文書の整理にも使えるのではないかと。
 参考にしたホームページはPDFの操作に関してはこちらだったと思う。ExcelVBA上で再帰的にディレクトリ参照する方法は申し訳ないが記録に取っていない。適当な検索キーを使えばヒットすると思うので詳細はそちらの方でお願いします。

Sub makealllist()
Dim i As Long
Dim dirname As String
Dim ws As Worksheet

Set ws = Worksheets("sheet1")
dirname = "Z:\Book"
i = 2
a = getfilename_sub(dirname, ws, i)

End Sub

 ExcelのSheet1にデータを作成する。
 実際にデータ取得とWorksheetへの出力を行う関数「getfilename_sub」に引き渡す初期のフォルダ名をdirnameという変数にセットし、ループ開始位置を指定して関数「getfilename_sub」を呼び出す。

Function getfilename_sub(dirname As String, ws As Worksheet, i As Long) As Long
    
With CreateObject("Scripting.FileSystemObject")
    For Each f In .GetFolder(dirname).SubFolders
        If Len(f.Path) > Len(".organizer") Then
            If Right(f.Path, Len(".organizer")) <> ".organizer" Then
                i = getfilename_sub(f.Path, ws, i)
            End If
        Else
            i = getfilename_sub(f.Path, ws, i)
        End If
    Next f
End With


MyName = Dir(dirname & "\*.pdf", vbNormal)     ' 最初のファイル名を返します。
Do While MyName <> ""    ' ループを開始します。
    ws.Range("I" & Format(i)).Value = MyName
    ws.Range("C" & Format(i)).Value = Format(FileDateTime(dirname & "\" & MyName), "yyyy/mm/dd")
    ws.Range("H" & Format(i)).Value = getpagedata(dirname & "\" & MyName)
    pos = InStr(MyName, "-")
    ws.Range("D" & Format(i)).Value = Left(MyName, pos - 1)
    pos1 = pos + 1
    pos = InStr(pos1, MyName, "-")
    If pos <> 0 Then
        ws.Range("E" & Format(i)).Value = Mid(MyName, pos1, pos - pos1)
        pos1 = pos + 1
        pos = InStr(pos1, MyName, "-")
        If pos <> 0 Then
            ws.Range("F" & Format(i)).Value = Mid(MyName, pos1, pos - pos1)
            pos1 = pos + 1
        End If
    End If
    pos = InStr(pos1, MyName, ".pdf")
    ws.Range("G" & Format(i)).Value = Mid(MyName, pos1, pos - pos1)
    
    ws.Range("J" & Format(i)).Value = dirname
    i = i + 1
    MyName = Dir                    ' 次のファイル名を返します。
    DoEvents
Loop
getfilename_sub = i

End Function

 フォルダ名/ファイル名を取得するVBAの「Dir」関数が再帰的に呼び出されることに対応していないのでWindowsファイルシステムオブジェクトを呼び出して処理するのが味噌その1。「.organizer」フォルダを避ける処理が入っている。「.organizer」フォルダはScanSnapのorganizerが勝手に生成するフォルダで中には「.organizer」があるフォルダのPDFのサムネールを表示するためだろう、jpegファイルが入っている。相手にすると時間の無駄なので回避するようにした。フォルダ名の長さを見てごちゃごちゃしているけど、せずに処理したら障りがあったんだろう。

 フォルダを見つけたらそのフォルダの中を調べるために自分自身を呼び出している。
 もうフォルダが見つからなかったら、後半のファイル名の取得処理に入る。
 VBAのDIR関数で.PDFファイルを取得するように指示してループに入る。
 取得したデータを各フィールドにセットして次のファイルの処理を行い、DIR関数がファイル名を返さなくなるまでループする。
 その途中の「getpagedata」関数でPDFファイルのページ数を取得する。

Function getpagedata(pathname As String) As Integer
    Dim objAcrobatPDDoc As New Acrobat.AcroPDDoc
    Dim i As Long  '行
     
    Dim lRet As Long
     
    i = 1
    lRet = objAcrobatPDDoc.Open(pathname)
    
    getpagedata = Val(objAcrobatPDDoc.GetNumPages)
    objAcrobatPDDoc.Close
    Set objAcrobatPDDoc = Nothing

End Function

 ここでAcrobatAPIを使用するのでAcrobat ReaderじゃなくてAcrobatが必要である。
 実はExcelだけでPDFファイルを読み込んで解析しページ数を出す方法もあるみたいだけど、そのやり方を実際に試してみたら正しいことも多いんだけど、たまに違うデータを取得する。
 PDFファイルの中にページ数のデータは複数存在していて、どれが正解かを判断するのは難しいみたい。
 と言うか、自分はScanSnapを買ってAcrobatが付いてきたのでこれ以上苦労して調べたり解析するのを断念した。面倒だから。

 この前の段階、作成したPDFファイルを定義した条件でフォルダ構造に分類してゆく処理はPerlで書いた。こちらはActivePerlで処理する。こちらも機会があったら公開するかもしれない。