#!/usr/bin/env perl

######################################################################
#
# pdfrotate - rotates each page in a pdf file by a given angle
# 
# Last Modified: 2004-06-27
#
# NB! This script modifies files in place. 
#     Backup is saved to 'filename.bak'.
#     
# I use the script to rotate pdf files produced by the following commands: 
# LaTeX2e (containing landscape specials) -> dvips -> ps2pdf
#  
# It will probably fail on landscape-files produced in other ways.
# If you make improvements to, or fix bugs in, this command,
# please mail a copy of the new version to me.
#  
# Robert Dick (http://www.ece.northwestern.edu/~dickrp)
#  - bug fixes and small improvements by 
#      Hans Fredrik Nordhaug  (hansfn@mi.uib.no)
#      Bill Baxter (baxter (from) cs.unc.edu)
#
#########################################################################
use IO::Handle;

use warnings;
use strict;

sub usage();
 
my ($rotation, $bytepos, $in_xref, @table, $rot_com, $outfile);
 
$rotation = 90;
$bytepos = 0;
$in_xref = 0;
@table = ();

# Parse flags.
if ( $#ARGV < 0) {
    usage ();
}
while ($#ARGV>=0)
{
  if ($ARGV[0] =~ m/^-r([0-9]+)$/) {
    shift(@ARGV);
    $rotation = $1;
  }
  elsif ($ARGV[0] =~ m/^-r$/) {
    shift(@ARGV);
    $rotation = shift(@ARGV);
  }
  elsif ($ARGV[0] =~ m/^-o(.+)$/) {
    shift(@ARGV);
    $outfile = $1;
  }
  elsif ($ARGV[0] =~ m/^-o$/) {
    shift(@ARGV);
    $outfile = shift(@ARGV);
  }
  else {
    last;
  }
}

$rotation %= 360;

usage() if ($rotation < 0 or $rotation > 360);  
usage() if (not scalar(@ARGV));

if ($outfile) {
  # Try to redirect std out
  open(OUT, ">$outfile") or usage();
  STDOUT->fdopen(\*OUT, "w") or usage();
} else {
  # Modify files in place, copy backup to .bak
  $^I='.bak'
}

# Process the input files.

while (<>) {
    if (not $in_xref) { 
        print $_;
    
        if (m/^([0-9]+) [0-9]+ obj( <<)?$/) {
            # Prepare the index.
            $table[$1 - 1] = $bytepos;
        } elsif (m/\/MediaBox \[[0-9]+ [0-9]+ [0-9]+ [0-9]+\]$/) {
            # Rotate each page.
            $bytepos += length($_);
            # check next line for an existing /Rotate command
            $_=<ARGV>;
            if (m/^\/Rotate ([0-9]+)/) {
                $bytepos -= length($_);
                my $ang = $1 + $rotation;
                $ang %= 360;
                $rot_com = "/Rotate $ang\n";
                print $rot_com;
            }
            else {
                $rot_com = "/Rotate $rotation\n";
                print $rot_com;
                print $_;
            }
            $bytepos += length($rot_com);
        } elsif (m/^xref$/) {
            # Prepare the index.
            $in_xref = 1;
            push @table, $bytepos;
        }
    } else {
        # Rebuild the xrefs.
        if (m/^[0-9]+ [0-9]+ n\s*$/) {
            printf "%0.10d 00000 n \n", shift(@table);
        } elsif (m/^[0-9]+$/) {
            print shift(@table), "\n";
        } else {
            print $_;
        }
    }
    
    $bytepos += length($_);
}

exit;

#-----------------------------------------------------------------------------
sub usage () {
    print "Usage: pdfrotate [-r ...] [-o output] [file] \n".
          " \t-r  sets angle of rotation (default: 90)\n".
          " \t-o  output file (default: in-place modification)\n\n";
    exit -1;
}
