pdfTeXじゃないけどGhostscriptさえあれば画像は埋め込めるよねっ
2015/1/24 16:48 GhostscriptをGhostScriptと記述していました.お詫び申し上げます.
はじめに
こんなことがTeXユーザの集いであった.そのなかで,TeXの限界というタイトルでいくつかのトピックが示された.私の知る限り,TeXはチューリング完全なのだから,原理的に計算可能ならどうにかなるだろうと思った.実際,「え,list環境再実装すれb……」などの容易に実現できそうな項目もある.ただ,一つのトピックに目を奪われた.
画像のBase64埋め込みの非対応
確かに,TeXでバイナリを扱うのは面白そうな課題である.
やってみた.
技術概要
某奥底にあるように,LuaTeXのような特殊な場合を除き,TeXエンジンでバイナリファイルを書くことは困難である.よって,TeXでバイナリで与えられる画像データを直接扱うのは不可能である.すなわち,テキストで書き出して,そのバイナリへの解釈をTeXの外部に任せることになる.さて,どうしようかと悩んだところで,ふと思いついた.EPSに出力して\includegraphicsで読めばいいじゃないか,と.EPSはテキスト形式なのでTeXから出力できる.また,ラスタ画像を埋め込むための命令もある.
作成した機能は,LaTeXのパッケージとして動作するようにした.そのパッケージ,binaryimage.styはここに置いておいた.このパッケージは,次のようにして利用する.
\documentclass{article} \usepackage[dvipdfmx]{graphicx} \usepackage{binaryimage} \begin{document} ... \includebinary[size=wxh,option={width=6cm, clip}]<<バイナリの16進数表現>>\endbinary ... \end{document}
画像を入れるために,includebinaryという命令を用意している.この命令は,オプションにsize(必須)とoptionをとる.sizeはwxhの形で幅と高さのピクセル数を与える.例えば,幅400px,高さ300pxの画像ならばsize=400x300とする.optionはこの命令によって自動的に挿入される\includegraphicsへ与えるオプションである*2.オプションの後には画像のバイナリを16進数表現したものを入れる.このバイナリは,JPEGなどの画像形式をそのまま入れるのではなく,「左上から右へ,右端まで行ったら次の行の左端へ」という順で対応するピクセルのRGBをそれぞれ1Byteで表現したものである.こんなPythonコードで変換できる*3.
# -*- coding:utf-8-unix -*- from PIL import Image import sys def main(): img = Image.open(sys.argv[1]) for j in range(img.size[1]): for i in range(img.size[0]): print("{:02X}{:02X}{:02X}".format(*img.getpixel((i,j))), end="") print() if __name__ == "__main__": main()
実行すると,なぜか
%!PS-Adobe-3.0 EPSF-3.0 %%BoundingBox: 0 0 w h /DeviceRGB setcolorspace 1 1 scale << /ImageType 1 /Width w /Height h /BitsPerComponent 8 /Decode [0 1 0 1 0 1] /ImageMatrix [w 0 0 h neg 0 h] /DataSource currentfile /ASCIIHexDecode filter >> image バイナリの16進数表現
ここで,w,h,および「バイナリの16進数表現」には\includebinaryで指定した値が入る.
実装小話(急カーブ注意×1)
ここで,binaryimage.styを実装するにあたり利用したテクニックを紹介する.
%の出力
TeXにおいて,%はコメントの開始文字なので,単純に出力できない.さらに,今回の場合は,\%も利用することはできない.\write(ファイル出力)のタイミングで\%が処理できないからである.そこで,binaryimage.styでは\lccodeを使ってコメント開始の役割を持たない%を作り出すことにした.
次のコードを見てみよう.
\lccode`+=`\%% \lowercase{\def~{+}}%
\lccodeはある文字に対して,その小文字の文字コードを指定するプリミティブである.上記のコードの1行目では,「+」の小文字に「%」を指定している.そして,\lowercaseを用いて「\def~{+}」の部分を小文字化する.これにより「\def~{%}」となるのだが,このときのカテゴリーコード(文字の役割)は変換前のものが使用されるため,「%」はカテゴリーコード12,すなわち一般の文字として扱われる.後は,%を出力したいところに~を書けば,~が展開されて%が出力される.
長いバイナリ入力への対応
\binaryimageでは,その用途の関係上,長大な入力を必要とする.しかし,TeXのマクロの引数として与えられる文字列はそんなに大きくない.そこで,今回は\edefを使って長大な入力を受け付けるようにした.
\binaryimageは,最初,オプション部分しか読まないようになっている.そして,\binaryimageを展開すると,末尾は次のようになる.
\edef\@bin@data{\ifnum`}=0\fi image^^J^^J
\@bin@dataの定義になっているように見えるが,閉じ波括弧が「\ifnum`}=0\fi」となっている.この部分が展開され,空になるので,上記のコードは
\edef\@bin@data{image^^J^^J
という閉じ波括弧がない未完成の\edefになる.いわゆる\ifnumトリックである.ここから,<<バイナリの16進数表現>>が読み込みおよび展開されていき,\endbinaryに達する.\endbinaryを展開すると,その先頭は次のようになっている.
\ifnum`{=0\fi}% end of \edef
これは,先の\ifnumトリックの対をなすコードで,閉じ波括弧単体となる.結果,\@bin@dataの定義が完成する.
この後,\endbinaryによってepsファイルの出力と\includegraphicsが行われ,画像が読み込まれる.
副次効果
binaryimage.styを通じて,Ghostscriptの闇などに触れてしまった.ここでは,その一部を紹介しよう.
関連研究
ZRさんによって,このような記事がすでに発表されている.これはpdfTeXの機能を用いて,直接pdfファイル中に画像を埋め込む手法になっている.Ghostscriptに悩まされない分,綺麗な手法といえるが,pdfTeX依存になるため,TeXエンジンを複数使い分ける場合などに支障が出る.