TeX Alchemist Online

TeX のこと,フォントのこと,Mac のこと

Mac環境でPDFのページ数をカウントする9(+1)通りの手法

【追記】この記事を macOS 12.3 以降の環境に対応させた記事を書きました。 doratex.hatenablog.jp

id:acetaminophen さんが,Windows環境でPDFのページ数をカウントするバッチファイルを,様々な手法で実装する試みをなされています。

d.hatena.ne.jp

d.hatena.ne.jp

そこで,Mac環境についても,コマンドラインからPDFのページ数をカウントする様々な手法をまとめておこうと思います。

目次

1. Spotlight 検索インデックスのメタデータを利用する(信頼性:低)

Mac環境でのPDFページ数カウントの方法として,よくこの手法が紹介されているのを見かけます。Spotlight 検索用のメタデータに保存されているページ数の情報を取り出すという手法です。

$ mdls "hoge.pdf" | grep kMDItemNumberOfPages | awk '{print $3}'

ただし,この方法は,

  • CD-ROM 内や,ユーザがシステム環境設定でSpotlight検索から除外している場所など,Spotlightのインデックス作成対象でない場所にあるPDFに対しては使えない。
  • Spotlight のメタデータが壊れている場合もあり,正しい結果が返ってくるとは限らない。

といった欠点があり,あまり信頼できる方法ではありません。

2. RubyCocoa を利用する(10.5 Leopard 〜 10.9 Mavericks)

OS X 10.5 Leopard 〜 10.9 Mavericks には,RubyからCocoaを利用するブリッジである RubyCocoa が標準添付されていました。これを利用すれば,ワンライナーRubyスクリプトから Cocoa / Quartz の機能を呼び出し,PDFのページ数を次のようにカウントすることが可能でした。

$ ruby -e 'require "osx/cocoa";include OSX;require_framework "Quartz";puts PDFDocument.alloc.initWithURL(NSURL.fileURLWithPath("hoge.pdf")).pageCount'

OS X 10.9 Mavericks では,Ruby 2.0 が標準になったものの,Ruby 1.8 も同梱されていますので,次のように Ruby 1.8 の絶対パスを指定すれば動きます。

$ /System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/bin/ruby -e 'require "osx/cocoa";include OSX;require_framework "Quartz";puts PDFDocument.alloc.initWithURL(NSURL.fileURLWithPath("hoge.pdf")).pageCount'

しかし,OS X 10.10 Yosemite からは,とうとう Ruby 1.8 が削除されて Ruby 2.0 に一本化された影響により,RubyCocoa も標準添付されなくなりました。よって現在ではこの方法は標準では使えなくなっています。 Yosemite の Ruby 2.0 に対応した RubyCocoa の新版 がリリースされているようですので,各自がそれをインストールすれば再びこの手法が使えるようになるでしょう。 しかし,気軽に使える方法ではなくなってしまいましたので,別の手法を探します。

3. pdfinfo を使う(要pdfinfo)

XpdfPoppler の付属ツールである pdfinfo を使えば,その出力からPDFのページ数が得られます。

$ pdfinfo hoge.pdf | grep '^Pages: ' | awk '{print $2}'

pdfinfo は各自でインストールする必要があります。例えば MacPorts ならば,次のようにしてインストールできます。

$ sudo port install xpdf-tools

4. QPDF を使う(要QPDF)

QPDF をインストールしておけば,それを利用してPDFのページ数カウントができます。

$ qpdf --show-npages hoge.pdf

QPDFも MacPorts で簡単にインストールできます。

$ sudo port install qpdf

5. Ghostscript を使う(要Ghostscript)

Ghostscript が利用可能な環境であれば,Ghostscriptでページ数をカウントすることも可能です。

$ gs -q -dNODISPLAY -c '(hoge.pdf) (r) file runpdfbegin pdfpagecount = quit'

6. exctractbb を使う(要TeX環境)

最近の TeX 環境(TeX Live 2014 以上)がインストールされている環境であれば,extractbb -O の標準出力を解析することでページ数を得ることができます。

$ extractbb -O 'hoge.pdf' | grep '^%%Pages:' | cut -d' ' -f 2

7. pdfTeX を使う(要TeX環境,ネタ)

これはネタ的な方法ですが,PDFのページ数を出力させる pdfTeX 用の plain TeX ソースをその場でコンパイルする方法です。 texput.log というログファイルが出力されるので,後でその掃除をしています。

$
echo '\pdfoutput=1\pdfximage{hoge.pdf}\message{Pages:\the\pdflastximagepages}\bye' | pdftex -draftmode | grep '^Pages:' | cut -d':' -f 2; rm texput.log

8. Swift を使う(要Xcode Command Line Tools,遅い)

Xcode Command Line Tools がインストールされている環境であれば,Swift コンパイラをワンライナー実行用に使うことで,Quartz の機能を利用してPDFページ数カウントが可能です。

$ echo 'import Quartz;print(PDFDocument(url:URL(fileURLWithPath:"hoge.pdf"))!.pageCount)' | swift | sed -e '1,1d'

ただし,Swift のコンパイルが遅いので,実行時間が長く,この方法はあまり現実的ではありません。

9. Python (PyObjC) を使う(最も汎用的)

Python から OS X のAPIにアクセスするためのブリッジである PyObjC は,OS X 10.5 Leopard 以上で標準添付されており,現在の OS X 10.10 Yosemite でも標準で利用することができます。 Python から Quartz の機能を利用することで,PDFのページ数が得られます。

$ python -c "import os;import CoreGraphics as cg;print(cg.CGPDFDocumentCreateWithProvider(cg.CGDataProviderCreateWithFilename('hoge.pdf')).getNumberOfPages())"

結論:決定版は Python!

以上9種類の手法を比較してみると,現時点で「どのMac環境でも標準で使える方法」としては,Python に軍配が上がることが分かります。

ユーザに追加インストールを強いることなく,OS X 10.5 以上の全てのMac環境で利用可能で,動作速度にも問題ありませんので,Mac環境下におけるPDFページ数カウント法の決定版と言えるでしょう。

AppleScript から PyObjC を利用する

決定版である Python を使う方法を,AppleScript から利用可能なハンドラとしてもまとめておきましょう。

--- 与えられたPDFのページ数を返すハンドラ pdfPageCount
on pdfPageCount(aPDF)
    return (do shell script ("
python -c \"import os;import CoreGraphics as cg;print(cg.CGPDFDocumentCreateWithProvider(cg.CGDataProviderCreateWithFilename('" & POSIX path of aPDF & "')).getNumberOfPages())\"")) as integer
end pdfPageCount

--- テスト:ダイアログで選択したPDFファイルのページ数を表示する
set pdf to choose file of type {"com.adobe.pdf"}
set pages to my pdfPageCount(pdf)
display dialog ("Pages: " & pages) buttons {"OK"}

番外編:AppleScriptObjC を利用する(10.10 Yosemite ~)

OS X 10.10 Yosemite からは,通常の AppleScript の中から Objective-C のクラスを呼び出す AppleScriptObjC が使えるようになりました。この方法を使えば,do shell script による外部コマンド呼び出しを行うことなく,AppleScript からのPDFページ数カウントが可能です。

考え方としては,Swift を使う方法 と同様に,Quartz の機能を利用して OS X の CoreGraphics API を呼び出します。

use AppleScript version "2.4"
use framework "Foundation"
use scripting additions

--- 与えられたPDFのページ数を返すハンドラ pdfPageCount
on pdfPageCount(aPDF)
    set aPath to POSIX path of aPDF
    set aURL to current application's NSURL's fileURLWithPath:aPath
    set aDoc to current application's PDFDocument's alloc's initWithURL:aURL
    aDoc's pageCount()
end pdfPageCount

--- テスト:ダイアログで選択したPDFファイルのページ数を表示する
set pdf to choose file of type {"com.adobe.pdf"}
set pages to my pdfPageCount(pdf)
display dialog ("Pages: " & pages) buttons {"OK"}