#+##############################################################################
#
# T2h_l2h.pm: interface to LaTeX2HTML
#
# Copyright (C) 1999, 2000, 2003, 2005, 2006, 2009 Free Software Foundation, Inc.
#
# 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 .
#
# This code was taken from the main texi2html file in 2006.
# Certainly originally written by Olaf Bachmann.
#
#-##############################################################################
require 5.0;
use strict;
package Texi2HTML::LaTeX2HTML;
use Cwd;
# latex2html conversions consist of three stages:
# 1) to latex: Put "latex" code into a latex file
# (init, to_latex, finish_to_latex)
# 2) to html: Use latex2html to generate corresponding html code and images
# (to_html)
# 3) from html: Extract generated code and images from latex2html run
# (init_from_html, do_tex)
# init l2h defaults for files and names
# global variable used for caching
# FIXME there is no reason for this variable to be global
use vars qw(
%l2h_cache
);
my ($l2h_name, $l2h_latex_file, $l2h_cache_file, $l2h_html_file, $l2h_prefix);
# holds the status of latex2html operations. If 0 it means that there was
# an error
my $status = 0;
my $debug;
my $verbose;
my $docu_rdir;
my $docu_name;
# init_from_html
my $extract_error_count;
my $invalid_counter_count;
# change_image_file_names
my %l2h_img; # associate src file to destination file
# such that files are not copied twice
my $image_count;
# do_tex
my $html_output_count = 0; # html text outputed in html result file
##########################
#
# First stage: Generation of Latex file
# Initialize with: init
# Add content with: to_latex ($text) --> HTML placeholder comment
# Finish with: finish_to_latex
#
my $l2h_latex_preamble = <{'top'}});
$docu_name = $Texi2HTML::THISDOC{'file_base_name'};
$docu_rdir = $Texi2HTML::THISDOC{'destination_directory'};
$docu_rdir = '' if (!defined($docu_rdir));
$l2h_name = "${docu_name}_l2h";
$l2h_latex_file = "$docu_rdir${l2h_name}.tex";
$l2h_cache_file = "${docu_rdir}${docu_name}-l2h_cache.pm";
# destination dir -- generated images are put there, should be the same
# as dir of enclosing html document --
$l2h_html_file = "$docu_rdir${l2h_name}.html";
$l2h_prefix = "${l2h_name}_";
$debug = $Texi2HTML::THISDOC{'debug_l2h'};
$verbose = Texi2HTML::Config::get_conf('VERBOSE');
unless (Texi2HTML::Config::get_conf('L2H_SKIP'))
{
unless (open(L2H_LATEX, ">$l2h_latex_file"))
{
main::document_error ("l2h: Can't open latex file '$l2h_latex_file' for writing: $!");
$status = 0;
return;
}
warn "# l2h: use ${l2h_latex_file} as latex file\n" if ($verbose);
print L2H_LATEX $l2h_latex_preamble;
}
# open the database that holds cached text
init_cache() if (!defined(Texi2HTML::Config::get_conf('L2H_SKIP')) or Texi2HTML::Config::get_conf('L2H_SKIP'));
$status = 1;
}
# print text (2nd arg) into latex file (if not already there nor in cache)
# which can be later on replaced by the latex2html generated text.
#
sub to_latex($$$)
{
my $command = shift;
my $text = shift;
my $counter = shift;
return unless ($status);
if ($command eq 'tex')
{
$text .= ' ';
}
elsif ($command eq 'math')
{
$text = "\$".$text."\$";
}
$to_latex_count++;
$text =~ s/(\s*)$//;
# try whether we have text already on things to do
my $count = $l2h_to_latex{$text};
unless ($count)
{
$latex_count++;
$count = $latex_count;
# try whether we can get it from cache
my $cached_text = from_cache($text);
if (defined($cached_text))
{
$cached_count++;
# put the cached result in the html result array
$l2h_from_html[$count] = $cached_text;
}
else
{
$latex_converted_count++;
unless (Texi2HTML::Config::get_conf('L2H_SKIP'))
{
print L2H_LATEX "\\begin{rawhtml}\n\n";
print L2H_LATEX "\n";
print L2H_LATEX "\\end{rawhtml}\n";
print L2H_LATEX "$text\n";
print L2H_LATEX "\\begin{rawhtml}\n";
print L2H_LATEX "\n\n";
print L2H_LATEX "\\end{rawhtml}\n";
}
}
$l2h_to_latex[$count] = $text;
$l2h_to_latex{$text} = $count;
}
$global_count{"${command}_$counter"} = $count;
return 1;
}
# print closing into latex file and close it
sub finish_to_latex()
{
my $reused = $to_latex_count - $latex_converted_count - $cached_count;
unless (Texi2HTML::Config::get_conf('L2H_SKIP'))
{
print L2H_LATEX $l2h_latex_closing;
close (L2H_LATEX);
}
warn "# l2h: finished to latex ($cached_count cached, $reused reused, $latex_converted_count to process)\n" if ($verbose);
unless ($latex_count)
{
# no @tex nor @math
finish();
return 0;
}
return 1;
}
###################################
# Second stage: Use latex2html to generate corresponding html code and images
#
# to_html([$l2h_latex_file, [$l2h_html_dir]]):
# Call latex2html on $l2h_latex_file
# Put images (prefixed with $l2h_name."_") and html file(s) in $l2h_html_dir
# Return 1, on success
# 0, otherwise
#
sub to_html()
{
my ($call, $dotbug);
# when there are no tex constructs to convert (happens in case everything
# comes from the cache), there is no latex2html run
if (Texi2HTML::Config::get_conf('L2H_SKIP') or ($latex_converted_count == 0))
{
warn "# l2h: skipping latex2html run\n" if ($verbose);
return 1;
}
# Check for dot in directory where dvips will work
if (Texi2HTML::Config::get_conf('L2H_TMP'))
{
if (Texi2HTML::Config::get_conf('L2H_TMP') =~ /\./)
{
main::document_warn ("l2h: l2h_tmp dir contains a dot.");
$dotbug = 1;
}
}
else
{
if (cwd() =~ /\./)
{
main::document_warn ("l2h: current dir contains a dot.");
$dotbug = 1;
}
}
# fix it, if necessary and hope that it works
#$Texi2HTML::Config::L2H_TMP = "/tmp" if ($dotbug);
return 0 if ($dotbug);
$call = Texi2HTML::Config::get_conf('L2H_L2H');
# use init file, if specified
#my $init_file = main::locate_init_file(Texi2HTML::Config::get_conf('L2H_FILE'));
my $init_file = Texi2HTML::Config::get_conf('L2H_FILE');
$call = $call . " -init_file " . $init_file if (defined($init_file) and $init_file ne '' and -f $init_file and -r $init_file);
# set output dir
$call .= (($docu_rdir ne '') ? " -dir $docu_rdir" : " -no_subdir");
# use l2h_tmp, if specified
$call .= " -tmp ".Texi2HTML::Config::get_conf('L2H_TMP')
if (defined(Texi2HTML::Config::get_conf('L2H_TMP')) and Texi2HTML::Config::get_conf('L2H_TMP') ne '');
# use a given html version if specified
$call .= " -html_version ".Texi2HTML::Config::get_conf('L2H_HTML_VERSION')
if (defined(Texi2HTML::Config::get_conf('L2H_HTML_VERSION')) and Texi2HTML::Config::get_conf('L2H_HTML_VERSION') ne '');
# options we want to be sure of
$call .= " -address 0 -info 0 -split 0 -no_navigation -no_auto_link";
$call .= " -prefix $l2h_prefix $l2h_latex_file";
warn "# l2h: executing '$call'\n" if ($verbose);
if (system($call))
{
main::document_error ("l2h: '${call}' did not succeed");
return 0;
}
else
{
warn "# l2h: latex2html finished successfully\n" if ($verbose);
return 1;
}
}
##########################
# Third stage: Extract generated contents from latex2html run
# Initialize with: init_from_html
# open $l2h_html_file for reading
# reads in contents into array indexed by numbers
# return 1, on success -- 0, otherwise
# Finish with: finish
# closes $l2h_html_dir/$l2h_name.".$docu_ext"
# the images generated by latex2html have names like ${docu_name}_l2h_img?.png
# they are copied to ${docu_name}_?.png, and html is changed accordingly.
# FIXME is it really necessary to bother doing that? Looks like an unneeded
# complication to me (pertusus, 2009), and it could go bad if there is some
# SRC="(.*?)" in the text (though the regexp could be made more specific).
# %l2h_img; # associate src file to destination file
# such that files are not copied twice
sub change_image_file_names($)
{
my $content = shift;
my @images = ($content =~ /SRC="(.*?)"/g);
my ($src, $dest);
for $src (@images)
{
$dest = $l2h_img{$src};
unless ($dest)
{
my $ext = '';
if ($src =~ /.*\.(.*)$/ and (!defined(Texi2HTML::Config::get_conf('EXTENSION')) or $1 ne Texi2HTML::Config::get_conf('EXTENSION')))
{
$ext = ".$1";
}
else
{ # A warning when the image extension is the same than the
# document extension. copying the file could result in
# overwriting an output file (almost surely if the default
# texi2html file names are used).
main::document_warn ("L2h image $src has invalid extension");
next;
}
while (-e "$docu_rdir${docu_name}_${image_count}$ext")
{
$image_count++;
}
$dest = "${docu_name}_${image_count}$ext";
if ($debug)
{
# not portable, but only used with debug.
system("cp -f $docu_rdir$src $docu_rdir$dest");
}
else
{
# FIXME error condition not checked.
rename ("$docu_rdir$src", "$docu_rdir$dest");
}
$l2h_img{$src} = $dest;
#unlink "$docu_rdir$src" unless ($debug);
}
$content =~ s/SRC="$src"/SRC="$dest"/g;
}
return $content;
}
sub init_from_html()
{
# when there are no tex constructs to convert (happens in case everything
# comes from the cache), the html file that was generated by previous
# latex2html runs isn't reused.
if ($latex_converted_count == 0)
{
return 1;
}
if (! open(L2H_HTML, "<$l2h_html_file"))
{
main::document_warn ("l2h: Can't open $l2h_html_file for reading");
return 0;
}
warn "# l2h: use $l2h_html_file as html file\n" if ($verbose);
my $html_converted_count = 0; # number of html resulting texts
# retrieved in the file
my ($count, $h_line);
while ($h_line = )
{
if ($h_line =~ /!-- l2h_begin $l2h_name ([0-9]+) --/)
{
$count = $1;
my $h_content = '';
my $h_end_found = 0;
while ($h_line = )
{
if ($h_line =~ /!-- l2h_end $l2h_name $count --/)
{
$h_end_found = 1;
chomp $h_content;
chomp $h_content;
$html_converted_count++;
# transform image file names and copy image files
$h_content = change_image_file_names($h_content);
# store result in the html result array
$l2h_from_html[$count] = $h_content;
# also add the result in cache hash
$l2h_cache{$l2h_to_latex[$count]} = $h_content;
last;
}
$h_content = $h_content.$h_line;
}
unless ($h_end_found)
{ # couldn't found the closing comment. Certainly a bug.
main::msg_debug ("l2h: l2h_end $l2h_name $count not found");
close(L2H_HTML);
return 0;
}
}
}
# Not the same number of converted elements and retrieved elements
if ($latex_converted_count != $html_converted_count)
{
main::msg_debug ("l2h: waiting for $latex_converted_count elements found $html_converted_count");
}
warn "# l2h: Got $html_converted_count of $latex_count html contents\n"
if ($verbose);
close(L2H_HTML);
return 1;
}
# $html_output_count = 0; # html text outputed in html result file
# called each time a construct handled by latex2html is encountered, should
# output the corresponding html
sub do_tex($$$$)
{
my $style = shift;
my $counter = shift;
my $state = shift;
return unless ($status);
my $count = $global_count{"${style}_$counter"};
################################## begin debug section (incorrect counts)
if (!defined($count))
{
# counter is undefined
$invalid_counter_count++;
main::msg_debug ("l2h: undefined count for ${style}_$counter");
return ("")
if ($debug);
return '';
}
elsif(($count <= 0) or ($count > $latex_count))
{
# counter out of range
$invalid_counter_count++;
main::msg_debug ("l2h: Request of $count content which is out of valide range [0,$latex_count)");
return ("")
if ($debug);
return '';
}
################################## end debug section (incorrect counts)
# this seems to be a valid counter
my $result = '';
$result = "" if ($debug);
if (defined($l2h_from_html[$count]))
{
$html_output_count++;
# maybe we could also have something if simple_format
# with Texi2HTML::Config::protect_text in case there
# was some @math on a line passed through simple_format.
# This would certainly be illegal texinfo, however.
if ($state->{'remove_texi'})
{# don't protect anything
$result .= $l2h_to_latex[$count];
}
else
{
$result .= $l2h_from_html[$count];
}
}
else
{
# if the result is not in @l2h_from_html, there is an error somewhere.
$extract_error_count++;
main::msg_debug ("l2h: can't extract content $count from html");
# try simple (ordinary) substitution (without l2h)
$result .= "" if ($debug);
$result .= main::substitute_text({}, undef, 'error in l2h', $l2h_to_latex[$count]);
}
$result .= "" if ($debug);
return $result;
}
# store results in the cache and remove temporary files.
sub finish()
{
return unless($status);
if ($verbose)
{
if ($extract_error_count + $invalid_counter_count)
{
warn "# l2h: finished from html ($extract_error_count extract and $invalid_counter_count invalid counter errors)\n";
}
else
{
warn "# l2h: finished from html (no error)\n";
}
if ($html_output_count != $latex_converted_count)
{ # this may happen if @-commands are collected at some places
# but @-command at those places are not expanded later. For
# example @math on @multitable lines.
warn "# l2h: $html_output_count html outputed for $latex_converted_count converted\n";
}
}
store_cache();
if (Texi2HTML::Config::get_conf('L2H_CLEAN'))
{
local ($_);
warn "# l2h: removing temporary files generated by l2h extension\n"
if $verbose;
while (<"$docu_rdir$l2h_name"*>)
{
# FIXME error condition not checked
unlink $_;
}
}
warn "# l2h: Finished\n" if $verbose;
return 1;
}
# the driver of end of first pass and second pass
#
sub latex2html()
{
return unless($status);
return unless ($status = finish_to_latex());
return unless ($status = to_html());
}
##############################
# stuff for l2h caching
#
# FIXME it is clear that l2h stuff takes very long compared with texi2html
# which is already quite long. However this also adds some complexity
# I tried doing this with a dbm data base, but it did not store all
# keys/values. Hence, I did as latex2html does it
sub init_cache
{
if (-r "$l2h_cache_file")
{
my $rdo = do "$l2h_cache_file";
main::document_error ("l2h: could not load $docu_rdir$l2h_cache_file: $@")
unless ($rdo);
}
}
# store all the text obtained through latex2html
sub store_cache
{
return unless $latex_count;
my ($key, $value);
unless (open(FH, ">$l2h_cache_file"))
{
main::document_error ("l2h: could not open $docu_rdir$l2h_cache_file for writing: $!");
return;
}
while (($key, $value) = each %l2h_cache)
{
# escape stuff
$key =~ s|/|\\/|g;
$key =~ s|\\\\/|\\/|g;
# weird, a \ at the end of the key results in an error
# maybe this also broke the dbm database stuff
$key =~ s|\\$|\\\\|;
$value =~ s/\|/\\\|/go;
$value =~ s/\\\\\|/\\\|/go;
$value =~ s|\\\\|\\\\\\\\|g;
print FH "\n\$l2h_cache_key = q/$key/;\n";
print FH "\$l2h_cache{\$l2h_cache_key} = q|$value|;\n";
}
print FH "1;";
close (FH);
}
# return cached html, if it exists for text, and if all pictures
# are there, as well
sub from_cache($)
{
my $text = shift;
my $cached = $l2h_cache{$text};
if (defined($cached))
{
while ($cached =~ m/SRC="(.*?)"/g)
{
unless (-e "$docu_rdir$1")
{
return undef;
}
}
return $cached;
}
return undef;
}
1;