Hatena::ブログ(Diary)

アセトアミノフェンの気ままな日常

2015-09-08

bcpdfcrop v0.3.2

そういえば、先日 bcpdfcrop のバージョンを v0.3.2 に上げた(付属の bcpdfcrop-multi は v0.0.3)。bcpdfcrop は「pdftex.exe / extractbb.exe / rungs.exe を呼べるかどうか」を、bcpdfcrop-multi は「bcpdfcrop.bat を呼べるかどうか」を事前に確認するようにしてみた。bcpdfcrop-multi を例に簡単に説明しておく(というか自分用のメモ)。

いまの bcpdfcrop-multi は bcpdfcrop.bat が

  • bcpdfcrop-multi.bat と同じディレクトリ
  • 環境変数 PATH に指定されたディレクトリ

のいずれかにあれば call できるようにしてあるのだけれど、この条件を満たすかどうか判定する。今回入れたコードは

for %%f in (bcpdfcrop.bat) do (
  if "%%~$PATH:f"=="" (
    echo Error: bcpdfcrop not found. 1>&2
    set BCERROR=1
  )
)

というもの。この %~$PATH:1 という引数展開、知らなかった…参考にしたのはここ

指定したファイルが、環境変数 PATH で定義されたフォルダ内に存在するかどうかを探し、あった場合はドライブ名も含めたフルパスで取得します。PATH 環境変数に指定されていなければ、カレントディレクトリは探しません。また、みつからない場合は空文字です。

どうやら任意の環境変数の中身とファイル名を結合して検索できるらしい。カレントディレクトリが入らないので、PATH に事前にバッチが存在するディレクトリを加えておけば大丈夫。もしこの方法を使わなければ

call bcpdfcrop 1>nul 2>nul
if %errorlevel% neq 0 (
  echo Error: bcpdfcrop not found. 1>&2
  set BCERROR=1
)

でも多分同じことが実装できるはず。

2015-08-31

バッチファイルの技巧

いま bcpdfcrop v0.3.1 を公開。昨日書いたとおり、オプション(“/”で始まるバッチファイル引数をオプションとみなす)を順不同で指定できるようにした。このなかで曲者なのがもちろん v0.3.0 で実装したばかりの /m オプションである。この /m オプション自体が次に引数を伴うので、ほかのオプションとは少々扱いが違う。実装はなんだか技巧的な気がする… ついでに Unknown option の警告も返すようにできた。同じ方法を使えば、出力する PDF のバージョンを指定する /v オプションもその気になれば作れるんだよなあ…とこれまた悩み中。

2015-08-30

バッチファイルのリファクタリング中

いま、僕がいままで作ってきた各種バッチファイルのリファクタリング中。いま思いついているだけでも改善点が多数。

  • bcpdfcrop
    • オプションの指定順序を固定しているのが不便→どんな順序でもオプションが有効になるように変更予定。
    • 余白指定オプションが第4-第7引数になっているのが使いづらい→本家 pdfcrop ライクなオプション指定法で再実装がほぼ完了。
    • ページ範囲指定で「*」が単独で指定された場合に1ページ目だけとして扱われるのが非直感的→これを全ページとみなすように変更予定。
    • ページ範囲指定や余白指定に数字以外が指定された場合への対処:以前考えていた途中だが、これを取り入れるかどうか悩み中。
  • dvipdf

作品集は後日まとめて公開予定。

2015-08-29

dvips + gs による PDF 生成

最近 pLaTeX + dvips + gs による PDF 生成を試している…別に新しいことは何もないのだけれど、僕自身はほとんど使ったことのないスキームだったから。そこで気づいたのだが、Mac などの Unix 環境ではどうやら gs に dvips と gs をパイプで実行するシェルスクリプト「dvipdf」が入っているらしい。これは Windows ネイティブで使えないので、試してみたくなって大雑把にバッチファイルに翻訳してみた。初めて知ったのだけど、dvips はログを標準出力でなく標準エラー出力に吐くのね…なんか不思議。

いまこんなことを試しているのは、TeX2img でこの経路をサポートすることを検討しているからである。というのも、日本ではほとんど DVI 経由で (u)pLaTeX + dvipdfmx が使われている一方で、海外では LaTeX + dvipdfm(x) はほとんど使われておらず「DVI 経由といえば LaTeX + dvips + gs」という状況であることが十分想定されるからである。そして、これがおそらく、graphics のドライバがデフォルトで dvipdfmx.def でなく dvips.def を採用している所以であろう。

もちろん海外では pdfLaTeX が主流ではあるが、PSTricks や PSfrag を使うという用途に限り DVI 経由でなければならないという見方が強いようである。これらのパッケージは dvips + gs に強く依存しているので、現行の TeX2img では僕がむかし紹介した裏技 (Win/Mac) を使わない限り難しい。これが海外で TeX2img 不利の原因になりかねないことを危惧して、機能拡張を検討しているところ

いま、Mac 版が dvips + gs や dvipdf を DVIware に指定してもよいようになりつつある。Win 版も dvips + gs をとりあえずサポートしつつあるところである。

2015-08-12

コマンドプロンプトの大小比較を切り抜けたい

bcpdfcrop の v0.1.9 で導入して v0.2.3 で廃止した「BBOX の左下と右上の座標比較」を復活させるにはどうすればよいか考えていたのだが、一筋縄ではいかなそう…

問題の v0.1.9 以降 v0.2.2 までに入っていたコードは以下のとおり。

:: PROCBBOX の値が空にならないように初期化
  set PROCBBOX=%%%BBOX%: 0 0 0 0
:: gs による BBOX をファイルから読み込み(もしファイルが空なら初期値保持)
  set /P PROCBBOX=<"%TPX%%%i-box.txt"
:: 最初の %%BoundingBox: や %%HiResBoundingBox: を切除
  set PROCBBOX=!PROCBBOX:* =!
:: とりあえず VALIDBOX=0 で初期化
  set VALIDBOX=0
:: PROCBBOX を解析して4つの座標を各変数に格納
  for /F "tokens=1-4 delims= " %%k in ("!PROCBBOX!") do (
    set BBLLX=%%k
    set BBLLY=%%l
    set BBURX=%%m
    set BBURY=%%n
  )
:: X 座標、Y 座標を比較し、ともに左下が右上より小さければ VALIDBOX=1(正常)
  if !BBLLX! lss !BBURX! (
    if !BBLLY! lss !BBURY! (
      set VALIDBOX=1
    )
  )
:: 以下、正常ならば TeX ソースに PROCBBOX を書き込んでクロップ

これだと、例えば以下のような BBOX 値は /h モードで警告が出てしまう。

%%BoundingBox: 29 70 86 126
%%HiResBoundingBox: 29.993765 70.104810 85.672122 125.944098

昨日わかったとおり「小数点を含む値は文字列とみなされ、辞書順比較にかけられる」からである。この場合は Y 座標がアウト。

実験(1):とりあえず小数点を削除

小数がだめなら小数点を削除したくなるのが筋だろう。幸い HiResBoundingBox は小数点以下6桁と一定なので、一見問題なさそうである。というわけで、以下のコードを加えてみる。

:: X 座標、Y 座標の比較直前に加える:小数点を削除して一つの整数に
  set BBLLX=!BBLLX:.=!
  set BBLLY=!BBLLY:.=!
  set BBURX=!BBURX:.=!
  set BBURY=!BBURY:.=!
:: 確認用コード
  echo !BBLLX! !BBLLY! !BBURX! !BBURY!
:: 以下、座標比較へ

これなら先ほどアウトだった HiResBoundingBox でも「29993765 70104810 85672122 125944098」という整数値になって比較可能だが、時に数値が大きくなりすぎる。実際に

%%BoundingBox: 10000 10399 12690 13255
%%HiResBoundingBox: 10000.012406 10399.992222 12689.054267 13254.032112

という BBOX 値では /h モードの場合に11桁に膨れ上がってしまう。これは、符号付きの32ビット値*1からはみ出すので

C:\>if 1000000000 lss 1100000000 echo yes
yes                                             <= OK

C:\>if 2000000000 lss 1100000000 echo yes
                                                <= OK
C:\>if 10000000000 lss 11000000000 echo yes
                                                <= NG
C:\>if 10000000000 equ 11000000000 echo yes
yes                                             <= NG

C:\>

というように数値比較できなくなってしまう(set /A の仕様とも合致)。つまり、単純に小数点を削除するのは数値が大きなりすぎるのでダメ。

実験(2):小数点以下を切り捨てる

通常の PDF なら BBOX 座標の整数部分が同じになることはありえないだろう*2。そこで、整数部分だけを比較するように方針転換。コマンドプロンプトは整数同士の除算では小数点以下を切り捨てるのだが、小数で同様のことができないか試す。

:: X 座標、Y 座標の比較直前に加える:小数点以下切り捨て[1]
  set /A BBLLX=!BBLLX!/1
  set /A BBLLY=!BBLLY!/1
  set /A BBURX=!BBURX!/1
  set /A BBURY=!BBURY!/1
:: 確認用コード
  echo !BBLLX! !BBLLY! !BBURX! !BBURY!
:: 以下、座標比較へ

この場合、/h モードでは4回「演算子がありません。」という警告が出て「10000 10399 12689 13254」のように整数値に丸められる。

警告が出るのは嫌なので、試しに事前にドットをカンマに置換してみよう。すると嬉しいことにカンマで“別々の数値”に分解したように扱われ、一つ目の数値に対して演算子が作用する。

:: X 座標、Y 座標の比較直前に加える:小数点以下切り捨て[2]
  set /A BBLLX=!BBLLX:.=,!/1
  set /A BBLLY=!BBLLY:.=,!/1
  set /A BBURX=!BBURX:.=,!/1
  set /A BBURY=!BBURY:.=,!/1
:: 確認用コード
  echo !BBLLX! !BBLLY! !BBURX! !BBURY!
:: 以下、座標比較へ

これでめでたく警告が消える… と思いきや、BBOX 値によっては「無効な数字です。数値定数は 10 進 (17 桁)、16 進 (0x11 桁)、または 8 進 (021 桁) です。」という別の警告が出る。

  • 警告なし
    • 「317.940037 257.020023 603.439997 383.759988」→ 317 257 603 383
    • 「198.870041 228.670024 563.749998 434.789987」→ 198 228 563 434
  • 警告1回
    • 「249.900039 126.610027 393.529980 259.019992」→ 249 126 393 259
    • 「29.993765 70.004810 85.672122 125.944098」→ 29 70 85 125
  • 警告2回
    • 「80.012406 99.992222 189.094269 154.082119」→ 80 99 189 154

共通点が分かるだろうか? 正解は「小数第一位が0で、かつその下の位に8または9がある場合」に警告が出る。理由は簡単で「リーディングゼロは8進数とみなされるから」。8と9は8進数ではあり得ないのだ。

そこで、リーディングゼロを起こさないように予め小数第一位より前に1を付加してみる。

:: X 座標、Y 座標の比較直前に加える:小数点以下切り捨て[3]
  set /A BBLLX=!BBLLX:.=,1!/1
  set /A BBLLY=!BBLLY:.=,1!/1
  set /A BBURX=!BBURX:.=,1!/1
  set /A BBURY=!BBURY:.=,1!/1
:: 確認用コード
  echo !BBLLX! !BBLLY! !BBURX! !BBURY!
:: 以下、座標比較へ

これなら小数点以下は必ず1で始まるので、常に10進数と判定される。

これで一応警告なしに HiResBoundingBox の整数部分を比較できそうである…ここまでくると、もはや藝人としかいえなくなってくるw

*1:すなわち -2147483648 から 2147483647 のこと。

*2:「BBOX 座標の整数部分が同じ」ということは、よほど図が極小でなければならない。そのような PDF が仮にあったとすれば、HiRes でなければ期待どおりにクロップするのは難しいだろう。