Postscript ファイルに参照情報スタンプを押す Perl script

最近、自分の文献リストの整備に凝っている。そもそも研究をして文献を増やさなければ文献リストを整備してもしょうがないのであるが、そのことはここでは追求しない(追求しないでください)。

で、自分でひとの文献をインターネットからひっぱってきたときによく起きるのが、どこに載った論文なのか分からないという事態である。クリックしてダウンロードしたファイルには、タイトルやら著者名やらは当然書いてあるのだが、論文誌名とかページ数といった参照情報が書いてないものが意外に多いのである。 せめて自分の論文では、最初のページにちゃんと参照情報を書いておきたい。と思うのだが、いったん Postscript ファイルになってしまうと、そこに参照情報を書き足すのは非常におっくうである。これがまあ参照情報のない論文が多い理由なのだろう。

しかし、Postscript ファイルの中身は描画情報である。適当にコマンドをはさみこめば、参照情報を追加することは難しくないはずである。……どこに何をはさむかを間違えなければ、だが。

そこで、Perl Script を書いてみた。Postscript ファイルを食わせると、1ページ目の左上に参照情報のスタンプを追加して、別のファイルに出力するものである。 大量の論文に処理するといった事態を考えれば、script になっているほうが有利だし、漢字の処理やら改行の処理も楽にできるだろう、と思い、いろいろ工夫してみた。

しかし、まともに postscript を生成したのは初めてなので苦労した……。後置系の言語と見当がついていたので楽だったけど、それでも Postscript リファレンス本がなければ手も足も出なかったと思われる。しかも、まだいい加減な点が多いので (DSC Comment 内のフォント情報を修正しないなど)、エラーが起きる可能性もあります。一応、作った後表示なり印字をしてみてください。

スタンプ適用前
スタンプ適用前の様子

スタンプ適用後
スタンプ適用前の様子
#!/usr/local/bin/perl
# Reference Stamper 1.0
#   by Makino Takaki

#  入力ファイルは1行に1エントリで、各エントリは次の形式です。
#   [InputFile] [OutputFile] [Reference...]
#
#  ファイル名には空白が入ってはだめです。.gz も使えます。
#  Reference には次の記号が使えます。
#     _..._ 囲まれた範囲をイタリックにします (英語だけ)。
#     \n    次の行にいきます。
#     \_    下線そのもの。
#     \\    バックスラッシュそのもの。
#
#  たとえばこんなかんじです。
#  hoge.ps.gz  done/hoge.ps.gz    なんとか学会 研究報告 AB-CDE, pp.23-34, 2002.
#  moge.ps     done/moge.ps       In _Proc. of NANTOKA '97_\npp.123-135, 1997.

use Jcode; 

while($process = <>)
{
    my ($in, $out, $comment) = ($process =~ /^\s*(\S+)\s+(\S+)\s+(.*)$/);

    print "$in -> $out\n";

    if( $in =~ /\.gz$/ ) { $in = "gzip -d -c $in |"; } else {$in = "<$in"};
    if( $out =~ /\.gz$/ ) { $out = "| gzip -9 >$out"; } else {$out = ">$out"};
    open(IN, $in) || die "Cannot open input file: $!";
    open(OUT, $out)  || die "Cannot open output file: $!";

    my $inserted = 0;

    my $str = new Jcode($comment)->euc;
    my $kanji = 0;
    my $italic = 0;
    my $ret = "";

    # 文字列 $str を Postscript 描画文字列 $ret に変換
    while( $str )
    {
        if( $str =~ /^[\x80-\xff][\x80-\xff]/ )
        {        # 漢字だったら (EUCコードであることを前提)
            $kanji = 1;
            $ret .= '$$XSKanji$$ ';
            while( $str =~ s/^([\x80-\xff][\x80-\xff])// )
            {
                my ($a,$b) = unpack("CC",$1);
                $ret .= sprintf("<%02x%02x> show ", $a-128, $b-128);
            }
        }
        if( $str =~ s/^([^_\\\(\)\x00-\x1f\x80-\xff]+)// )
        {        # Ascii テキストだったら
            $ret .= ($italic ? '$$XSItalic$$ ' :  '$$XSAscii$$ ') . "($1) show ";
        }
        elsif( $str =~ s/^[\x00-\x1f]// )
        {        # コントロール文字だったら
                 # 何もしない
        }
        elsif( $str =~ s/^_// )
        {        # アンダーライン (イタリック状態変更)
            $italic = ! $italic;
        }
        elsif( $str =~ s/^\\n// )
        {        # 改行
            $ret .= "grestore 0 -12 rmoveto gsave ";
        }        # \ の後の1文字
        elsif( $str =~ s/^\\(.)// )
        {
            $ret .= ($italic ? '$$XSItalic$$ ' :  '$$XSAscii$$ ') . "(\\$1) show ";
        }        # その他特殊記号 (かっこなど)
        elsif( $str =~ s/^(.)// )
        {
            $ret .= ($italic ? '$$XSItalic$$ ' :  '$$XSAscii$$ ') . "(\\$1) show ";
        }
    }
    $ret = "gsave " . $ret . "grestore";

    while(<IN>)
    {
        if( /^%%EndComments$/ )
        {      # EndComments の後 (最初のほう) に専用定義
            print OUT;
            $_ = "";
            print OUT "/\$\$XSKanji\$\$ " . 
                    "{/Ryumin-Light-H findfont 11 scalefont setfont} def\n";
            print OUT "/\$\$XSAscii\$\$ " .
                    "{/Times findfont 11 scalefont setfont} def\n";
            print OUT "/\$\$XSItalic\$\$ " .
                    "{/Times-Italic findfont 11 scalefont setfont} def\n";
            print OUT "/\$\$XSInsertedReferenceString\$\$ " . 
                    "{" . $ret . "} def\n";
            print OUT "/\$\$XSInsertReferenceString\$\$ " . 
                    "{gsave initmatrix 24 805 moveto " . 
                    "\$\$XSInsertedReferenceString\$\$ grestore} def\n";
               # 「11」(3か所) がフォントサイズです。適当に変更してください。
               # 「24 805」が印字位置(紙の左下からのX座標とY座標)です。
               #  単位は 1/72 インチです。現状は A4 サイズの論文に合わせて
               #  あるので、これも適当に変更してください。
        }
        elsif( /^\/\$\$XS[A-Za-z}+\$\$ / )
        {      # すでに参照スタンプ用の定義があった場合
            next;  # 表示しない
        }
        elsif( /^\$\$XSInsertReferenceString\$\$\n/ )
        {      # すでに参照スタンプ用のコマンドがあった場合
            next;
        }
        elsif( /^%%Page: / )
        {      # 最初の Page DSC コメントがあった場合 (逆順 PS には未対応)
            if( ! $inserted++ )
            {
                print OUT;
                $_ = "";
                print OUT "\$\$XSInsertReferenceString\$\$\n";
            }
        }

        print OUT;
    }
    close IN;
    close OUT;
}