1 |
#! /usr/bin/perl -w |
2 |
############################################################################## |
3 |
# $Id: pstoimg.pin,v 1.16 2001/10/25 10:16:12 RRM Exp $ |
4 |
# |
5 |
# pstoimg |
6 |
# |
7 |
# Accompanies LaTeX2HTML |
8 |
# |
9 |
# Script to convert an arbitrary PostScript image to a cropped GIF or PNG |
10 |
# image suitable for incorporation into HTML documents as inlined images |
11 |
# to be viewed with WWW browsers. |
12 |
# |
13 |
# This software is provided as is without any guarantee. |
14 |
# |
15 |
############################################################################## |
16 |
# |
17 |
# $Log: pstoimg.pin,v $ |
18 |
# Revision 1.16 2001/10/25 10:16:12 RRM |
19 |
# -- make sure that $PNMCROPOPT is declared before -sides |
20 |
# |
21 |
# Revision 1.15 2001/08/21 10:48:59 RRM |
22 |
# -- corrects errors in implementing $PNMCROPOPT |
23 |
# |
24 |
# Revision 1.14 2001/08/20 08:51:20 RRM |
25 |
# -- implement -sides for the arguments to $PNMCROP |
26 |
# This is needed for v9.12+ of pnmcrop . |
27 |
# -- when shaving fails, don't try to copy or rename |
28 |
# |
29 |
# Revision 1.13 2001/03/27 09:27:38 RRM |
30 |
# -- inserted missing '$' for $edge . |
31 |
# |
32 |
# Revision 1.12 2001/03/25 02:05:14 RRM |
33 |
# -- implement the temporary hack to pnmcrop, using -black to help detect |
34 |
# the correct color for cropping-bars; only workd with black bars. |
35 |
# A complete fix will be available with Netpbm v9.12 . |
36 |
# |
37 |
# Revision 1.11 1999/10/25 21:18:22 MRO |
38 |
# |
39 |
# -- added more configure options (Jens' suggestions) |
40 |
# -- fixed bug in regexp range reported by Achim Haertel |
41 |
# -- fixed old references in documentation (related to mail list/archive) |
42 |
# |
43 |
# Revision 1.10 1999/10/06 22:04:13 MRO |
44 |
# |
45 |
# -- texexpand: latex2html calls texexpand with the -out option instead of |
46 |
# output redirection: this is safer on non-UNIX platforms |
47 |
# -- pstoimg: now there's no default cropping (useful for standalone |
48 |
# conversions). latex2html was changes appropriately |
49 |
# -- minor cleanups in latex2html script and documentation |
50 |
# |
51 |
# Revision 1.9 1999/09/14 22:02:02 MRO |
52 |
# |
53 |
# -- numerous cleanups, no new features |
54 |
# |
55 |
# Revision 1.8 1999/07/19 09:51:00 RRM |
56 |
# -- added -aaliastext switch, for easier way to specify anti-aliased |
57 |
# font characters, without also anti-aliasing other graphics objects. |
58 |
# |
59 |
# Revision 1.7 1999/06/24 07:28:59 MRO |
60 |
# |
61 |
# |
62 |
# -- removed L2HMODULE |
63 |
# -- fixed processing of -info switch |
64 |
# -- changed option order for dvips on win32 (thanks JCL) |
65 |
# -- bumped version to 99.2a8 |
66 |
# |
67 |
# Revision 1.6 1999/06/06 14:24:50 MRO |
68 |
# |
69 |
# |
70 |
# -- many cleanups wrt. to TeXlive |
71 |
# -- changed $* to /m as far as possible. $* is deprecated in perl5, all |
72 |
# occurrences should be removed. |
73 |
# |
74 |
# Revision 1.5 1999/06/04 20:14:25 MRO |
75 |
# |
76 |
# |
77 |
# -- Reworked option parsing completely. Should behave much the same as before, |
78 |
# options with -no_* work just like before. |
79 |
# -- Changed $NOFORK to $CAN_FORK and inverted the logic. |
80 |
# -- Small debugging enhancement in pstoimg |
81 |
# |
82 |
# Revision 1.4 1999/06/04 15:30:15 MRO |
83 |
# |
84 |
# |
85 |
# -- fixed errors introduced by cleaning up TMP* |
86 |
# -- made pstoimg -quiet really quiet |
87 |
# -- pstoimg -debug now saves intermediate result files |
88 |
# -- several fixes for OS/2 |
89 |
# |
90 |
# Revision 1.3 1999/06/01 06:55:35 MRO |
91 |
# |
92 |
# |
93 |
# - fixed small bug in L2hos/* |
94 |
# - added some test_mode related output to latex2html |
95 |
# - improved documentation |
96 |
# - fixed small bug in pstoimg wrt. OS2 |
97 |
# |
98 |
# Revision 1.2 1999/05/17 21:30:59 MRO |
99 |
# |
100 |
# |
101 |
# -- make texexpand warning-free and start making it use strict |
102 |
# compliant |
103 |
# |
104 |
# Revision 1.1 1999/05/11 06:10:00 MRO |
105 |
# |
106 |
# |
107 |
# - merged config stuff, did first tries on Linux. Simple document |
108 |
# passes! More test required, have to ger rid of Warnings in texexpand |
109 |
# |
110 |
# Revision 1.18 1999/05/05 19:47:03 MRO |
111 |
# |
112 |
# |
113 |
# - many cosmetic changes |
114 |
# - final backup before merge |
115 |
# |
116 |
# Revision 1.17 1999/03/15 23:00:53 MRO |
117 |
# |
118 |
# |
119 |
# - moved L2hos modules to top level directory, so that no dir- |
120 |
# delimiter is necessary in the @INC-statement. |
121 |
# - changed strategy for "shave": Do not rely on STDERR redirection any |
122 |
# more (caused problems on at least Win32) |
123 |
# |
124 |
# Revision 1.16 1999/02/14 23:44:34 MRO |
125 |
# |
126 |
# |
127 |
# -- first attempt to fix Win32 problems |
128 |
# |
129 |
# Revision 1.15 1999/02/11 00:18:29 MRO |
130 |
# |
131 |
# |
132 |
# -- cleaned up warppers, TeXlive stuff and Makefile |
133 |
# |
134 |
# Revision 1.14 1999/02/10 01:37:12 MRO |
135 |
# |
136 |
# |
137 |
# -- changed os-dependency structure again - now neat OO modules are |
138 |
# used: portable, extensible, neat! |
139 |
# -- some minor cleanups and bugfixes |
140 |
# |
141 |
# Revision 1.13 1998/12/07 23:19:58 MRO |
142 |
# |
143 |
# |
144 |
# -- added POD documentation to pstoimg and did a general cleanup |
145 |
# -- some finetuning of config procedure and modules |
146 |
# |
147 |
# Revision 1.12 1998/10/31 14:13:05 MRO |
148 |
# -- changed OS-dependent module loading strategy: Modules are now located in |
149 |
# different (OS-specific) directories nut have the same name: Easier to |
150 |
# maintain and cleaner code |
151 |
# -- Cleaned up config procedure |
152 |
# -- Extended makefile functionality |
153 |
# |
154 |
# Revision 1.11 1998/08/09 20:45:20 MRO |
155 |
# -- some cleanup |
156 |
# |
157 |
# Revision 1.10 1998/06/14 14:10:38 latex2html |
158 |
# -- Started to implement TeXlive configuration and better OS specific |
159 |
# handling (Batch files) (Marek) |
160 |
# |
161 |
# Revision 1.9 1998/06/07 22:35:24 latex2html |
162 |
# -- included things I learned from the Win95 port to config procedure: |
163 |
# GS_LIB, Win32 module calls, directory separator stuff, ... (Marek) |
164 |
# |
165 |
# Revision 1.8 1998/06/01 12:57:56 latex2html |
166 |
# -- Cleanup and cosmetics. |
167 |
# |
168 |
# Revision 1.7 1998/05/14 22:27:37 latex2html |
169 |
# -- more work on config procedure (Makefile, GS_LIB) |
170 |
# -- tested pstoimg in 98.1 environment successfully on Linux |
171 |
# |
172 |
# Revision 1.6 1998/05/06 22:31:09 latex2html |
173 |
# -- Enhancements to the config procedure: Added a "generic" target |
174 |
# in the Makefile for the TeXlive CD (not perfect yet) |
175 |
# -- included test for kpsewhich / Web2C |
176 |
# -- included latest stuff from Override.pm into os_*.pm |
177 |
# |
178 |
# Revision 1.5 1998/04/28 22:18:11 latex2html |
179 |
# - The platform specific stuff is now kept in a separate perl module. This |
180 |
# does not introduce significant overhead and enhances maintainability. |
181 |
# |
182 |
# Revision 1.4 1998/03/19 23:38:06 latex2html |
183 |
# -- made pstoimg plug-in compatible with old one (touchwood!) |
184 |
# -- cleaned up, added some comments |
185 |
# -- inserted version information output |
186 |
# -- incorporated patches to make OS/2 run better (thanks Uli) |
187 |
# -- updated Makefile: make, make test, make install should work now |
188 |
# |
189 |
# Revision 1.3 1998/03/11 23:44:00 latex2html |
190 |
# -- cleaned up config.pl and reworked dvips checks |
191 |
# -- got pstoimg.pin up to par with the regular pstoimg |
192 |
# -- cosmetic changes |
193 |
# -- runs now under Win95 with Fabrice Popineau's Win32 tools (gs, TeX,...) |
194 |
# |
195 |
# Revision 1.2 1998/03/02 23:38:40 latex2html |
196 |
# Reworked configuration procedure substantially. Fixed some killing bugs. |
197 |
# Should now run on Win32, too. |
198 |
# The file prefs.pm contains user-configurable stuff for DOS platforms. |
199 |
# UNIX users can override the settings with the configure utility (preferred). |
200 |
# |
201 |
# Revision 1.1 1998/02/14 19:31:55 latex2html |
202 |
# Preliminary checkin of configuration procedure |
203 |
# |
204 |
# (end CVS log) |
205 |
############################################################################### |
206 |
|
207 |
# This file has been automatically generated by build.pl from pstoimg.pin |
208 |
# Do not edit this file as your changes will be lost when reconfiguring. |
209 |
# If you want to supply patches, please apply them to pstoimg.pin and send |
210 |
# the diff relative to the original pstoimg.pin. Thank you. |
211 |
|
212 |
=head1 NAME |
213 |
|
214 |
pstoimg - Convert a PostScript file to a bitmap image using |
215 |
Ghostscript and the Netpbm utilities |
216 |
|
217 |
=cut |
218 |
|
219 |
use 5.003; |
220 |
use strict; |
221 |
#use diagnostics; |
222 |
use vars qw(*SAVEERR $LATEX2HTMLDIR $SCRIPT); |
223 |
|
224 |
# This variable points to the DIRECTORY where the latex2html files |
225 |
# can be found. |
226 |
|
227 |
use Getopt::Long; |
228 |
|
229 |
|
230 |
# see below for a description of the environment |
231 |
|
232 |
BEGIN { |
233 |
# print "scanning for l2hdir\n"; |
234 |
if($ENV{LATEX2HTMLDIR}) { |
235 |
$LATEX2HTMLDIR = $ENV{LATEX2HTMLDIR}; |
236 |
} else { |
237 |
$ENV{LATEX2HTMLDIR} = $LATEX2HTMLDIR = '/usr/share/latex2html'; |
238 |
} |
239 |
|
240 |
if(-d $LATEX2HTMLDIR) { |
241 |
push(@INC,$LATEX2HTMLDIR); |
242 |
} else { |
243 |
die qq{Fatal: Directory "$LATEX2HTMLDIR" does not exist.\n}; |
244 |
} |
245 |
} |
246 |
|
247 |
use L2hos; # load OS-specific stuff |
248 |
|
249 |
my $RELEASE = '2002-2-1'; |
250 |
my ($VERSION) = q$Revision: 1.16 $ =~ /:\s*(\S+)/; |
251 |
|
252 |
$| = 1; # unbuffer STDOUT |
253 |
|
254 |
my $dd = L2hos->dd; # Directory delimiter |
255 |
my $prompt; |
256 |
($prompt = $0) =~ s|^.*[/\\]||; |
257 |
|
258 |
# Configuration as determined by "configure" |
259 |
# |
260 |
# Ghostscript |
261 |
my $GS = '/usr/bin/gs'; |
262 |
my $GSDEVICE = 'pnmraw'; |
263 |
my $GSALIASDEVICE = 'ppmraw'; |
264 |
# Supported format(s) |
265 |
my @IMAGE_TYPES = qw(png gif); |
266 |
# Netpbm |
267 |
my $PNMCROP = '/home/cnh/downloads/netpbm-10.19bin/bin/pnmcrop -verbose'; |
268 |
# my $PNMCROP = '/bin/cat'; |
269 |
my $PNMCROPOPT = ''; |
270 |
# $PNMCROPOPT = '-sides'; |
271 |
# $PNMCROPOPT = '-white'; |
272 |
$PNMCROPOPT = '-black'; |
273 |
my $PPMQUANT = '/home/cnh/downloads/netpbm-10.19bin/bin/ppmquant'; |
274 |
my $PNMFLIP = '/home/cnh/downloads/netpbm-10.19bin/bin/pnmflip'; |
275 |
my $PNMCAT = '/home/cnh/downloads/netpbm-10.19bin/bin/pnmcat'; |
276 |
my $PNMFILE = '/home/cnh/downloads/netpbm-10.19bin/bin/pnmfile'; |
277 |
my $PBMMAKE = '/home/cnh/downloads/netpbm-10.19bin/bin/pbmmake'; |
278 |
# GIF support |
279 |
my $PPMTOGIF = '/home/cnh/downloads/netpbm-10.19bin/bin/ppmtogif'; |
280 |
# PNG support |
281 |
my $PNMTOPNG = '/home/cnh/downloads/netpbm-10.19bin/bin/pnmtopng'; |
282 |
# Temporary diskspace |
283 |
my $def_tmp = '/tmp'; # Space for temporary files |
284 |
|
285 |
# Some lengths used by dvips |
286 |
# MRO: Is this true for all runs of dvips? |
287 |
|
288 |
my $PAGE_HEIGHT = 841.889; # dvips page height, in pts. |
289 |
my $PAGE_WIDTH = 595.275; # dvips page width, in pts. |
290 |
my $PAGE_HMARGIN = 72; # dvips margin: 1 inch = 72pt |
291 |
my $PAGE_VMARGIN = 72; # dvips margin: 1 inch = 72pt |
292 |
|
293 |
# The color to be made transparent |
294 |
# my $trans_color = '#ffffff'; |
295 |
my $trans_color = '#000000'; |
296 |
|
297 |
############################################################################### |
298 |
# Default settings |
299 |
# Environment overrides defaults, command line options override everything |
300 |
|
301 |
unless(@ARGV) { |
302 |
print_help(); |
303 |
exit 0; |
304 |
} |
305 |
|
306 |
=head1 SYNOPSIS |
307 |
|
308 |
B<pstoimg> B<-help> | B<-version> |
309 |
|
310 |
B<pstoimg> |
311 |
S<[ B<-antialias> ]> |
312 |
S<[ B<-aaliastext> ]> |
313 |
S<[ B<-center> I<num> ]> |
314 |
S<[ B<-color> I<num> ]> |
315 |
S<[ B<-crop> I<code> ]> |
316 |
S<[ B<-debug> ]> |
317 |
S<[ B<-density> I<num>]> |
318 |
S<[ B<-depth> I<num> ]> |
319 |
S<[ B<-discard> ]> |
320 |
S<[ B<-flip> I<code> ]> |
321 |
S<[ B<-geometry> I<X>xI<Y> ]> |
322 |
S<[ B<-interlaced> ]> |
323 |
S<[ B<-margins> I<X>,I<Y> ]> |
324 |
S<[ B<-multipage> ]> |
325 |
S<[ B<-out> I<file> ]> |
326 |
S<[ B<-quiet> ]> |
327 |
S<[ B<-rightjustify> I<num> ]> |
328 |
S<[ B<-scale> I<num> ]> |
329 |
S<[ B<-tmp> I<path> ]> |
330 |
S<[ B<-topjustify> [B<x>]I<num> ]> |
331 |
S<[ B<-transparent> ]> |
332 |
S<[ B<-type> I<type> ]> |
333 |
S<[ B<-shoreup> I<num>[B<d>] ]> |
334 |
S<[ B<-white> ]> |
335 |
I<file> |
336 |
S<[ I<file2> ... ]> |
337 |
|
338 |
=cut |
339 |
|
340 |
my %opt = (); |
341 |
unless(&GetOptions(\%opt, qw(-help -version -debug -discard -antialias -aaliastext |
342 |
-multipage -type=s -gif -png -out=s -depth=i -color=i -flip=s -density=i |
343 |
-scale=f -geometry=s -margins=s -crop=s -transparent -interlaced |
344 |
-rightjustify=i -center=i -topjustify=s -shoreup=s -tmp=s -white -quiet))) { |
345 |
print_usage("$prompt: Error: Invalid option(s) specified."); |
346 |
exit 1; |
347 |
} |
348 |
|
349 |
=head1 OPTIONS |
350 |
|
351 |
The command line options may be abbreviated to the shortest unique |
352 |
prefix. |
353 |
|
354 |
=over 4 |
355 |
|
356 |
=item B<-help> |
357 |
|
358 |
Show this help page and exit. |
359 |
|
360 |
=cut |
361 |
|
362 |
if($opt{help}) { |
363 |
print_help(); |
364 |
exit 0; |
365 |
} |
366 |
|
367 |
=item B<-version> |
368 |
|
369 |
Show the release and version of pstoimg and exit. |
370 |
|
371 |
=cut |
372 |
|
373 |
if($opt{version}) { |
374 |
print_version(); |
375 |
exit 0; |
376 |
} |
377 |
|
378 |
banner() unless($opt{quiet}); |
379 |
|
380 |
=item B<-antialias> |
381 |
|
382 |
Use Ghostscript's anti-aliasing feature for rendering "softer" images. |
383 |
This applies to lines and edges of polygonal and oval or circular shapes. |
384 |
Only valid if Ghostscipt 4.03 or higher is installed. |
385 |
|
386 |
=item B<-aaliastext> |
387 |
|
388 |
Use Ghostscript's anti-aliasing feature for "smoother" font characters, |
389 |
without the jagged edges. Similar to B<-antialias> for graphic components. |
390 |
Only valid if Ghostscipt 4.03 or higher is installed. |
391 |
|
392 |
=item B<-center> I<num> |
393 |
|
394 |
Add the appropriate amount of whitespace to the left of the image so |
395 |
that the image appears to be centered in a total width of I<num> pixels. |
396 |
|
397 |
=cut |
398 |
|
399 |
my $CENTER = 0; # No centering by default |
400 |
if($opt{center}) { |
401 |
$CENTER = $opt{center}; |
402 |
die <<"EOF" unless ($CENTER =~ /^\d+$/ && $CENTER > 0); |
403 |
$prompt: Error: Illegal width for -center specified: "$CENTER" |
404 |
Value must be a positive integer. |
405 |
EOF |
406 |
} |
407 |
|
408 |
=item B<-crop> I<code> |
409 |
|
410 |
Crop the bitmap from the given directions. I<code> may be a string of |
411 |
several cropping instructions, which are executed strictly in the given |
412 |
order. Possible values are: B<h> (horizontal, i.e. crop top and |
413 |
bottom), B<v> (vertical), B<tblr> (top, bottom, left, right) and B<a> (all |
414 |
directions). A special case is B<s>: "shave" the image at the bottom, but only |
415 |
if a single line of whitespace exists. |
416 |
|
417 |
=cut |
418 |
|
419 |
my $EXTRA_CROP = ''; |
420 |
if($opt{crop}) { |
421 |
$EXTRA_CROP = lc($opt{crop}); |
422 |
die <<"EOF" unless ( $EXTRA_CROP =~ /^([vhtblras]+)$/i ); |
423 |
$prompt: Error: Illegal crop specified: "$EXTRA_CROP" |
424 |
Crop must be h, v, t, b, l, r, a, s or combination |
425 |
EOF |
426 |
} |
427 |
|
428 |
=item B<-debug> |
429 |
|
430 |
Turn on debugging output. This can get rather verbose. Any intermediate |
431 |
files generated are not removed to help debugging. |
432 |
|
433 |
=cut |
434 |
|
435 |
if($ENV{DEBUG}) { |
436 |
$opt{debug} = 1; |
437 |
} |
438 |
|
439 |
=item B<-density> I<num> |
440 |
|
441 |
The density (resolution) in DPI in which to render the bitmap. The default |
442 |
is 72. |
443 |
|
444 |
=cut |
445 |
|
446 |
my $DENSITY = 72; |
447 |
if($opt{density}) { |
448 |
$DENSITY = $opt{density}; |
449 |
} |
450 |
elsif($ENV{DENSITY}) { |
451 |
$DENSITY = $ENV{DENSITY}; |
452 |
} |
453 |
die <<"EOF" unless $DENSITY =~ /^\d+$/; |
454 |
$prompt: Error: Illegal density specified: "$DENSITY" |
455 |
Density must be an integer value. Default is 72. |
456 |
EOF |
457 |
|
458 |
=item B<-depth> I<num> or B<-color> I<num> |
459 |
|
460 |
Specify the color depth of the bitmap. Legal values are 1 (black & white), |
461 |
8 (256 colors) and 24 (true color). |
462 |
|
463 |
=cut |
464 |
|
465 |
unless($opt{depth}) { |
466 |
if($opt{color}) { |
467 |
$opt{depth} = $opt{color}; |
468 |
} |
469 |
elsif($ENV{DEPTH}) { |
470 |
$opt{depth} = $ENV{DEPTH}; |
471 |
} |
472 |
else { |
473 |
$opt{depth} = 8; |
474 |
} |
475 |
} |
476 |
die <<"EOF" unless $opt{depth} =~ /^(1|8|24)$/; |
477 |
$prompt: Error: Illegal color depth specified: "$opt{depth}" |
478 |
Depth must be either 1, 8 or 24. |
479 |
EOF |
480 |
|
481 |
=item B<-discard> |
482 |
|
483 |
Delete the input postscript file if the conversion was successful. Setting |
484 |
the environment DISCARD to a true value (as perl sees it) has the same |
485 |
effect. |
486 |
|
487 |
=cut |
488 |
|
489 |
if($ENV{DISCARD}) { |
490 |
$opt{discard} = 1; |
491 |
} |
492 |
|
493 |
=item B<-flip> I<code> |
494 |
|
495 |
Flip all generated output bitmaps. The following codes are recognized: |
496 |
lr (flip left-right), tb (flip top-bottom), xy (flip bottom/left-top/right), |
497 |
r90 and ccw (rotate by 90 degrees counterclockwise), r270 and cw (rotate |
498 |
90 degrees clockwise) and r180 (rotate 180 degrees). |
499 |
|
500 |
=cut |
501 |
|
502 |
if($opt{flip}) { |
503 |
$opt{flip} = lc($opt{flip}); |
504 |
die <<"EOF" unless $opt{flip} =~ /^(lr|tb|xy|r90|ccw|r270|cw|r180)$/; |
505 |
$prompt: Error: Illegal flip option specified: "$opt{flip}" |
506 |
Flip must be one of: lr tb xy r90 ccw r270 cw r180 |
507 |
EOF |
508 |
} |
509 |
|
510 |
=item B<-geometry> I<X>xI<Y> |
511 |
|
512 |
Render only this "window" of the PostScript file. If given, this option |
513 |
can dramatically reduce memory requirements and speed up conversion. The |
514 |
geometry is automatically detected in case of EPS files (Encapsulated |
515 |
PostScript). |
516 |
|
517 |
=cut |
518 |
|
519 |
my $GEOMETRY = ''; |
520 |
if($opt{geometry}) { |
521 |
$GEOMETRY = $opt{geometry}; |
522 |
if($GEOMETRY =~ s/-//o ) { |
523 |
$EXTRA_CROP .= 'bl'; |
524 |
} |
525 |
die <<"EOF" unless ($GEOMETRY =~ /^\d+x\d+$/i); |
526 |
$prompt: Error: Illegal geometry specified: "$GEOMETRY" |
527 |
Geometry must be <width>x<height> |
528 |
EOF |
529 |
} |
530 |
|
531 |
=item B<-interlaced> |
532 |
|
533 |
Generate an interlaced bitmap. Interlaced images build up from coarse to |
534 |
fine as they are loaded. This option may not work on every installation |
535 |
and/or bitmap type, depending of the capabilities of external programs. |
536 |
|
537 |
=cut |
538 |
|
539 |
my $INTERLACE = 0; # Do not make interlaced images by default |
540 |
if($opt{interlaced}) { |
541 |
$INTERLACE=1; |
542 |
} |
543 |
|
544 |
=item B<-margins> I<X>,I<Y> |
545 |
|
546 |
The offset of the rectangle in the postscript file that is going to be |
547 |
rendered from top/left. Can be used together with B<-geometry> to further |
548 |
reduce the size of the intermediate bitmap file generated by Ghostscript. |
549 |
|
550 |
=cut |
551 |
|
552 |
if($opt{margins}) { |
553 |
die <<"EOF" unless (($opt{margins} =~ /^(\d+),(\d+)$/)); |
554 |
$prompt: Error: Illegal margins specified: "$opt{margins}" |
555 |
Margins must be <hmargin>,<vmargin> |
556 |
EOF |
557 |
$PAGE_HMARGIN = $1; |
558 |
$PAGE_VMARGIN = $2; |
559 |
} |
560 |
|
561 |
=item B<-multipage> |
562 |
|
563 |
Process a multi-page PostScript file, i.e. create an individual bitmap |
564 |
for every page. The resulting files are numbered: The decimal number |
565 |
(starting with 1) is appended to the basename of the PostScript input |
566 |
file (or the basename of the filename specified with B<-out>), while |
567 |
keeping the extension. |
568 |
|
569 |
=item B<-out> I<file> |
570 |
|
571 |
The file where to write the bitmap. If multiple PostScript files are |
572 |
supplied on the command line, this option is ignored. The bitmap type |
573 |
extension is appended automatically if I<file> does not contain a dot. |
574 |
In connection with B<-multipage> I<file> is extended by the page number |
575 |
as shown in this example: |
576 |
|
577 |
-outfile foo.gif --------E<gt> foo1.gif, foo2.gif, ... |
578 |
|
579 |
=cut |
580 |
|
581 |
if(!$opt{out} && $ENV{OUTFILE}) { |
582 |
$opt{out} = $ENV{OUTFILE}; |
583 |
} |
584 |
|
585 |
=item B<-quiet> |
586 |
|
587 |
Do not print anything except error messages. |
588 |
|
589 |
=item B<-rightjustify> I<num> |
590 |
|
591 |
Add the appropriate amount of whitespace to the left of the image so that |
592 |
it appears to be aligned to the right in a total width of I<num> pixels. |
593 |
|
594 |
=cut |
595 |
|
596 |
my $RIGHT_JUSTIFY = 0; # No right justifying by default |
597 |
if($opt{rightjustify}) { |
598 |
$RIGHT_JUSTIFY = $opt{rightjustify}; |
599 |
die <<"EOF" unless ($RIGHT_JUSTIFY =~ /^\d+$/ && $RIGHT_JUSTIFY > 0); |
600 |
$prompt: Error: Illegal width for -rightjustify specified: "$RIGHT_JUSTIFY" |
601 |
Value must be a positive integer. |
602 |
EOF |
603 |
} |
604 |
|
605 |
=item B<-scale> I<factor> |
606 |
|
607 |
Scale the image by I<factor>. Valid choices are any numbers greater than |
608 |
zero. Useful choices are numbers between 0.1 - 5. |
609 |
Large numbers may generate very large intermediate files and will take |
610 |
longer to process. If this option is omitted, the environment SCALE is |
611 |
considered. |
612 |
|
613 |
=cut |
614 |
|
615 |
unless($opt{scale}) { |
616 |
if($ENV{SCALE}) { |
617 |
$opt{scale} = $ENV{SCALE}; |
618 |
} |
619 |
else { |
620 |
$opt{scale} = 1; |
621 |
} |
622 |
} |
623 |
die <<"EOF" unless ($opt{scale} =~ /^[\d.e]+$/i && $opt{scale} > 0); |
624 |
$prompt: Error: Illegal scale specified: "$opt{scale}" |
625 |
Scale must be nonnegative float value. |
626 |
EOF |
627 |
|
628 |
=item B<-shoreup> I<num>[B<d>] |
629 |
|
630 |
Make height and width of the bitmap(s) an exact multiple of I<num>. If |
631 |
I<num> is followed by a "d", then half the extra vertical space is placed |
632 |
underneath. This option is useful, if you want to have "blown-up" images |
633 |
of high quality for print, but downscale them in HTML using |
634 |
C<E<lt>IMG WIDTH=x HEIGHT=yE<gt>>. If the actual image is is not an |
635 |
integer multiple of x,y then browsers tend to display distorted images. |
636 |
|
637 |
=cut |
638 |
|
639 |
my $SHORE_UP = 0; # No pixel alignment by default |
640 |
if($opt{shoreup}) { |
641 |
$SHORE_UP = $opt{shoreup}; |
642 |
die <<"EOF" unless $SHORE_UP =~ /^\d+d?$/i; |
643 |
$prompt: Error: Illegal shore-up specified: "$SHORE_UP" |
644 |
Value must be a positive integer, optionally followed by d |
645 |
EOF |
646 |
} |
647 |
|
648 |
=item B<-tmp> I<path> |
649 |
|
650 |
Use I<path> to store temporary files. Defaults to /tmp on this |
651 |
installation. This parameter can be set by the environment B<TMP> or |
652 |
B<TEMP>, too. |
653 |
|
654 |
=cut |
655 |
|
656 |
my $TMP = ''; |
657 |
if($opt{tmp}) { |
658 |
$opt{tmp} =~ s|\Q$dd\E+$||; # remove trailing directory separator(s) |
659 |
if(-d $opt{tmp} && -r _ && -w _) { |
660 |
$TMP = $opt{tmp}; |
661 |
} else { |
662 |
print "$prompt: Warning: Cannot use $opt{tmp} as temporary directory.\n"; |
663 |
} |
664 |
} |
665 |
if(!$TMP && ($ENV{TMP} || $ENV{TEMP})) { |
666 |
($opt{tmp} = $ENV{TMP} || $ENV{TEMP}) =~ s|\Q$dd\E+$||; |
667 |
if(-d $opt{tmp} && -r _ && -w _) { |
668 |
$TMP = $opt{tmp}; |
669 |
} else { |
670 |
print "$prompt: Warning: Cannot use $opt{tmp} as temporary directory.\n"; |
671 |
} |
672 |
} |
673 |
if(!$TMP && -d $def_tmp && -r _ && -w _) { |
674 |
$TMP = $def_tmp; |
675 |
} |
676 |
print "$prompt: Temporary directory is $TMP\n" if($opt{debug}); |
677 |
|
678 |
=item B<-topjustify> [B<x>]I<num> |
679 |
|
680 |
Add padding whitespace to the image so that it gets a defined height. |
681 |
If an integer value is given, it defines the total height. The whitespace |
682 |
is added at the bottom. If the number is preceded by "x", then this |
683 |
multiple of the image height is added as whitespace at the bottom. |
684 |
|
685 |
=cut |
686 |
|
687 |
my $TOP_JUSTIFY = 0; # No top justifying by default |
688 |
if($opt{topjustify}) { |
689 |
$TOP_JUSTIFY = $opt{topjustify}; |
690 |
die <<"EOF" unless $TOP_JUSTIFY =~ /^x?\d+[.]?\d*$/i; |
691 |
$prompt: Error: Illegal align specified: "$TOP_JUSTIFY" |
692 |
Value must be positive numeric, optionally preceded by x |
693 |
EOF |
694 |
} |
695 |
|
696 |
=item B<-transparent> |
697 |
|
698 |
Generate transparent bitmaps, i.e. the background color (white) is |
699 |
transparent if viewed with certain viewers (e.g. browsers). This option |
700 |
may not be available due to missing capabilities of external |
701 |
programs. |
702 |
|
703 |
=cut |
704 |
|
705 |
my $TRANSPARENT = 0; # Do not make make images transparent by default |
706 |
if($opt{transparent}) { |
707 |
$TRANSPARENT = 1; |
708 |
} |
709 |
|
710 |
=item B<-type> I<type> |
711 |
|
712 |
Instruct pstoimg to render the bitmap in I<type> format. Depending on |
713 |
the local installation, pstoimg is capable of generating either GIF or |
714 |
PNG bitmaps. This site features the following types: png gif |
715 |
|
716 |
If omitted, the first type in this list is taken. |
717 |
|
718 |
=cut |
719 |
|
720 |
if($opt{type}) { |
721 |
$opt{type} = lc($opt{type}); |
722 |
die <<"EOF" unless grep($_ eq $opt{type},@IMAGE_TYPES); |
723 |
$prompt: Error: This version of pstoimg does not support |
724 |
"$opt{type}" image format. |
725 |
EOF |
726 |
} |
727 |
else { |
728 |
($opt{type}) = @IMAGE_TYPES; # default image type |
729 |
} |
730 |
# Support -gif and -png for a transition period |
731 |
if($opt{gif}) { |
732 |
print qq{$prompt: Warning: The -gif switch is deprecated. Use "-type gif" instead.\n}; |
733 |
if(grep($_ eq 'gif',@IMAGE_TYPES)) { |
734 |
$opt{type} = 'gif'; |
735 |
} |
736 |
else { |
737 |
die <<"EOF"; |
738 |
$prompt: Error: This version of pstoimg does not support "gif" format. |
739 |
EOF |
740 |
} |
741 |
} |
742 |
if($opt{png}) { |
743 |
print qq{$prompt: Warning: The -png switch is deprecated. Use "-type png" instead.\n}; |
744 |
if(grep($_ eq 'png',@IMAGE_TYPES)) { |
745 |
$opt{type} = 'png'; |
746 |
} |
747 |
else { |
748 |
die <<"EOF"; |
749 |
$prompt: Error: This version of pstoimg does not support "png" format. |
750 |
EOF |
751 |
} |
752 |
} |
753 |
|
754 |
=item B<-white> |
755 |
|
756 |
Remove TeX's page color information from the PostScript file before |
757 |
converting so that a white background is used. |
758 |
|
759 |
=back |
760 |
|
761 |
=cut |
762 |
|
763 |
# do some consistency checks on the options |
764 |
|
765 |
|
766 |
die <<"EOF" if($RIGHT_JUSTIFY && $CENTER); |
767 |
$prompt: Error: Conflicting options -center and -rightjustify. |
768 |
EOF |
769 |
|
770 |
# now setup some parameters |
771 |
|
772 |
# calculate dpi resolution from density and scale |
773 |
$DENSITY = int($opt{scale} * $DENSITY + .5) if($opt{scale} != 1); |
774 |
|
775 |
my $reduce_color = ''; |
776 |
if($opt{depth} == 1) { |
777 |
$reduce_color = "$PPMQUANT 2"; |
778 |
} |
779 |
elsif ($opt{depth} == 8) { |
780 |
$reduce_color = "$PPMQUANT 256"; |
781 |
} |
782 |
|
783 |
my $gs_aalias = ''; |
784 |
if($opt{antialias}) { |
785 |
$GSDEVICE = $GSALIASDEVICE; |
786 |
if($opt{depth} == 1) { |
787 |
$gs_aalias = '-dTextAlphaBits=4 '; |
788 |
$reduce_color = "$PPMQUANT -floyd 256"; |
789 |
} |
790 |
else { |
791 |
$gs_aalias = '-dTextAlphaBits=4 -dGraphicsAlphaBits=4 '; |
792 |
} |
793 |
} |
794 |
elsif ($opt{aaliastext}) { |
795 |
$GSDEVICE = $GSALIASDEVICE; |
796 |
$gs_aalias = '-dTextAlphaBits=4 '; |
797 |
$reduce_color = "$PPMQUANT -floyd 256"; |
798 |
} |
799 |
my $PAPERSIZE = $ENV{PAPERSIZE} || ''; |
800 |
# This rx matches float values in Bounding Box expressions |
801 |
my $Brx = '-?\d+(?:\.\d*|)'; |
802 |
|
803 |
############################################################################## |
804 |
# Main program |
805 |
|
806 |
=head1 DESCRIPTION |
807 |
|
808 |
B<pstoimg> iterates over the given input files and runs them through |
809 |
Ghostscipt. The resulting pnm (portable anymap files) are processed |
810 |
with different Netpbm tools (cropping, color mapping, aligning, ...) |
811 |
and finally converted into (currently) either GIF or PNG format. The |
812 |
bitmaps can now be included e.g. in WWW pages. |
813 |
|
814 |
The PostScript file is converted as is. If a valid bounding box is |
815 |
found (EPS format), then only this area is converted. The image is |
816 |
I<not> cropped by default. |
817 |
|
818 |
=cut |
819 |
|
820 |
die "$prompt: Error: No input file(s) specified\n" |
821 |
unless(@ARGV); |
822 |
|
823 |
# suppress diagnostics messages if possible |
824 |
my $NULLFILE = '/dev/null'; |
825 |
open(STDERR, ">$NULLFILE") unless($opt{debug}); |
826 |
|
827 |
my $exit = 0; |
828 |
|
829 |
$opt{out} = '' if(@ARGV > 1); # disable -out if multiple ps files given |
830 |
|
831 |
my $psfile; |
832 |
foreach $psfile (@ARGV) { |
833 |
unless (-f $psfile) { |
834 |
print qq{$prompt: Error: Cannot find file "$psfile": $!\n}; |
835 |
exit 1; |
836 |
} |
837 |
$exit += (&pstoimg($psfile) ? 0 : 1); |
838 |
} |
839 |
|
840 |
=head1 RETURN VALUE |
841 |
|
842 |
=over 4 |
843 |
|
844 |
=item 0 |
845 |
|
846 |
if everything went all right |
847 |
|
848 |
=item x |
849 |
|
850 |
(x != 0) something went wrong. See the message output. |
851 |
|
852 |
=back |
853 |
|
854 |
=cut |
855 |
|
856 |
exit $exit ? 1 : 0; |
857 |
|
858 |
############################################################################## |
859 |
# Subroutines |
860 |
|
861 |
sub pstoimg { |
862 |
my ($psfile) = @_; |
863 |
|
864 |
print "$prompt: Processing $psfile\n" unless($opt{quiet}); |
865 |
# remove a trailing suffix the same way a shell would do it |
866 |
my $base = $psfile; |
867 |
$base =~ s|[.][^.$dd$dd]*$||; |
868 |
|
869 |
my $outfile; |
870 |
if($opt{out}) { |
871 |
$outfile = $opt{out}; |
872 |
# append the type unless -outfile has a "." in it |
873 |
$outfile .= ".$opt{type}" unless($outfile =~ /[.]/); |
874 |
} |
875 |
else { |
876 |
$outfile = "$base.$opt{type}"; |
877 |
} |
878 |
|
879 |
# Invoke Ghostscript |
880 |
my $pnmdir = $TMP ? "$TMP$dd" : ".$dd"; |
881 |
my $pnmbase = "p$$"; # keep it short for dos |
882 |
my $pnmfile = $pnmdir . |
883 |
($opt{multipage} ? "%d_${pnmbase}.pnm" : "$pnmbase.pnm"); |
884 |
|
885 |
ps2pnm($psfile,$pnmfile) || return 0; |
886 |
|
887 |
my $ok = 1; |
888 |
if (-f $pnmfile) { |
889 |
if(crop_scale_etc($pnmfile, $outfile)) { |
890 |
L2hos->Unlink($pnmfile) unless($opt{debug}); |
891 |
} |
892 |
else { |
893 |
return 0; |
894 |
} |
895 |
} |
896 |
elsif($opt{multipage}) { |
897 |
unless(opendir(DIR,$pnmdir)) { |
898 |
print qq{$prompt: Error: Could not open directory "$pnmdir": $!\n}; |
899 |
return 0; |
900 |
} |
901 |
my @list = grep(/^\d+_\w*\./,readdir(DIR)); |
902 |
closedir(DIR); |
903 |
if(@list) { |
904 |
my $i; |
905 |
foreach $i (@list) { |
906 |
my ($n) = $i =~ /^(\d+)_/; |
907 |
my $j = $outfile; |
908 |
$j =~ s|(\.[^/.]*)$|$n$1|; |
909 |
if(crop_scale_etc("$pnmdir$i", $j)) { |
910 |
L2hos->Unlink("$pnmdir$i") unless($opt{debug}); |
911 |
} |
912 |
else { |
913 |
$ok = 0; |
914 |
} |
915 |
} |
916 |
} |
917 |
else { |
918 |
goto not_found; |
919 |
} |
920 |
} |
921 |
else { |
922 |
not_found: |
923 |
print "$prompt: Error: Couldn't find pnm output of $psfile\n"; |
924 |
} |
925 |
L2hos->Unlink($psfile) if($opt{discard} && !$opt{debug}); |
926 |
$ok; |
927 |
} |
928 |
|
929 |
sub ps2pnm { |
930 |
my ($psfile,$pnmfile) = @_; |
931 |
my $gs_size = $PAPERSIZE ? "-sPAPERSIZE=$PAPERSIZE" : ''; |
932 |
my $gs_density = ($DENSITY != 72) ? "-r$DENSITY" : ''; |
933 |
|
934 |
my ($bbx, $bby, $bbw, $bbh) = (0,0,0,0); |
935 |
my $max_lines = 100; |
936 |
my ($epsf,$have_geometry) = (0,0); |
937 |
|
938 |
# Parse postscript file for information |
939 |
unless(open(PS, "<$psfile")) { |
940 |
print qq{$prompt: Error: Cannot read "$psfile": $!}; |
941 |
return 0; |
942 |
} |
943 |
$_ = <PS>; # read one line |
944 |
if( /^%!.*EPSF/ ) { |
945 |
# we're in a EPSF file |
946 |
$epsf = 1; |
947 |
} |
948 |
if($GEOMETRY || $epsf) { |
949 |
while (defined ($_ = <PS>)) { |
950 |
# Look for bounding box comment |
951 |
# MRO: add support of precise bounding boxes |
952 |
if ($epsf && ( |
953 |
/^%+(?:HiRes|Exact)BoundingBox:\s+($Brx)\s+($Brx)\s+($Brx)\s+($Brx)/o || |
954 |
/^\%\%BoundingBox:\s+(-?\d+)\s+(-?\d+)\s+(-?\d+)\s+(-?\d+)/)) { |
955 |
$bbx = 0 - $1; $bby = 0 - $2; |
956 |
$bbw = $3 + $bbx; $bbh = $4 + $bby; |
957 |
if(($bbw > 0) && ($bbh > 0)) { # we have a valid bounding box |
958 |
print "$prompt: EPSF dimensions are ${bbw}x$bbh\n" if($opt{debug}); |
959 |
# this overrides the -geometry switch |
960 |
if($DENSITY) { # scale the output |
961 |
my $scale = $DENSITY / 72.0; |
962 |
$bbw *= $scale; |
963 |
$bbh *= $scale; |
964 |
} |
965 |
$bbw = int($bbw + 0.99); |
966 |
$bbh = int($bbh + 0.99); |
967 |
$GEOMETRY = "${bbw}x${bbh}"; |
968 |
$have_geometry = 1; |
969 |
last; |
970 |
} |
971 |
} |
972 |
# Look for page size information |
973 |
elsif($GEOMETRY && /TeXDict\s+begin\s+(\d+)\s+(\d+)\s+/) { |
974 |
$PAGE_WIDTH = int($1 / 65536 * 72 /72.27 +.5); |
975 |
$PAGE_HEIGHT = int($2 / 65536 * 72 /72.27 +.5); |
976 |
print "$prompt: Page dimensions are ${PAGE_WIDTH}x$PAGE_HEIGHT\n" |
977 |
if($opt{debug}); |
978 |
# we don't have to look further for EPSF stuff at this point |
979 |
last; |
980 |
} |
981 |
elsif(!$GEOMETRY && (/^\%\%EndComments/ || --$max_lines == 0)) { |
982 |
# abort at a certain point to avoid scanning huge ps files |
983 |
last; |
984 |
} |
985 |
} |
986 |
} |
987 |
close PS; |
988 |
if($GEOMETRY && !$have_geometry) { # RRM: overrides $PAPERSIZE |
989 |
# no geometry info found in the Postscript file |
990 |
$bbx = $PAGE_HMARGIN; |
991 |
$bby = $PAGE_HEIGHT - $PAGE_VMARGIN; |
992 |
unless($GEOMETRY =~ /\s*(\d+)x(\d+)/i) { |
993 |
print qq{$prompt: Illegal geometry "$GEOMETRY" specified.\n}; |
994 |
return 0; |
995 |
} |
996 |
$bbw = $1 + 10; # allow for the side-bars |
997 |
$bbh = $2; |
998 |
$bby = int(-$bby + $bbh + 8); # allow small margin for error |
999 |
$bbx = int(-$bbx + 5); # allow small margin for error |
1000 |
if($DENSITY) { |
1001 |
my $scale = $DENSITY / 72.0; |
1002 |
$bbw = int($scale * $bbw + .99); |
1003 |
$bbh = int($scale * $bbh + .99); |
1004 |
} |
1005 |
$bbw += 10; # add a 5pt margin for safety |
1006 |
$bbh += 40; # add a 20pt margin for safety |
1007 |
$GEOMETRY = "${bbw}x$bbh"; |
1008 |
$have_geometry = 1; |
1009 |
} |
1010 |
if($have_geometry) { |
1011 |
$gs_size = "-g$GEOMETRY "; |
1012 |
} |
1013 |
|
1014 |
my $ps_changed = 0; |
1015 |
if($have_geometry || $opt{white}) { |
1016 |
# Remove any Postscript commands concerning Papersize if -g switch is used |
1017 |
# thanks to Axel Ramge for identifying the problem and for this code |
1018 |
local($/) = undef; |
1019 |
open(PS,"<$psfile"); |
1020 |
my $ps = <PS>; |
1021 |
close(PS); |
1022 |
my $had_papersize; |
1023 |
if($have_geometry) { |
1024 |
$had_papersize = ($ps =~ s/\n%%BeginPaperSize.*?%%EndPaperSize[^\n]*\n/\n/sg); |
1025 |
} |
1026 |
my $had_nonwhite; |
1027 |
if($opt{white}) { |
1028 |
$had_nonwhite = ($ps =~ s/(\n\d+ \d+ bop gsave) \d*\.\d+ (TeXcolorgray clippath fill grestore)/$1 1 $2/s); |
1029 |
} |
1030 |
$ps_changed = $had_papersize || $had_nonwhite; |
1031 |
if($ps_changed) { |
1032 |
my $tmppsfile = $pnmfile; # was "tmpps$$.ps" |
1033 |
$tmppsfile =~ s/\.[^.]*$/.ps/; |
1034 |
unless(open(PS,">$tmppsfile") && (print PS $ps) && (close PS)) { |
1035 |
if($had_papersize) { |
1036 |
print <<"EOF"; |
1037 |
$prompt: Warning: Could not write "$tmppsfile": $! |
1038 |
"$psfile" contains %%Papersize comments. |
1039 |
Any of these should be removed else GS will fail. |
1040 |
EOF |
1041 |
} |
1042 |
if($had_nonwhite) { |
1043 |
print <<"EOF"; |
1044 |
$prompt: Warning: Could not write "$tmppsfile": $! |
1045 |
"$psfile" has a non-white background. |
1046 |
This may cause ugly images. |
1047 |
EOF |
1048 |
} |
1049 |
} |
1050 |
$psfile = $tmppsfile; |
1051 |
print qq{Debug: Papersize comment in "$psfile" deleted.\n} |
1052 |
if($had_papersize && $opt{debug}); |
1053 |
print qq{Debug: Background switched to white in "$psfile".\n} |
1054 |
if($had_nonwhite && $opt{debug}); |
1055 |
} |
1056 |
} |
1057 |
|
1058 |
my $gs_quiet = $opt{debug} ? '' : '-q -dNOPAUSE -dNO_PAUSE'; |
1059 |
my $out_redirect = $opt{debug} ? '' : "> $NULLFILE"; |
1060 |
my $gs_out = "-sOutputFile=$pnmfile"; |
1061 |
my $gsfile = $psfile; |
1062 |
# Ghostscript understands only '/' as path delimiter! |
1063 |
if($opt{debug}) { |
1064 |
print "$prompt: Running $GS $gs_quiet -sDEVICE=$GSDEVICE $gs_size $gs_density $gs_aalias $gs_out $out_redirect\n"; |
1065 |
print "GS>$bbx $bby translate\n" if($have_geometry); |
1066 |
print "GS>($gsfile) run\n"; |
1067 |
print "GS>showpage\n" if ($epsf); |
1068 |
print "GS>quit\n"; |
1069 |
} |
1070 |
open (GS, "|$GS $gs_quiet -sDEVICE=$GSDEVICE $gs_size $gs_density $gs_aalias $gs_out $out_redirect"); |
1071 |
print GS "$bbx $bby translate\n" if ($have_geometry); |
1072 |
print GS "($gsfile) run\n"; |
1073 |
print GS "showpage\n" if ($epsf); |
1074 |
print GS "quit\n"; |
1075 |
print "\n" if($opt{debug}); |
1076 |
unless(close(GS)) { |
1077 |
print "$prompt: Error: Ghostscript returned error status ",$?>>8,"\n"; |
1078 |
} |
1079 |
L2hos->Unlink($psfile) if($ps_changed && !$opt{debug}); |
1080 |
1; |
1081 |
} |
1082 |
|
1083 |
|
1084 |
# This sub post-processes the PNM images that come out of Ghostscript. |
1085 |
# The image is cropped, flipped and finally converted to PNG or GIF. |
1086 |
|
1087 |
sub crop_scale_etc { |
1088 |
my ($in, $out) = @_; |
1089 |
|
1090 |
# create temp filename; should be auto-incrementable |
1091 |
my $tmp = $in; |
1092 |
$tmp =~ s/(\.[^.]*)?$/.t00/; |
1093 |
# save the original Ghostscript result |
1094 |
if($opt{debug}) { |
1095 |
L2hos->Copy($in,$tmp); |
1096 |
&increment_name($tmp); |
1097 |
} |
1098 |
my ($cmd,$type,$width,$height,$just); |
1099 |
|
1100 |
my $must_align = 0; |
1101 |
#$EXTRA_CROP = "a$EXTRA_CROP" # hack to ensure first all-over cropping |
1102 |
# unless($EXTRA_CROP =~ /^a/i); |
1103 |
|
1104 |
# RRM: Remove justification bars |
1105 |
$EXTRA_CROP =~ s/h/bt/gi; # crop horizontally |
1106 |
$EXTRA_CROP =~ s/v/rl/gi; # crop vertically |
1107 |
while ($EXTRA_CROP =~ /([atblrs])/gi) { |
1108 |
my $edge = $1; |
1109 |
my $croparg = ' '; |
1110 |
if($edge =~ /b/i) { |
1111 |
$croparg = "-bot $PNMCROPOPT "; |
1112 |
} elsif($edge =~ /[tlr]/i) { |
1113 |
$croparg = "-$edge $PNMCROPOPT "; |
1114 |
} elsif($edge =~ /s/i) { |
1115 |
#RRM: shave at most 1-2 rows of white from the bottom |
1116 |
if($cmd) { |
1117 |
# Terminate command pipe |
1118 |
$cmd .= "| pnmcrop -white -bot -verbose | pamcut -bottom 200 -pad | pnmcrop -black -bot -verbose"; |
1119 |
&do_cmd($in,$tmp,$cmd) || return 0; # failure |
1120 |
$cmd = ''; |
1121 |
} |
1122 |
my ($type,$width,$height) = get_image_geometry($in); |
1123 |
#CNH add begin |
1124 |
###CNH next |
1125 |
#CNH add end |
1126 |
next unless($type); # skip if no geometry |
1127 |
if(&do_cmd_norename("$PNMCROP $PNMCROPOPT -bot < $in",$tmp)) { |
1128 |
my ($type,$width,$height2) = get_image_geometry($tmp); |
1129 |
if($type && ($height - $height2) < 30 ) { |
1130 |
# command succeeded and shaved less than 3 rows |
1131 |
if($opt{debug}&&(-f $tmp)) { |
1132 |
L2hos->Copy($tmp,$in); |
1133 |
&increment_name($tmp); |
1134 |
} elsif (-f $tmp) { |
1135 |
L2hos->Rename($tmp,$in); |
1136 |
} |
1137 |
next; |
1138 |
} |
1139 |
} |
1140 |
# MRO: this shouldn't be necessary: L2hos->Unlink($tmp); |
1141 |
next; # go to next crop argument |
1142 |
} # end switch on crop codes |
1143 |
if($cmd) { |
1144 |
# Continue command pipe |
1145 |
$cmd .= "| $PNMCROP $croparg"; |
1146 |
} else { |
1147 |
# start new pipe |
1148 |
$cmd = "$PNMCROP $croparg< $in "; |
1149 |
} |
1150 |
} # end cropping |
1151 |
|
1152 |
|
1153 |
if($opt{flip}) { |
1154 |
unless($cmd) { |
1155 |
$cmd = "$PNMFLIP -$opt{flip} < $in"; |
1156 |
} else { |
1157 |
$cmd .= "| $PNMFLIP -$opt{flip} "; |
1158 |
} |
1159 |
} |
1160 |
|
1161 |
if($RIGHT_JUSTIFY || $TOP_JUSTIFY || $CENTER || $SHORE_UP) { |
1162 |
if($cmd) { |
1163 |
# empty command pipe, we need the image's geometry |
1164 |
&do_cmd($in,$tmp,$cmd); |
1165 |
$cmd=''; |
1166 |
} |
1167 |
|
1168 |
# Get bitmap type and dimensions |
1169 |
($type,$width,$height) = &get_image_geometry($in); |
1170 |
return 0 unless($type); |
1171 |
|
1172 |
my ($white_left,$white_right,$white_top,$white_bottom) = (0,0,0,0); |
1173 |
|
1174 |
if($RIGHT_JUSTIFY || $CENTER) { |
1175 |
if($RIGHT_JUSTIFY) { |
1176 |
$white_left = int($RIGHT_JUSTIFY-$width); |
1177 |
} else { # CENTER |
1178 |
$white_left = int(($CENTER-$width) / 2); |
1179 |
} |
1180 |
$white_left = 0 unless($white_left > 0); |
1181 |
|
1182 |
$width += $white_left; |
1183 |
} |
1184 |
|
1185 |
if($TOP_JUSTIFY) { |
1186 |
if($TOP_JUSTIFY =~ /^x([0-9.]+)/io) { |
1187 |
$white_bottom = $1 * $height; |
1188 |
} else { |
1189 |
$white_bottom = $TOP_JUSTIFY - $height; |
1190 |
} |
1191 |
if($white_bottom > 0) { |
1192 |
$white_bottom = int($white_bottom + 0.99); # round up |
1193 |
$height += $white_bottom; |
1194 |
} else { |
1195 |
$white_bottom = 0; |
1196 |
} |
1197 |
} |
1198 |
|
1199 |
if($SHORE_UP =~ /(\d+)(d?)/ && $1) { |
1200 |
# RRM: make height and width an exact multiple of $SHORE_UP |
1201 |
my ($shoreup,$depth) = ($1,$2); |
1202 |
my $extra = $height % $shoreup; |
1203 |
if($depth) { # image needs depth, place half the extra space underneath |
1204 |
my $bextra = int($extra/2); |
1205 |
$white_bottom += $bextra; |
1206 |
$white_top += $extra - $bextra; |
1207 |
} else { |
1208 |
$white_top += $extra; |
1209 |
} |
1210 |
|
1211 |
$extra = $width % $shoreup; |
1212 |
my $rextra = int($extra/2); |
1213 |
$white_right += $rextra; |
1214 |
$white_left += $extra - $rextra; |
1215 |
$cmd = ''; |
1216 |
} |
1217 |
|
1218 |
|
1219 |
if($white_left) { |
1220 |
if($cmd) { |
1221 |
&do_cmd($in,$tmp,$cmd) || return 0; |
1222 |
} |
1223 |
# Start new command pipe |
1224 |
$cmd = "$PBMMAKE -white $white_left 1 | $PNMCAT -white -lr - $in "; |
1225 |
} |
1226 |
|
1227 |
if($white_right) { |
1228 |
if($cmd) { |
1229 |
&do_cmd($in,$tmp,$cmd) || return 0; |
1230 |
} |
1231 |
# Start new command pipe |
1232 |
$cmd = "$PBMMAKE -white $white_right 1 | $PNMCAT -white -lr $in - "; |
1233 |
} |
1234 |
|
1235 |
if($white_top) { |
1236 |
if($cmd) { |
1237 |
&do_cmd($in,$tmp,$cmd) || return 0; |
1238 |
} |
1239 |
# Start new command pipe |
1240 |
$cmd = "$PBMMAKE -white 1 $white_top | $PNMCAT -white -tb - $in "; |
1241 |
} |
1242 |
|
1243 |
if($white_bottom) { |
1244 |
if($cmd) { |
1245 |
&do_cmd($in,$tmp,$cmd) || return 0; |
1246 |
} |
1247 |
# Start new command pipe |
1248 |
$cmd = "$PBMMAKE -white 1 $white_bottom | $PNMCAT -white -tb $in - "; |
1249 |
} |
1250 |
} # endif must_align |
1251 |
|
1252 |
my $pnmtoimg; |
1253 |
if($opt{type} eq 'gif') { |
1254 |
$pnmtoimg = $PPMTOGIF; |
1255 |
if($INTERLACE) { |
1256 |
$pnmtoimg .= ' -interlace'; |
1257 |
} |
1258 |
if($TRANSPARENT) { |
1259 |
$pnmtoimg .= ' -trans ' . L2hos->quote($trans_color); |
1260 |
} |
1261 |
} |
1262 |
if($opt{type} eq 'png') { |
1263 |
$pnmtoimg = $PNMTOPNG; |
1264 |
if($INTERLACE) { |
1265 |
$pnmtoimg .= ' -interlace'; |
1266 |
} |
1267 |
if($TRANSPARENT) { |
1268 |
$pnmtoimg .= ' -trans ' . L2hos->quote($trans_color); |
1269 |
} |
1270 |
} |
1271 |
unless($pnmtoimg) { |
1272 |
print qq($prompt: Error: unknown image type "$opt{type}".\n); |
1273 |
exit 2; |
1274 |
} |
1275 |
|
1276 |
unless($type) { |
1277 |
($type,$width,$height) = &get_image_geometry($in); |
1278 |
return 0 unless($type); |
1279 |
} |
1280 |
# run ppmquant only on color/gray images |
1281 |
if(!$type || $type =~ /(ppm|pgm)/i) { |
1282 |
if($cmd) { |
1283 |
$cmd .= "| $reduce_color " |
1284 |
} else { |
1285 |
$cmd = "$reduce_color < $in "; |
1286 |
} |
1287 |
} |
1288 |
|
1289 |
if($cmd) { |
1290 |
$cmd .= "| $pnmtoimg " |
1291 |
} else { |
1292 |
$cmd = "$pnmtoimg < $in "; |
1293 |
} |
1294 |
&do_cmd_norename($cmd,$out) || return 0; |
1295 |
print qq{$prompt: Written $out\n} unless($opt{quiet}); |
1296 |
|
1297 |
1; |
1298 |
} |
1299 |
|
1300 |
sub banner { |
1301 |
print "$prompt V$RELEASE (Revision $VERSION, Perl $])\n"; |
1302 |
} |
1303 |
|
1304 |
sub print_version { |
1305 |
my $formats = join(',',@IMAGE_TYPES); |
1306 |
print <<"EOM"; |
1307 |
$prompt (Revision $VERSION, perl $]), |
1308 |
part of LaTeX2HTML Release V$RELEASE. |
1309 |
|
1310 |
Supported output image format(s): $formats |
1311 |
EOM |
1312 |
1; |
1313 |
} |
1314 |
|
1315 |
sub print_help { |
1316 |
L2hos->perldoc($SCRIPT); |
1317 |
1; |
1318 |
} |
1319 |
|
1320 |
sub print_usage { |
1321 |
my $start = 0; |
1322 |
my $usage = 'Usage: '; |
1323 |
my $indent = ''; |
1324 |
|
1325 |
print (@_, "\n") if @_; |
1326 |
|
1327 |
my $perldoc = '/usr/bin'.$dd."perldoc"; |
1328 |
my $script = $SCRIPT || $0; |
1329 |
open(PIPE, "$perldoc -t $script |") |
1330 |
|| die "Fatal: can't open pipe: $!"; |
1331 |
while (<PIPE>) |
1332 |
{ |
1333 |
if (/^\s*$/) { |
1334 |
next; |
1335 |
} elsif (/^SYNOPSIS/) { |
1336 |
$start = 1; |
1337 |
} elsif (/^\w/) { |
1338 |
$start = 0; |
1339 |
} elsif ($start == 1) { |
1340 |
($indent) = /^(\s*)/; |
1341 |
s/^$indent/$usage/; |
1342 |
$usage =~ s/./ /g; |
1343 |
$start = 2; |
1344 |
print $_; |
1345 |
} elsif ($start == 2) { |
1346 |
s/^$indent/$usage/; |
1347 |
print $_; |
1348 |
} |
1349 |
} |
1350 |
close PIPE; |
1351 |
1; |
1352 |
} |
1353 |
|
1354 |
sub do_cmd { |
1355 |
my ($in,$tmp,$cmd) = @_; |
1356 |
|
1357 |
print qq{Running "$cmd > $tmp"\n} if($opt{debug}); |
1358 |
my $stat = system("$cmd > $tmp"); |
1359 |
if($stat) { # error |
1360 |
print qq{$prompt: Error: "$cmd > $tmp" failed: $!\n}; |
1361 |
return 0; # failure |
1362 |
} |
1363 |
elsif(!-s $tmp) { # does not exist or zero size |
1364 |
print qq{$prompt: Error: "$cmd > $tmp" produced empty file\n}; |
1365 |
L2hos->Unlink($tmp) if(-e $tmp); |
1366 |
return 0; # failure |
1367 |
} |
1368 |
if($opt{debug}) { |
1369 |
# increase the temporary filename by 1 |
1370 |
# this uses perl's magic autoincrement |
1371 |
&increment_name($_[1]); |
1372 |
return L2hos->Copy($tmp,$in); |
1373 |
} elsif(!L2hos->Rename($tmp,$in)) { |
1374 |
print qq{$prompt: Error: rename of "$tmp" to "$in" failed: $!\n}; |
1375 |
return 0; # failure |
1376 |
} |
1377 |
1; |
1378 |
} |
1379 |
|
1380 |
sub do_cmd_norename { |
1381 |
my ($cmd,$out) = @_; |
1382 |
|
1383 |
print qq{Running "$cmd > $out"\n} if($opt{debug}); |
1384 |
my $stat = system("$cmd > $out"); |
1385 |
if($stat) { # error |
1386 |
print qq{$prompt: Error: "$cmd > $out" failed: $!\n}; |
1387 |
return 0; # failure |
1388 |
} |
1389 |
elsif(!-s $out) { # does not exist or zero size |
1390 |
print qq{$prompt: Error: "$cmd > $out" produced empty file\n}; |
1391 |
L2hos->Unlink($out) if(-e $out); |
1392 |
return 0; # failure |
1393 |
} |
1394 |
1; |
1395 |
} |
1396 |
|
1397 |
sub do_cmd_plain { |
1398 |
my ($cmd) = @_; |
1399 |
|
1400 |
print qq{Running "$cmd"\n} if($opt{debug}); |
1401 |
my $stat = system($cmd); |
1402 |
if($stat) { # error |
1403 |
print qq{$prompt: Error: "$cmd" failed: $!\n}; |
1404 |
return 0; # failure |
1405 |
} |
1406 |
1; |
1407 |
} |
1408 |
|
1409 |
sub get_image_geometry { |
1410 |
my ($pnmfile) = @_; |
1411 |
|
1412 |
my ($type,$width,$height); |
1413 |
my $out = `$PNMFILE $pnmfile`; |
1414 |
if($? || $out =~ /(P[BGP]M)[^0-9]*(\d+)\s*by\s*(\d+)/i) { |
1415 |
$type = $1; |
1416 |
$width = $2; |
1417 |
$height = $3; |
1418 |
print qq{Image "$pnmfile" is $type, ${width}x$height\n} if($opt{debug}); |
1419 |
} else { |
1420 |
print "$prompt: Error: Could not determine image size: $out\n"; |
1421 |
return undef; |
1422 |
} |
1423 |
($type,$width,$height); |
1424 |
} |
1425 |
|
1426 |
# push the number in the suffix up one notch |
1427 |
sub increment_name { |
1428 |
$_[0] =~ s/(\d+)$/$a=$1;++$a/e; |
1429 |
} |
1430 |
|
1431 |
__DATA__ |
1432 |
|
1433 |
=head1 EXAMPLES |
1434 |
|
1435 |
=over 4 |
1436 |
|
1437 |
=item C<pstoimg foo.ps> |
1438 |
|
1439 |
Convert the first page of foo.ps to the default bitmap type. |
1440 |
|
1441 |
=item C<pstoimg -type png -crop a -trans -interlace foo.ps> |
1442 |
|
1443 |
Same as above, but force png output and crop all the whitespace |
1444 |
around the image and make the color white transparent and |
1445 |
generate an interlaced bitmap. |
1446 |
|
1447 |
=item C<pstoimg -multi -out bar -type gif -crop a foo.ps> |
1448 |
|
1449 |
Consider foo.ps a multiple page PostScript file and create output |
1450 |
files bar1.gif, bar2.gif, etc. |
1451 |
|
1452 |
=back |
1453 |
|
1454 |
=head1 ENVIRONMENT |
1455 |
|
1456 |
=over 4 |
1457 |
|
1458 |
=item DENSITY, DEPTH, DEBUG, DISCARD |
1459 |
|
1460 |
See B<-density>, B<-depth>, B<-debug>, B<-discard>, respectively. |
1461 |
|
1462 |
=item GS_LIB |
1463 |
|
1464 |
This variable is set to the path(s) where Ghostscript libraries have |
1465 |
been found on this system during configuration, but only if the built-in |
1466 |
paths are not correct. This fixes the problem of relocation that is quite |
1467 |
common on Win32 installations. This behavior can be overridden by |
1468 |
setting GS_LIB manually before starting pstoimg. |
1469 |
|
1470 |
=item LATEX2HTMLDIR |
1471 |
|
1472 |
The directory where the LaTeX2HTML library and perl modules are found. |
1473 |
Defaults to "/usr/share/latex2html" on this installation. |
1474 |
|
1475 |
=item OUTFILE |
1476 |
|
1477 |
Setting this has the same effect as specifying B<-out>. Please do not rely |
1478 |
on this feature any more, it will disappear from the next releases! |
1479 |
|
1480 |
=item PAPERSIZE |
1481 |
|
1482 |
The papersize to use by Ghostscript to render the image. pstoimg tries |
1483 |
hard to optimize for rendering on the smallest possible bitmap size. |
1484 |
Still this option is there to enable tuning by hand, although it is |
1485 |
deprecated. If pstoimg finds a better setting, this parameter is ignored. |
1486 |
|
1487 |
=item SCALE |
1488 |
|
1489 |
See the discussion of B<-scale>. |
1490 |
|
1491 |
=item TMP and TEMP |
1492 |
|
1493 |
Unless overridden by B<-tmp>, these variables denote a directory where |
1494 |
to store temporary files. TMP is considered first, then TEMP. |
1495 |
|
1496 |
=back |
1497 |
|
1498 |
=head1 SEE ALSO |
1499 |
|
1500 |
gs, pnmcrop, pnmquant, pbmmake, pnmcat, pnmfile, pnmflip, ppmtogif, |
1501 |
pnmtopng, giftool, giftrans. |
1502 |
|
1503 |
=head1 NOTES |
1504 |
|
1505 |
Several people have suggested to use ImageMagick's convert instead of |
1506 |
pstoimg. A few comments on this: convert uses (of course) Ghostscript |
1507 |
for conversion of PostScript to bitmap, so one still needs gs. And |
1508 |
for the special requirements of LaTeX2HTML convert's features are not |
1509 |
sufficient. The ImageMagick toolset has everything in place, but it |
1510 |
has some overhead that can prove killing when processing some 100 |
1511 |
images. pstoimg only does what it really has to, so it should be |
1512 |
quite efficient. Don't get me wrong - I like ImageMagick, but not in |
1513 |
the context of LaTeX2HTML. |
1514 |
|
1515 |
=head1 CAVEATS |
1516 |
|
1517 |
This utility is automatically configured and built to work on the |
1518 |
local setup. If this setup changes (e.g. some of the external commands |
1519 |
are moved), the script has be be reconfigured. |
1520 |
|
1521 |
Despite the portability of perl, a pstoimg configured on UNIX will |
1522 |
probably not work on Win32 and vice versa. |
1523 |
|
1524 |
=head1 BUGS |
1525 |
|
1526 |
This is a major enhancement release, so there may be a few bugs. As |
1527 |
the user inteface changed a bit, some of your tools that were using |
1528 |
pstoimg may not work any more. |
1529 |
|
1530 |
Please report bugs to latex2html@tug.org, stating the (debug) output |
1531 |
of pstoimg, your perl version and the versions of the external tools. |
1532 |
Best is to include the cfgcache.pm file from the configuration procedure. |
1533 |
|
1534 |
=head1 AUTHOR |
1535 |
|
1536 |
Marek Rouchal E<lt>marek@saftsack.fs.uni-bayreuth.deE<gt> |
1537 |
|
1538 |
=head1 HISTORY |
1539 |
|
1540 |
This script went through a long evolution, beginning with a modification |
1541 |
of Doug Crabill's E<lt>dgc@cs.purdue.eduE<gt> ps2epsi script. |
1542 |
The first perl version was done by Nikos Drakos <nikos@cbl.leeds.ac.uk>. |
1543 |
It was gradually improved by numerous LaTeX2HTML developers: |
1544 |
Ross Moore <ross@mpce.mq.edu.au>, Jens Lippmann |
1545 |
<lippmann@rbg.informatik.tu-darmstadt.de> and others (sorry for not |
1546 |
mentioning everyone and thanks for your contributions). |
1547 |
|
1548 |
=cut |