source: trunk/doc/epstosmth

Last change on this file was 4, checked in by Pavel Demin, 17 years ago

first commit

  • Property svn:executable set to *
File size: 9.3 KB
Line 
1#!/usr/bin/env perl
2
3# Copyright 1998-2001 by Sebastian Rahtz et al.
4# epstopdf is free software; you can redistribute it and/or modify
5# it under the terms of the GNU General Public License as published by
6# the Free Software Foundation; either version 2 of the License, or
7# (at your option) any later version.
8#
9# epstopdf is distributed in the hope that it will be useful,
10# but WITHOUT ANY WARRANTY; without even the implied warranty of
11# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12# GNU General Public License for more details.
13#
14# You should have received a copy of the GNU General Public License
15# along with epstopdf; if not, write to the Free Software
16# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17
18use strict;
19
20# A script to transform an EPS file so that:
21# a) it is guarenteed to start at the 0,0 coordinate
22# b) it sets a page size exactly corresponding to the BoundingBox
23# This means that when Ghostscript renders it, the result needs no
24# cropping, and the PDF MediaBox is correct.
25# c) the result is piped to Ghostscript and a PDF version written
26#
27# It needs a Level 2 PS interpreter.
28# If the bounding box is not right, of course, you have problems...
29#
30# The only thing I have not allowed for is the case of
31# "%%BoundingBox: (atend)", which is more complicated.
32#
33# Sebastian Rahtz, for Elsevier Science
34#
35# now with extra tricks from Hans Hagen's texutil.
36#
37# History
38# 1999/05/06 v2.5 (Heiko Oberdiek)
39# * New options: --hires, --exact, --filter, --help.
40# * Many cosmetics: title, usage, ...
41# * New code for debug, warning, error
42# * Detecting of cygwin perl
43# * Scanning for %%{Hires,Exact,}BoundingBox.
44# * Scanning only the header in order not to get a wrong
45# BoundingBox of an included file.
46# * (atend) supported.
47# * uses strict; (earlier error detecting).
48# * changed first comment from '%!PS' to '%!';
49# * corrected (atend) pattern: '\s*\(atend\)'
50# * using of $bbxpat in all BoundingBox cases,
51# correct the first white space to '...Box:\s*$bb...'
52# * corrected first line (one line instead of two before 'if 0;';
53# 2000/11/05 v2.6 (Heiko Oberdiek)
54# * %%HiresBoundingBox corrected to %%HiResBoundingBox
55# 2001/03/05 v2.7 (Heiko Oberdiek)
56# * Newline before grestore for the case that there is no
57# whitespace at the end of the eps file.
58# 2006/05/09 v2.8 (David W. Rankin, Jr.)
59# * option --pdfvers to specify the Ghostscript command to set
60# the PDF version output.
61
62my $IsWin32 = ($^O =~ /MSWin32/i);
63
64### program identification
65my $program = "epstosmth";
66my $filedate="2006/05/09";
67my $fileversion="2.8";
68my $copyright = "Copyright 1998-2001 by Sebastian Rahtz et al.";
69my $title = "\U$program\E $fileversion, $filedate - $copyright\n";
70
71### ghostscript command name
72my $GS = "gs";
73$GS = "gswin32c" if $^O eq 'MSWin32';
74
75if ($IsWin32) {
76 $GS = `kpsecheck --ghostscript`;
77 $GS =~ m/^dll\s*:\s*(.+)/mio;
78 $GS = $1;
79 $GS =~ s/gsdll32.dll/gswin32c.exe/io;
80 if ($GS eq "") {
81 $GS = "gswin32c.exe";
82 }
83 $GS = "\"$GS\"" if ($GS =~ m/\s/);
84}
85
86### options
87$::opt_help=0;
88$::opt_debug=0;
89$::opt_compress=1;
90$::opt_gs=1;
91$::opt_hires=0;
92$::opt_exact=0;
93$::opt_filter=0;
94$::opt_outfile="";
95$::opt_pdfvers="";
96$::opt_gsdev="pdfwrite";
97$::opt_gsopt="";
98
99### usage
100my @bool = ("false", "true");
101my $usage = <<"END_OF_USAGE";
102${title}Syntax: $program [options] <eps file>
103Options:
104 --help: print usage
105 --outfile=<file>: write result to <file>
106 --(no)filter: read standard input (default: $bool[$::opt_filter])
107 --(no)gs: run ghostscript (default: $bool[$::opt_gs])
108 --(no)compress: use compression (default: $bool[$::opt_compress])
109 --(no)pdfvers: PDF Version for Output (default: [GhostScript's default])
110 --(no)hires: scan HiResBoundingBox (default: $bool[$::opt_hires])
111 --(no)exact: scan ExactBoundingBox (default: $bool[$::opt_exact])
112 --(no)debug: debug informations (default: $bool[$::opt_debug])
113 --gsdev=<dev>: select gs device
114 --gsopt=<opts>: additional gs options
115Examples for producing 'test.pdf':
116 * $program test.eps
117 * produce postscript | $program --filter >test.pdf
118 * produce postscript | $program -f -d -o=test.pdf
119Example: look for HiResBoundingBox and produce corrected PostScript:
120 * $program -d --nogs -hires test.ps>testcorr.ps
121END_OF_USAGE
122
123### process options
124use Getopt::Long;
125GetOptions (
126 "help!",
127 "debug!",
128 "filter!",
129 "compress!",
130 "gs!",
131 "hires!",
132 "exact!",
133 "pdfvers=s",
134 "outfile=s",
135 "gsdev=s",
136 "gsopt=s",
137) or die $usage;
138
139### help functions
140sub debug {
141 print STDERR "* @_\n" if $::opt_debug;
142}
143sub warning {
144 print STDERR "==> Warning: @_!\n";
145}
146sub error {
147 die "$title!!! Error: @_!\n";
148}
149sub errorUsage {
150 die "$usage\n!!! Error: @_!\n";
151}
152
153### option help
154die $usage if $::opt_help;
155
156### get input filename
157my $InputFilename = "";
158if ($::opt_filter) {
159 @ARGV == 0 or
160 die errorUsage "Input file cannot be used with filter option";
161 $InputFilename = "-";
162 debug "Input file: standard input";
163}
164else {
165 @ARGV > 0 or die errorUsage "Input filename missing";
166 @ARGV < 2 or die errorUsage "Unknown option or too many input files";
167 $InputFilename = $ARGV[0];
168 -f $InputFilename or error "'$InputFilename' does not exist";
169 debug "Input filename:", $InputFilename;
170}
171
172### option compress
173my $GSOPTS = "$::opt_gsopt ";
174
175debug "gs opts:", $GSOPTS;
176
177$GSOPTS .= "-dUseFlateCompression=false " unless $::opt_compress;
178
179### option pdfvers (GhostScript PDF Compatability options)
180$GSOPTS .= "-dCompatibilityLevel=$::opt_pdfvers " unless $::opt_pdfvers eq "";
181
182### option BoundingBox types
183my $BBName = "%%BoundingBox:";
184!($::opt_hires and $::opt_exact) or
185 error "Options --hires and --exact cannot be used together";
186$BBName = "%%HiResBoundingBox:" if $::opt_hires;
187$BBName = "%%ExactBoundingBox:" if $::opt_exact;
188debug "BoundingBox comment:", $BBName;
189
190### option outfile
191my $OutputFilename = $::opt_outfile;
192if ($OutputFilename eq "") {
193 if ($::opt_gs) {
194 $OutputFilename = $InputFilename;
195 if (!$::opt_filter) {
196 $OutputFilename =~ s/\.[^\.]*$//;
197 if ($::opt_gsdev =~ m/^pdf/) {
198 $OutputFilename .= '.pdf';
199 } elsif ($::opt_gsdev =~ m/^png/) {
200 $OutputFilename .= '.png';
201 } elsif ($::opt_gsdev =~ m/^jpeg/) {
202 $OutputFilename .= '.jpg';
203 } else {
204 $OutputFilename .= ($::opt_gsdev eq "" ? ".pdf" : ".$::opt_gsdev");
205 }
206 }
207 }
208 else {
209 $OutputFilename = "-"; # standard output
210 }
211}
212if ($::opt_filter) {
213 debug "Output file: standard output";
214}
215else {
216 debug "Output filename:", $OutputFilename;
217}
218
219### option gs
220if ($::opt_gs) {
221 debug "Ghostscript command:", $GS;
222 debug "Compression:", ($::opt_compress) ? "on" : "off";
223}
224
225### open input file
226open(IN,"<$InputFilename") or error "Cannot open",
227 ($::opt_filter) ? "standard input" : "'$InputFilename'";
228binmode IN;
229
230### open output file
231if ($::opt_gs) {
232 my $pipe = "$GS -q -sDEVICE=$::opt_gsdev $GSOPTS " .
233 "-sOutputFile=$OutputFilename - -c quit";
234 debug "Ghostscript pipe:", $pipe;
235 open(OUT,"|$pipe") or error "Cannot open Ghostscript for piped input";
236}
237else {
238 open(OUT,">$OutputFilename") or error "Cannot write '$OutputFilename";
239}
240
241### scan first line
242my $header = 0;
243$_ = <IN>;
244if (/%!/) {
245 # throw away binary junk before %!
246 s/(.*)%!/%!/o;
247}
248$header = 1 if /^%/;
249debug "Scanning header for BoundingBox";
250print OUT;
251
252### variables and pattern for BoundingBox search
253my $bbxpatt = '[0-9eE\.\-]';
254 # protect backslashes: "\\" gets '\'
255my $BBValues = "\\s*($bbxpatt+)\\s+($bbxpatt+)\\s+($bbxpatt+)\\s+($bbxpatt+)";
256my $BBCorrected = 0;
257
258sub CorrectBoundingBox {
259 my ($llx, $lly, $urx, $ury) = @_;
260 debug "Old BoundingBox:", $llx, $lly, $urx, $ury;
261 my ($width, $height) = ($urx - $llx, $ury - $lly);
262 my ($xoffset, $yoffset) = (-$llx, -$lly);
263 debug "New BoundingBox: 0 0", $width, $height;
264 debug "Offset:", $xoffset, $yoffset;
265
266 print OUT "%%BoundingBox: 0 0 $width $height\n";
267 print OUT "<< /PageSize [$width $height] >> setpagedevice\n";
268 print OUT "gsave $xoffset $yoffset translate\n";
269}
270
271### scan header
272if ($header) {
273 while (<IN>) {
274
275 ### end of header
276 if (!/^%/ or /^%%EndComments/) {
277 print OUT;
278 last;
279 }
280
281 ### BoundingBox with values
282 if (/^$BBName$BBValues/) {
283 CorrectBoundingBox $1, $2, $3, $4;
284 $BBCorrected = 1;
285 last;
286 }
287
288 ### BoundingBox with (atend)
289 if (/^$BBName\s*\(atend\)/) {
290 debug $BBName, "(atend)";
291 if ($::opt_filter) {
292 warning "Cannot look for BoundingBox in the trailer",
293 "with option --filter";
294 last;
295 }
296 my $pos = tell(IN);
297 debug "Current file position:", $pos;
298
299 # looking for %%BoundingBox
300 while (<IN>) {
301 # skip over included documents
302 if (/^%%BeginDocument/) {
303 while (<IN>) {
304 last if /^%%EndDocument/;
305 }
306 }
307 if (/^$BBName$BBValues/) {
308 CorrectBoundingBox $1, $2, $3, $4;
309 $BBCorrected = 1;
310 last;
311 }
312 }
313
314 # go back
315 seek(IN, $pos, 0) or error "Cannot go back to line '$BBName (atend)'";
316 last;
317 }
318
319 # print header line
320 print OUT;
321 }
322}
323
324### print rest of file
325while (<IN>) {
326 print OUT;
327}
328
329### close files
330close(IN);
331print OUT "\ngrestore\n" if $BBCorrected;
332close(OUT);
333warning "BoundingBox not found" unless $BBCorrected;
334debug "Ready.";
335;
Note: See TracBrowser for help on using the repository browser.