#!/usr/bin/perl # PS/TeX/TeX-eqn to GIF/PNG/BMP converter by Kumagai Masaaki # PS/TeX/TeX-eqn to GIF/PNG/BMP 熊谷正朗 (google://熊谷正朗/) # core library # # 本プログラムの使用、配布、改良は自由ですが、それを行う方の # 責任にて、お願いします。 (05/01/08) #$psimglibdebug=1; #@r=ps2img_update(@ARGV); #@r=ps2img(@ARGV); #@r=tex2img_update(@ARGV); #@r=tex2img(@ARGV); #print STDERR "size: ".$r{'w'}.'x'.$r{'h'}."\n"; 1; # require のため # コマンド定義群 # コマンドは絶対パスを書くか、パスを通しておくこと。 $CMD_file="/usr/bin/file"; $CMD_ppmquant="/usr/bin/ppmquant"; $CMD_pgmtoppm="/usr/bin/pgmtoppm"; $CMD_pnmscale="/usr/bin/pnmscale"; $CMD_platex="platex"; #$CMD_dvips="dvips"; # Redhat 6 $CMD_dvips="dvips -P pdf"; # Redhat 7 $CMD_ps2epsi="ps2epsi"; $CMD_gs="gs"; $ColorFile='/usr/X11R6/lib/X11/rgb.txt'; # 最終出力を決定。標準でgif。gifがいやな場合 bmp,pngなど。 $CMD_PPMTOGIF="$CMD_ppmquant 256 2>/dev/null | /usr/bin/ppmtogif"; $CMD_PPMTOBMP="/usr/bin/ppmtobmp"; $CMD_PPMTOPNG="/usr/bin/pnmtopng"; $CMD_PPMTOFINAL=$CMD_PPMTOGIF; $tmpdir="/tmp/"; # 環境依存関数 # file コマンドの出力に合わせて修正必要 sub ImageSize { local($fn)=@_[0]; local(%r); open(PSIMGF,"$CMD_file $fn |"); $_=; close(PSIMGF); if(/GIF/){ if(/version[^,]*, (\d+) x (\d+)/) { local(%r); $r{'w'}=$1; $r{'h'}=$2; return %r; } } if(/bitmap data/){ if(/format[^,]*, (\d+) x (\d+)/) { local(%r); $r{'w'}=$1; $r{'h'}=$2; return %r; } } if(/PNG/){ if(/image data[^,]*, (\d+) x (\d+)/) { local(%r); $r{'w'}=$1; $r{'h'}=$2; return %r; } } $r{'w'}=-1; $r{'h'}=-1; return %r; } sub SelectOutputFormat { local(%r); $r{'ext'}='gif'; $r{'type'}='GIF'; if(@_[0]=~/PNG/i) { $CMD_PPMTOFINAL=$CMD_PPMTOPNG; $r{'ext'}='png'; $r{'type'}='PNG'; } if(@_[0]=~/BMP/i) { $CMD_PPMTOFINAL=$CMD_PPMTOBMP; $r{'ext'}='bmp'; $r{'type'}='Windows Bitmap'; } if(@_[0]=~/GIF/i) { $CMD_PPMTOFINAL=$CMD_PPMTOGIF; $r{'ext'}='gif'; $r{'type'}='GIF'; } return %r; } # 互換ルーチン kmgiflib sub ps2gif_update { $CMD_PPMTOFINAL=$CMD_PPMTOGIF; ps2img_update(@_); } sub ps2gif { $CMD_PPMTOFINAL=$CMD_PPMTOGIF; ps2img(@_); } sub tex2gif_update { $CMD_PPMTOFINAL=$CMD_PPMTOGIF; tex2img_update(@_); } sub tex2gif { $CMD_PPMTOFINAL=$CMD_PPMTOGIF; tex2img(@_); } # ps2img/ ps2img_update # PostScript(EPS) から画像ファイルを生成する # # %r=ps2img($sourcefile,$destfile,$width,$height,$color); # # $destfile まで必須。 # width,height は無指定もしくは0 でデフォルト # 両方無し: BoundingBox通りのサイズ # 片方 : BoundingBoxの縦横比で設定 # 両方指定: 縦横比を変更し、指定サイズで # サイズ指定: "数値"(pixel)/"x数値"(倍率) # color は色指定 'color name' or '#RRGGBB' なければそのまま # # %r: {'w'} 幅 {'h'} 高さ sub ps2img_update { local($src)=@_[0]; local($dest)=@_[1]; local($dw)=@_[2]; local($dh)=@_[3]; local($ofx,$ofy,$bw,$bh); if((-M $src)>(-M $dest)) { # ソースが古いけど... open(PSIMGF,"grep BoundingBox $src |"); $_=; close(PSIMGF); /BoundingBox: ([-\d]+) ([-\d]+) ([-\d]+) ([-\d]+)/; $ofx=$1; $ofy=$2; $bw=$3-$1; $bh=$4-$2; # print STDERR "$_ $1 $2 $3 $4 $ofx $ofy $bw $bh\n"; if($dw=~/x([\d.]+)/) { $dw=$bw*$1; } if($dh=~/x([\d.]+)/) { $dh=$bh*$1; } if(($dw=="0")&&($dh=="0")) { $dw=$bw; $dh=$bh; } elsif($dw=='0'){ $dw=$bw*$dh/$bh+0.49; } elsif($dh=="0"){ $dh=$bh*$dw/$bw+0.49; } $dh=int($dh); $dw=int($dw); #print STDERR "examing $dest\n"; local(%r)=ImageSize($dest); #print STDERR "destfile: $r{'w'} $r{'h'}\n"; if((($dw == '0')||($dw == $r{'w'}))&& (($dh == '0')||($dh == $r{'h'}))){ print STDERR "$src is already processed\n"; return %r; # やっぱし同じ } } return ps2img(@_); } sub ps2img { local($src)=@_[0]; # ソースファイル local($dest)=@_[1]; # 出力ファイル local($dw)=@_[2]; # 出力幅 local($dh)=@_[3]; # 出力高さ local($bg)=@_[4]; # バックグラウンド (RRGGBB) local($ofx,$ofy,$bw,$bh); local($sedcmd); local($temp)=$tmpdir.$tmpbase."ps2img.tmp"; local($debug)=$psimglibdebug; local($dn,$dn2); if($debug){ print STDERR "ps2img: Debug mode\n"; } else { $dn=' > /dev/null'; $dn2=' 2> /dev/null'; } # モード切り替え $gsdriver="ppmraw"; $threshold=3080000; $internalfilter=""; if($ENV{'MONOIMG'}) { if($debug) { print STDERR "ps2img: monocrome mode\n"; } $gsdriver="pgmraw"; $threshold*=3; $internalfiler="| $CMD_pgmtoppm black "; } open(PSIMGF,"grep BoundingBox $src |"); $_=; close(PSIMGF); /BoundingBox: ([-\d]+) ([-\d]+) ([-\d]+) ([-\d]+)/; $ofx=$1; $ofy=$2; $bw=$3-$1; $bh=$4-$2; if($debug){print STDERR "Offset: ($1,$2) Size: ",$3-$1,"x",$4-$2,"\n";} if($dw=~/x([\d.]+)/) { $dw=$bw*$1; } if($dh=~/x([\d.]+)/) { $dh=$bh*$1; } if(($dw=="0")&&($dh=="0")) { $dw=$bw; $dh=$bh; } elsif($dw=='0'){ $dw=$bw*$dh/$bh+0.49; } elsif($dh=="0"){ $dh=$bh*$dw/$bw+0.49; } if($dh=~s/\.\d+//){ $dh=$dh; } if($dw=~s/\.\d+//){ $dw=$dw; } if($dw*$dh>20000000) { print STDERR "size $dw*$dh too large\n"; return; } if($debug){print STDERR "Output resolution: $dw x $dh \n";} local($scaling)=1; if(($dw*$dh<$threshold/4)&&($ENV{'NOSMOOTH'}=='')) { $scaling="2"; # アンチエイリアス if($debug){print STDERR "Auto anti-alias\n";} } local($xscale)=$scaling*$dw/$bw; local($yscale)=$scaling*$dh/$bh; # PSファイルの工作 open(PSIMGF,$src); open(PSIMGG,"> $temp"); $boundingf=1; while() { if(($boundingf)&&(/^%%BoundingBox.*$/)){ print PSIMGG '%%BoundingBox: ',"0 0 ",($dw*$scaling); print PSIMGG " ",($dh*$scaling),"\n"; if($bg ne '') { # バックグラウンドカラー $_=$bg; colorstrtorgb(); local($color)=$_; #print STDERR $color; print PSIMGG $color; print PSIMGG 'newpath 0 0 moveto '; print PSIMGG ($dw*$scaling),' 0 lineto '; print PSIMGG ($dw*$scaling),' ',($dh*$scaling),' lineto '; print PSIMGG '0 ',($dh*$scaling),' lineto '; print PSIMGG "0 0 lineto fill\n"; } print PSIMGG "$xscale $yscale scale ".-$ofx." ".-$ofy." translate \n"; $boundingf=0; } else { print PSIMGG $_; } } print PSIMGG "\nshowpage\n"; close(PSIMGF); close(PSIMGG); # IMGへの出力 # A4: 595x842 local($cmd)= "$CMD_gs -sDEVICE=$gsdriver -sOutputFile=$temp"."2 -g". ($dw*$scaling)."x".($dh*$scaling)." -dNOPAUSE -dBATCH -q $temp"; if($debug){print STDERR $cmd,"\n";} system($cmd); $cmd="cat $temp"."2"; if($scaling!="1") { $cmd=$cmd." | $CMD_pnmscale ".(1/$scaling); } $cmd=$cmd.$internalfilter.$dn2; $cmd=$cmd."| $CMD_PPMTOFINAL > $dest".$dn2; if($debug){print STDERR $cmd,"\n";} system($cmd); if(!$debug) { unlink($temp); unlink($temp."2"); } local(%r); $r{'w'}=$dw; $r{'h'}=$dh; return %r; } # tex2img/ tex2img_update # pLaTeXソースから PostScript(EPS)経由で画像ファイルを生成する # # %r=tex2img($tex_command,$destfile,$res,$color); # # $destfile まで必須。 # $res は拡大率 # color は色指定 'color name' or '#RRGGBB' なければそのまま # # %r: {'w'} 幅 {'h'} 高さ sub tex2img_update { local($tex)=@_[0]; # TEXソース local($dest)=@_[1]; # 出力ファイル local($res)=@_[2]; # 倍率 local($bg)=@_[3]; # バックグラウンド (color or #RRGGBB) local($desc)=$res.$bg." ".$tex; local(%r); if(-f $dest.".tgi") { open(TEXIMG,$dest.".tgi"); $_=; close(TEXIMG); s/ R(\d+)x(\d+)\s*$//; $r{'w'}=$1; $r{'h'}=$2; if($desc eq $_) { print STDERR "no need to process '$dest'\n"; $r{'w'}=$1; $r{'h'}=$2; return %r; } } %r=tex2img(@_); open(TEXIMG,"> ".$dest.".tgi"); print TEXIMG $desc.' R'.$r{'w'}.'x'.$r{'h'}; close(TEXIMG); return %r; } sub tex2img { local($separator)=$/; local($tex)=@_[0]; # TEXソース local($dest)=@_[1]; # 出力ファイル local($res)=@_[2]; # 倍率 local($bg)=@_[3]; # バックグラウンド (color or #RRGGBB) local($temp)=$tmpdir.$tmpbase."tex2img.tmp"; local($cmd); local($debug)=$psimglibdebug; local($dn,$dn2); if($res == 0) {$res='1';} if($debug){ print STDERR "tex2img: Debug mode\n"; } else { $dn=' > /dev/null'; $dn2=' 2> /dev/null'; } open(TEXIMG,"> $temp".".tex"); print TEXIMG '\documentclass[12pt]{jarticle}\pagestyle{empty}',"\n"; # print TEXIMG '\usepackage{epsbox}'."\n"; print TEXIMG '\def\theequation{}\begin{document}',"\n"; print TEXIMG $tex; print TEXIMG "\n",'\end{document}'; close(TEXIMG); $cmd="(cd $tmpdir; $CMD_platex $temp; $CMD_dvips $temp".".dvi; $CMD_ps2epsi $temp".".ps)"; $cmd=$cmd." ; close(TEXIMG); /BoundingBox: (\d+) (\d+) (\d+) (\d+)/; $ofx=$1-2; $ofy=$2-2; $bw=$3-$1+4; $bh=$4-$2+4; if($debug){ print STDERR "Offset: ($1,$2) Size: ",$3-$1,"x",$4-$2,"\n"; } $dw=$bw*$res; $dh=$bh*$res; if($dw*$dh>20000000) { print STDERR "size $dw*$dh too large\n"; return; } local($scaling)=1; if(($dw*$dh<3080000/4)&&($ENV{'NOSMOOTH'} == '')) { $scaling="2"; # アンチエイリアス if($debug){print STDERR "Auto anti-alias\n";} } local($xscale)=$scaling*$dw/$bw; local($yscale)=$scaling*$dh/$bh; # PSファイルの工作 open(TEXIMGF,"$temp".".epsi"); open(TEXIMGG,"> $temp".".ps"); while() { if(/^%%BoundingBox.*$/){ print TEXIMGG '%%BoundingBox: ',"0 0 ",($dw*$scaling); print TEXIMGG " ",($dh*$scaling),"\n"; if($bg ne ""){ $_=$bg; colorstrtorgb(); local($color)=$_; #print STDERR $color; print TEXIMGG $color; print TEXIMGG 'newpath 0 0 moveto '; print TEXIMGG ($dw*$scaling),' 0 lineto '; print TEXIMGG ($dw*$scaling),' ',($dh*$scaling),' lineto '; print TEXIMGG '0 ',($dh*$scaling),' lineto '; print TEXIMGG "0 0 lineto fill 0 0 0 setrgbcolor\n"; } print TEXIMGG "$xscale $yscale scale -$ofx -$ofy translate \n"; } else { print TEXIMGG $_; } } print TEXIMGG "showpage\n"; close(TEXIMGF); close(TEXIMGG); # IMGへの出力 # A4: 595x842 local($cmd)= "$CMD_gs -sDEVICE=ppmraw -sOutputFile=$temp".".ppm -g". ($dw*$scaling)."x".($dh*$scaling). " -dNOPAUSE -dBATCH -q $temp".".ps"; if($debug){ print STDERR $cmd,"\n"; } system($cmd); $cmd="/bin/cat $temp".".ppm"; if($scaling!="1") { $cmd=$cmd." | $CMD_pnmscale ".(1/$scaling); } # $cmd=$cmd."| $CMD_ppmquant 256 2>/dev/null "; $cmd=$cmd."| $CMD_PPMTOFINAL > $dest 2>/dev/null"; if($debug){ print STDERR $cmd,"\n"; } system($cmd); if(!$debug){ unlink($temp.'.aux'); unlink($temp.'.log'); unlink($temp.'.epsi'); unlink($temp.'.dvi'); unlink($temp.'.ps'); unlink($temp.'.tex'); unlink($temp.'.ppm'); } $/=$separator; # 情報返却 local(%r); $r{'w'}=$dw; $r{'h'}=$dh; return %r; } sub colorstrtorgb () { local($color)=$_; if(substr($color,0,1) eq '#'){ $_=(hex(substr($color,1,2))/255).' '. (hex(substr($color,3,2))/255).' '. (hex(substr($color,5,2))/255)." setrgbcolor\n"; return; } open(TEXIMGC,"grep '$color' $ColorFile |"); while() { # print STDERR $_; if(/(\d+)\s+(\d+)\s+(\d+)\s+$color[\s*]$/){ close(TEXIMGC); $_=($1/255).' '.($2/255).' '.($3/255)." setrgbcolor\n"; return ; } } close(TEXIMGC); print STDERR "No color '$color'\n"; $_="1 1 1 setrgbcolor\n"; return; }