summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorGuilhem Moulin <guilhem.moulin@chalmers.se>2010-09-26 01:18:01 +0200
committerGuilhem Moulin <guilhem.moulin@chalmers.se>2010-09-26 01:18:01 +0200
commitb72e0359e2015f2beef8364ca6513c9d186a0254 (patch)
tree9e48b0dfaf95415e4db39f042d18f40633333b86
parent7374906460c1973bb145fb32684f53e05bf6d51d (diff)
Documentation
-rwxr-xr-xpdftool.pl409
1 files changed, 292 insertions, 117 deletions
diff --git a/pdftool.pl b/pdftool.pl
index fb1785a..69c0484 100755
--- a/pdftool.pl
+++ b/pdftool.pl
@@ -1,98 +1,178 @@
-#! /usr/bin/perl
+#! /usr/bin/perl -w
-use Getopt::Long;
+
+use Getopt::Long qw(:config posix_default no_ignore_case bundling);
+use Pod::Usage;
use IPC::Open2;
-use warnings;
+use POSIX qw(floor);
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';
+=head1 NAME
+
+pdftool.pl - a PDF swiss army knife
+
+=head1 SYNOPSIS
+
+B<pdftool.pl> [-s I<pages>] [-p I<paper>] [-m I<margin>] [-c] [-b]
+[-n I<nup>] [-q] [I<infile> [I<outfile>]]
+
+=head1 DESCRIPTION
+
+I<PDFTool>
+
+=head1 OPTIONS
+
+=over 8
+
+=item B<-s, --select>
+
+=item B<-p, --paper>
+
+=item B<-m, --margin>
+
+=item B<-c, --crop>
+
+=item B<-b, --book>
+
+=item B<-n, --nup>
+
+=item B<-q, --quiet>
+
+I<PDFTool> normally prints the page numbers of the pages output; this
+option suppresses this.
+
+=item B<--help>
+
+Display a brief help.
+
+=item B<--man>
+
+Display the manual page.
+
+=back
+
+=head1 REQUIRE
+
+Requires psutils installed and available in the command line
+http://www.tardis.ed.ac.uk/~ajcd/psutils/
+
+=head1 AUTHOR
+
+Public domain, (c) Guilhem Moulin.
+
+=head1 VERSION
+
+Version: 0.1, 25 September 2010
+
+=cut
+
+
my $tmpdir = '/tmp';
+#
+# Options & arguments
+#
-my $nup = 1;
-my $margin = 1;
-#TODO: units
+my $select;
+my $paper;
+my $margin;
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;
+my $nup = 1;
+my $quiet;
+my $man;
+my $help;
+
+# TODO: select pages
+# TODO: choose the output type
+GetOptions( "select|s=s" => \$select,
+ "paper|p=s" => \$paper,
+ "margin|m=s" => \$margin,
+ "crop|c" => \$crop,
+ "book|b" => \$book,
+ "nup|n=i" => \$nup,
+ "1" => sub { $nup = 1 },
+ "2" => sub { $nup = 2 },
+ "3" => sub { $nup = 3 },
+ "4" => sub { $nup = 4 },
+ "5" => sub { $nup = 5 },
+ "6" => sub { $nup = 6 },
+ "7" => sub { $nup = 7 },
+ "8" => sub { $nup = 8 },
+ "9" => sub { $nup = 9 },
+ "q|quiet" => \$quiet,
+ "help" => \$help,
+ "man" => \$man )
+ or pod2usage(2);
+
+pod2usage(1) if (defined $help or $#ARGV > 1);
+pod2usage(-exitstatus => 0, -verbose => 2) if defined $man;
+
+# Input and output files
+my ($infile, $outfile) = @ARGV;
+
+
+
+#
+# Default values
+#
+
+# Default output papersize
+$paper = "a4" unless defined $paper;
+
+
+# Default margin
+unless (defined $margin) {
+ $margin = 0;
+ $margin = "1cm" if defined $crop;
}
-for (my $n = $nup; $n > 1; $n /= 2) {
- die "nup should be a power of two"
- unless $n % 2 == 0;
-}
+# Default unit: PostScript point
+&topoints (\$margin);
-my $filename = $ARGV[0];
-$filename = "-" unless defined $filename;
-my $filename2 = $filename;
-$filename2 = "(stdin)" if $filename eq "-";
+# Inner and outer margins
+my ($mresize, $mnup) = (0,0);
+if ($nup > 1 && not defined $book) {
+ $mresize = $margin/2;
+ $mnup = $mresize;
+} else {
+ $mresize = $margin;
+}
-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;
- }
+# Open input and output files
+my $infile_display;
+if (defined $infile && $infile ne "-") {
+ open FIN, '<', "$infile" or die "Can't read `$infile': $!";
+ $infile_display = $infile;
} else {
- $mcrop = 0;
- if (not defined $book && $nup > 1) {
- $mresize = $margin/2;
- $mnup = $mresize;
- } else {
- $mresize = $margin;
- $mnup = 0;
- }
+ undef $infile;
+ *FIN = *STDIN;
+ $infile_display = "(stdin)";
}
+if (defined $outfile && $outfile ne "-") {
+ open FOUT, '>', "$outfile" or die "Can't create `$outfile': $!";
+} else {
+ *FOUT = *STDOUT;
+}
-open(FILE, $filename)
- or die "Can't read `$filename'";
-
+#
+# Detect filetype
+#
my $filetype;
-my $fstline = <FILE>;
-if (defined $fstline && $fstline =~ /%!PS.*/) {
+my $fstline = <FIN>;
+if (defined $fstline && $fstline =~ /^%!PS.*/) {
$filetype = "PS";
-} elsif (defined $fstline && $fstline =~ /%PDF.*/) {
+} elsif (defined $fstline && $fstline =~ /^%PDF.*/) {
$filetype = "PDF";
} else {
- die "Can't recognize the type of `$filename2'";
+ die "Can't recognize the type of `$infile_display'";
}
# Auxiliary files, to remove
@@ -101,80 +181,127 @@ 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 (<FILE>) {
- print AUXFD $_;
+
+
+#
+# Conversion from PDF to PS, if necessary
+#
+my @cmd;
+if ($filetype eq "PDF") {
+ unless (defined $infile) {
+ # Need to copy the whole input to an auxiliary file, since
+ # conversion from PDF to PS requires random access to the data
+
+ $infile = "$tmpdir/pdftool-stdin-" .
+ int(rand 2**16) . lc ".$filetype";
+
+ open FIN2, '>', "$infile"
+ or die "Can't write into `$infile': $!";
+ push @auxfiles, "$infile";
+
+ # cat > $filename
+ # TODO: useless seek
+ seek FIN, 0, 0 or die $!;
+ while (<FIN>) {
+ print FIN2 $_ or die "Can't print: $!";
+ }
+ close FIN2;
}
- close AUXFD;
+ close FIN;
+
+ # Convert to PS
+ @cmd = ('pdftops', "$infile", '-');
+ push @cmd, '-q' if defined $quiet;
+ $pid = open *PSIN, "-|", @cmd
+ or die "Can't run: `" . &printcmd (@cmd) . "'";
+ push @pids, $pid;
+} else {
+ # TODO: useless seek
+ seek FIN, 0, 0 or die $!;
+ *PSIN = *FIN;
}
-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 -'";
+#
+# Select, if necessary
+#
+# TODO: preselection, during the conversion from pdf?
+if (defined $select) {
+ @cmd = ('psselect', "-p$select");
+ push @cmd, '-q' if defined $quiet;
+ $pid = open2 *PSSELECT, "<&PSIN", @cmd
+ or die "Can't run: `" . &printcmd (@cmd) . "'";
push @pids, $pid;
} else {
- open *INPS, '<', "$filename"
- or die "Can't open `$filename'";
+ *PSSELECT = *PSIN;
}
-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 :)
+
+#
+# Resize file to our paper
+#
+@cmd = ('./psresize2.pl', "-p$paper", "-m$mresize");
+push @cmd, "-c" if defined $crop;
+push @cmd, '-q' if defined $quiet;
+
+$pid = open2 *PSRESIZE, "<&PSSELECT", @cmd
+ or die "Can't run: `" . &printcmd (@cmd) . "'";
push @pids, $pid;
+# Note: open2 closes the filehandles for us :)
+
-# Pscrop
-if (defined $crop) {
- $pid = open2 *PSCROP, "<&PSRESIZE", @pscrop, "-p$papersize", "-m${mnup}"
- or die "Error with `@pscrop -p$papersize -m${mnup}cm'";
+#
+# PSBook
+#
+if (defined $book) {
+ @cmd = ('psbook');
+ push @cmd, '-q' if defined $quiet;
+ $pid = open2 *PSBOOK, "<&PSRESIZE", @cmd
+ or die "Can't run: `" . &printcmd (@cmd) . "'";
push @pids, $pid;
} else {
- *PSCROP = *PSRESIZE;
+ *PSBOOK = *PSRESIZE;
}
-# Psbook
-if (defined $book) {
- $pid = open2 *PSBOOK, "<&PSCROP", "@psbook"
- or die "Error with `@psbook";
+
+
+#
+# PSNup
+#
+if ($nup > 1) {
+ @cmd = ('psnup', "-p$paper", "-m$mnup", "-$nup");
+ push @cmd, '-q' if defined $quiet;
+ $pid = open2 *PSOUT, "<&PSBOOK", @cmd
+ or die "Can't run: `" . &printcmd (@cmd) . "'";
push @pids, $pid;
} else {
- *PSBOOK = *PSCROP;
+ *PSOUT = *PSBOOK;
}
-# 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;
+
+#
+# Final file
+#
+if ($filetype eq "PDF") {
+ # Convert back to PDF
+
+ @cmd = ('ps2pdf', "-dEmbedAllFonts=true", "-sPAPERSIZE=$paper", '-', '-');
+ $pid = open2 ">&FOUT", "<&PSOUT", @cmd
+ or die "Can't run: `" . &printcmd (@cmd) . "'";
+ push @pids, $pid;
+} else {
+ # cat > FOUT
+ while (<PSOUT>) {
+ print FOUT $_ or die "Can't print: $!";
+ }
+}
# Avoid zombies
@@ -183,7 +310,55 @@ map {waitpid $_, 0} @pids;
# Close auxiliary filehandles
map {close $_} @auxfds;
+close FIN;
+close FOUT;
# Delete auxiliary files
unlink @auxfiles;
+
+
+
+
+# =========================================================
+
+#
+# In-place convert the given length to PostScript points
+#
+sub topoints {
+ my $l = $_;
+ return unless defined $$l;
+
+ $$l =~ /^([+-]?\d*\.?\d+)(\w*)$/ or die "Unable to parse `$$l'";
+
+ my $r = $1;
+ if ($2 eq "" or $2 eq "pt") {
+ # nothing
+ } elsif ($2 eq "in") {
+ $r *= 72;
+ } elsif ($2 eq "cm") {
+ $r *= 72/2.54;
+ } elsif ($2 eq "mm") {
+ $r *= 72/25.4;
+ } else {
+ die "Unknown unit: `$2'";
+ }
+ $$l = floor ($r + .5);
+}
+
+
+#
+# Print a command just like you'd do in a shell
+#
+sub printcmd {
+ my @cmd;
+
+ for (@_) {
+ my $s = $_;
+ $s =~ s/"/\\"/;
+ $s = "\"$s\"" if $s =~ /[ ()';#{}*?~&|`]/;
+ push @cmd, $s;
+ }
+
+ join ' ', @cmd;
+}