マクロツイーター

はてダから移行した記事の表示が崩れてますが、そのうちに直せればいいのに(えっ)

TeX でバイナリファイルを読む話

TeX 言語では他の多くのプログラミング言語と同じようにファイル入出力の機能がある。例えば、LaTeX で文書ファイルを組版すると、DVI ファイル(または PDF ファイル)の他に拡張子が .aux のファイルが出力されるが、これは TeX のファイル入出力機能を用いて出力されたものである。((.aux ファイルには TeX のコードが書かれており、“2 回目”の LaTeX の実行時に \input で「プログラム」として読み込まれる。))

ところが、TeX のファイル入出力機能には大きな制限があって、「テキストファイルを行単位に読み書きする」((厳密にいうと \read は行単位ではない。e-TeX 拡張プリミティブの \lineread は厳密に行単位である。 ))ことしかできない。これはつまりバイナリファイルを「正しく」読み書きすることが原理的に不可能であることを示している。全てのファイルはテキストファイルとして扱われるので、例えば、LF(0x0A)と CR(0x0D)と CR+LF はどれも行の区切りと見做されてそれらの間の区別は失われる。また TeX の行入力には癖があって、行末の空白文字(ASCII の 0x20)を無視してしまう。従って、バイナリファイルを無理やり読んだとしても、元のファイルのバイトの並びによっては、「一部が破損したデータ」しか得られないのである。またバイナリのバイト列を無理やり書き出そうとすると、例えば NUL 文字(0x00)は TeX が「気を利かせて」「^^@」という表現に置き換えてしまう。これらの問題はカテゴリコード(\catcode)等を変更して解決できるものではない。

ところが pdfTeX に関しては話が違って、この問題の半分が解決している。具体的には、pdfTeX 拡張プリミティブの \pdffiledump を使うとバイナリファイルを「読む」ことが可能なのである。

  • \pdffiledump [offset 〈開始位置〉] length 〈長さ〉{〈ファイル名〉} : [展開可能]指定のファイルの、指定の開始位置(先頭は 0)から指定の長さ(バイト単位)だけ読み込み、得られたバイト列を大文字 16 進表記した \the-文字列に展開される。

また、次のようなプリミティブも用意されている。(何れも \the-文字列に展開される。)

  • \pdffilesize{〈ファイル名〉} : [展開可能] 指定のファイルの(バイト単位の)サイズの整数の 10 進表記。
  • \pdfescapehex{〈テキスト〉} : [展開可能] テキスト(を完全展開して文字列化したもの)を大文字 16 進表記に変換する。
  • \pdfunescapehex{〈テキスト〉} : [展開可能] テキスト(を完全展開して文字列化したもの)を 16 進表記(大小文字ともに可)と見做しそれが表すバイト列に変換する。*1

以上に挙げた pdfTeX 拡張プリミティブを利用した LaTeX パッケージの例として bmpsize パッケージが挙げられる。これは PNGJPEG 形式(これらはバイナリである)の画像ファイルの中身を解析して画像の寸法(バウンディングボックス)を取得する機能を提供するものである。(bmpsize については後日改めて話したい。)

pTeX 系エンジンでは(たとえ e-TeX 拡張が追加された e-(u)pTeX でも)原則的には pdfTeX 拡張には対応していないので、この \pdffiledump プリミティブは使えなかった。ところが、TeX Live 2014 から、「bmpsize パッケージに対応する」ことを目的として e-pTeX(および e-upTeX)エンジンに \pdffiledump プリミティブが追加される。(現状の W32TeX ではすでに使用可能になっている。)最近の TeX システムでは (u)pLaTeX は e-(u)pTeX エンジンを使用しているので、これからは pLaTeX 系でも「バイナリファイルが読める」ようになる。この他に、TeX Live 2014 の e-(u)pTeX では \pdffilesize も追加される(W32TeX では 2013 年 6 月頃に追加されている)。残念ながら \pdfescapehex\pdfunescapehex は非対応のままのようである。

*1:16 進数字でない文字は無視されるようである。