eval '(exit $?0)' && eval 'exec perl -S $0 ${1+"$@"}' && eval 'exec perl -S $0 $argv:q' # -*-perl-*- if 0; # The expression in the previous line replaces the unix specific line # {#!/usr/bin/perl}. # ps2eps - convert PostScript to EPS (Encapsulated PostScript) files # ------------------------------------------------------------------- # (C)opyright 1999-2002 Roland Bless # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # # Author: Roland Bless # Send bug reports to roland@bless.de # ------------------------------------------------------------------- # Additional filtering is done when Windows generated PostScript files # are processed. Some instructions will otherwise lead to bad output # if EPS-file gets embedded into other PostScript files. # Requirements: # + perl # + gs (ghostscript supporting pbm output) # + bbox (a little C program [ANSI-C - should work on every platform] # for calculation of the actual BoundingBox) # ------------------------------------------------------- # $Id: ps2eps,v 1.39 2002/07/10 09:50:28 bless Exp $ # ------------------------------------------------------- $prgname= "ps2eps"; $ghostscriptname= "gs"; $bboxname= "bbox"; $version= '$Id: ps2eps,v 1.39 2002/07/10 09:50:28 bless Exp $'; #' $insertPScode= 1; # Insert surrounding Postscript code $infhandle = STDIN; # Standard input is the default input file $outfhandle = STDOUT; # Standard output is default output if STDIN is input $infname= '-'; $outfname= '-'; $forceoverwrite= 0; # do not overwrite existing files $ignoreBB= 0; # ignore existing Bounding Box comment $removeDSC= 1; # remove Adobe document structure comments $removeADO= 1; # remove Adobe printer Driver console Output [Page: ...] $ignoreEOFDSC= 0; # ignore %%EOF DSC hint $removePreview= 0; # remove preview $quiet= 0; # write progress to stdout $resolution= 144; # resolution for bounding box calculation is 2x72 dpi $trytofixps= 1; # try to fix postscript code $forcefixps= 0; # fix postscript code unconditionally if eq 1 $clip=0; # do not clip $inch=2.54; # one inch is 2.54cm $fullcolors= 1; # use ppm format (24-bit full color) $trailerseen= 0; # Trailer comment seen? $PSversion="2.0"; # default Postscript Version $PSDSCversion="2.0"; # default Postscript DSC Version $gpar=""; $trigger= 0; #$debug=1; # Turn this on if you want to see the gs call @ver= split(/ /,$version); # filename for temporary files if (defined($ENV{'TMP'})) { $tmpdir= $ENV{'TMP'}; $filesep= ($tmpdir =~ /^?\:\\/) ? '\\' : '/'; if ($tmpdir =~ /$filesep$/) { $tmpfname= $tmpdir . "$prgname.$$"; } else { $tmpfname= $tmpdir . "$filesep$prgname.$$"; } } else #assume we're on a UNIXBOX { $tmpfname= "/tmp/" . "$prgname.$$"; } $licensetxt= "\ This program is free software; you can redistribute it and/or modify\ it under the terms of the GNU General Public License as published by\ the Free Software Foundation; either version 2 of the License, or\ (at your option) any later version.\ \ This program is distributed in the hope that it will be useful,\ but WITHOUT ANY WARRANTY; without even the implied warranty of\ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\ GNU General Public License for more details.\ \ You should have received a copy of the GNU General Public License\ along with this program; if not, write to the Free Software\ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA\n"; @prgidtxt= ( "$prgname - convert PostScript to EPS (Encapsulated PostScript) files\n", "(C)opyright 1998-2002 Roland Bless\n\n" ); @helptxt= ("Version: @ver[2]\n", "Operation:\n", " Without any argument, $prgname reads from standard input\n", " and writes to standard output.\n", " If filenames are given as arguments they are processed\n", " one by one and output files are written to filenames\n", " with extension '.eps'. If input filenames have the extension\n", " '.ps' or '.prn', this extension is replaced with '.eps'.\n", " In all other cases '.eps' is appended to the input filename.\n", " Please note that PostScript files for input should contain\n", " only one single page.\n\n"); @usagetxt= ("Syntax:\n", " $prgname [-f] [-q] [-N] [-n] [-P] [-c] [-C] [-m] [-B] [-E] [-s] [-l] [-h|--help] [-L] [-V|--version] [--] [psfile1] [psfile2] [...]\n", "Options:\n", " -f force overwriting existing files\n", " -q quiet operation (no output while processing files)\n", " -N do not insert any postscript code\n", " -n do not try to fix postscript code\n", " -P remove preview image (smaller file, but no preview)\n", " -F fix postscript code unconditionally\n", " -c preserve document structure comments\n", " -C insert postscript code for clipping\n", " -m use black/white bitmap as base for calculation\n", " -s pagesize in cm (default) or in, format XxY[cm|in], where X and Y are numbers\n", " -l expand the original bounding box by one point in each direction\n", " -B do not use existing bounding box as page size for rendering\n", " -E do not use %%EOF as hint for end of file\n", " --help, \n", " -h help information\n", " -L show licensing information\n", " --version,\n", " -V show version information\n", " -- all following arguments are treated as files\n", " (allows filenames starting with -)\n", "\n", "Arguments:\n", " One or more names of PostScript files for input\n"); # -- argument checking $no_of_files= 0; ARGCHECK: while ($arg= shift) # process options { if ( $arg =~ /^-/ ) #it is an option { if ( $arg =~ /^--$/ ) # after -- only filenames can follow { while ($arg= shift) { @filenames[$no_of_files]= $arg; $no_of_files++; }; last ARGCHECK; } if ( $arg eq '-f' ) { $forceoverwrite= 1; next ARGCHECK; } if ( $arg eq '-q' ) { $quiet= 1; next ARGCHECK; } if ( $arg eq '-m' ) { $fullcolors= 0; next ARGCHECK; } if ( $arg eq '-n' ) { $trytofixps= 0; next ARGCHECK; } if ( $arg eq '-F' ) { $forcefixps= 1; $trytofixps= 1; next ARGCHECK; } if ( $arg eq '-N' ) { $insertPScode= 0; next ARGCHECK; } if ( $arg eq '-P' ) { $removePreview= 1; next ARGCHECK; } if ( $arg eq '-c' ) { $removeDSC= 0; next ARGCHECK; } if ( $arg eq '-C' ) { $clip= 1; next ARGCHECK; } if ( $arg eq '-l' ) { $looseBB= '-l'; next ARGCHECK; } if ( $arg eq '-B' ) { $ignoreBB= 1; next ARGCHECK; } if ( $arg eq '-E' ) { $ignoreEOFDSC= 1; next ARGCHECK; } if ( $arg =~ '^-s' ) { # if explicit size is given, ignore existing BoundingBox always $ignoreBB= 1; $pagedimension= substr($arg,2); # dimension follows -s directly ($x_dim, $x, $y_dim, $unit)= split(/(x|cm|in)/,$pagedimension); if ( $x_dim !~ /^\d*\.?\d*$/ ) { die "$x_dim in $arg is not a valid number"; } if ( $y_dim !~ /^\d*\.?\d*$/ ) { die "$y_dim in $arg is not a valid number"; } #print STDERR "xdim: $x_dim ydim: $y_dim unit:$unit\n" ; if ( $unit ne 'in' ) # assume centimeters { # calculate dimension in pixels (remember: resolution is in dpi) $xpixels= int(($x_dim * $resolution) / $inch)+1; $ypixels= int(($y_dim * $resolution) / $inch)+1; $gpar= "-g${xpixels}x${ypixels}"; } else { $xpixels= int($x_dim * $resolution)+1; $ypixels= int($y_dim * $resolution)+1; $gpar= "-g${xpixels}x${ypixels}"; } next ARGCHECK; } if ( $arg eq '-h' || $arg eq '--help' ) { die @prgidtxt,@helptxt,@usagetxt, "\nAuthor: Roland Bless (roland\@bless.de)\n\n" } #end -h if ( $arg eq '-L' ) { die @prgidtxt,$licensetxt, "\nAuthor: Roland Bless (roland\@bless.de)\n\n" } #end -L if ( $arg eq '-V' || $arg eq '--version') { die @prgidtxt,"Version: @ver[2]\n"; } #end -V die "$prgname: unknown option $arg\n",@usagetxt; } else { # it is a filename @filenames[$no_of_files++]= $arg; } } #end while ARGCHECK if ($no_of_files == 0) # no file arguments, use STDIN as input { @filenames[0]= '-'; } if (!$quiet) { print STDERR "Input files: @filenames\n"; } $device= $fullcolors ? "ppmraw" : "pbmraw"; PROCESSFILE: while ($infname= (shift @filenames)) { # reset filter definitions for each file undef $linefilter; undef $rangefilter_begin; undef $rangefilter_end; $fixthisps= $trytofixps; $fixmsgprinted= 0; if (!$quiet) { print STDERR "Processing: $infname\n"; } unless (open($infhandle,"<$infname")) { # skip over this file print STDERR "$prgname: Can't open $infname: $!\n"; next PROCESSFILE; } # buffer input from stdin into temporary file, because it has to be read twice # one for ghostscript, the other for generating output if ($infname eq '-') # input is stdin { $tmpfhandle=''; open($tmpfhandle,">$tmpfname") or die "Cannot open temporary file $tmpfname for writing: $!\n"; } else # input is not stdin { undef $tmpfhandle; #if filename ends with .ps or .prn, replace the extension with .eps if ($infname =~ /\.(ps|prn)$/i) { $outfname= $infname; $outfname =~ s/\.(ps|prn)$/.eps/i; } else # otherwise simply append the extension .eps { $outfname= $infname . ".eps"; } if (!$forceoverwrite and -s "$outfname") { die "$prgname: Sorry, file named $outfname already exists,", " will not overwrite it.\n", " You will have to use the -f option, delete it or rename it", " before running $prgname again.\n"; } else { open($outfhandle,">$outfname") or die "Can't open file $outfname for writing: $!\n"; } } #end else input is not stdin $linefilter= '^$'; # filter empty lines by default while (<$infhandle>) { if (/%!PS-Adobe-(\S+).*EPSF-(\S+)/) { $PSversion=$1; $PSDSCversion=$2; if (! ($PSversion =~ /\d+\.\d+/)) { $PSDSCversion="2.0"; } if (! ($PSDSCversion =~ /\d+\.\d+/)) { $PSDSCversion="2.0"; } } if (!$ignoreBB) { if ( /^%%\s*BoundingBox:\s*(.*)/ && !defined($eBBllx) ) { ($eBBllx,$eBBlly,$eBBurx,$eBBury,$rest)= split /\s/,$1; # print STDERR "$eBBllx,$eBBlly,$eBBurx,$eBBury\n"; $xpixels= int(($eBBurx * $resolution)/72)+1; $ypixels= int(($eBBury * $resolution)/72)+1; $gpar= "-g${xpixels}x${ypixels}"; # check for meaningful values if (($xpixels <= 1) || ($ypixels <= 1)) { $gpar=""; undef $eBBllx; } else { if (!$quiet) { print STDERR "Rendering with existing $_"; } } } } if ($fixthisps) # try to fix bad postscript code { # check for Windows 3.x output if ( /^Win.*Dict/ ) { if (!$quiet && !$fixmsgprinted) { print STDERR "Windows 3.5 generated Postscript file detected, fixing\n"; } $linefilter= '^(EJ|RS)'; $rangefilter_begin= '^statusdict'; $rangefilter_end= 'cvx\ settransfer$'; #' $fixmsgprinted= 1; # stop printing message } else { if ( /^%%Creator:\s*Wind.U\s*Xprinter/ ) { if (!$quiet && !$fixmsgprinted) { print STDERR "StarOffice generated Postscript file detected, fixing\n"; } $linefilter= '^rs'; $fixmsgprinted= 1; # stop printing message } else { if ( $forcefixps || /^\/NTPS/ || /Creator:\s*(AdobePS|Pscript|.*Windows)/i ) #check for NT generated output { if (!$quiet && !$fixmsgprinted) { print STDERR "Windows generated Postscript file detected, fixing\n"; } $rangefilter_begin= '^(\[\{|featurebegin\{)$'; #' $rangefilter_end= '^(\} stopped cleartomark|\}featurecleanup)'; $triggered_rangefilter_begin= '^(1 1 1 SC)$'; #' $triggered_rangefilter_end= '^(AF)$'; $fixmsgprinted= 1; # stop printing message } # end if NTPS } #end else } } #end if trytofixps if (defined($tmpfhandle)) { print $tmpfhandle $_ or die "$prgname: Failure during writing to temporary file $tmpfname"; } if (/^%%EOF\s*$/) { $totalEOFDSC++ } } #end while <$infhandle> if (defined($tmpfhandle)) { close($tmpfhandle); } else { $tmpfhandle= $infhandle; $tmpfname= $infname; } # calculate the bounding box if (!$quiet) { print STDERR "Calculating Bounding Box..."; } $cmdline="$ghostscriptname -dNOPAUSE -q $gpar -r$resolution -sDEVICE=$device -sOutputFile=- $tmpfname -c showpage -c quit | $bboxname $looseBB -r $resolution"; if ($debug) { print STDERR "\ncalling: $cmdline\n"; } $boundingbox=`$cmdline`; #check result if ($boundingbox !~ /^%%BoundingBox/) { print STDERR "Error: Could not determine bounding box!\n", "I suppose $ghostscriptname had some trouble interpreting the ps-file\n", "or $bboxname is not in your search path of executables\n"; } if ($clip) { # extend clipping box by 1 point $boundingbox =~ /^%%.*BoundingBox:\s*(.*)/; ($cBBllx,$cBBlly,$cBBurx,$cBBury,$rest)= split(/\s/,$1); if ($cBBllx > 0) { $cBBllx--; } if ($cBBlly > 0) { $cBBlly--; } $cBBurx++; $cBBury++; $boundingbox = "%%BoundingBox: $cBBllx $cBBlly $cBBurx $cBBury\n"; } if (!$quiet) { print STDERR "ready. $boundingbox" }; $before_startps= 1; $inserted_prolog= 0; $prolog_passed= 0; if (!$quiet) { print STDERR "Creating output file $outfname..."; } open($tmpfhandle,"<$tmpfname") or die "Cannot open file $tmpfname for reading"; CREATEOUTPUT: while (<$tmpfhandle>) { $binarysection=/^(%%BeginBinary)|(beginimage)$/ ... /^(%%EndBinary)|^(endimage)/; if ( !$binarysection ) { s/\r?\n?$//; # remove CR and/or LF at end of line } if ($before_startps) { if ( /%!PS-Adobe.*/ ) { $before_startps= 0; } next CREATEOUTPUT; } else # we are in regular postscript code { if ( /^%%EOF\s*$/ ) { $seenEOF++ } if (! $inserted_prolog) { print $outfhandle "%!PS-Adobe-$PSversion EPSF-$PSDSCversion\n"; print $outfhandle $boundingbox; print $outfhandle "% EPSF created by ps2eps @ver[2]\n"; $inserted_prolog= 1; redo CREATEOUTPUT; } else #inserted_prolog { if (! $prolog_passed) { #ignore the following lines in the prolog if ( /^%%BoundingBox/ || /^%%Pages/ || /^%%BeginProlog/ || /^%%EndProlog/ || ($removeDSC && /^%%.*: \(atend\)/) || ($removePreview && (/^%%BeginPreview/ ... /^%%EndPreview/)) ) { next CREATEOUTPUT; } else { if ( /^[^%].*/ || /^%%EndComments/ ) # line is not a comment { #output postscript code for proper EPS file if ($insertPScode) { print $outfhandle "%%EndComments\n", "%%BeginProlog\n"; } if ($clip) { printf $outfhandle "newpath %d %d moveto %d %d lineto %d %d lineto %d %d lineto closepath clip\n",$cBBllx,$cBBlly,$cBBurx,$cBBlly,$cBBurx,$cBBury,$cBBllx,$cBBury } if ($insertPScode) { print $outfhandle "save\n", "countdictstack\n", "mark\n", "newpath\n", "/showpage {} def\n", "/setpagedevice {pop} def", "%%EndProlog\n", "%%Page 1 1\n"; } $prolog_passed= 1; if (/^%%EndComments/) { next CREATEOUTPUT; } } } } else # main part of postscript file { #end of postscript file reached? #Usually the DSC %%EOF signifies the end if ( eof($tmpfhandle) || ($ignoreEOFDSC == 0 && /^%%EOF\s*$/ && $seenEOF == $totalEOFDSC) || ( $trailerseen && /^II\*\000.*/ ) ) { #do not forget to print last line if not terminated by LF if ( eof($tmpfhandle) && !/^$/ && !/^%%EOF\s*$/ ) # do not insert %%EOF twice { print $outfhandle $_,"\n"; } #add appropriate trailer if ($insertPScode) { print $outfhandle "%%Trailer\n", "cleartomark\n", "countdictstack\n", "exch sub { end } repeat\n", "restore\n", "%%EOF\n"; } last CREATEOUTPUT; } # stop output if ( /^%%Trailer\s*$/ ) { $trailerseen=1; } else { if (!/^/s*$/) { $trailerseen=0; } } # check for trigger if (/^eoclip$/) { $trigger= 1; }; # remove the following lines if ( !$binarysection # only when not in binary section && ( ($removePreview && (/^%%BeginPreview/ ... /^%%EndPreview/)) || # no preview (defined($rangefilter_begin) && (/$rangefilter_begin/ ... /$rangefilter_end/) ) || (defined($triggered_rangefilter_begin) && $trigger && (/$triggered_rangefilter_begin/ ... /$triggered_rangefilter_end/) ) || /$linefilter/ # lines by linefilter || ($removeDSC && (/^%( |!)(\w )+/ || /^%%/)) # any type of structured comment || ($removeADO && (/^statusdict begin.*ProductName.*print product print.*flush end\r?\n?$/ || /^\(%%\[\s*(Page:.*|LastPage)\s*\]%%\)\s*=\s*\r?\n?/ )) || /^$/ # empty lines ) ) { next CREATEOUTPUT; } # sanity check if ( /clear|erasepage|initmatrix|initclip|initgraphics|startjob|\ cleardictstack|setmatrix|setpagedevice|copypage|grestoreall|\ exitserver|quit/ ) { $notsane= 1; } else { $notsane= 0; } } # end else (this is main part) # Output the postscript line print $outfhandle $_; if (!$binarysection) { print $outfhandle "\n"; } } # end else prolog_passed } # end else inserted_prolog } # end while CREATEOUTPUT close($tmpfhandle); if ($tmpfname ne $infname) { unlink "$tmpfname"; } #remove temporary file close ($outfhandle); if (!$quiet) { print STDERR "ready.\n"; } if ($notsane and !$quiet) { print STDERR "Warning: EPS-output for $infname is not sane, at least one\n", "of the following commands was still present:\n", "clear erasepage initmatrix initclip initgraphics startjob\n", "cleardictstack setmatrix setpagedevice copypage grestoreall\n", "exitserver quit\n"; } } #end while PROCESSFILE # ---- end of perl-script -------