アルファのブログ

ことば、計算機、SF、ヒコーキ

alpha3166 alpha3166

自炊PDFを第3世代iPadに最適化する その3

2012-08-12最終更新、2012-07-15作成

(→その1その2、その3、その4)

自炊したPDFを第3世代iPad(以下、iPad3)に最適化することに、いまだこだわっております。

以前はChainLPを使っていましたが(「その1」「その2」)、最近はFreeBSDで各種ツールを組み合わせて変換しています。

そもそもやりたいことはこんな感じ。

とりあえず、現時点で採用している方法をメモしておきます。

まず、オリジナルのPDFをJPEGにバラします。これには、xpdfに付いてくるpdfimagesコマンドを使っています。xpdfのツール群は、poppler-utilsと競合するとかで、通常の/usr/local/binではなく/usr/local/libexec/xpdfにインストールされているので、ここではコマンドをフルパスで指定しています。-jは出力をJPEGにするオプション、tmpは出力ファイル名の接頭辞です。このコマンドを実行すると、tmp-???.jpgというファイルがページ数分だけできます。???は000~999の連番です。オリジナルが1000ページを超えている場合は、1001ページ目からは連番が4桁(tmp-1000.jpgとか)になります。

/usr/local/libexec/xpdf/pdfimages -j オリジナル.pdf tmp

次に、JPEGの画素数を1ファイルずつ変換していきます。Bシェル系でまとめてやるならこんな感じ。基本は1024×1536へのリサイズですが、幅か高さのいずれかが2539ピクセルを超えている場合は1536×2048にリサイズしたいので、ImageMagickのidentifyコマンドでJPEGの幅(%w)と高さ(%h)を調べています。サイズが決まったら、ImageMagickのmogrifyで、リサイズとPDFへの変換を一緒にやってしまいます。-quality 50は圧縮レベルを100段階中の50にする設定。-rotate “-90>”は、もし幅が高さより大きい(つまり横長の)画像だった場合は、「リサイズ前に」画像を反時計周りに90度回転させるという指定です。「リサイズ前に」回転するには、-rotateを-resizeの前に置く必要があります。-resizeオプションの引数の末尾にある>(シェルのエスケープがあるので\>になっている)は、元画像が指定サイズより小さい場合は変換しない(つまり引き伸ばして大きな画像にはしない)という意味です。なお、動作テストするときは、mogrifyに-verboseオプションを付けておくと、変換の様子を確かめられて良いでしょう。

for jpeg in tmp-*.jpg; do
    resize=1024x1536
    if [ $(identify -format "%w" "$jpeg") -gt 2539 -o $(identify -format "%h" "$jpeg") -gt 2539 ]; then
        resize=1536x2048
    fi
    mogrify -format pdf -quality 50 -rotate "-90>" -resize $resize\> "$jpeg"
done

次に、ページ単位になっているPDFを1冊のPDFに結合します。これにはpdftkを使っています(ImageMagickのconvertコマンドでも同じことができますが、すぐメモリを食い尽くしてハングアップしてしまうので実用になりません)。なお、前述のとおり1000ページ超の場合は連番が3桁のファイルと4桁のファイルが混在しているので、何も考えずに「pdftk tmp-*.pdf cat…」のように指定すると、ファイルが文字コード順に処理され、tmp-100.jpgの次にtmp-1000.jpgが来るようなことになってしまいます。かといって、一律「pdftk tmp-???.pdf tmp-????.pdf cat…」と指定すると、今度はtmp-????.pdfに該当するファイルが無いときにエラーになってしまいます。そこで、まずlsで連番4桁のファイルがあるかどうか調べて処理を分岐させています。(実際には「if [ -f tmp-1000.jpg ]; then」で十分かもしれませんが、万一連番に抜けがあるといけないので、一応lsで4桁のファイル全部を対象に調べています。)

if ls tmp-????.pdf > /dev/null 2>&1; then
    pdftk tmp-???.pdf tmp-????.pdf cat output 出力.pdf
else
    pdftk tmp-???.pdf cat output 出力.pdf
fi

次に、オリジナルのPDFが右綴じだったら、変換後のPDFも右綴じになるよう設定します。オリジナルのファイル中に「ViewerPreferences«/Direction/R2L»」という文字列があれば右綴じと判断しています。少なくとも、Adobe Acrobat 9 Standardで綴じ方向を設定している場合は、これで大丈夫だと思われます。

(2012年8月12日追記: この方法は問題がありました。→「PDFの右綴じ判定」)

if grep -q -a 'ViewerPreferences<</Direction/R2L>>' オリジナル.pdf; then
    java -classpath .:itextpdf-5.3.0.jar R2Ler 変換後.pdf
fi

で、変換後のPDFを右綴じにする部分は、「技術日記@kiwanami」の「大量のPDFファイルを右綴じに変えたい」という記事を参考に、JavaのiTextを使ってR2Lerというクラスに実装しています。下記にR2Ler.javaのソースを載せておきます。引数で指定したPDF(複数可)を右綴じに変換します。javaの-classpathに指定しているとおり、カレントディレクトリにiTextのライブラリ(itextpdf-5.3.0.jar)と、R2Ler.classを置いておく必要があります。

import java.io.File;
import java.io.FileOutputStream;

import com.itextpdf.text.pdf.PdfName;
import com.itextpdf.text.pdf.PdfReader;
import com.itextpdf.text.pdf.PdfStamper;

public class R2Ler {
    public static void main(String[] args) throws Exception {
        for (String file : args) {
            System.out.println("R2Ler: " + file);

            File in = new File(file);
            File out = File.createTempFile("r2l_", ".pdf", in.getAbsoluteFile().getParentFile());

            PdfReader reader = new PdfReader(file);
            PdfStamper stamper = new PdfStamper(reader, new FileOutputStream(out));
            stamper.addViewerPreference(PdfName.DIRECTION, PdfName.R2L);

            stamper.close();
            reader.close();

            if (!in.delete()) {
                throw new RuntimeException("Failed to delete " + in);
            }
            if (!out.renameTo(in)) {
                throw new RuntimeException("Failed to rename " + out + " to " + in);
            }
        }
    }
}

ここまでの処理は、実際にはシェルスクリプトで複数ファイルを一括処理できるように自動化しています。

画素数の変換が終わったら、AcrobatのOCRテキスト認識で透明テキストを埋め込みます(ここはだけはWindowsでの作業)。その際、PDF出力形式を「検索可能な画像(非圧縮)」にすることで、傾き補正で文字のクッキリ感が薄れるのを防ぎます。これでiPad3用の最適化は完了です。

JavaでiTextを使うんだったら、がんばればJPEGのリサイズなんかも全部Javaでできるような気もしますが、そこまでやるほどPDFの知識が無いので、とりあえずは有り物のツールで、という感じです。また、最近ソースネクストから出た「いきなりPDF for 自炊」なんかを使えば、これら全部の作業をもっと簡単にやってくれるみたいですが、その辺はまたは追々と。

(→その1その2、その3、その4)

※バージョンメモ