#!/usr/bin/env perl
# /Time-stamp: "2009-08-09 12:58:16 leycec"/

=head1 NAME

fragmaster.pl - Create EPS and PDF graphics from source EPS and control files
                using \psfrag commands.

=head1 SYNOPSIS

fragmaster.pl scans the current directory for files matching the
pattern "*_fm" and "*_fm.eps" and converts them to the respective
".eps"- and ".pdf"-files if they are outdated.

=head1 CONFIGURATION

IMPORTANT: ALLOW DVIPS TO MAKE _PORTRAIT_ PS WITH WIDTH > HEIGHT
BY ADDING

@ custom 0pt 0pt

TO YOUR /usr/share/texmf/dvips/config/config.pdf
IF THIS ENTRY IS MISSING, DVIPS WILL GUESS ORIENTATION FROM
WIDTH / HEIGHT RATIO. THIS STILL CAN HAPPEN IN CASE YOUR INPUT EPS
MATCHES A STANDARD PAPER SIZE!

=head1 USAGE         

Source files:
   <graphics>_fm.eps
      a source EPS file
   <graphics>_fm
      a control file containing \psfrag commands and optionally
      special comments:
        % fmclass: <class>
          use <class> instead of "article"
        % fmclassopt: <options>
          use <options> as class options instead of "12pt"
        % head:
        % <texcode>
        % end head
          causes <texcode> to be put into the preamble
        % fmopt: <option>
          causes <option> to be given to \includegraphics as
          optional parameter

=head1 CREDITS

This script was inspired by a posting from
  Karsten Roemke <k roemke gmx de> (dot at dot)
with subject
  "psfrag pdflatex, lange her"
in de.comp.text.tex on 2003-11-11 05:25:44 PST.

Karsten Roemke was inspired for his solution by postings from
Thomas Wimmer.

=cut

# ....................{ USE STATEMENTS                     }....................
#FIXME: This should definitely be enabled, at some point. :)
#use strict;
use warnings;

use Getopt::Long;
use Pod::Usage;

use constant {
	true => '1',
	false=> '',

	success=> 0,
	failure=> 1,
};

# ....................{ CONSTANTS                          }....................
my $script_name = 'fragmaster';
my $VERSION = 0.001;

# ....................{ CLI OPTIONS                        }....................
my %options;

# Enable first-level bundling; e.g., auto-expand a passed "-vax" to "-v -a -x";
# all other configuration settings should be reasonably self-explanatory.
Getopt::Long::Configure
	qw(posix_default gnu_compat auto_help auto_version bundling no_ignore_case);

#FIXME: Document the "--select_second_page" option. (It's rather helpful!)
Getopt::Long::GetOptions(\%options,
												 'help|h|?',
												 'quiet|q',
                         'select_second_page',
												 ) ||
	pod2usage({
						 -exitval=> failure,
						 -message=> qq[${script_name}: v${VERSION}; GNU GPL v3; open, aspiring, and up.],
						 -verbose=> 2
						});

# ....................{ IMPLEMENTATION                     }....................
chomp($cwd = `pwd`);
$cwd =~ /\s/ and die "Current path contains whitespace. I am sorry, but LaTeX cannot handle this correctly, move somewhere else. Stopped";

foreach $fm_file (<*_fm>) {
  ($base = $fm_file) =~ s/_fm$//;
  $source = "$fm_file.eps";

  if(! -f $source) {
    print "Cannot find EPS file '$source' for fragmaster file '$fm_file'! Skipped.\n";
    next;
  }
  
  $dest_eps = "$base.eps";
  $dest_pdf = "$base.pdf";
  
  $do_it = 0;

  $do_it = 1
    if ! -f $dest_eps;
  $do_it = 1
    if ! -f $dest_pdf;

  if(! $do_it) {
    $oldest_dest = -M $dest_eps;
    $oldest_dest = -M $dest_pdf
      if -M $dest_pdf > $oldest_dest;
  
    $youngest_source = -M $fm_file;
    $youngest_source = -M $source
      if -M $source < $youngest_source;
    $do_it = 1
      if $oldest_dest > $youngest_source;
  }
  
  if( $do_it ) {
    print "$script_name: $fm_file, $source -> $dest_eps, $dest_pdf\n";
    open FMFILE, "<$fm_file" or die "Cannot read $fm_file!";

    #FIXME: Use a Perl package for creating this temporary path, instead.
    $tempdir = `mktemp -d fm-$base.XXXXXX`
      or die "Cannot make temporary directory!";
    print "$script_name: [sh] + '$tempdir'\n";
    chomp($tempdir);

    $fmopt = "";
    @fmfile = ();
    @fmhead = ();
    $fmclass = "article";
    $fmclassopt = "12pt";
    while (<FMFILE>) {
      chomp;
      $fmopt = $1 if /fmopt:(.*)/;
      $fmclass = $1 if /fmclass:(.*)/;
      $fmclassopt = $1 if /fmclassopt:(.*)/;
      if (/head:/) {
        push @fmfile, "  $_%\n";
        while(<FMFILE>) {
          chomp;
          last if /end head/;
          push @fmfile, "  $_%\n";
          # Remove comment prefix
          s/^[\s%]*//;
          push @fmhead, "$_%\n";
        }
      }
            
      push @fmfile, "  $_%\n";
    }

    print "$script_name: [perl] '$fm_file' -> '$tempdir/fm.tex'\n";
    open TEXFILE, ">$tempdir/fm.tex" or die "Cannot write LaTeX file!";

    # If this fm file has a header line "fmclass:false", this indicates that
    # fragmaster should not explicitly declare a document class. (i.e., some
    # package in this fm file's preamble already does so.)
    print TEXFILE <<"EOF" unless $fmclass eq "false";
\\documentclass[$fmclassopt]{$fmclass}
EOF
    print TEXFILE foreach(@fmhead);
    print TEXFILE <<'EOF';
\usepackage{graphicx,psfrag,color}
%FIXME: The following five height-related statements are an unfortunately bad
%idea, when converting a wide EPS graphic under KOMA-Script document classes.
%Perhaps they should be enabled, otherwise?
\setlength{\topmargin}{-1in}
%\setlength{\headheight}{0pt}
\setlength{\headsep}{0pt}
\setlength{\topskip}{0pt}
\setlength{\textheight}{\paperheight}
\setlength{\oddsidemargin}{-1in}
\setlength{\evensidemargin}{-1in}
\setlength{\textwidth}{\paperwidth}
\setlength{\parindent}{0pt}
%FIXME: Try the "lscape" package, instead.
\special{! TeXDict begin /landplus90{true}store end }
%\special{! statusdict /setpage undef }
%\special{! statusdict /setpageparams undef }
\pagestyle{empty}
\newsavebox{\pict}
EOF
    print TEXFILE "\\graphicspath{{../}}\n";
    print TEXFILE <<'EOF';
\begin{document}
  \begin{lrbox}{\pict}%
EOF

    print TEXFILE foreach (@fmfile);
    print TEXFILE "  \\includegraphics[$fmopt]{$source}%\n";
    print TEXFILE <<'EOF';
  \end{lrbox}
  \special{papersize=\the\wd\pict,\the\ht\pict}
  \usebox{\pict}
\end{document}
EOF
    
    close TEXFILE;
    chdir($tempdir) or die "Cannot chdir to $tempdir!";

    print "$script_name: [latex] '$tempdir/fm.tex' -> '$tempdir/fm.dvi'\n";
    system("latex fm.tex") / 256 == 0 or die "Cannot latex fm.tex!";

    # Using "-E" here causes dvips to detect the psfrag phantom stuff and set
    # the BoundingBox wrong. We correct for this below.
    print "$script_name: [dvips] converting '$tempdir/fm.dvi' to '$tempdir/fm.ps'\n";
    my $dvips_options = "-E -P pdf";
       $dvips_options .= " -pp 2" if $options{select_second_page};
    system("dvips $dvips_options fm.dvi -o fm.ps") / 256 == 0
      or die "Cannot dvips!";
    
    print "$script_name: [perl] '$tempdir/fm.ps' -> '$dest_eps'\n";
    chdir("..")                or die "Cannot chdir back up!";
    open PS, "<$tempdir/fm.ps" or die "Cannot read fm.ps!";
    open EPS, ">$dest_eps"     or die "Cannot write $dest_eps!";

    # Correct the bounding box by setting the left margin to 0
    # top margin to top of letterpaper!
    # (I hope that is general enough...)
    $saw_bounding_box = 0;
    while(<PS>) {
      if(! $saw_bounding_box) {
        # if(s/^\%\%BoundingBox:\s+(\S+)\s+(\S+)\s+(\S+)\s+(\S+)/\%\%BoundingBox: 0 $2 $3 $4/) {
        if(s/^\%\%BoundingBox:\s+(\S+)\s+(\S+)\s+(\S+)\s+(\S+)/\%\%BoundingBox: 0 $2 $3 792/) {
          $saw_bounding_box = 1;
        }
      }
      print EPS;
    }
    
    # Not using -E above causes
    # papersizes to be included into the PS
    # Strip off the specifications.
    # Otherwise gv doesn't show the BBox
    # and epstopdf won't detect the correct
    # PDF media size!
    
    #     while(<PS>) {
    #       s/^%!PS-Adobe.*/%!PS-Adobe-3.0 EPSF-3.0/;
    
    #       next if /^\%\%DocumentPaperSizes:/;
    #       if(/^\%\%BeginPaperSize:/) {
    # 	while(<PS>) {
    # 	  last if /^\%\%EndPaperSize/;
    # 	}
    # 	next;
    #       }
    #       s/statusdict \/setpage known/false/;
    #       s/statusdict \/setpageparams known/false/;
    #       print EPS;
    #     }

    close EPS;
    close PS;

    print "$script_name: [epstopdf] '$dest_eps' -> '$dest_pdf'\n";
    system("epstopdf $dest_eps --outfile=$dest_pdf") / 256 == 0
      or die "Cannot epstopdf!";

    print "$script_name: [sh] - '$tempdir'\n";
    system("rm -rf $tempdir") / 256 == 0
      or die "Cannot remove $tempdir!";

    close FMFILE;
  }
}

  1
__END__

=head1 COPYRIGHT AND LICENSE

The information below applies to everything in this distribution,
except where noted.

Copyleft  2009 by Cecil Curry <http://raiazome.com>.
Copyright 2004 by Tilman Vogel <tilman vogel web de>.

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.

=cut
