| 1 |
eval '(exit $?0)' && eval 'exec perl -S $0 ${1+"$@"}' && eval 'exec perl -S $0 $argv:q' |
| 2 |
if 0; |
| 3 |
use strict; |
| 4 |
|
| 5 |
# Change by Thomas Esser, Sept. 1998: The above lines allows us to find |
| 6 |
# perl along $PATH rather than guessing a fixed location. The above |
| 7 |
# construction should work with most shells. |
| 8 |
|
| 9 |
# A script to transform an EPS file so that: |
| 10 |
# a) it is guarenteed to start at the 0,0 coordinate |
| 11 |
# b) it sets a page size exactly corresponding to the BoundingBox |
| 12 |
# This means that when Ghostscript renders it, the result needs no |
| 13 |
# cropping, and the PDF MediaBox is correct. |
| 14 |
# c) the result is piped to Ghostscript and a PDF version written |
| 15 |
# |
| 16 |
# It needs a Level 2 PS interpreter. |
| 17 |
# If the bounding box is not right, of course, you have problems... |
| 18 |
# |
| 19 |
# The only thing I have not allowed for is the case of |
| 20 |
# "%%BoundingBox: (atend)", which is more complicated. |
| 21 |
# |
| 22 |
# Sebastian Rahtz, for Elsevier Science |
| 23 |
# |
| 24 |
# now with extra tricks from Hans Hagen's texutil. |
| 25 |
# |
| 26 |
# History |
| 27 |
# 1999/05/06 v2.5 (Heiko Oberdiek) |
| 28 |
# * New options: --hires, --exact, --filter, --help. |
| 29 |
# * Many cosmetics: title, usage, ... |
| 30 |
# * New code for debug, warning, error |
| 31 |
# * Detecting of cygwin perl |
| 32 |
# * Scanning for %%{Hires,Exact,}BoundingBox. |
| 33 |
# * Scanning only the header in order not to get a wrong |
| 34 |
# BoundingBox of an included file. |
| 35 |
# * (atend) supported. |
| 36 |
# * uses strict; (earlier error detecting). |
| 37 |
# * changed first comment from '%!PS' to '%!'; |
| 38 |
# * corrected (atend) pattern: '\s*\(atend\)' |
| 39 |
# * using of $bbxpat in all BoundingBox cases, |
| 40 |
# correct the first white space to '...Box:\s*$bb...' |
| 41 |
# * corrected first line (one line instead of two before 'if 0;'; |
| 42 |
# 2000/11/05 v2.6 (Heiko Oberdiek) |
| 43 |
# * %%HiresBoundingBox corrected to %%HiResBoundingBox |
| 44 |
# 2001/03/05 v2.7 (Heiko Oberdiek) |
| 45 |
# * Newline before grestore for the case that there is no |
| 46 |
# whitespace at the end of the eps file. |
| 47 |
# |
| 48 |
|
| 49 |
### program identification |
| 50 |
my $program = "epstopdf"; |
| 51 |
my $filedate="2001/03/05"; |
| 52 |
my $fileversion="2.7"; |
| 53 |
my $copyright = "Copyright 1998-2001 by Sebastian Rahtz et al."; |
| 54 |
my $title = "\U$program\E $fileversion, $filedate - $copyright\n"; |
| 55 |
|
| 56 |
### ghostscript command name |
| 57 |
my $GS = "gs"; |
| 58 |
$GS = "gswin32c" if $^O eq 'MSWin32'; |
| 59 |
$GS = "gswin32c" if $^O =~ /cygwin/; |
| 60 |
|
| 61 |
### options |
| 62 |
$::opt_help=0; |
| 63 |
$::opt_debug=0; |
| 64 |
$::opt_compress=1; |
| 65 |
$::opt_gs=1; |
| 66 |
$::opt_hires=0; |
| 67 |
$::opt_exact=0; |
| 68 |
$::opt_filter=0; |
| 69 |
$::opt_outfile=""; |
| 70 |
|
| 71 |
### usage |
| 72 |
my @bool = ("false", "true"); |
| 73 |
my $usage = <<"END_OF_USAGE"; |
| 74 |
${title}Syntax: $program [options] <eps file> |
| 75 |
Options: |
| 76 |
--help: print usage |
| 77 |
--outfile=<file>: write result to <file> |
| 78 |
--(no)filter: read standard input (default: $bool[$::opt_filter]) |
| 79 |
--(no)gs: run ghostscript (default: $bool[$::opt_gs]) |
| 80 |
--(no)compress: use compression (default: $bool[$::opt_compress]) |
| 81 |
--(no)hires: scan HiResBoundingBox (default: $bool[$::opt_hires]) |
| 82 |
--(no)exact: scan ExactBoundingBox (default: $bool[$::opt_exact]) |
| 83 |
--(no)debug: debug informations (default: $bool[$::opt_debug]) |
| 84 |
Examples for producing 'test.pdf': |
| 85 |
* $program test.eps |
| 86 |
* produce postscript | $program --filter >test.pdf |
| 87 |
* produce postscript | $program -f -d -o=test.pdf |
| 88 |
Example: look for HiResBoundingBox and produce corrected PostScript: |
| 89 |
* $program -d --nogs -hires test.ps>testcorr.ps |
| 90 |
END_OF_USAGE |
| 91 |
|
| 92 |
### process options |
| 93 |
use Getopt::Long; |
| 94 |
GetOptions ( |
| 95 |
"help!", |
| 96 |
"debug!", |
| 97 |
"filter!", |
| 98 |
"compress!", |
| 99 |
"gs!", |
| 100 |
"hires!", |
| 101 |
"exact!", |
| 102 |
"outfile=s", |
| 103 |
) or die $usage; |
| 104 |
|
| 105 |
### help functions |
| 106 |
sub debug { |
| 107 |
print STDERR "* @_\n" if $::opt_debug; |
| 108 |
} |
| 109 |
sub warning { |
| 110 |
print STDERR "==> Warning: @_!\n"; |
| 111 |
} |
| 112 |
sub error { |
| 113 |
die "$title!!! Error: @_!\n"; |
| 114 |
} |
| 115 |
sub errorUsage { |
| 116 |
die "$usage\n!!! Error: @_!\n"; |
| 117 |
} |
| 118 |
|
| 119 |
### option help |
| 120 |
die $usage if $::opt_help; |
| 121 |
|
| 122 |
### get input filename |
| 123 |
my $InputFilename = ""; |
| 124 |
if ($::opt_filter) { |
| 125 |
@ARGV == 0 or |
| 126 |
die errorUsage "Input file cannot be used with filter option"; |
| 127 |
$InputFilename = "-"; |
| 128 |
debug "Input file: standard input"; |
| 129 |
} |
| 130 |
else { |
| 131 |
@ARGV > 0 or die errorUsage "Input filename missing"; |
| 132 |
@ARGV < 2 or die errorUsage "Unknown option or too many input files"; |
| 133 |
$InputFilename = $ARGV[0]; |
| 134 |
-f $InputFilename or error "'$InputFilename' does not exist"; |
| 135 |
debug "Input filename:", $InputFilename; |
| 136 |
} |
| 137 |
|
| 138 |
### option compress |
| 139 |
my $GSOPTS = ""; |
| 140 |
$GSOPTS = "-dUseFlateCompression=false " unless $::opt_compress; |
| 141 |
|
| 142 |
### option BoundingBox types |
| 143 |
my $BBName = "%%BoundingBox:"; |
| 144 |
!($::opt_hires and $::opt_exact) or |
| 145 |
error "Options --hires and --exact cannot be used together"; |
| 146 |
$BBName = "%%HiResBoundingBox:" if $::opt_hires; |
| 147 |
$BBName = "%%ExactBoundingBox:" if $::opt_exact; |
| 148 |
debug "BoundingBox comment:", $BBName; |
| 149 |
|
| 150 |
### option outfile |
| 151 |
my $OutputFilename = $::opt_outfile; |
| 152 |
if ($OutputFilename eq "") { |
| 153 |
if ($::opt_gs) { |
| 154 |
$OutputFilename = $InputFilename; |
| 155 |
if (!$::opt_filter) { |
| 156 |
$OutputFilename =~ s/\.[^\.]*$//; |
| 157 |
$OutputFilename .= ".pdf"; |
| 158 |
} |
| 159 |
} |
| 160 |
else { |
| 161 |
$OutputFilename = "-"; # standard output |
| 162 |
} |
| 163 |
} |
| 164 |
if ($::opt_filter) { |
| 165 |
debug "Output file: standard output"; |
| 166 |
} |
| 167 |
else { |
| 168 |
debug "Output filename:", $OutputFilename; |
| 169 |
} |
| 170 |
|
| 171 |
### option gs |
| 172 |
if ($::opt_gs) { |
| 173 |
debug "Ghostscript command:", $GS; |
| 174 |
debug "Compression:", ($::opt_compress) ? "on" : "off"; |
| 175 |
} |
| 176 |
|
| 177 |
### open input file |
| 178 |
open(IN,"<$InputFilename") or error "Cannot open", |
| 179 |
($::opt_filter) ? "standard input" : "'$InputFilename'"; |
| 180 |
binmode IN; |
| 181 |
|
| 182 |
### open output file |
| 183 |
if ($::opt_gs) { |
| 184 |
my $pipe = "$GS -q -sDEVICE=pdfwrite $GSOPTS " . |
| 185 |
"-sOutputFile=$OutputFilename - -c quit"; |
| 186 |
debug "Ghostscript pipe:", $pipe; |
| 187 |
open(OUT,"|$pipe") or error "Cannot open Ghostscript for piped input"; |
| 188 |
} |
| 189 |
else { |
| 190 |
open(OUT,">$OutputFilename") or error "Cannot write '$OutputFilename"; |
| 191 |
} |
| 192 |
|
| 193 |
### scan first line |
| 194 |
my $header = 0; |
| 195 |
$_ = <IN>; |
| 196 |
if (/%!/) { |
| 197 |
# throw away binary junk before %! |
| 198 |
s/(.*)%!/%!/o; |
| 199 |
} |
| 200 |
$header = 1 if /^%/; |
| 201 |
debug "Scanning header for BoundingBox"; |
| 202 |
print OUT; |
| 203 |
|
| 204 |
### variables and pattern for BoundingBox search |
| 205 |
my $bbxpatt = '[0-9eE\.\-]'; |
| 206 |
# protect backslashes: "\\" gets '\' |
| 207 |
my $BBValues = "\\s*($bbxpatt+)\\s+($bbxpatt+)\\s+($bbxpatt+)\\s+($bbxpatt+)"; |
| 208 |
my $BBCorrected = 0; |
| 209 |
|
| 210 |
sub CorrectBoundingBox { |
| 211 |
my ($llx, $lly, $urx, $ury) = @_; |
| 212 |
debug "Old BoundingBox:", $llx, $lly, $urx, $ury; |
| 213 |
my ($width, $height) = ($urx - $llx, $ury - $lly); |
| 214 |
my ($xoffset, $yoffset) = (-$llx, -$lly); |
| 215 |
debug "New BoundingBox: 0 0", $width, $height; |
| 216 |
debug "Offset:", $xoffset, $yoffset; |
| 217 |
|
| 218 |
print OUT "%%BoundingBox: 0 0 $width $height\n"; |
| 219 |
print OUT "<< /PageSize [$width $height] >> setpagedevice\n"; |
| 220 |
print OUT "gsave $xoffset $yoffset translate\n"; |
| 221 |
} |
| 222 |
|
| 223 |
### scan header |
| 224 |
if ($header) { |
| 225 |
while (<IN>) { |
| 226 |
|
| 227 |
### end of header |
| 228 |
if (!/^%/ or /^%%EndComments/) { |
| 229 |
print OUT; |
| 230 |
last; |
| 231 |
} |
| 232 |
|
| 233 |
### BoundingBox with values |
| 234 |
if (/^$BBName$BBValues/) { |
| 235 |
CorrectBoundingBox $1, $2, $3, $4; |
| 236 |
$BBCorrected = 1; |
| 237 |
last; |
| 238 |
} |
| 239 |
|
| 240 |
### BoundingBox with (atend) |
| 241 |
if (/^$BBName\s*\(atend\)/) { |
| 242 |
debug $BBName, "(atend)"; |
| 243 |
if ($::opt_filter) { |
| 244 |
warning "Cannot look for BoundingBox in the trailer", |
| 245 |
"with option --filter"; |
| 246 |
last; |
| 247 |
} |
| 248 |
my $pos = tell(IN); |
| 249 |
debug "Current file position:", $pos; |
| 250 |
|
| 251 |
# looking for %%BoundingBox |
| 252 |
while (<IN>) { |
| 253 |
# skip over included documents |
| 254 |
if (/^%%BeginDocument/) { |
| 255 |
while (<IN>) { |
| 256 |
last if /^%%EndDocument/; |
| 257 |
} |
| 258 |
} |
| 259 |
if (/^$BBName$BBValues/) { |
| 260 |
CorrectBoundingBox $1, $2, $3, $4; |
| 261 |
$BBCorrected = 1; |
| 262 |
last; |
| 263 |
} |
| 264 |
} |
| 265 |
|
| 266 |
# go back |
| 267 |
seek(IN, $pos, 0) or error "Cannot go back to line '$BBName (atend)'"; |
| 268 |
last; |
| 269 |
} |
| 270 |
|
| 271 |
# print header line |
| 272 |
print OUT; |
| 273 |
} |
| 274 |
} |
| 275 |
|
| 276 |
### print rest of file |
| 277 |
while (<IN>) { |
| 278 |
print OUT; |
| 279 |
} |
| 280 |
|
| 281 |
### close files |
| 282 |
close(IN); |
| 283 |
print OUT "\ngrestore\n" if $BBCorrected; |
| 284 |
close(OUT); |
| 285 |
warning "BoundingBox not found" unless $BBCorrected; |
| 286 |
debug "Ready."; |
| 287 |
; |