#!/usr/bin/perl =head1 NAME docbook2odf - DocBook to OpenDocument XSL Transformation utils Copyright (C) 2006 Roman Fordinal http://open.comsultia.com/docbook2odf/ =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 Getopt::Long; # variable depends my $USE_IMAGE_MAGICK = 0; if (eval "require Image::Magick") { $USE_IMAGE_MAGICK = 1; } my $USE_SABLOTRON = 0; my $USE_LIBXSLT = 0; if (eval "require XML::Sablotron;") { $USE_SABLOTRON = 1; } elsif (eval "require XML::LibXSLT;") { $USE_LIBXSLT = 1; } my $USE_ZIP = 0; if (eval "require Archive::Zip qw( :ERROR_CODES :CONSTANTS )") { $USE_ZIP = 1; } ################################################################ # 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, $output, $output_dir); my ($help, $quiet, $verbose, $debug); my ($params, $force); our $program_Date='$Date$'; our $program_Rev='$Rev$'; our $program_Author='$Author$'; our $program_Id='$Id$'; $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="docbookfile [-o opendocumentfile]";# [--params]"; my $result = GetOptions ( "o|output-file=s" => \$output, "output-dir=s" => \$output_dir, "params=s" => \$params, "xsl-file=s" => \$PATH_XSL, "debug" => \$debug, "quiet" => \$quiet, "verbose" => \$verbose, "help" => \$help, "f|force" => \$force, ); my $input = $ARGV[0]; if ($help) { print "$program_name, $program_description\n"; print "Usage: docbook2odf $program_usage\n"; print "\n"; print <<"HELP"; Arguments: -o|--output-file specify output opendocument filename. --output-dir specify output directory. --params list of params ( var=value,var2=value2 ). --xsl-file use this xsl stylesheet instead. --debug show debug messages. -q|--quiet quiet (no output). -v|--verbose verbose (extra output). -h|--help print this help. -f|--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; } ################################################################################## # START ################################################################################## 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=$PATH.'/'.$output_file; } elsif ($output_dir) { $output_file=~s|^.*/||; } else { # output directory is in input file directory } $output_file=~s/\.(docbook|db|doc|xml)$//; $output_file.=".od"; } if ($output_dir) { $output_dir=~s|/$||; } elsif ($output_file=~s|^(.*/)||) { $output_dir=$1; $output_dir=~s|/$||; } else { $output_dir=$PATH; } my $input_file=$input; my $input_dir; if ($input_file=~s|^(.*/)||) { $input_dir=$1; $input_dir=~s|/$||; } else { $input_dir='.'; } # program information if ($verbose) { print "$program_name, $program_description\n"; } # input / output files if ($verbose) { print "\n"; print "input file: \"$input\"\n"; print "output file: \"$output_dir/$output_file?\"\n"; print "stylesheets: \"$PATH_XSL\"\n"; } ################################################################################## # TEMPORARY DIRECTORY ################################################################################## # 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'; ################################################################################## # TRANSFORMATION ################################################################################## print "XSL transformation\n" if $debug; # DOCBOOK -> ODF (one big xml) # parse params; my @params_arr; foreach my $param(split(',',$params)) { foreach (split('=',$param)) { push @params_arr,$_; } } my $XML_DOC = $input; my $XSL = $PATH_XSL.'/docbook.xsl'; open (HND, '>'.$TEMP.'/process/full.xml'); print HND xml_process($XSL, $XML_DOC,@params_arr); # MIMETYPE open (HND, '>'.$TEMP.'/mimetype'); print HND 'application/vnd.oasis.opendocument.text'; close HND; ################################################################################## # SPLIT ################################################################################## $XML_DOC = $TEMP.'/process/full.xml'; $XSL = $PATH_XSL.'/odf.xsl'; # MANIFEST open (HND, '>'.$TEMP.'/META-INF/manifest.xml'); print HND xml_process($XSL, $XML_DOC, 'part'=>'manifest'); # META open (HND, '>'.$TEMP.'/meta.xml'); print HND xml_process($XSL, $XML_DOC, 'part'=>'meta'); # STYLES open (HND, '>'.$TEMP.'/styles.xml'); binmode(HND); print HND xml_process($XSL, $XML_DOC, 'part'=>'styles'); # CONTENT my $content = xml_process($XSL, $XML_DOC, 'part'=>'content'); #utf8::encode($content); if ($debug) { open (HND, '>'.$TEMP.'/process/content.xml'); binmode(HND); print HND $content; } print "\n" if $debug; ################################################################################## # POSTPROCESSING ################################################################################## 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=|) { 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||"$uris[$1]"|g; while($content=~s|function:([\w:\-]+):\((.*?)\)||) { my $function=$1; my $data=$2; print "function='$function' data='$data'\n" if $debug; if ($USE_IMAGE_MAGICK) { 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||$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||$height|; next; } } elsif ($function eq "getimage-width" || $function eq "getimage-height") { $data=$input_dir."/".$data unless $data=~/^\//; my ($width, $height) = img_dimmensions($data); print "output='$width'\n" if $debug; print "output='$height'\n" if $debug; ($function=~/width/) and $content=~s||$width|; ($function=~/height/) and $content=~s||$height|; next; } #751mm=284px*2.644 196mm=74px $content=~s|||; } # convert alternative nbsp character to ODF spaces $content=~s|(\xC2\x82)|''|eg; }; print "\n" if $debug; open (HND, '>'.$TEMP.'/content.xml'); binmode(HND); print HND $content; ################################################################################## # ZIPPING ################################################################################## # when --output-file is not defined # then I run autodetection of document type $output_file.=do { ($content=~/new(); $zip->addTree($TEMP); $zip->writeToFileNamed($output_dir.'/'.$output_file); } else { print "using zip command\n" if $debug; chdir($TEMP); my $out=`zip -rq "$output_dir/$output_file" *`; } print "\n" if $debug; print "Saved $output_file\n" unless $quiet; ################################################################################## # CLEANING ################################################################################## if (!$debug) { # delete temporary directory print "delete temporary directory='$TEMP' (PWD='$PATH')\n" if $debug; chdir '..'; rmtree $TEMP; } ################################################################################## # FUNCTIONS ################################################################################## sub img_dimmensions { my $imgfile = shift; my $tmpdir = "/tmp/docbook2odf-$$-".int(100*rand()); my $ext = ''; mkdir($tmpdir); ($imgfile =~ /\.(\w+)$/) and $ext = $1; # Copy to make sure the file name is reasonable. copy($imgfile,"$tmpdir/img-file.$ext") || print "can't copy file\n"; $imgfile = "$tmpdir/img-file.$ext"; # Convert to PNG. #chdir($tmpdir); if ($ext eq "gif") { `gif2png -O $imgfile`; } elsif ($ext ne "png") { `anytopnm $imgfile 2> /dev/null| pnmtopng > $tmpdir/img-file.png`; } $imgfile = "$tmpdir/img-file.png"; # Get the image dimmensions. my $data = `file $imgfile`; rmtree($tmpdir); ($data =~ /PNG image data, (\d+) x (\d+)/) and return (($1*0.02644)."cm", ($2*0.02644)."cm"); return ("3cm","3cm"); } sub xml_process { my $XSL = shift; my $XML_DOC = shift; print "param = @_\n" if $debug; if ($USE_SABLOTRON) { print "xslt by sablotron\n" if $debug; my $sab = new XML::Sablotron(); my $situa = new XML::Sablotron::Situation(); while (@_) { my $name = shift; my $val = shift; $sab->addParam($situa, $name, $val); } $sab->process($situa, $XSL, $XML_DOC, 'arg:/output'); return $sab->getResultArg('arg:/output'); } elsif ($USE_LIBXSLT) { print "xslt by libxslt\n" if $debug; my $xslt = XML::LibXSLT->new(); my $stylesheet = $xslt->parse_stylesheet_file($XSL); my @params; while (@_) { my $name = shift; my $val = shift; push @params, $name; push @params, $val; } my $results = $stylesheet->transform_file($XML_DOC, XML::LibXSLT::xpath_to_string(@params)); return $stylesheet->output_string($results); } else { print "xslt by xsltproc\n" if $debug; my $PARAM = ''; while (@_) { my $name = shift; my $val = shift; $PARAM .= " --stringparam $name $val "; } $XML_DOC=~s| |\\ |g; # my $cmd="xsltproc --xinclude $PARAM $XSL $XML_DOC"; my $cmd="xsltproc --xinclude $PARAM $XSL $XML_DOC"; print "$cmd\n" if $debug; return `$cmd`; } } 1;