#!/usr/bin/perl -w 

=pod
    fig2ps : convert xfig files to ps/eps/pdf processing text with LaTeX
    Copyright (C) 2004-2006, 2008, 2009, 2010  Vincent Fourmond
    
    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 2 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, write to the Free Software Foundation, Inc.,
    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
=cut

# TODO-list:
# * switch to the geometry package (done, Vincent Fourmond 31/12/2008)
# * use proper temporary files to avoid problems
#   (done, Vincent Fourmond 31/12/2008)
# * add --verbose/--quiet/--silent options ?
# * add the possibility to include a given file (say, by default, .fig2ps.ins)
#   found in the directory where each file is.
# * rewrite the add/packages stuff, confusing and not very useful
# * add the possibility to write a part of the preamble directly within
#   XFIG comments (using a special syntax ???)
# * FIX BUG: image inclusion does not work anymore !!!!!!!!
#
# There are a fair number of TODOs as well in the file.

use Getopt::Long qw(:config  bundling);
use File::Temp qw/tempdir/;
use File::Path;			# For rmtree
use Cwd qw(getcwd realpath);	# To get the current directory and
				# resolve files names
use File::Copy;
use File::Basename; 		# For dirname

my $help = 0;
my $packages = "";
my $packages_add = "";
my $gv = 1;
# The viewer to use, if not GV
my $viewer;
my $doc_class = "article";

# Now using geometry for margins specifications.
my $pre_beg_commands = "\\usepackage[a0paper, margin=5cm]{geometry}\n";

my $post_beg_commands = "\\pagestyle{empty}%\n";
my $keep_ps = 0;

# whether the extension of the target file is ps or eps.
my $eps_output = 0;

my $manual_ps2pdf = 0;
my $ps2pdf_options;

my $inputfile = 0;

my $force_special = 0;

# Whether to keep temporary files or not.
my $keep = 0;

my $bbox = "gs";

my $dvips_options = "";
my $dvips_additional_options = "";

my $fig2dev_options = "";

my $pdf = 0;

my $pre_add = "";

# Now versioning using SVN's keywords and the like.
my $svn_url = '$HeadURL: https://fig2ps.svn.sourceforge.net/svnroot/fig2ps/tags/fig2ps-1.5/bin/fig2ps $';
'$Revision: 38 $' =~ /(\d+)/;
my $svn_revision = $1 || 0;

my $version;
if($svn_url =~ /tags\/fig2ps-([^\/]+)/) {
    $version = $1;
} else {
    $version = "svn r$svn_revision";
}

my $banner = << "EOB;" ;
This is fig2ps $version, Copyright (C) 2004-2006, 2008-2010 by Vincent Fourmond

fig2ps comes with ABSOLUTELY NO WARRANTY. This is free software, and
you are welcome to redistribute it under the conditions stated in the
GPL.txt file in the source archive.

EOB;


my $help_text = << "EOH;" ;
Usage: 
fig2ps [-h|--help]\tPrints this help
fig2ps [options] file.fig [[options] file2.fig ...]
Converts file.fig into ps using LaTeX for texts.
    --[no]gv runs or not gv at the end;
    --packages=pack1,pack2,... redefine packages to be used
    --add=pack1,pack2,... supplementary packages to be used
    -k|--keep  whether to keep or not the temporary files
    --bbox=dvips|gs|a,b,c,d method for the bounding box
    --input=file use file as a TeX template (\\input file)
    --dvips=s options to go to dvips
    --fig2dev=s options to go to fig2dev
    --preamble=s add string to the preamble
    --[no]pdf whether fig2ps should produce ps or pdf output
    --eps whether the extension of the target file is eps or ps for postscript
    --keepps when producing pdf, tells to keep the intermediary ps file
    --[no]forcespecial forces every text object to be exported as special, that 
      is processed with LaTeX.

  See the man page for more details.
EOH;




 
###############################################################################
#############################  read config ####################################
###############################################################################




my $sysconfigfile = "/etc/fig2ps/fig2ps.rc";

my %conffilehash = ( 'PACKAGES' => \$packages, 
		     'ADD' => \$packages_add,
		     'DOC_CLASS' => \$doc_class, 
		     'DOC_OPTIONS' => \$doc_option,
		     'FORCE_SPECIAL' => \$force_special, 
		     'INPUT' => \$inputfile, 
		     'GV' => \$gv,
		     'RUNGV' => \$gv,
		     'PREAMBLE' => \$pre_add,
		     'KEEP_PS' => \$keep_ps);


if(-f $sysconfigfile) {
    &readconfig($sysconfigfile,%conffilehash); 
}
else {
    print STDERR "Warning : the system-wide configuration file is missing\n";
}

my $persoconfigfile = $ENV{'HOME'}."/.fig2ps.rc";

if(-f $persoconfigfile) {
    &readconfig($persoconfigfile,%conffilehash); 
}


# We now set a few default values which can be overridden by command-line
# arguments

# first, we choose the output depending on program name:
if ($0 =~ /pdf/ ){ $pdf = 1;}
if ($0 =~ /eps/ ){ $eps_output = 1;}

# second, we disable gv if STDIN is not a terminal

if(! -t STDIN) {
    $gv = 0;
}

GetOptions('help|h' => \$help,
	   'version|V' => sub {
	       print STDERR $banner;
	       exit 0;
	   },
	   'packages=s' => sub {
	       # Reset the added packages as well.
	       $packages_add = "";
	       $packages = $_[1];
	   },
	   'add=s' => sub {
	       if($packages_add) {
		   $packages_add .= "," . $_[1];
	       }
	       else {
		   $packages_add = $_[1];
	       }
	   },
	   'gv!' => \$gv,
	   'viewer=s' => \$viewer,
	   'xpdf' => sub {
	       $viewer = "xpdf";
	   },
	   'keep|k' => \$keep,
	   'keepps' => \$keep_ps,
	   'bbox|b=s' => \$bbox,
	   'input|i=s' => \$inputfile,
	   'dvips=s' =>  \$dvips_additional_options,
	   'fig2dev=s' =>  \$fig2dev_options,
	   'pdf!' => \$pdf,
	   'eps' => \$eps_output,
	   'manual-ps2pdf' => \$manual_ps2pdf,
	   'ps2pdf-options=s' => \$ps2pdf_options,
	   'preamble=s' => sub {
	       $pre_add .= $_[1] . "\n";
	   },
	   'forcespecial!' => \$force_special
	   );

print STDERR $banner;

if ($help) { print $help_text;exit;}
# added 23/04/04, Vincent Fourmond
$pre_beg_commands.= $pre_add;


my @Packages = (split (/,/,$packages));
my @Add =  (split (/,/,$packages_add));

my $header;

# Prepares the $header variable... Ugly.
prepareTex();
 

# modified (Vincent Fourmond 20/10/2004), to account for several
# file conversions in the command line :

if(@ARGV <=0) 
{ 
    print STDERR "No files specified\n\n";
    print STDERR $help_text;
}

# We need to save the current directory;
my $current_directory = &getcwd;

# We create the temporary directory used for temporary files:
my $tmpdir = &tempdir();
END {
    if($tmpdir && -d $tmpdir) {
	if($keep) {
	    print STDERR "Temporary files kept in directory: $tmpdir\n";
	}
	else {
	    &rmtree($tmpdir);
	}
    }
}

# We number the jobs.
my $job = 0;

 MAINLOOP:
    foreach my $file (@ARGV) {
	# We first make sure we are in the original directory:
	chdir $current_directory;
	
	$job++;
	print STDERR "Job $job -- file $file\n";

	if (! (-r $file)) {
	    print STDERR " -> not readable\n";
	    next MAINLOOP;
	}

	$base = sprintf("job-%03d", $job);
	
	# Copying the original to the temporary directory:
	&copy_fig_file($file, "$tmpdir/$base.fig", 
		       {'forcespecial' => $force_special});

	# Now, we cd to the temporary dir:
	chdir $tmpdir;
	my $source_fig = "$base.fig";

	# We now run fig2dev to get the ps part:
	my $fig2dev_ps = "$base.fig2dev.ps";
	if(system "fig2dev -L pstex $fig2dev_options $source_fig > $fig2dev_ps")
	{ 
	    print STDERR " -> Problems with fig2dev: command returned $?";
	    next MAINLOOP;
	}
	

	# We now run fig2dev to get the LaTeX part of the game:
	my $textmp = "$base.tex";
	$command = "fig2dev -L pstex_t $fig2dev_options -p $fig2dev_ps $source_fig |";
	open PSTEX, $command;
	open TEX, "> $textmp";
	
	my $tail = "\\end{document}\n";
	
	print TEX $header;
	
	while(<PSTEX>)
	{
	    print TEX;
	}
	print TEX $tail;
	close PSTEX;
	close TEX;
	
	# we use batchmode so that latex doesn't ask the user on error
	if(system "latex -interaction batchmode $textmp </dev/null")
	{
	    print STDERR " -> Problems with latex: command returned $?";
	    next MAINLOOP;
	}

	# Now, we create a PS file using the right bounding box:
	my $tmpdvi = "$base.dvi";
	my $tmpps = "$base.ps";
	my $intermediate_ps = "$base.dvips.ps";
	&make_PS($tmpdvi, $tmpps, $intermediate_ps) || 
	    next MAINLOOP;

	# Setting up the target names:
	my $psfile = $file;
	if($eps_output) {
	    # Really, this is just a naming convention.
	    $psfile =~ s/(\.fig)?$/.eps/;
	}
	else {
	    $psfile =~ s/(\.fig)?$/.ps/;
	}
	my $pdffile = $file;
	$pdffile =~ s/(\.fig)?$/.pdf/;

	# Now, we copy the file back to its source directory:
	chdir $current_directory;
	copy("$tmpdir/$tmpps", $psfile);

	if($pdf) {
	    &make_PDF($psfile, $pdffile) || next MAINLOOP;
	    unlink $psfile unless $keep_ps;
	}

	if($gv || $viewer) {
	    $viewer ||= "gv";
	    print STDERR "Starting $viewer\n";
	    if($pdf) {
		system($viewer,$pdffile);
	    }
	    else {
		system($viewer,$pdffile);
	    }
	}
    }




# copy_fig_file:
# This function takes :
# * a source fig file
# * a target fig file
# * an optional options hash
#
# It copies the fig file from source to target, mangles and symlinks
# the included images if applicable and modifies the tex file according
# to the options.

sub copy_fig_file {
    my $source = &realpath(shift);
    my $target = &realpath(shift);
    my $options = shift || {};

    my $source_dir = &dirname($source);
    my $target_dir = &dirname($target);
    my $target_name = &basename($target, ".fig");

    my $old_dir = &getcwd();
    # We change into the source file's directory, to make life easier
    chdir $source_dir;
    
    
    open SOURCE, $source;
    open TARGET, "> $target"; 
    
    my $next_is_image = 0;
    my $image_number = 0;
    while(<SOURCE>) {
	# If we force all text to be special and this line
	# is a text line:
	if($options->{'forcespecial'} && /^4 /) # if this is a text
	{
	    my @data = split ' '; # One space exactly, in order not to disturb
				# any text afterwards, where spaces might
				# be significant (although the probability is
				# rather small)...
	    if ($data[8] & 2) {	# Already special
		print TARGET;
	    }
	    else {		# Make is special !
		$data[8] ^= 2;
		print TARGET join ' ', @data;
	    }
	}
	elsif($next_is_image) {
	    $next_is_image = 0;
	    /^(\s+\d+\s+)(.*)/;
	    my $start = $1;
	    my $filename = &realpath($2);
	    # We give a dummy extension; we'll see if XFIG has to
	    # complain about that
	    my $target_image_name = $target_name . "-" . 
		$image_number . ".img";
	    $image_number++;
	    # We symlink the file:
	    symlink $filename, $target_dir . "/" . $target_image_name;
	    # And print the modified line:
	    print TARGET $start.$target_image_name."\n";
	}
	elsif(/^2\s+5/) { 	# Next line will hold an image file.
	    print TARGET;
	    $next_is_image = 1;
	}
	else {
	    print TARGET;
	}
    }
    close SOURCE;
    close TARGET;
}
 


###############################################################################
############################# prepare header ##################################
###############################################################################


sub prepareTex {

    if($inputfile) # use a common style
    {
	my $file = `kpsewhich $inputfile`;
	chomp $file; # we need to remove the trailing \n left by kpsewhich
	open FILE,$file;
	my @lines = <FILE>;
	close FILE;
	# Vincent Fourmond, 03/07/05: corrected bad parentheses.
	if(grep (/\\documentclass/,@lines) > 0) {
	    # we have already document class
	    $header = "\\input{$inputfile}\n";
	}
	else {
	    $header = "\\documentclass[".$doc_option.
		"]{".$doc_class."}\n";
	    $header.= "\\input{$inputfile}\n";
	}

	# adds the add packages, (Teteph...)
	foreach(@Add) {
	    if(/\[(\S+)\](\S+)/)
	    {
		$header .=  "\\usepackage[$1]{$2}\n";
	    }
	    else {
		my @_add = split ':';
		my $pack = pop @_add;
		if(@add> 0)
		{
		    $header.= "\\usepackage[".join(',',@add)."]{$pack}\n";
		}
		else 
		{
		    $header.= "\\usepackage{$_}\n";
		}
		
	    }
	}
	
	# for the use of colors...
	$header.= "\\usepackage{color}\n";
	#if($pdf) {
	#    $header.= "\\usepackage{aeguill}\n";
	#}
	    
    }
    else # builds "by hand" the package list
    {
	$header = "\\documentclass[".$doc_option.
	    "]{".$doc_class."}\n";
	
	foreach(@Packages,@Add) {
	    if(/\[(\S+)\](\S+)/)
	    {
		$header .=  "\\usepackage[$1]{$2}\n";
	    }
	    else {
		my @add = split ':';
		my $pack = pop @add;
		if(@add> 0)
		{
		    $header.= "\\usepackage[".join(',',@add)."]{$pack}\n";
		}
		else 
		{
		    $header.= "\\usepackage{$_}\n";
		}
		
	    }
	}
	
    }
    $header.=  $pre_beg_commands."\n\\begin{document}".$post_beg_commands;
}


###############################################################################
############################# make PS #########################################
###############################################################################


# Note that the files must not have fancy characters, as they are used
# unquoted ! Use only with normal names.
sub make_PS {
    my $dvifile = shift;	# The DVI file on which to work.
    my $psfile = shift;		# The target PS file
    my $tmpps = shift;		# A temporary PS file

    my $current_dvips_options = 
	"$dvips_options $dvips_additional_options";
    if($pdf) {
	# (Vincent Fourmond 31/12/2008) I'm not sure this really
	# is necessary anymore...
	$current_dvips_options = "-Ppdf ".$current_dvips_options;
    }
    if($bbox eq "dvips") {
	# We're using the -E option of dvips to get the bbox
	if(system "dvips $current_dvips_options -E $dvifile -o $psfile") {
	    print STDERR " -> dvips returned error code $?";
	    return 0;
	}
	print STDERR "-> using divps for the bounding box\n";
    }
    else {
	if(system "dvips $current_dvips_options $dvifile -o $tmpps") {
	    print STDERR " -> dvips failed with error code $?";
	    return 0;
	}
	# Now getting the bounding box !
	my ($LLX, $LLY, $URX, $URY);
	if($bbox eq "gs") { # we let gs compute the Bounding box
	    # we specify the paper size to be b0 so that there is no problems
	    # with outbound items
	    $command = "gs -dNOPAUSE -dSAFER -q -sDEVICE=bbox -sPAPERSIZE=b0 ".
		"$tmpps < /dev/null 2>& 1 |";
	    open BBOX, $command;
	    my $found = 0;
	    while(<BBOX>) {
		if(/^%%BoundingBox:/) {
		    s/%%BoundingBox:\s+//;
		    ($LLX, $LLY, $URX, $URY) = split /\s+/;
		    $found = 1;
		}
	    }
	    close BBOX;
	    die "Problems with gs" unless ($found == 1);

	    # TODO: here, check that the BBox is not null.

	    # We're adding 1 point around the bounding box in order to
	    # make the viewing easier.
	    --$LLX; --$LLY; ++$URX; ++$URY;
	}
	# TODO: here, a method based on convert -- faster and more reliable
	# than gs ???
	else {
	    # Manual specification of the bounding box
	    ($LLX, $LLY, $URX, $URY) = split /\s*,\s*/, $bbox ;
	}

	open IN, $tmpps;
	open OUT, "> $psfile";
	my $skipping = 0;
	while (<IN>) {
	    if (/^%%BoundingBox/) {
		print OUT "%%BoundingBox: $LLX $LLY $URX $URY\n";
	    }
	    elsif(/^%%DocumentPaperSizes/) {
		# We skip that, as it probably is the cause of
		# our troubles
	    }
	    elsif(/^%%BeginPaperSize/) {
		$skipping = 1;
		# We also skip everything inbetween a
		# BeginPaperSize/EndPaperSize declaration
	    }
	    elsif(/^%%EndPaperSize/) {
		$skipping = 0;
	    }
	    else {
		print OUT unless $skipping;
	    }
	}
	close OUT;
	close IN;
	print STDERR "Using $LLX $LLY $URX $URY for the bounding box\n";
	# (Vincent Fourmond 4/10/2004) : print the actual size of the image

	# Conversion to centimeters:
	my $factor = (596.0/21.);
	my $width = (-$LLX + $URX)/$factor; 
	my $height = ( - $LLY + $URY)/$factor; 
	print STDERR sprintf(" -> image is %.1fcm wide".
			     " and %.1fcm high\n", 
			     $width, $height);
    }
    return 1;
}

sub make_PDF {
    my $psfile = shift;
    my $pdffile = shift;

    if($manual_ps2pdf || $ps2pdf_options) {
	my $tmpps = "$pdffile.tmpfile.ps";
	if(system("epstopdf", "--nogs", $psfile, "--outfile=$tmpps")) {
	    print STDERR " -> epstopdf returned $?";
	    return 0;
	}
	if(system("ps2pdf ".($ps2pdf_options || "")." '$tmpps' '$pdffile'")) {
	    print STDERR " -> ps2pdf returned $?";
	    return 0;
	}
	unlink $tmpps;
    }
    else {
	if(system("epstopdf",$psfile, "--outfile=$pdffile")) {
	    print STDERR " -> epstopdf returned $?";
	    return 0;
	}
    }
    return 1;
}


###############################################################################
############################ read config files ################################
###############################################################################


sub readconfig {
    my $file = shift @_;
    my %options = @_;

    open CONFIG, "$file";
    my $line = 0;
    while(<CONFIG>)
    {
	$line ++;
	while( /\\$/)
	{
	    chop;
	    chop;
	    $_.=<CONFIG>;
	}
	if ((!/^\#.*$/) && (!/^\s*$/) )
	{
	    (my $name, my $vals) = /(\S*)\s*=\s*(\S*)$/;
	    if((grep /$name/,keys(%options)) > 0)
	    {
		${$options{$name}} = $vals;
	    }
	    else {
		print "Warning : line ".$line." of file ".
		    $file." not understood\n";
	    }
	}
    }
    close CONFIG;
}
