#! /usr/bin/perl use Getopt::Long; use IPC::Open2; use warnings; use strict; Getopt::Long::Configure ("bundling"); # Give an array if the command has least one argument. my @pdf2ps = 'pdftops'; my @pscrop = ('psnup2.pl', '-s1', '-l1'); my @psresize = 'psresize'; my @psnup = 'psnup'; my @psbook = 'psbook'; my @ps2pdf = 'ps2pdf'; my $tmpdir = '/tmp'; my $nup = 1; my $margin = 1; #TODO: units my $crop; my $book; my $papersize; GetOptions( "nup|n=i" => \$nup, "1" => sub { $nup = 1 }, "2" => sub { $nup = 2 }, "4" => sub { $nup = 4 }, "8" => sub { $nup = 8 }, "crop|c" => \$crop, "book|b" => \$book, "papersize|p=s" => \$papersize, "margin|m=s" => \$margin ); die "I can handle only one file at the same time :P" if $#ARGV > 0; die "How should I put a non-positive number of pages per sheet?" if $nup <= 0; unless (defined $papersize) { $papersize = `paperconf` or die "Can't guess papersize"; chomp $papersize; } for (my $n = $nup; $n > 1; $n /= 2) { die "nup should be a power of two" unless $n % 2 == 0; } my $filename = $ARGV[0]; $filename = "-" unless defined $filename; my $filename2 = $filename; $filename2 = "(stdin)" if $filename eq "-"; my ($mresize, $mcrop, $mnup) = (0,0,0); if (defined $crop) { $mresize = 0; if (not defined $book && $nup > 1) { $mcrop = $margin/2; $mnup = $mcrop; } else { $mcrop = $margin; $mnup = 0; } } else { $mcrop = 0; if (not defined $book && $nup > 1) { $mresize = $margin/2; $mnup = $mresize; } else { $mresize = $margin; $mnup = 0; } } open(FILE, $filename) or die "Can't read `$filename'"; my $filetype; my $fstline = ; if (defined $fstline && $fstline =~ /%!PS.*/) { $filetype = "PS"; } elsif (defined $fstline && $fstline =~ /%PDF.*/) { $filetype = "PDF"; } else { die "Can't recognize the type of `$filename2'"; } # Auxiliary files, to remove my @auxfiles; # Auxiliary file descriptors, to close my @auxfds; # Pids, to waid for my @pids; my $pid; if ($filename eq "-") { # Need to copy the whole input to an auxiliary file, since # conversion from pdf to ps requires random access to the data # TODO: only for pdfs $filename = "$tmpdir/pdftool-stdin-" . int(rand 2**16) . lc ".$filetype"; open AUXFD, '>', "$filename" or die "Can't read write into `$filename'"; push @auxfiles, "$filename"; # cat > $filename seek STDIN, 0, 0; while () { print AUXFD $_; } close AUXFD; } close FILE; # Convert to ps, or read the input file if ($filetype eq "PDF") { $pid = open *INPS, "-|", @pdf2ps, "$filename", '-' or die "Error with `@pdf2ps $filename -'"; push @pids, $pid; } else { open *INPS, '<', "$filename" or die "Can't open `$filename'"; } push @auxfds, fileno INPS; # Resize file to our papersize $pid = open2 *PSRESIZE, "<&INPS", @psresize, "-p$papersize" or die "Error with `@psresize -p$papersize'"; # Note: open2 closes the filehandles for us :) push @pids, $pid; # Pscrop if (defined $crop) { $pid = open2 *PSCROP, "<&PSRESIZE", @pscrop, "-p$papersize", "-m${mnup}" or die "Error with `@pscrop -p$papersize -m${mnup}cm'"; push @pids, $pid; } else { *PSCROP = *PSRESIZE; } # Psbook if (defined $book) { $pid = open2 *PSBOOK, "<&PSCROP", "@psbook" or die "Error with `@psbook"; push @pids, $pid; } else { *PSBOOK = *PSCROP; } # Psnup # TODO: sometimes unecessary $pid = open2 *PSNUP, "<&PSBOOK", @psnup, "-p$papersize", "-m${mnup}cm", "-$nup" or die "Error with `@psnup -p$papersize -m${mnup}cm -$nup'"; push @pids, $pid; # Convert back to pdf # TODO: not always stdout # TODO: return same type as input $pid = open2 ">&STDOUT", "<&PSNUP", @ps2pdf, "-sPAPERSIZE=$papersize", '-', '-' or die "Error with `@ps2pdf -sPAPERSIZE=$papersize - -'"; push @pids, $pid; # Avoid zombies map {waitpid $_, 0} @pids; # Close auxiliary filehandles map {close $_} @auxfds; # Delete auxiliary files unlink @auxfiles;