# -*-perl-*- #+############################################################################## # # tex4ht.pm: use tex4ht to convert tex to html # # Copyright 2005, 2007, 2009, 2011, 2012, 2013 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 . # # Originally written by Patrice Dumas. # #-############################################################################## # To customize the command and the options, you could set # $Texinfo::TeX4HT::STYLE_MATH to latex/tex # $Texinfo::TeX4HT::STYLE_TEX to latex/texi # and/or change # $Texinfo::TeX4HT::tex4ht_command_math # and $Texinfo::TeX4HT::tex4ht_options_math # $Texinfo::TeX4HT::tex4ht_command_tex # and $Texinfo::TeX4HT::tex4ht_options_tex use strict; my $global_cmds = get_conf('GLOBAL_COMMANDS'); if (!defined($global_cmds)) { set_from_init_file('GLOBAL_COMMANDS', []); $global_cmds = get_conf('GLOBAL_COMMANDS'); } push @$global_cmds, ('math', 'tex'); texinfo_register_handler('structure', \&tex4ht_prepare); texinfo_register_handler('init', \&tex4ht_convert); texinfo_register_handler('finish', \&tex4ht_finish); texinfo_register_command_formatting('math', \&tex4ht_do_tex); texinfo_register_command_formatting('tex', \&tex4ht_do_tex); { use Cwd; package Texinfo::TeX4HT; use vars qw( $STYLE_MATH $STYLE_TEX $tex4ht_command_math $tex4ht_command_tex $tex4ht_options_math $tex4ht_options_tex ); $STYLE_MATH = 'texi' if (!defined($STYLE_MATH)); $STYLE_TEX = 'tex' if (!defined($STYLE_TEX)); if (!defined($tex4ht_command_math)) { $tex4ht_command_math = 'httexi'; $tex4ht_command_math = 'htlatex' if ($STYLE_MATH eq 'latex'); $tex4ht_command_math = 'httex' if ($STYLE_MATH eq 'tex'); } if (!defined($tex4ht_command_tex)) { $tex4ht_command_tex = 'httex'; $tex4ht_command_tex = 'htlatex' if ($STYLE_TEX eq 'latex'); $tex4ht_command_tex = 'httexi' if ($STYLE_TEX eq 'texi'); } } my %commands = (); my $tex4ht_initial_dir; my $tex4ht_out_dir; sub tex4ht_prepare($) { # set file names my $self = shift; return 1 if (defined($self->get_conf('OUTFILE')) and $Texinfo::Common::null_device_file{$self->get_conf('OUTFILE')}); $tex4ht_initial_dir = Cwd::abs_path; $tex4ht_out_dir = $self->{'destination_directory'}; $tex4ht_out_dir = File::Spec->curdir() if (!defined($tex4ht_out_dir) or $tex4ht_out_dir =~ /^\s*$/); my $document_name = $self->{'document_name'}; my $tex4ht_basename = "${document_name}_tex4ht"; # this initialization doesn't seems to be needed, but it is cleaner anyway %commands = (); $commands{'math'}->{'style'} = $Texinfo::TeX4HT::STYLE_MATH; $commands{'tex'}->{'style'} = $Texinfo::TeX4HT::STYLE_TEX; $commands{'math'}->{'exec'} = $Texinfo::TeX4HT::tex4ht_command_math; $commands{'tex'}->{'exec'} = $Texinfo::TeX4HT::tex4ht_command_tex; foreach my $command ('math', 'tex') { my $style = $commands{$command}->{'style'}; $commands{$command}->{'basename'} = $tex4ht_basename . "_$command"; my $suffix = '.tex'; $suffix = '.texi' if ($style eq 'texi'); $commands{$command}->{'basefile'} = $commands{$command}->{'basename'} . $suffix; $commands{$command}->{'html_file'} = $commands{$command}->{'basename'} . '.html'; $commands{$command}->{'rfile'} = File::Spec->catfile($tex4ht_out_dir, $commands{$command}->{'basefile'}); my $rfile = $commands{$command}->{'rfile'}; $commands{$command}->{'counter'} = 0; $commands{$command}->{'output_counter'} = 0; if ($self->{'extra'}->{$command}) { local *TEX4HT_TEXFILE; unless (open (*TEX4HT_TEXFILE, ">$rfile")) { $self->document_warn(sprintf($self->__("tex4ht.pm: could not open %s: %s"), $rfile, $!)); return 1; } $commands{$command}->{'handle'} = *TEX4HT_TEXFILE; my $style = $commands{$command}->{'style'}; my $fh = $commands{$command}->{'handle'}; my $comment = '@c'; $comment = '%' if ($style ne 'texi'); $comment .= " Automatically generated\n"; if ($style eq 'texi') { print $fh "\\input texinfo \@setfilename $commands{$command}->{'basename'}.info\n"; print $fh "$comment"; } else { print $fh "$comment"; if ($style eq 'latex') { print $fh "\\documentstyle{article}\n\\begin{document}\n"; } elsif ($style eq 'tex') { print $fh "\\csname tex4ht\\endcsname\n"; } } foreach my $root (@{$self->{'extra'}->{$command}}) { $commands{$command}->{'counter'}++; my $counter = $commands{$command}->{'counter'}; my $tree; if ($command eq 'math') { $tree = $root->{'args'}->[0]; } else { $tree = {'contents' => [@{$root->{'contents'}}]}; if ($tree->{'contents'}->[0] and $tree->{'contents'}->[0]->{'type'} and $tree->{'contents'}->[0]->{'type'} eq 'empty_line_after_command') { shift @{$tree->{'contents'}}; } if ($tree->{'contents'}->[-1]->{'cmdname'} and $tree->{'contents'}->[-1]->{'cmdname'} eq 'end') { pop @{$tree->{'contents'}}; } } my $text = Texinfo::Convert::Texinfo::convert($tree); $commands{$command}->{'commands'}->[$counter-1] = $root; # write to tex file my ($before_comment_open, $after_comment_open, $before_comment_close, $after_comment_close); if ($style eq 'texi') { $before_comment_open = "\@verbatim\n\n"; $after_comment_open = "\n\@end verbatim\n"; $before_comment_close = "\@verbatim\n"; $after_comment_close = "\n\n\@end verbatim\n"; } else { $before_comment_open = "\\HCode{\\Hnewline \\Hnewline "; $after_comment_open = "\\Hnewline}\n"; $before_comment_close = "\\HCode{\\Hnewline "; $after_comment_close = "\\Hnewline \\Hnewline}\n"; } my $begin_comment = ""; print $fh "$before_comment_open$begin_comment$after_comment_open"; if ($command eq 'tex') { print $fh $text; } elsif ($command eq 'math') { if ($style eq 'texi') { print $fh '@math{' . $text . "}\n"; } else { print $fh "\\IgnorePar \$" . $text . "\$"; } } my $end_comment = ""; print $fh "$before_comment_close$end_comment$after_comment_close"; } # finish the tex file if ($style eq 'latex') { print $fh "\\end{document}\n"; } elsif ($style eq 'tex') { print $fh "\n\\bye\n"; } else { print $fh "\n\@bye\n"; } close ($fh); # this has to be done during the 'process' phase, in 'output' it is # too late. push @{$self->{'css_import_lines'}}, "\@import \"$commands{$command}->{'basename'}.css\";\n"; } } return 1; } sub tex4ht_convert($) { my $self = shift; unless (chdir $tex4ht_out_dir) { $self->document_warn(sprintf($self->__("tex4ht.pm: chdir %s failed: %s"), $tex4ht_out_dir, $!)); return 0; } print STDERR "cwd($tex4ht_out_dir): " . Cwd::cwd() ."\n" if ($self->get_conf('VERBOSE')); my $errors = 0; foreach my $command (keys(%commands)) { $errors += tex4ht_process_command($self, $command); } unless (chdir $tex4ht_initial_dir) { $self->document_warn(sprintf($self->__( "tex4ht.pm: unable to return to initial directory: %s"), $!)); return 0; } return 1; } sub tex4ht_process_command($$) { my $self = shift; my $command = shift; return 0 unless ($commands{$command}->{'counter'}); $self->document_warn(sprintf($self->__("tex4ht.pm: output file missing: %s"), $commands{$command}->{'basefile'})) unless (-f $commands{$command}->{'basefile'}); my $style = $commands{$command}->{'style'}; # now run tex4ht my $options = ''; if ($style eq 'math' and defined($Texinfo::TeX4HT::tex4ht_options_math)) { $options = $Texinfo::TeX4HT::tex4ht_options_math } elsif ($style eq 'tex' and defined($Texinfo::TeX4HT::tex4ht_options_tex)) { $options = $Texinfo::TeX4HT::tex4ht_options_tex; } my $cmd = "$commands{$command}->{'exec'} $commands{$command}->{'basefile'} $options"; print STDERR "tex4ht command: $cmd\n" if ($self->get_conf('VERBOSE')); if (system($cmd)) { $self->document_warn(sprintf($self->__( "tex4ht.pm: command failed: %s"), $cmd)); return 1; } # extract the html from the file created by tex4ht my $html_basefile = $commands{$command}->{'html_file'}; unless (open (TEX4HT_HTMLFILE, $html_basefile)) { $self->document_warn(sprintf($self->__("tex4ht.pm: could not open %s: %s"), $html_basefile, $!)); return 1; } my $got_count = 0; my $line; while ($line = ) { #print STDERR "$html_basefile: while $line"; if ($line =~ /!-- tex4ht_begin $commands{$command}->{'basename'} (\w+) (\d+) --/) { my $command = $1; my $count = $2; my $text = ''; my $end_found = 0; while ($line = ) { #print STDERR "while search $command $count $line"; if ($line =~ /!-- tex4ht_end $commands{$command}->{'basename'} $command $count --/) { $got_count++; chomp($text) if ($command eq 'math'); $commands{$command}->{'results'}->{$commands{$command}->{'commands'}->[$count-1]} = $text; $end_found = 1; last; } else { $text .= $line; } } unless ($end_found) { $self->document_warn(sprintf($self->__( "tex4ht.pm: end of \@%s item %d not found"), $command, $count)); } } } if ($got_count != $commands{$command}->{'counter'}) { $self->document_warn(sprintf($self->__( "tex4ht.pm: processing produced %d items in HTML; expected %d, the number of items found in the document for \@%s"), $got_count, $commands{$command}->{'counter'}, $command)); } close (TEX4HT_HTMLFILE); return 0; } sub tex4ht_do_tex($$$$) { my $self = shift; my $cmdname = shift;; my $command = shift; # return the resulting html if (exists ($commands{$cmdname}->{'results'}->{$command}) and defined($commands{$cmdname}->{'results'}->{$command})) { $commands{$cmdname}->{'output_counter'}++; return $commands{$cmdname}->{'results'}->{$command}; } else { $self->document_warn(sprintf($self->__( "tex4ht.pm: output has no HTML item for \@%s %s"), $cmdname, $command)); return ''; } } sub tex4ht_finish($) { my $self = shift; # this is different from the warning in tex4ht_process_command as, here, # this is the number of retrieved fragment, not processed fragment. if ($self->get_conf('VERBOSE')) { foreach my $command (keys(%commands)) { if ($commands{$command}->{'output_counter'} != $commands{$command}->{'counter'}) { $self->document_warn(sprintf($self->__( "tex4ht.pm: processing retrieved %d items in HTML; expected %d, the number of items found in the document for \@%s"), $commands{$command}->{'output_counter'}, $commands{$command}->{'counter'}, $command)); } } } return 1; } 1;