macOS で自炊データを PDF 化する際の覚え書き
目次
きっかけ
特筆するほどでも無いのだが、手元の自炊データ (e.g. JPG, PNG) をスマホや Kindle で読みたかったのがきっかけだった。
もちろんやり方はいろいろあるとは思うし、自動化も普通にやればできるだろうという見通しはあったが、いちいち毎回調べながらやるのも億劫なのでまとめておくことにした。
目標
JPG や PNG 形式の自炊データを、電子書籍リーダーで読める PDF 形式に変換する。
要件の整理
スキャナーやデジカメなどを使用して書籍を自炊した場合、各ページの 画像ファイルが JPG や PNG など形式で大量に作成される。 また、特に変わったことをしなければ、それらのファイルはページの順番に応じて昇順のファイル名が振られていて、書籍毎に書籍のタイトルの付けられたディレクトリ配下にまとめられていることだろう。 やりたいこととしては、こうした大量の画像ファイルを元の書籍のページ順に並べて、一つの PDF ファイルとしたい。
上記をふまえると、変換処理を行う際に想定される入力は、書籍の画像ファイルの配置されたディレクトリへのパスであり、ディレクトリの構成は以下のようになっていると想定できる。
$ cd BookTitle # 書籍タイトルディレクトリ
$ tree .
.
├── Page001.jpg # 各ページの画像ファイル
├── Page002.jpg
├── Page003.jpg
.
.
├── Page198.jpg
└── Page199.jpg
また、PDF にする書籍がシリーズ物だったりして複数あるケースも想定すると、以下のような形でディレクトリが階層化されているケースも考えられる。
$ tree .
.
├── Series # シリーズ名ディレクトリ
│ ├── Subseries # サブシリーズ名ディレクトリ
│ │ ├── BookTitle1
│ │ │ ├── Page001.jpg
│ │ │ ├── Page002.jpg
│ │ │ .
│ │ │ .
│ │ │ └── Page199.jpg
│ │ └── BookTitle2
│ │ ├── Page001.jpg
│ │ ├── Page002.jpg
│ │ .
│ │ .
│ │ └── Page199.jpg
│ └─── BookTitle3
│ ├── Page001.jpg
│ ├── Page002.jpg
│ .
│ .
│ └── Page199.jpg
└── BookTitle4
├── Page001.jpg
├── Page002.jpg
.
.
└── Page199.jpg
このケースだと、異なる深さのディレクトリに階層に、書籍タイトルディレクトリが存在し、その配下に各ページの画像ファイルが存在する。
そこで今回の要件を、「上記のように階層化のディレクトリのルートへのパスを入力として与えた場合に、その配下に存在する全ての書籍(上記例における、BookTitle1 ~ 4)をまとめて PDF として出力すること」として定義した。
環境
- macOS High Sierra
手順
要件が、以下の 2 ステップで実現できることは言わずもがだと思う。
- 入力として受け取ったディレクトリ階層から、書籍タイトルディレクトリへのパスを抽出する
- 各書籍タイトルディレクトリ配下の画像ファイル群を PDF に変換し、書籍タイトルのファイル名で保存する
今回は、これらの処理を Bash のスクリプトで実現する。
① 書籍名ディレクトリの抽出
やるべきこととしては、要件の整理 の章で例として挙げたようなディレクトリ階層の中から、最下層のディレクトリのみを抽出すれば良いのだが、POSIX コマンドの中に単体でこれを実現できそうなものは無かった。(find
コマンドのオプションなんかで実現できるかとも思ったが、無理そうだった。)
そこで Stack Exchange 上の Gilles 氏による回答 を参考に find
と awk
のコマンドを組み合わせて、以下のような関数で実現した。1
leaf () {
{ find "$1" -type d; echo; } | \
awk 'index($0,prev"/")!=1 && NR!=1 {print prev}
1 {sub(/\/$/,""); prev=$0}'
}
簡単に処理内容を説明すると、関数内 1 行目では特に変わったことはしておらず、普通に find
コマンドで、入力として受け取ったパス配下のディレクトリを出力している。
ただ、今回は最終的に最下層のディレクトリのみを出力したいため、後の awk
コマンドでその判定を行う際には一行先読みを行う必要が発生し、関数内 1 行目の echo
コマンドによる空行の出力はそのためのものである。
関数内 2 行目、3 行目の awk
コマンドが少しトリッキーだが、以下のような処理を行っている。
- 入力行に
prev
変数の文字列(一つ前の行のもの)が存在しないかどうかをチェックし、存在しなければprev
を出力 - 入力行の末尾のスラッシュを削除し、
prev
変数にセット
なお、NR!=1
は awk
の入力の一行目については print を行わないこと、関数内 3 行目の 1
は真偽値(True)を意味し、常に後続の処理を実行することを意味する。
この leaf
関数は、使い勝手が良いので、.bashrc
なり .zshrc
などに登録しておくと良いかもしれない。
② 画像ファイル群を PDF へ変換
いろいろな方法がありそうだが、王道の ImageMagick を使うのが一番手っ取り早そうだったので、これを使用した。2
macOS であれば、Homebrew を使用して、以下のコマンド一つで ImageMagick のインストールが行える。
$ brew install imagemagick
上記コマンドで ImageMagick をインストールすると、convert
というコマンドが使用可能になり、以下のように画像ファイルを PDF へ変換が可能となる。
$ convert *.jpg BookTitle.pdf
変換対象の画像ファイルを上記のようにワイルドカード指定すると、ファイル名の昇順で PDF のページに変換される。
また、そのままの画質のまま変換を行うと、作成される PDF のファイルサイズがかなり大きくなってしまうため、以下のように -quality
オプション (1 ~ 100) で画質を落とすことも可能。
$ convert *.jpg -quality 20 BookTitle.pdf
元の画像にもよるかと思うが、手元で試し感じだと quality は 20 程度にまで下げても、十分に文字の判読が可能な形で変換を行うことができた。
まとめ
以下のような Bash スクリプトを作成し、はじめに定義した要件の通りの変換処理を実現することができた。
変換対象の画像ファイルが存在するディレクトリを第一引数として指定し、画質を第二引数として指定(省略可能、既定値 20)することで、対象ディレクトリ配下の全ての画像ファイルを PDF に変換することができる。
$ ./images2pdf path/to/images 50