#!/usr/bin/perl
=head1 NAME
	
	docbook2odf - DocBook to OpenDocument XSL Transformation utils
	Copyright (C) 2006 Roman Fordinal
	http://open.comsultia.com/docbook2odf/
	
=cut

=head1 LICENSE
	
	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.
	
=cut

use strict;
use utf8;
use encoding 'utf-8';
use open ':utf8', ':std';

# depends on
use Cwd;
use File::Copy;
use File::Path;
use Image::Magick;
use XML::Sablotron;
#use XML::XPath;
#use XML::XPath::XMLParser;
use Getopt::Long;
use Archive::Zip qw( :ERROR_CODES :CONSTANTS );


##################################################################################
# INITIALIZATION
##################################################################################



# initial variables
our $PATH=Cwd::abs_path();
our $PATH_INSTALL="/usr/share/docbook2odf/xsl"; # not final
our $PATH_XSL = do
{
	(-e $PATH.'/../xsl/docbook.xsl') ? $PATH.'/../xsl' :
	$PATH_INSTALL
};
my $input;
my $output;
my $output_dir;
my $help;
my $quiet;
my $debug;
my $params;
my $force;

our $program_Date='$Date: 2006-10-27 12:52:03 +0200 (Fri, 27 Oct 2006) $';
our $program_Rev='$Rev: 210 $';
our $program_Author='$Author: fordinal $';
our $program_Id='$Id: docbook2odf 210 2006-10-27 10:52:03Z fordinal $';

	$program_Rev=~/(\d+)/;
my $program_version="0.".$1;
my $program_name="docbook2odf ".$program_version;
my $program_description="a non-interactive docbook to opendocument convertor";
my $program_usage="[--input-file docbookfile] [--output-file opendocumentfile]";# [--params]";

my $result = GetOptions
	(
		"input-file=s"   => \$input,
		"output-file=s"  => \$output,
		"output-dir=s"   => \$output_dir,
		"params=s"       => \$params,
		"xsl-file=s"     => \$PATH_XSL,
		"debug"          => \$debug,
		"quiet"          => \$quiet,
		"help"           => \$help,
		"force"          => \$force,
	);


if ($help)
{
	print "$program_name, $program_description\n";
	print "Usage: docbook2odf $program_usage\n";
	print "\n";
	print <<"HELP";
Arguments:
  --input-file        specify input docbook filename.
  --output-file       specify output opendocument filename.
  --output-dir        specify output directory.
  --params            list of params (disabled).
  --xsl-file          use this xsl stylesheet instead.
  --debug             show debug messages.
  --quiet             quiet (no output).
  --help              print this help.
  --force             overwrite existing output filename.
HELP
	exit;
}

if (!$input)
{
	print "$program_name, $program_description\n";
	print "Usage: docbook2odf $program_usage\n";
	print "Try `docbook2odf --help` for more information\n";
	exit;
}


my $output_file=$output;
if (!$output)
{
	$output_file=$input;
	# if I run this script from commandline
	# the output filename is in current workdir
	# otherwise in directory of input filename (docbook)
	if ($ENV{'TERM'} && !$output_dir)
	{
		$output_file=~s|^.*/||;
		$output_file='./'.$output_file;
	}
	elsif ($output_dir)
	{
		$output_file=~s|^.*/||;
	}
	else
	{
		# output directory is in input file directory
	}
	$output_file=~s/\.(docbook|db|xml)$//;
	$output_file.=".od";
}
if ($output_dir)
{
	$output_dir=~s|/$||;
}
elsif ($output_file=~s|^(.*/)||)
{
	$output_dir=$1;
	$output_dir=~s|/$||;
}
else
{
	$output_dir='.';
}

my $input_file=$input;
my $input_dir;
if ($input_file=~s|^(.*/)||)
{
	$input_dir=$1;
	$input_dir=~s|/$||;
}
else
{
	$input_dir='.';
}


# program information
if (!$quiet)
{
	print "$program_name, $program_description\n";
}

# input / output files
if (!$quiet)
{
	print "\n";
	print "input file:   \"$input\"\n";
	print "output file:  \"$output_dir/$output_file?\"\n";
	print "stylesheets:  \"$PATH_XSL\"\n";
}

#print "\n";
#print "params:       \"$params\"\n";
#my %param;
#foreach my $pair(split(''))


##################################################################################
# TEMPORARY DIRECTORY
##################################################################################

print "\n" unless $quiet;

# create a temporary directory
#my $TEMP=$output_dir.'/'.$output_file.'.temp';
my $TEMP='/tmp/docbook2odf-'.$$.'-'.$output_file.'.tmp';
print "Creating TEMP directory ($TEMP)\n" if $debug;
rmtree $TEMP if -e $TEMP; # delete TEMP directory if exists
mkpath $TEMP;
mkpath $TEMP.'/Pictures';
mkpath $TEMP.'/META-INF';
mkpath $TEMP.'/process';

##################################################################################
# PREPROCESSING
##################################################################################

# works not with xinclude :(
=head1
print "\n" unless $quiet;

my $xp = XML::XPath->new(filename => $input);
my $nodeset = $xp->find('//imagedata/@fileref');
foreach my $node ($nodeset->get_nodelist)
{
	print "FOUND\n";
}
=cut

##################################################################################
# TRANSFORMATION
##################################################################################

# transformation
print "\n" unless $quiet;


print "Sablotron transformation\n" if $debug;
my $sab = new XML::Sablotron();
my $situa = new XML::Sablotron::Situation();
$sab->process($situa, $PATH_XSL.'/docbook.xsl', $input, $TEMP.'/process/full.xml');


# MIMETYPE
open (HND, '>'.$TEMP.'/mimetype');
print HND 'application/vnd.oasis.opendocument.text';
close HND;


# META
my $sab = new XML::Sablotron();
my $situa = new XML::Sablotron::Situation();
$sab->addParam($situa, 'part', 'meta');
$sab->process($situa, $PATH_XSL.'/odf.xsl', $TEMP.'/process/full.xml', 'arg:/output');
open (HND, '>'.$TEMP.'/meta.xml');
print HND $sab->getResultArg('arg:/output');


# STYLES
my $sab = new XML::Sablotron();
my $situa = new XML::Sablotron::Situation();
$sab->addParam($situa, 'part', 'styles');
$sab->process($situa, $PATH_XSL.'/odf.xsl', $TEMP.'/process/full.xml', 'arg:/output');
open (HND, '>'.$TEMP.'/styles.xml');
binmode(HND);
print HND $sab->getResultArg('arg:/output');


# CONTENT
my $sab = new XML::Sablotron();
my $situa = new XML::Sablotron::Situation();
$sab->addParam($situa, 'part', 'content');
$sab->process($situa, $PATH_XSL.'/odf.xsl', $TEMP.'/process/full.xml', 'arg:/output');
my $content=$sab->getResultArg('arg:/output');


utf8::decode($content);
if ($debug)
{
	open (HND, '>'.$TEMP.'/process/content.xml');
	print HND $content;
}

print "\n" if $debug;

do # post processing of content
{
	print "content postprocess\n" if $debug;
	# copy pictures into TEMP directory
	
	my @uris;
	my $i=1;
	while ($content=~s|<([\w:]+)([^<]*?)(xlink:href)="(.*?)"|<$1$2xlink:href=<!TMPHREF-$i!>|)
	{
		my $tag=$1;
		my $oth=$2;
		my $href=$3;
		my $uri=$4;
		
		print "-postprocessing $href\[$i]='$uri' in tag '$tag'\n" if $debug;
		
		if ($tag ne "draw:image")
		{
			$uris[$i]=$4;
			$i++;
			next;
		}
		
		my $ext=$uri;$ext=~s|^.*\.||;
		if ($uri=~/^\//)
		{
			# uri processing
		}
		else
		{
			# uri processing
			$uri=$input_dir."/".$uri;
		}
		
		my $filename=sprintf("%07d",$i);
		$uris[$i]='Pictures/'.$filename.".".$ext;
		my $dest=$TEMP.'/Pictures/'.$filename.'.'.$ext;
		print "-copy '$uri'->'$dest'\n" if $debug;
		copy($uri,$dest);
		$i++;
	}
	$content=~s|<!TMPHREF-(\d+)!>|"$uris[$1]"|g;
	
	while($content=~s|function:([\w:\-]+):\((.*?)\)|<!TMP!>|)
	{
		my $function=$1;
		my $data=$2;
		print "function='$function' data='$data'\n" if $debug;
		if ($function eq "getimage-width")
		{
			my $p = new Image::Magick;
			$data=$input_dir."/".$data unless $data=~/^\//;
			$p->Read($data);
			my $width=($p->Get('columns')*0.02644)."cm";
			print "output='$width'\n" if $debug;
			$content=~s|<!TMP!>|$width|;
			next;
		}
		if ($function eq "getimage-height")
		{
			my $p = new Image::Magick;
			$data=$input_dir."/".$data unless $data=~/^\//;
			$p->Read($data);
			my $height=($p->Get('height')*0.02644)."cm";
			print "output='$height'\n" if $debug;
			$content=~s|<!TMP!>|$height|;
			next;
		}
		#751mm=284px*2.644 196mm=74px
		$content=~s|<!TMP!>||;
	}
	
	# convert alternative nbsp character to ODF spaces
	$content=~s|([\xC2\x82]+)|'<text:s text:c="'.length($1).'"/>'|eg;
};
print "\n" if $debug;

open (HND, '>'.$TEMP.'/content.xml');
#binmode(HND);
print HND $content;

# MANIFEST
my $sab = new XML::Sablotron();
my $situa = new XML::Sablotron::Situation();
$sab->addParam($situa, 'part', 'manifest');
$sab->process($situa, $PATH_XSL.'/odf.xsl', $TEMP.'/process/full.xml', $TEMP.'/META-INF/manifest.xml');



# when --output-file is not defined
# then I run autodetection of document type
$output_file.=do
{
	($content=~/<office:text/) ? 't' :
	($content=~/<office:presentation/) ? 'p' :
	($content=~/<office:spread/) ? 's' :
	'm'
} unless $output;

if (-e $output_dir.'/'.$output_file && !$force)
{
	rmtree $TEMP;
	die "file $output_dir/$output_file exists\n";
}

if (!$debug)
{
	rmtree $TEMP.'/process';
}

# zipping directory
print "zipping directory '$TEMP' (PWD='$PATH')\n" if $debug;
my $zip = Archive::Zip->new();
$zip->addTree($TEMP);

print "\n" if $debug;

print "saving zipped content into file '$output_dir/$output_file'\n" unless $quiet;
$zip->writeToFileNamed( $output_dir.'/'.$output_file );

if (!$debug)
{
	# delete temporary directory
	print "delete temporary directory='$TEMP' (PWD='$PATH')\n" if $debug;
	chdir '..';
	rmtree $TEMP;
}

1;
