blob: d7b08f8c5d4fc594f7ecd4fb81d8650d2447a1d7 [file] [log] [blame]
# $Id: HTML.pm 6991 2016-02-06 12:16:13Z gavin $
# HTML.pm: output tree as HTML.
#
# Copyright 2011, 2012, 2013, 2014, 2015 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 <http://www.gnu.org/licenses/>.
#
# Original author: Patrice Dumas <pertusus@free.fr>
package Texinfo::Convert::HTML;
use 5.00405;
use strict;
use Texinfo::Convert::Converter;
use Texinfo::Common;
use Texinfo::Convert::Texinfo;
use Texinfo::Convert::Text;
use Texinfo::Convert::Unicode;
use Carp qw(cluck);
require Exporter;
use vars qw($VERSION @ISA @EXPORT @EXPORT_OK %EXPORT_TAGS);
@ISA = qw(Exporter Texinfo::Convert::Converter);
# Items to export into callers namespace by default. Note: do not export
# names by default without a very good reason. Use EXPORT_OK instead.
# Do not simply export all your public functions/methods/constants.
# This allows declaration use Texinfo::Convert::HTML ':all';
# If you do not need this, moving things directly into @EXPORT or @EXPORT_OK
# will save memory.
%EXPORT_TAGS = ( 'all' => [ qw(
convert
convert_tree
output
output_internal_links
) ] );
@EXPORT_OK = ( @{ $EXPORT_TAGS{'all'} } );
@EXPORT = qw(
);
$VERSION = '6.1';
# misc commands that are of use for formatting.
my %formatting_misc_commands = %Texinfo::Convert::Text::formatting_misc_commands;
my %no_brace_commands = %Texinfo::Common::no_brace_commands;
my %accent_commands = %Texinfo::Common::accent_commands;
my %misc_commands = %Texinfo::Common::misc_commands;
my %sectioning_commands = %Texinfo::Common::sectioning_commands;
my %def_commands = %Texinfo::Common::def_commands;
my %ref_commands = %Texinfo::Common::ref_commands;
my %brace_commands = %Texinfo::Common::brace_commands;
my %block_commands = %Texinfo::Common::block_commands;
my %menu_commands = %Texinfo::Common::menu_commands;
my %root_commands = %Texinfo::Common::root_commands;
my %preformatted_commands = %Texinfo::Common::preformatted_commands;
my %explained_commands = %Texinfo::Common::explained_commands;
my %item_container_commands = %Texinfo::Common::item_container_commands;
my %raw_commands = %Texinfo::Common::raw_commands;
my %format_raw_commands = %Texinfo::Common::format_raw_commands;
my %inline_commands = %Texinfo::Common::inline_commands;
my %inline_format_commands = %Texinfo::Common::inline_format_commands;
my %code_style_commands = %Texinfo::Common::code_style_commands;
my %regular_font_style_commands = %Texinfo::Common::regular_font_style_commands;
my %preformatted_code_commands = %Texinfo::Common::preformatted_code_commands;
my %default_index_commands = %Texinfo::Common::default_index_commands;
my %style_commands = %Texinfo::Common::style_commands;
my %align_commands = %Texinfo::Common::align_commands;
my %region_commands = %Texinfo::Common::region_commands;
my %context_brace_commands = %Texinfo::Common::context_brace_commands;
my %letter_no_arg_commands = %Texinfo::Common::letter_no_arg_commands;
foreach my $def_command (keys(%def_commands)) {
$formatting_misc_commands{$def_command} = 1 if ($misc_commands{$def_command});
}
# FIXME remove raw commands?
my %format_context_commands = (%block_commands, %root_commands);
foreach my $misc_context_command('tab', 'item', 'itemx', 'headitem') {
$format_context_commands{$misc_context_command} = 1;
}
my %composition_context_commands = (%preformatted_commands, %root_commands,
%menu_commands, %align_commands);
$composition_context_commands{'float'} = 1;
my %pre_class_types;
# FIXME allow customization? (also in DocBook)
my %upper_case_commands = ( 'sc' => 1 );
sub in_math($)
{
my $self = shift;
return $self->{'document_context'}->[-1]->{'math'};
}
# set if in menu or preformatted command
sub in_preformatted($)
{
my $self = shift;
my $context = $self->{'document_context'}->[-1]->{'composition_context'}->[-1];
if ($preformatted_commands{$context}
or $pre_class_types{$context}
or ($menu_commands{$context} and $self->_in_preformatted_in_menu())) {
return $context;
} else {
return undef;
}
}
sub in_upper_case($)
{
my $self = shift;
return $self->{'document_context'}->[-1]->{'formatting_context'}->[-1]->{'upper_case'};
}
sub in_space_protected($)
{
my $self = shift;
return $self->{'document_context'}->[-1]->{'formatting_context'}->[-1]->{'space_protected'};
}
sub in_code($)
{
my $self = shift;
return $self->{'document_context'}->[-1]->{'monospace'}->[-1];
}
sub in_string($)
{
my $self = shift;
return $self->{'document_context'}->[-1]->{'string'};
}
sub in_verbatim($)
{
my $self = shift;
return $self->{'document_context'}->[-1]->{'verbatim'};
}
sub in_raw($)
{
my $self = shift;
return $self->{'document_context'}->[-1]->{'raw'};
}
sub paragraph_number($)
{
my $self = shift;
return $self->{'document_context'}->[-1]->{'formatting_context'}->[-1]->{'paragraph_number'};
}
sub preformatted_number($)
{
my $self = shift;
return $self->{'document_context'}->[-1]->{'formatting_context'}->[-1]->{'preformatted_number'};
}
sub top_format($)
{
my $self = shift;
return $self->{'document_context'}->[-1]->{'formats'}->[-1];
}
sub commands_stack($)
{
my $self = shift;
return @{$self->{'document_context'}->[-1]->{'commands'}};
}
sub preformatted_classes_stack($)
{
my $self = shift;
return @{$self->{'document_context'}->[-1]->{'preformatted_classes'}};
}
sub in_align($)
{
my $self = shift;
my $context
= $self->{'document_context'}->[-1]->{'composition_context'}->[-1];
if ($align_commands{$context}) {
return $context;
} else {
return undef;
}
}
sub _get_target($$)
{
my $self = shift;
my $command = shift;
my $target;
if (!defined($command)) {
cluck("_get_target command not defined");
}
if ($self->{'targets'}->{$command}) {
$target = $self->{'targets'}->{$command};
} elsif ($command->{'cmdname'}
# This should only happen for @*heading*, root_commands targets should
# already be set.
and $sectioning_commands{$command->{'cmdname'}}
and !$root_commands{$command->{'cmdname'}}) {
$target = $self->_new_sectioning_command_target($command);
}
return $target;
}
# API for the elements formatting
sub command_id($$)
{
my $self = shift;
my $command = shift;
my $target = $self->_get_target($command);
if ($target) {
return $target->{'id'};
} else {
return undef;
}
}
sub command_contents_id($$$)
{
my $self = shift;
my $command = shift;
my $contents_or_shortcontents = shift;
my $target = $self->_get_target($command);
if ($target) {
return $target->{$contents_or_shortcontents .'_id'};
} else {
return undef;
}
}
sub command_contents_target($$$)
{
my $self = shift;
my $command = shift;
my $contents_or_shortcontents = shift;
$contents_or_shortcontents = 'shortcontents'
if ($contents_or_shortcontents eq 'summarycontents');
my $target = $self->_get_target($command);
if ($target) {
return $target->{$contents_or_shortcontents .'_target'};
} else {
return undef;
}
}
sub command_target($$)
{
my $self = shift;
my $command = shift;
my $target = $self->_get_target($command);
if ($target) {
return $target->{'target'};
} else {
return undef;
}
}
sub command_filename($$)
{
my $self = shift;
my $command = shift;
my $target = $self->_get_target($command);
if ($target) {
if (defined($target->{'filename'})) {
return $target->{'filename'};
}
my ($element, $root_command) = $self->_get_element($command, 1);
if (defined($root_command)) {
$target->{'root_command'} = $root_command;
}
if (defined($element)) {
$target->{'element'} = $element;
$target->{'filename'} = $element->{'filename'};
return $element->{'filename'};
}
}
#print STDERR "No filename ".Texinfo::Parser::_print_command_args_texi($command);
return undef;
}
sub command_element($$)
{
my $self = shift;
my $command = shift;
my $target = $self->_get_target($command);
if ($target) {
$self->command_filename($command);
return $target->{'element'};
}
return undef;
}
sub command_element_command($$)
{
my $self = shift;
my $command = shift;
my ($element, $root_command) = $self->_get_element($command);
#my $element = $self->command_element($command);
if ($element and $element->{'extra'}) {
return $element->{'extra'}->{'element_command'};
}
return undef;
}
sub element_command($$)
{
my $self = shift;
my $element = shift;
if ($element and $element->{'extra'}) {
if ($element->{'extra'}->{'element_command'}) {
return $element->{'extra'}->{'element_command'};
} elsif ($element->{'extra'}->{'special_element'}) {
return $element;
}
}
return undef;
}
sub command_node($$)
{
my $self = shift;
my $command = shift;
my $target = $self->_get_target($command);
if ($target) {
$self->command_filename($command);
my $root_command = $target->{'root_command'};
if (defined($root_command)) {
if ($root_command->{'cmdname'} and $root_command->{'cmdname'} eq 'node') {
return $root_command;
}
if ($root_command->{'extra'} and $root_command->{'extra'}->{'associated_node'}) {
return $root_command->{'extra'}->{'associated_node'};
}
}
}
return undef;
}
sub command_href($$;$$)
{
my $self = shift;
my $command = shift;
my $filename = shift;
my $link_command = shift;
$filename = $self->{'current_filename'} if (!defined($filename));
if ($command->{'manual_content'} or $command->{'top_node_up'}) {
return $self->_external_node_href($command, $filename, $link_command);
}
my $target = $self->command_target($command);
return '' if (!defined($target));
my $href = '';
my $target_filename = $self->command_filename($command);
if (!defined($target_filename)) {
# Happens if there are no pages, for example if OUTPUT is set to ''
# as in the test cases. Also for things in @titlepage when
# titlepage is not output.
if ($self->{'elements'} and $self->{'elements'}->[0]
and defined($self->{'elements'}->[0]->{'filename'})) {
# In that case use the first page.
$target_filename = $self->{'elements'}->[0]->{'filename'};
}
}
if (defined($target_filename)) {
if (!defined($filename)
or $filename ne $target_filename) {
$href .= $target_filename;
}
}
$href .= '#' . $target if ($target ne '');
return $href;
}
my %contents_command_element_name = (
'contents' => 'Contents',
'shortcontents' => 'Overview',
'summarycontents' => 'Overview',
);
sub command_contents_href($$$$)
{
my $self = shift;
my $command = shift;
my $contents_or_shortcontents = shift;
my $filename = shift;
my $href;
my $name = $contents_command_element_name{$contents_or_shortcontents};
my $target = $self->command_contents_target($command, $contents_or_shortcontents);
my $target_element = $self->special_element($name);
my $target_filename;
# !defined happens when called as convert() and not output()
if (defined($target_element)) {
$target_filename = $self->command_filename($target_element);
}
if (defined($target_filename) and
(!defined($filename)
or $filename ne $target_filename)) {
$href .= $target_filename;
}
$href .= '#' . $target if ($target ne '');
return $href;
}
sub command_text($$;$)
{
my $self = shift;
my $command = shift;
my $type = shift;
if (!defined($type)) {
$type = 'text';
}
if (!defined($command)) {
cluck "in command_text($type) command not defined";
}
if ($command->{'manual_content'} or $command->{'top_node_up'}) {
my $node_content = [];
$node_content = $command->{'node_content'}
if (defined($command->{'node_content'}));
my $tree;
if ($command->{'manual_content'}) {
$tree = {'type' => '_code',
'contents' => [{'text' => '('}, @{$command->{'manual_content'}},
{'text' => ')'}, @$node_content]};
} else {
$tree = {'type' => '_code',
'contents' => $node_content};
}
if ($type eq 'tree') {
return $tree;
} else {
if ($type eq 'string') {
$tree = {'type' => '_string',
'contents' => [$tree]};
}
my $result = $self->convert_tree_new_formatting_context(
$tree, $command->{'cmdname'});
return $result;
}
}
my $target = $self->_get_target($command);
if ($target) {
my $explanation;
$explanation = "command_text \@$command->{'cmdname'}"
if ($command->{'cmdname'});
if (defined($target->{$type})) {
return $target->{$type};
}
my $tree;
if (!$target->{'tree'}) {
if ($command->{'extra'}
and $command->{'extra'}->{'special_element'}) {
my $special_element = $command->{'extra'}->{'special_element'};
$tree = $self->get_conf('SPECIAL_ELEMENTS_NAME')->{$special_element};
$explanation = "command_text $special_element";
} elsif ($command->{'cmdname'} and ($command->{'cmdname'} eq 'node'
or $command->{'cmdname'} eq 'anchor')) {
$tree = {'type' => '_code',
'contents' => $command->{'extra'}->{'node_content'}};
} elsif ($command->{'cmdname'} and ($command->{'cmdname'} eq 'float')) {
$tree = $self->_float_type_number($command);
} elsif ($command->{'extra'}->{'missing_argument'}) {
if ($type eq 'tree' or $type eq 'tree_nonumber') {
return {};
} else {
return '';
}
} else {
if (!$command->{'extra'}->{'misc_content'}) {
cluck "No misc_content: "
.Texinfo::Parser::_print_current($command);
}
if (defined($command->{'number'})
and ($self->get_conf('NUMBER_SECTIONS')
or !defined($self->get_conf('NUMBER_SECTIONS')))) {
if ($command->{'cmdname'} eq 'appendix' and $command->{'level'} == 1) {
$tree = $self->gdt('Appendix {number} {section_title}',
{'number' => {'text' => $command->{'number'}},
'section_title'
=> {'contents'
=> $command->{'extra'}->{'misc_content'}}});
} else {
$tree = $self->gdt('{number} {section_title}',
{'number' => {'text' => $command->{'number'}},
'section_title'
=> {'contents'
=> $command->{'extra'}->{'misc_content'}}});
}
} else {
$tree = {'contents' => [@{$command->{'extra'}->{'misc_content'}}]};
}
$target->{'tree_nonumber'}
= {'contents' => $command->{'extra'}->{'misc_content'}};
}
$target->{'tree'} = $tree;
} else {
$tree = $target->{'tree'};
}
return $target->{'tree_nonumber'} if ($type eq 'tree_nonumber'
and $target->{'tree_nonumber'});
return $tree if ($type eq 'tree' or $type eq 'tree_nonumber');
$self->_new_document_context($command->{'cmdname'});
if ($type eq 'string') {
$tree = {'type' => '_string',
'contents' => [$tree]};
}
print STDERR "DO $target->{'id'}($type)\n" if ($self->get_conf('DEBUG'));
if ($type =~ /^(.*)_nonumber$/) {
$tree = $target->{'tree_nonumber'}
if (defined($target->{'tree_nonumber'}));
}
$self->{'ignore_notice'}++;
$target->{$type} = $self->_convert($tree, $explanation);
$self->{'ignore_notice'}--;
pop @{$self->{'document_context'}};
return $target->{$type};
}
return undef;
}
sub label_command($$)
{
my $self = shift;
my $label = shift;
return $self->{'labels'}->{$label};
}
sub special_element($$)
{
my $self = shift;
my $type = shift;
return $self->{'special_elements_types'}->{$type};
}
sub global_element($$)
{
my $self = shift;
my $type = shift;
return $self->{'global_target_elements'}->{$type};
}
# it is considered 'top' only if element corresponds to @top or
# element is a node
sub element_is_top($$)
{
my $self = shift;
my $element = shift;
return ($self->{'global_target_elements'}->{'Top'}
and $self->{'global_target_elements'}->{'Top'} eq $element
and $element->{'extra'}
and (($element->{'extra'}->{'section'}
and $element->{'extra'}->{'section'}->{'cmdname'} eq 'top')
or ($element->{'extra'}->{'element_command'}
and $element->{'extra'}->{'element_command'}->{'cmdname'} eq 'node')));
}
sub default_formatting_function($$)
{
my $self = shift;
my $format = shift;
return $self->{'default_formatting_functions'}->{$format};
}
sub get_value($$)
{
my $self = shift;
my $value = shift;
if (defined($self->{'parser'})
and exists ($self->{'parser'}->{'values'}->{$value})) {
return $self->{'parser'}->{'values'}->{$value};
} else {
return undef;
}
}
sub convert_tree_new_formatting_context($$;$$)
{
my $self = shift;
my $tree = shift;
my $context_string = shift;
my $multiple_pass = shift;
if (defined($context_string)) {
$self->_new_document_context($context_string);
}
if ($multiple_pass) {
$self->{'ignore_notice'}++;
push @{$self->{'multiple_pass'}}, $multiple_pass;
}
my $result = $self->convert_tree($tree);
if (defined($context_string)) {
pop @{$self->{'document_context'}};
}
if ($multiple_pass) {
$self->{'ignore_notice'}--;
pop @{$self->{'multiple_pass'}};
}
return $result;
}
# see http://www.w3.org/TR/REC-html40/types.html#type-links
my %BUTTONS_REL =
(
'Top', 'start',
'Contents', 'contents',
'Overview', '',
'Index', 'index',
'This', '',
'Back', 'prev',
'FastBack', '',
'Prev', 'prev',
'Up', 'up',
'Next', 'next',
'NodeUp', 'up',
'NodeNext', 'next',
'NodePrev', 'prev',
'NodeForward', '',
'NodeBack', '',
'Forward', 'next',
'FastForward', '',
'About' , 'help',
'First', '',
'Last', '',
'NextFile', 'next',
'PrevFile', 'prev',
);
my %BUTTONS_ACCESSKEY =
(
'Top', '',
'Contents', '',
'Overview', '',
'Index', '',
'This', '',
'Back', 'p',
'FastBack', '',
'Prev', 'p',
'Up', 'u',
'Next', 'n',
'NodeUp', 'u',
'NodeNext', 'n',
'NodePrev', 'p',
'NodeForward', '',
'NodeBack', '',
'Forward', 'n',
'FastForward', '',
'About' , '',
'First', '',
'Last', '',
'NextFile', '',
'PrevFile', '',
);
my %BUTTONS_EXAMPLE =
(
'Top', ' &nbsp; ',
'Contents', ' &nbsp; ',
'Overview', ' &nbsp; ',
'Index', ' &nbsp; ',
'This', '1.2.3',
'Back', '1.2.2',
'FastBack', '1',
'Prev', '1.2.2',
'Up', '1.2',
'Next', '1.2.4',
'NodeUp', '1.2',
'NodeNext', '1.2.4',
'NodePrev', '1.2.2',
'NodeForward', '1.2.4',
'NodeBack', '1.2.2',
'Forward', '1.2.4',
'FastForward', '2',
'About', ' &nbsp; ',
'First', '1.',
'Last', '1.2.4',
'NextFile', ' &nbsp; ',
'PrevFile', ' &nbsp; ',
);
my (%BUTTONS_TEXT, %BUTTONS_GOTO, %BUTTONS_NAME, %SPECIAL_ELEMENTS_NAME);
sub _translate_names($)
{
my $self = shift;
#print STDERR "encoding_name: ".$self->get_conf('OUTPUT_ENCODING_NAME')." documentlanguage: ".$self->get_conf('documentlanguage')."\n";
%BUTTONS_TEXT = (
'Top', $self->gdt('Top'),
'Contents', $self->gdt('Contents'),
'Overview', $self->gdt('Overview'),
'Index', $self->gdt('Index'),
' ', ' &nbsp; ',
'This', $self->gdt('current'),
'Back', ' &lt; ',
'FastBack', ' &lt;&lt; ',
'Prev', $self->gdt('Prev'),
'Up', $self->gdt(' Up '),
'Next', $self->gdt('Next'),
#'NodeUp', $self->gdt('Node up'),
'NodeUp', $self->gdt('Up'),
#'NodeNext', $self->gdt('Next node'),
'NodeNext', $self->gdt('Next'),
#'NodePrev', $self->gdt('Previous node'),
'NodePrev', $self->gdt('Previous'),
'NodeForward', $self->gdt('Forward node'),
'NodeBack', $self->gdt('Back node'),
'Forward', ' &gt; ',
'FastForward', ' &gt;&gt; ',
'About', ' ? ',
'First', ' |&lt; ',
'Last', ' &gt;| ',
'NextFile', $self->gdt('Next file'),
'PrevFile', $self->gdt('Previous file'),
);
#%BUTTONS_TEXT = %NAVIGATION_TEXT;
%BUTTONS_GOTO = (
'Top', $self->gdt('Cover (top) of document'),
'Contents', $self->gdt('Table of contents'),
'Overview', $self->gdt('Short table of contents'),
'Index', $self->gdt('Index'),
'This', $self->gdt('Current section'),
'Back', $self->gdt('Previous section in reading order'),
'FastBack', $self->gdt('Beginning of this chapter or previous chapter'),
'Prev', $self->gdt('Previous section on same level'),
'Up', $self->gdt('Up section'),
'Next', $self->gdt('Next section on same level'),
'NodeUp', $self->gdt('Up node'),
'NodeNext', $self->gdt('Next node'),
'NodePrev', $self->gdt('Previous node'),
'NodeForward', $self->gdt('Next node in node reading order'),
'NodeBack', $self->gdt('Previous node in node reading order'),
'Forward', $self->gdt('Next section in reading order'),
'FastForward', $self->gdt('Next chapter'),
'About' , $self->gdt('About (help)'),
'First', $self->gdt('First section in reading order'),
'Last', $self->gdt('Last section in reading order'),
'NextFile', $self->gdt('Forward section in next file'),
'PrevFile', $self->gdt('Back section in previous file'),
);
%BUTTONS_NAME = (
'Top', $self->gdt('Top'),
'Contents', $self->gdt('Contents'),
'Overview', $self->gdt('Overview'),
'Index', $self->gdt('Index'),
' ', ' ',
'This', $self->gdt('This'),
'Back', $self->gdt('Back'),
'FastBack', $self->gdt('FastBack'),
'Prev', $self->gdt('Prev'),
'Up', $self->gdt('Up'),
'Next', $self->gdt('Next'),
'NodeUp', $self->gdt('NodeUp'),
'NodeNext', $self->gdt('NodeNext'),
'NodePrev', $self->gdt('NodePrev'),
'NodeForward', $self->gdt('NodeForward'),
'NodeBack', $self->gdt('NodeBack'),
'Forward', $self->gdt('Forward'),
'FastForward', $self->gdt('FastForward'),
'About', $self->gdt('About'),
'First', $self->gdt('First'),
'Last', $self->gdt('Last'),
'NextFile', $self->gdt('NextFile'),
'PrevFile', $self->gdt('PrevFile'),
);
%SPECIAL_ELEMENTS_NAME = (
'About' => $self->gdt('About This Document'),
'Contents' => $self->gdt('Table of Contents'),
'Overview' => $self->gdt('Short Table of Contents'),
'Footnotes' => $self->gdt('Footnotes'),
);
# delete the tree and formatted results for special elements
# such that they are redone with the new tree when needed.
foreach my $special_element (keys (%SPECIAL_ELEMENTS_NAME)) {
if ($self->{'special_elements_types'}->{$special_element} and
$self->{'targets'}->{$self->{'special_elements_types'}->{$special_element}}) {
my $target
= $self->{'targets'}->{$self->{'special_elements_types'}->{$special_element}};
foreach my $key ('text', 'string', 'tree') {
delete $target->{$key};
}
}
}
foreach my $hash (\%BUTTONS_TEXT, \%BUTTONS_GOTO, \%BUTTONS_NAME) {
foreach my $button (keys (%$hash)) {
if (ref($hash->{$button})) {
$hash->{$button} = $self->convert_tree_new_formatting_context(
$hash->{$button}, "button $button");
}
}
}
if ($self->{'commands_translation'}) {
my %translated_commands;
foreach my $context ('normal', 'preformatted', 'string') {
foreach my $command (keys(%{$self->{'commands_translation'}->{$context}})) {
$translated_commands{$command} = 1;
delete $self->{'commands_formatting'}->{$context}->{$command};
if (defined($self->{'commands_translation'}->{$context}->{$command})) {
$self->{'commands_formatting'}->{$context}->{$command}
= $self->gdt($self->{'commands_translation'}->{$context}->{$command},
undef, 'translated_text');
}
}
}
foreach my $command(keys(%translated_commands)) {
$self->_complete_commands_formatting($command);
}
}
}
# insert here name of icon images for buttons
# Icons are used, if ICONS and resp. value are set
my %ACTIVE_ICONS = (
'Top', '',
'Contents', '',
'Overview', '',
'Index', '',
'This', '',
'Back', '',
'FastBack', '',
'Prev', '',
'Up', '',
'Next', '',
'NodeUp', '',
'NodeNext', '',
'NodePrev', '',
'NodeForward', '',
'NodeBack', '',
'Forward', '',
'FastForward', '',
'About' , '',
'First', '',
'Last', '',
'NextFile', '',
'PrevFile', '',
' ', '',
);
# insert here name of icon images for these, if button is inactive
my %PASSIVE_ICONS = (
'Top', '',
'Contents', '',
'Overview', '',
'Index', '',
'This', '',
'Back', '',
'FastBack', '',
'Prev', '',
'Up', '',
'Next', '',
'NodeUp', '',
'NodeNext', '',
'NodePrev', '',
'NodeForward', '',
'NodeBack', '',
'Forward', '',
'FastForward', '',
'About', '',
'First', '',
'Last', '',
'NextFile', '',
'PrevFile', '',
);
my %defaults = (
'ENABLE_ENCODING' => 0,
'SHOW_MENU' => 1,
'OUTPUT_ENCODING_NAME' => 'utf-8',
#'encoding_name' => undef,
#'perl_encoding' => undef,
'OUTFILE' => undef,
'SUBDIR' => undef,
'USE_NODES' => 1,
'INLINE_CONTENTS' => 1,
'SPLIT' => 'node',
# if set style is added in attribute.
'INLINE_CSS_STYLE' => 0,
# if set, no css is used.
'NO_CSS' => 0,
# if set, use node anchors for sections targets
'USE_NODE_TARGET' => 1,
'OPEN_QUOTE_SYMBOL' => '&lsquo;',
'CLOSE_QUOTE_SYMBOL' => '&rsquo;',
'USE_ISO' => 1,
# file name used for Top node when NODE_FILENAMES is true
'TOP_NODE_FILE' => 'index',
'NODE_FILE_EXTENSION' => 'html',
'EXTENSION' => 'html',
'TOP_NODE_FILE_TARGET' => 'index',
'TRANSLITERATE_FILE_NAMES' => 1,
'USE_LINKS' => 1,
'USE_NUMERIC_ENTITY' => 1,
'ENABLE_ENCODING_USE_ENTITY' => 1,
'DATE_IN_HEADER' => 0,
'AVOID_MENU_REDUNDANCY' => 0,
'HEADERS' => 1,
'DO_ABOUT' => 0,
'USE_ACCESSKEY' => 1,
'USE_REL_REV' => 1,
'NODE_NAME_IN_MENU' => 1,
'NODE_NAME_IN_INDEX' => 1,
'XREF_USE_NODE_NAME_ARG' => undef,
'XREF_USE_FLOAT_LABEL' => 0,
'OVERVIEW_LINK_TO_TOC' => 1,
'COMPLEX_FORMAT_IN_TABLE' => 0,
'WORDS_IN_PAGE' => 300,
'SECTION_BUTTONS' => [[ 'NodeNext', \&_default_node_direction ],
[ 'NodePrev', \&_default_node_direction ],
[ 'NodeUp', \&_default_node_direction ], ' ',
'Contents', 'Index'],
'LINKS_BUTTONS' => ['Top', 'Index', 'Contents', 'About',
'NodeUp', 'NextFile', 'PrevFile'],
# 'TOP_BUTTONS' => ['Back', 'Forward', ' ',
# 'Contents', 'Index', 'About'],
#
# 'MISC_BUTTONS' => [ 'Top', 'Contents', 'Index', 'About' ],
# 'CHAPTER_BUTTONS' => [ 'FastBack', 'FastForward', ' ',
# ' ', ' ', ' ', ' ',
# 'Top', 'Contents', 'Index', 'About', ],
# 'SECTION_FOOTER_BUTTONS' => [ 'FastBack', 'Back', 'Up', 'Forward', 'FastForward' ],
# 'NODE_FOOTER_BUTTONS' => [ 'FastBack', 'Back', 'Up', 'Forward', 'FastForward' ],
'misc_elements_targets' => {
'Overview' => 'SEC_Overview',
'Contents' => 'SEC_Contents',
'Footnotes' => 'SEC_Foot',
'About' => 'SEC_About',
'Top' => 'SEC_Top',
},
'misc_pages_file_string' => {
'Contents' => '_toc',
'Overview' => '_ovr',
'Footnotes' => '_fot',
'About' => '_abt',
},
'frame_pages_file_string' => {
'Frame' => '_frame',
'Toc_Frame' => '_toc_frame',
},
'misc_elements_order' => ['Footnotes', 'Contents', 'Overview', 'About'],
'DOCTYPE' => '<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">',
'FRAMESET_DOCTYPE' => '<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Frameset//EN" "http://www.w3.org/TR/html4/frameset.dtd">',
'DEFAULT_RULE' => '<hr>',
'BIG_RULE' => '<hr>',
'MENU_SYMBOL' => '&bull;',
'MENU_ENTRY_COLON' => ':',
'INDEX_ENTRY_COLON' => ':',
'BODYTEXT' => undef,
'documentlanguage' => 'en',
'xrefautomaticsectiontitle' => 'off',
'SHOW_TITLE' => 1,
'USE_TITLEPAGE_FOR_TITLE' => 0,
'MONOLITHIC' => 1,
'CHAPTER_HEADER_LEVEL' => 2,
'MAX_HEADER_LEVEL' => 4,
'FOOTNOTE_END_HEADER_LEVEL' => 4,
'FOOTNOTE_SEPARATE_HEADER_LEVEL' => 4,
'BUTTONS_REL' => \%BUTTONS_REL,
'BUTTONS_ACCESSKEY' => \%BUTTONS_ACCESSKEY,
'BUTTONS_EXAMPLE' => \%BUTTONS_EXAMPLE,
'BUTTONS_GOTO' => \%BUTTONS_GOTO,
'BUTTONS_NAME' => \%BUTTONS_NAME,
'BUTTONS_TEXT' => \%BUTTONS_TEXT,
'ACTIVE_ICONS' => \%ACTIVE_ICONS,
'PASSIVE_ICONS' => \%PASSIVE_ICONS,
'SPECIAL_ELEMENTS_NAME' => \%SPECIAL_ELEMENTS_NAME,
'SPECIAL_ELEMENTS_CLASS' => {
'About' => 'about',
'Contents' => 'contents',
'Overview' => 'shortcontents',
'Footnotes' => 'footnotes',
},
'output_format' => 'html',
);
foreach my $buttons ('CHAPTER_BUTTONS', 'SECTION_FOOTER_BUTTONS', 'NODE_FOOTER_BUTTONS',
'MISC_BUTTONS', 'TOP_BUTTONS') {
$defaults{$buttons} = [@{$defaults{'SECTION_BUTTONS'}}];
}
sub converter_defaults($$)
{
my $self = shift;
my $conf = shift;
if (defined($conf->{'TEXI2HTML'})) {
_set_variables_texi2html();
}
return %defaults;
}
my $NO_BULLET_LIST_STYLE = 'list-style: none';
my $NO_BULLET_LIST_CLASS = 'no-bullet';
my $NO_BULLET_LIST_ATTRIBUTE = ' class="'.$NO_BULLET_LIST_CLASS.'"';
my $MENU_PRE_STYLE = 'font-family: serif';
my %css_map = (
"ul.$NO_BULLET_LIST_CLASS" => "$NO_BULLET_LIST_STYLE",
'pre.menu-comment' => "$MENU_PRE_STYLE",
'pre.menu-preformatted' => "$MENU_PRE_STYLE",
'a.summary-letter' => 'text-decoration: none',
'blockquote.smallquotation' => 'font-size: smaller',
'pre.display' => 'font-family: inherit',
'pre.smalldisplay' => 'font-family: inherit; font-size: smaller',
'pre.smallexample' => 'font-size: smaller',
'span.sansserif' => 'font-family: sans-serif; font-weight: normal',
'span.roman' => 'font-family: initial; font-weight: normal',
'span.nolinebreak' => 'white-space: nowrap',
'kbd' => 'font-style: oblique',
);
$css_map{'pre.format'} = $css_map{'pre.display'};
$css_map{'pre.smallformat'} = $css_map{'pre.smalldisplay'};
$css_map{'pre.smalllisp'} = $css_map{'pre.smallexample'};
my %preformatted_commands_context = %preformatted_commands;
$preformatted_commands_context{'verbatim'} = 1;
my %pre_class_commands;
foreach my $preformatted_command (keys(%preformatted_commands_context)) {
$pre_class_commands{$preformatted_command} = $preformatted_command;
}
$pre_class_commands{'menu'} = 'menu-preformatted';
$pre_class_types{'menu_comment'} = 'menu-comment';
my %indented_preformatted_commands;
foreach my $indented_format ('example', 'display', 'lisp') {
$indented_preformatted_commands{$indented_format} = 1;
$indented_preformatted_commands{"small$indented_format"} = 1;
$css_map{"div.$indented_format"} = 'margin-left: 3.2em';
$css_map{"div.small$indented_format"} = 'margin-left: 3.2em';
}
$css_map{"blockquote.indentedblock"} = 'margin-right: 0em';
$css_map{"blockquote.smallindentedblock"}
= 'margin-right: 0em; font-size: smaller';
# types that are in code style in the default case
my %default_code_types = (
'_code' => 1,
);
# default specification of arguments formatting
my %default_commands_args = (
'email' => [['monospace', 'monospacestring'], ['normal']],
'anchor' => [['monospacestring']],
'uref' => [['monospacestring'], ['normal'], ['normal']],
'url' => [['monospacestring'], ['normal'], ['normal']],
'printindex' => [[]],
'sp' => [[]],
'inforef' => [['monospace'],['normal'],['monospacetext']],
'xref' => [['monospace'],['normal'],['normal'],['monospacetext'],['normal']],
'pxref' => [['monospace'],['normal'],['normal'],['monospacetext'],['normal']],
'ref' => [['monospace'],['normal'],['normal'],['monospacetext'],['normal']],
'image' => [['monospacetext'],['monospacetext'],['monospacetext'],['string', 'normal'],['monospacetext']],
# FIXME shouldn't it better not to convert if later ignored?
'inlinefmt' => [['monospacetext'],['normal']],
'inlinefmtifelse' => [['monospacetext'],['normal'],['normal']],
'inlineraw' => [['monospacetext'],['raw']],
'inlineifclear' => [['monospacetext'],['normal']],
'inlineifset' => [['monospacetext'],['normal']],
'item' => [[]],
'itemx' => [[]],
);
foreach my $explained_command (keys(%explained_commands)) {
$default_commands_args{$explained_command}
= [['normal'], ['string']];
}
# Default for the function references used for the formatting
# of commands.
my %default_commands_conversion;
sub default_commands_conversion($$)
{
my $self = shift;
my $command = shift;
return $default_commands_conversion{$command};
}
my %kept_misc_commands;
my @informative_global_commands = ('contents', 'shortcontents',
'summarycontents', 'allowcodebreaks', 'documentlanguage',
'footnotestyle', 'documentencoding',
'setcontentsaftertitlepage', 'setshortcontentsaftertitlepage',
'xrefautomaticsectiontitle', 'deftypefnnewline');
# taken from global
# 'documentencoding'
# 'novalidate'
foreach my $misc_command(@informative_global_commands,
'verbatiminclude', 'insertcopying', 'printindex', 'listoffloats',
'author', 'subtitle',
'title', keys(%default_index_commands),
keys(%formatting_misc_commands)) {
$kept_misc_commands{$misc_command} = 1;
}
sub converter_global_commands($)
{
return @informative_global_commands;
}
my %contents_commands = (
'contents' => 1,
'shortcontents' => 1,
'summarycontents' => 1,
);
#my %ignored_misc_commands;
foreach my $misc_command (keys(%misc_commands)) {
# $ignored_misc_commands{$misc_command} = 1
$default_commands_conversion{$misc_command} = undef
unless ($kept_misc_commands{$misc_command});
}
foreach my $ignored_brace_commands ('caption', 'shortcaption',
'hyphenation', 'sortas') {
#$ignored_commands{$ignored_brace_commands} = 1;
$default_commands_conversion{$ignored_brace_commands} = undef;
}
# commands that leads to advancing the paragraph number. This is mostly
#used to determine the first line, in fact.
my %advance_paragraph_count_commands;
foreach my $command (keys(%block_commands)) {
next if ($menu_commands{$command}
or $block_commands{$command} eq 'raw');
$advance_paragraph_count_commands{$command} = 1;
}
foreach my $ignored_block_commands ('ignore', 'macro', 'rmacro', 'copying',
'documentdescription', 'titlepage', 'direntry') {
#$ignored_commands{$ignored_block_commands} = 1;
$default_commands_conversion{$ignored_block_commands} = undef;
};
# Formatting of commands without args
# The hash holding the defaults for the formatting of
# most commands without args
my %default_commands_formatting;
foreach my $command (keys(%{$Texinfo::Convert::Converter::default_xml_commands_formatting{'normal'}})) {
$default_commands_formatting{'normal'}->{$command} =
$Texinfo::Convert::Converter::default_xml_commands_formatting{'normal'}->{$command};
}
$default_commands_formatting{'normal'}->{' '} = '&nbsp;';
$default_commands_formatting{'normal'}->{"\t"} = '&nbsp;';
$default_commands_formatting{'normal'}->{"\n"} = '&nbsp;';
my %default_commands_translation;
# possible example of use, right now not used, as the generic
# translated command with gdt tree is used.
#$default_commands_translation{'normal'}->{'error'} = 'error--&gt;';
## This is used to have gettext pick up the chain to be translated
#if (0) {
# my $not_existing;
# $not_existing->gdt('error--&gt;');
#}
#foreach my $command (keys(%{$default_commands_formatting{'normal'}})) {
# $default_commands_formatting{'preformatted'}->{$command} =
# $default_commands_formatting{'normal'}->{$command};
# $default_commands_formatting{'string'}->{$command} =
# $default_commands_formatting{'normal'}->{$command};
#}
$default_commands_formatting{'normal'}->{'enddots'}
= '<small class="enddots">...</small>';
$default_commands_formatting{'preformatted'}->{'enddots'} = '...';
$default_commands_formatting{'normal'}->{'*'} = '<br>';
$default_commands_formatting{'preformatted'}->{'*'} = "\n";
sub _convert_no_arg_command($$$)
{
my $self = shift;
my $cmdname = shift;
my $command = shift;
if ($cmdname eq 'click' and $command->{'extra'}
and exists($command->{'extra'}->{'clickstyle'})) {
my $click_cmdname = $command->{'extra'}->{'clickstyle'};
if (($self->in_preformatted() or $self->in_math()
and $self->{'commands_formatting'}->{'preformatted'}->{$click_cmdname})
or ($self->in_string() and
$self->{'commands_formatting'}->{'string'}->{$click_cmdname})
or ($self->{'commands_formatting'}->{'normal'}->{$click_cmdname})) {
$cmdname = $click_cmdname;
}
}
if ($self->in_upper_case() and $letter_no_arg_commands{$cmdname}
and $self->{'commands_formatting'}->{'normal'}->{uc($cmdname)}) {
$cmdname = uc($cmdname);
}
my $result;
if ($self->{'translated_commands'}->{$cmdname}) {
return $self->convert_tree(
$self->gdt($self->{'translated_commands'}->{$cmdname}));
}
if ($self->in_preformatted() or $self->in_math()) {
$result = $self->{'commands_formatting'}->{'preformatted'}->{$cmdname};
} elsif ($self->in_string()) {
$result = $self->{'commands_formatting'}->{'string'}->{$cmdname};
} else {
$result = $self->{'commands_formatting'}->{'normal'}->{$cmdname};
}
return $result;
}
foreach my $command(keys(%{$default_commands_formatting{'normal'}})) {
$default_commands_conversion{$command} = \&_convert_no_arg_command;
}
sub _convert_today_command($$$)
{
my $self = shift;
my $cmdname = shift;
my $command = shift;
my $tree = $self->Texinfo::Common::expand_today();
return $self->convert_tree($tree);
}
$default_commands_conversion{'today'} = \&_convert_today_command;
# style commands
my %quoted_style_commands;
foreach my $quoted_command ('samp') {
$quoted_style_commands{$quoted_command} = 1;
}
my %style_attribute_commands;
$style_attribute_commands{'normal'} = {
'b' => 'b',
'cite' => 'cite',
'code' => 'code',
'command' => 'code',
'dfn' => 'em',
'emph' => 'em',
'env' => 'code',
'file' => 'samp',
'headitemfont' => 'b', # not really that, in fact it is
# in <th> rather than <td>
'i' => 'i',
'slanted' => 'i',
'sansserif' => 'span class="sansserif"',
'kbd' => 'kbd',
'option' => 'samp',
'r' => 'span class="roman"',
'samp' => 'samp',
'sc' => 'small',
'strong' => 'strong',
'sub' => 'sub',
'sup' => 'sup',
't' => 'tt',
'var' => 'var',
'verb' => 'tt',
'math' => 'em',
};
my %style_commands_formatting;
# this weird construct does like uniq, it avoids duplicates.
# it is required since math is not in the %style_commands as it is
# in context command.
my @all_style_commands = keys %{{ map { $_ => 1 }
(keys(%style_commands), keys(%{$style_attribute_commands{'normal'}}),
'dmn') }};
foreach my $command(@all_style_commands) {
# default is no attribute.
if ($style_attribute_commands{'normal'}->{$command}) {
$style_commands_formatting{'normal'}->{$command}->{'attribute'}
= $style_attribute_commands{'normal'}->{$command};
$style_commands_formatting{'preformatted'}->{$command}->{'attribute'}
= $style_attribute_commands{'normal'}->{$command};
}
if ($style_attribute_commands{'preformatted'}->{$command}) {
$style_commands_formatting{'preformatted'}->{$command}->{'attribute'} =
$style_attribute_commands{'preformatted'}->{$command};
}
if ($quoted_style_commands{$command}) {
foreach my $context ('normal', 'string', 'preformatted') {
$style_commands_formatting{$context}->{$command}->{'quote'} = 1;
}
}
$default_commands_conversion{$command} = \&_convert_style_command;
}
delete $style_commands_formatting{'preformatted'}->{'sc'}->{'attribute'};
delete $style_commands_formatting{'preformatted'}->{'sc'};
sub _parse_attribute($)
{
my $element = shift;
return ('', '', '') if (!defined($element));
my ($class, $attributes) = ('', '');
if ($element =~ /^(\w+)(\s+.*)/)
{
$element = $1;
$attributes = $2;
if ($attributes =~ s/^\s+class=\"([^\"]+)\"//) {
$class = $1;
}
}
return ($element, $class, $attributes);
}
sub _convert_style_command($$$$)
{
my $self = shift;
my $cmdname = shift;
my $command = shift;
my $args = shift;
my $text = $args->[0]->{'normal'};
if (!defined($text)) {
# happens with bogus @-commands without argument, like @strong something
#cluck "text not defined in _convert_style_command";
#print STDERR Texinfo::Parser::_print_current($command);
return '';
}
# handle the effect of kbdinputstyle
if ($cmdname eq 'kbd' and $command->{'extra'}
and $command->{'extra'}->{'code'}) {
$cmdname = 'code';
}
my $attribute_hash = {};
if ($self->in_preformatted()) {
$attribute_hash = $self->{'style_commands_formatting'}->{'preformatted'};
} elsif (!$self->in_string()) {
$attribute_hash = $self->{'style_commands_formatting'}->{'normal'};
}
if (defined($attribute_hash->{$cmdname})) {
if (defined($attribute_hash->{$cmdname}->{'attribute'})) {
my ($style, $class, $attribute_text)
= _parse_attribute ($attribute_hash->{$cmdname}->{'attribute'});
my $open = $self->_attribute_class($style, $class);
if ($open ne '') {
$text = $open . "$attribute_text>"
. $text . "</$style>";
} elsif ($attribute_text ne '') {
$text = "<$style $attribute_text>". $text . "</$style>";
}
}
if (defined($attribute_hash->{$cmdname}->{'quote'})) {
$text = $self->get_conf('OPEN_QUOTE_SYMBOL') . $text
. $self->get_conf('CLOSE_QUOTE_SYMBOL');
}
}
return $text;
}
sub _convert_w_command($$$$)
{
my $self = shift;
my $cmdname = shift;
my $command = shift;
my $args = shift;
my $text = $args->[0]->{'normal'};
if (!defined($text)) {
$text = '';
}
if ($self->in_string) {
return $text;
} else {
return $text . '<!-- /@w -->';
}
}
$default_commands_conversion{'w'} = \&_convert_w_command;
sub _convert_value_command($$$$)
{
my $self = shift;
my $cmdname = shift;
my $command = shift;
my $args = shift;
return $self->convert_tree($self->gdt('@{No value for `{value}\'@}',
{'value' => $command->{'type'}}));
}
$default_commands_conversion{'value'} = \&_convert_value_command;
sub _convert_email_command($$$$)
{
my $self = shift;
my $cmdname = shift;
my $command = shift;
my $args = shift;
my $mail_arg = shift @$args;
my $text_arg = shift @$args;
my $mail = '';
my $mail_string = '';
if (defined($mail_arg)) {
$mail = $mail_arg->{'monospace'};
$mail_string = $mail_arg->{'monospacestring'};
}
my $text = '';
if (defined($text_arg)) {
$text = $text_arg->{'normal'};
}
$text = $mail unless ($text ne '');
return $text if ($mail eq '');
if ($self->in_string()) {
return "$mail_string ($text)";
} else {
return "<a href=\"mailto:$mail_string\">$text</a>";
}
}
$default_commands_conversion{'email'} = \&_convert_email_command;
sub _convert_explained_command($$$$)
{
my $self = shift;
my $cmdname = shift;
my $command = shift;
my $args = shift;
my $with_explanation;
my $explanation_string;
if ($args->[1] and defined($args->[1]->{'string'})
and $args->[1]->{'string'} =~ /\S/) {
$with_explanation = 1;
$explanation_string = $args->[1]->{'string'};
}
if ($command->{'extra'}->{'explanation_contents'}) {
$explanation_string = $self->convert_tree_new_formatting_context(
{'type' => '_string',
'contents' => $command->{'extra'}->{'explanation_contents'}},
$cmdname, $cmdname);
}
my $result = $args->[0]->{'normal'};
if (!$self->in_string()) {
if (defined($explanation_string)) {
$result = "<$cmdname title=\"$explanation_string\">".$result;
} else {
$result = "<$cmdname>".$result;
}
$result .= "</$cmdname>";
}
if ($with_explanation) {
$result = $self->convert_tree($self->gdt('{explained_string} ({explanation})',
{'explained_string' => {'type' => '_converted',
'text' => $result},
'explanation' => $args->[1]->{'tree'} }));
}
return $result;
}
foreach my $explained_command (keys(%explained_commands)) {
$default_commands_conversion{$explained_command}
= \&_convert_explained_command;
}
sub _convert_anchor_command($$$$)
{
my $self = shift;
my $cmdname = shift;
my $command = shift;
my $args = shift;
my $id = $self->command_id($command);
if (defined($id) and $id ne '' and !@{$self->{'multiple_pass'}}
and !$self->in_string()) {
return "<a name=\"$id\"></a>";
}
return '';
}
$default_commands_conversion{'anchor'} = \&_convert_anchor_command;
my $foot_num;
my $foot_lines;
my $NO_NUMBER_FOOTNOTE_SYMBOL = '*';
# to avoid duplicate names, use a prefix that cannot happen in anchors
my $target_prefix = "t_h";
my %footnote_id_numbers;
sub _convert_footnote_command($$$$)
{
my $self = shift;
my $cmdname = shift;
my $command = shift;
my $args = shift;
my $number_in_doc;
$foot_num++;
if ($self->get_conf('NUMBER_FOOTNOTES')) {
$number_in_doc = $foot_num;
} else {
$number_in_doc = $NO_NUMBER_FOOTNOTE_SYMBOL;
}
return "($number_in_doc)" if ($self->in_string());
#print STDERR "FOOTNOTE $command\n";
my $docid = $self->command_id($command);
my $footid = $self->command_target($command);
# happens for bogus footnotes
if (!defined($footid)) {
die "docid defined but not footid for footnote $foot_num\n"
if (defined($docid));
return '';
}
my $document_filename;
my $footnote_filename;
if ($self->get_conf('footnotestyle') eq 'separate') {
$footnote_filename = $self->command_filename($command);
$document_filename = $self->{'current_filename'};
$footnote_filename = '' if (!defined($footnote_filename));
$document_filename = '' if (!defined($document_filename));
if ($document_filename eq $footnote_filename) {
$document_filename = $footnote_filename = '';
}
} else {
$document_filename = $footnote_filename = '';
}
my $footnote_text;
if ($args->[0]) {
$footnote_text = $args->[0]->{'normal'};
} else {
$footnote_text = '';
}
chomp ($footnote_text);
$footnote_text .= "\n";
if (@{$self->{'multiple_pass'}}) {
$footid = $target_prefix.$self->{'multiple_pass'}->[-1].'_'.$footid.'_'.$foot_num;
$docid = $target_prefix.$self->{'multiple_pass'}->[-1].'_'.$docid.'_'.$foot_num;
} else {
if (!defined($footnote_id_numbers{$footid})) {
$footnote_id_numbers{$footid} = $foot_num;
} else {
# This should rarely happen, except for @footnote is @copying and
# multiple @insertcopying...
# Here it is not checked that there is no clash with another anchor.
# However, unless there are more than 1000 footnotes this should not
# happen.
$footid .= '_'.$foot_num;
$docid .= '_'.$foot_num;
}
}
$foot_lines .= '<h3>' .
"<a name=\"$footid\" href=\"$document_filename#$docid\">($number_in_doc)</a></h3>\n"
. $footnote_text;
my $footnote_number_text;
if ($self->in_preformatted()) {
$footnote_number_text = "($number_in_doc)";
} else {
$footnote_number_text = "<sup>$number_in_doc</sup>";
}
return "<a name=\"$docid\" href=\"$footnote_filename#$footid\">$footnote_number_text</a>";
}
$default_commands_conversion{'footnote'} = \&_convert_footnote_command;
sub _convert_uref_command($$$$)
{
my $self = shift;
my $cmdname = shift;
my $command = shift;
my $args = shift;
my @args = @$args;
my $url_arg = shift @args;
my $text_arg = shift @args;
my $replacement_arg = shift @args;
my ($url, $text, $replacement);
$url = $url_arg->{'monospacestring'} if defined($url_arg);
$text = $text_arg->{'normal'} if defined($text_arg);
$replacement = $replacement_arg->{'normal'} if defined($replacement_arg);
$text = $replacement if (defined($replacement) and $replacement ne '');
$text = $url if (!defined($text) or $text eq '');
return $text if (!defined($url) or $url eq '');
return "$text ($url)" if ($self->in_string());
return "<a href=\"$url\">$text</a>";
}
$default_commands_conversion{'uref'} = \&_convert_uref_command;
$default_commands_conversion{'url'} = \&_convert_uref_command;
my @image_files_extensions = ('.png', '.jpg', '.jpeg', '.gif');
sub _convert_image_command($$$$)
{
my $self = shift;
my $cmdname = shift;
my $command = shift;
my $args = shift;
my @extensions = @image_files_extensions;
if (defined($args->[0]->{'monospacetext'}) and $args->[0]->{'monospacetext'} ne '') {
my $basefile = $args->[0]->{'monospacetext'};
return $basefile if ($self->in_string());
my $extension;
if (defined($args->[4]) and defined($args->[4]->{'monospacetext'})) {
$extension = $args->[4]->{'monospacetext'};
unshift @extensions, ("$extension", ".$extension");
}
my $image_file;
foreach my $extension (@extensions) {
if ($self->Texinfo::Common::locate_include_file ($basefile.$extension)) {
# use the basename and not the file found. It is agreed that it is
# better, since in any case the files are moved.
$image_file = $basefile.$extension;
last;
}
}
if (!defined($image_file) or $image_file eq '') {
if (defined($extension) and $extension ne '') {
$image_file = "$basefile.$extension";
} else {
$image_file = "$basefile.jpg";
}
#cluck "err ($self->{'ignore_notice'})";
$self->line_warn(sprintf(
$self->__("\@image file `%s' (for HTML) not found, using `%s'"),
$basefile, $image_file), $command->{'line_nr'});
}
if (defined($self->get_conf('IMAGE_LINK_PREFIX'))) {
$image_file = $self->get_conf('IMAGE_LINK_PREFIX') . $image_file;
}
if ($self->in_preformatted()) {
my $alt_text;
if (defined($args->[3]) and defined($args->[3]->{'normal'})) {
$alt_text = $args->[3]->{'normal'};
}
if (!defined($alt_text) or ($alt_text eq '')) {
$alt_text = $self->protect_text($basefile);
}
return "[ $alt_text ]";
} else {
my $alt_string;
if (defined($args->[3]) and defined($args->[3]->{'string'})) {
$alt_string = $args->[3]->{'string'};
}
if (!defined($alt_string) or ($alt_string eq '')) {
$alt_string = $self->protect_text($basefile);
}
return "<img src=\"".$self->protect_text($image_file)."\" alt=\"$alt_string\">";
}
}
return '';
}
$default_commands_conversion{'image'} = \&_convert_image_command;
#sub _convert_math_command($$$$)
#{
# my $self = shift;
# my $cmdname = shift;
# my $command = shift;
# my $args = shift;
#
# return $args->[0]->{'normal'};
#}
#$default_commands_conversion{'math'} = \&_convert_math_command;
sub _convert_accent_command($$$$)
{
my $self = shift;
my $cmdname = shift;
my $command = shift;
my $args = shift;
return $self->xml_accents($command, $self->in_upper_case());
}
foreach my $command (keys(%accent_commands)) {
$default_commands_conversion{$command} = \&_convert_accent_command;
}
# key is formatted as code since it is in code_style_commands
sub _convert_key_command($$$$)
{
my $self = shift;
my $cmdname = shift;
my $command = shift;
my $args = shift;
my $text = $args->[0]->{'normal'};
if (!defined($text)) {
# happens with bogus @-commands without argument, like @strong something
#print STDERR Texinfo::Parser::_print_current($command);
return '';
}
if ($self->in_string()) {
return $text;
}
#return $self->protect_text('<') .$text .$self->protect_text('>');
my $class = $cmdname;
if (!$self->in_code()) {
return $self->_attribute_class('tt', $class).'>'.$text .'</tt>';;
} else {
my $open = $self->_attribute_class('span', $class);
if ($open ne '') {
return $open.'>'.$text.'</span>';
} else {
return $text;
}
}
}
$default_commands_conversion{'key'} = \&_convert_key_command;
# argument is formatted as code since indicateurl is in code_style_commands
sub _convert_indicateurl_command($$$$)
{
my $self = shift;
my $cmdname = shift;
my $command = shift;
my $args = shift;
my $text = $args->[0]->{'normal'};
if (!defined($text)) {
# happens with bogus @-commands without argument, like @strong something
#print STDERR Texinfo::Parser::_print_current($command);
return '';
}
if (!$self->in_string()) {
return $self->get_conf('OPEN_QUOTE_SYMBOL').'<code>' .$text
.'</code>'.$self->get_conf('CLOSE_QUOTE_SYMBOL');
} else {
return $self->get_conf('OPEN_QUOTE_SYMBOL').$text.
$self->get_conf('CLOSE_QUOTE_SYMBOL');
}
}
$default_commands_conversion{'indicateurl'} = \&_convert_indicateurl_command;
sub _convert_ctrl_command($$$$)
{
my $self = shift;
my $cmdname = shift;
my $command = shift;
my $args = shift;
my $text = $args->[0]->{'normal'};
if (!defined($text)) {
# happens with bogus @-commands without argument, like @strong something
#print STDERR Texinfo::Parser::_print_current($command);
return '';
}
return $self->protect_text('^') .$text;
}
$default_commands_conversion{'ctrl'} = \&_convert_ctrl_command;
sub _convert_titlefont_command($$$$)
{
my $self = shift;
my $cmdname = shift;
my $command = shift;
my $args = shift;
my $text = $args->[0]->{'normal'};
if (!defined($text)) {
# happens with bogus @-commands without argument, like @strong something
#print STDERR Texinfo::Parser::_print_current($command);
return '';
}
return &{$self->{'format_heading_text'}}($self, 'titlefont', $text, 0, $command);
}
$default_commands_conversion{'titlefont'} = \&_convert_titlefont_command;
sub _convert_U_command($$$$)
{
my $self = shift;
my $cmdname = shift;
my $command = shift;
my $args = shift;
my $arg = $args->[0]->{'normal'};
my $res;
if (defined($arg) && $arg) {
# checks on the value already done in Parser, just output it here.
$res = "&#x$arg;";
} else {
$res = '';
}
return $res;
}
$default_commands_conversion{'U'} = \&_convert_U_command;
sub _default_comment($$) {
my $self = shift;
my $text = shift;
return $self->xml_comment(' '.$text);
}
sub protect_text($$) {
my $self = shift;
my $text = shift;
return &{$self->{'format_protect_text'}}($self, $text);
}
sub _default_protect_text($$) {
my $self = shift;
my $text = shift;
my $result = $self->xml_protect_text($text);
$result =~ s/\f/&#12;/g;
return $result;
}
sub _default_heading_text($$$$$)
{
my $self = shift;
my $cmdname = shift;
my $text = shift;
my $level = shift;
my $command = shift;
return '' if ($text !~ /\S/);
# This should seldom happen.
if ($self->in_string()) {
$text .= "\n" unless ($cmdname eq 'titlefont');
return $text;
}
my $class;
if ($cmdname eq 'node') {
$class = 'node-heading';
} else {
$class = $cmdname;
}
my $align = '';
$align = ' align="center"' if ($cmdname eq 'centerchap' or $cmdname eq 'settitle');
if ($level < 1) {
$level = 1;
} elsif ($level > $self->get_conf('MAX_HEADER_LEVEL')) {
$level = $self->get_conf('MAX_HEADER_LEVEL');
}
my $result = $self->_attribute_class("h$level", $class) ."$align>$text</h$level>";
# titlefont appears inline in text, so no end of line is
# added. The end of line should be added by the user if needed.
$result .= "\n" unless ($cmdname eq 'titlefont');
$result .= $self->get_conf('DEFAULT_RULE') . "\n"
if ($cmdname eq 'part'
and defined($self->get_conf('DEFAULT_RULE'))
and $self->get_conf('DEFAULT_RULE') ne '');
return $result;
}
# Associated to a button
sub _default_node_direction($$)
{
my $self = shift;
my $direction = shift;
my $result = undef;
my $href = $self->_element_direction($self->{'current_element'},
$direction, 'href');
my $node = $self->_element_direction($self->{'current_element'},
$direction, 'node');
my $anchor;
if (defined($href) and defined($node) and $node =~ /\S/) {
my $anchor_attributes = $self->_direction_href_attributes($direction);
$anchor = "<a href=\"$href\"${anchor_attributes}>$node</a>";
#} elsif (defined($node) and $node =~ /\S/) {
# $anchor = $node;
#} else {
}
if (defined($anchor)) {
# i18n
$result = $self->get_conf('BUTTONS_TEXT')->{$direction}.": $anchor";
}
return $result;
}
# how to create IMG tag
# this is only used in html, and only if ICONS is set and the button
# is active.
sub _default_button_icon_img($$$;$)
{
my $self = shift;
my $button = shift;
my $icon = shift;
my $name = shift;
return '' if (!defined($icon));
$button = "" if (!defined ($button));
$name = '' if (!defined($name));
my $alt = '';
if ($name ne '') {
if ($button ne '') {
$alt = "$button: $name";
} else {
$alt = $name;
}
} else {
$alt = $button;
}
return qq{<img src="$icon" border="0" alt="$alt" align="middle">};
}
sub _direction_href_attributes($$)
{
my $self = shift;
my $direction = shift;
my $href_attributes = '';
if ($self->get_conf('USE_ACCESSKEY')
and $self->get_conf('BUTTONS_ACCESSKEY')) {
my $accesskey = $self->get_conf('BUTTONS_ACCESSKEY')->{$direction};
if (defined($accesskey) and ($accesskey ne '')) {
$href_attributes = " accesskey=\"$accesskey\"";
}
}
if ($self->get_conf('USE_REL_REV') and $self->get_conf('BUTTONS_REL')) {
my $button_rel = $self->get_conf('BUTTONS_REL')->{$direction};
if (defined($button_rel) and ($button_rel ne '')) {
$href_attributes .= " rel=\"$button_rel\"";
}
}
return $href_attributes;
}
sub _default_button_formatting($$)
{
my $self = shift;
my $button = shift;
my ($active, $passive);
if (ref($button) eq 'CODE') {
$active = &$button($self);
} elsif (ref($button) eq 'SCALAR') {
$active = "$$button" if defined($$button);
} elsif (ref($button) eq 'ARRAY' and scalar(@$button == 2)) {
my $text = $button->[1];
my $button_href = $button->[0];
# verify that $button_href is simple text and text is a reference
if (defined($button_href) and !ref($button_href)
and defined($text) and (ref($text) eq 'SCALAR') and defined($$text)) {
# use given text
my $href = $self->_element_direction($self->{'current_element'},
$button_href, 'href');
if ($href) {
my $anchor_attributes = $self->_direction_href_attributes($button_href);
$active = "<a href=\"$href\"${anchor_attributes}>$$text</a>";
} else {
$passive = $$text;
}
# button_href is simple text and text is a reference on code
} elsif (defined($button_href) and !ref($button_href)
and defined($text) and (ref($text) eq 'CODE')) {
$active = &$text($self, $button_href);
# button_href is simple text and text is also a simple text
} elsif (defined($button_href) and !ref($button_href)
and defined($text) and !ref($text)) {
if ($text =~ s/^->\s*//) {
$active = $self->_element_direction($self->{'current_element'},
$button_href, $text);
} else {
my $href = $self->_element_direction($self->{'current_element'},
$button_href, 'href');
my $text_formatted = $self->_element_direction($self->{'current_element'},
$button_href, $text);
if ($href) {
my $anchor_attributes = $self->_direction_href_attributes($button_href);
$active = "<a href=\"$href\"${anchor_attributes}>$text_formatted</a>";
} else {
$passive = $text_formatted;
}
}
}
} elsif ($button eq ' ') {
# handle space button
if ($self->get_conf('ICONS') and $self->get_conf('ACTIVE_ICONS')
and defined($self->get_conf('ACTIVE_ICONS')->{$button})
and $self->get_conf('ACTIVE_ICONS')->{$button} ne '') {
my $button_name = $self->get_conf('BUTTONS_NAME')->{$button};
$active = &{$self->{'format_button_icon_img'}}($self, $button_name,
$self->get_conf('ACTIVE_ICONS')->{' '});
} else {
$active = $self->get_conf('BUTTONS_TEXT')->{$button};
}
} else {
my $href = $self->_element_direction($self->{'current_element'},
$button, 'href');
if ($href) {
# button is active
my $btitle = '';
if ($self->get_conf('BUTTONS_GOTO')
and defined($self->get_conf('BUTTONS_GOTO')->{$button})) {
$btitle = ' title="' . $self->get_conf('BUTTONS_GOTO')->{$button} . '"';
}
if ($self->get_conf('USE_ACCESSKEY') and $self->get_conf('BUTTONS_ACCESSKEY')) {
my $accesskey = $self->get_conf('BUTTONS_ACCESSKEY')->{$button};
if (defined($accesskey) and $accesskey ne '') {
$btitle .= " accesskey=\"$accesskey\"";
}
}
if ($self->get_conf('USE_REL_REV') and ($self->get_conf('BUTTONS_REL'))) {
my $button_rel = $self->get_conf('BUTTONS_REL')->{$button};
if (defined($button_rel) and $button_rel ne '') {
$btitle .= " rel=\"$button_rel\"";
}
}
my $use_icon;
if ($self->get_conf('ICONS') and $self->get_conf('ACTIVE_ICONS')
and $self->get_conf('BUTTONS_NAME')) {
my $active_icon = $self->get_conf('ACTIVE_ICONS')->{$button};
my $button_name = $self->get_conf('BUTTONS_NAME')->{$button};
if (defined($active_icon) and $active_icon ne ''
and defined($button_name)) {
# use icon
$active = "<a href=\"$href\"${btitle}>".
&{$self->{'format_button_icon_img'}}($self, $button_name, $active_icon,
$self->_element_direction($self->{'current_element'},
$button, 'string')) ."</a>";
$use_icon = 1;
}
}
if (!$use_icon) {
# use text
$active = '[' . "<a href=\"$href\"${btitle}>".
$self->get_conf('BUTTONS_TEXT')->{$button}."</a>" . ']';
}
} else {
# button is passive
my $use_icon;
if ($self->get_conf('ICONS') and $self->get_conf('PASSIVE_ICONS')
and $self->get_conf('BUTTONS_NAME')) {
my $passive_icon = $self->get_conf('PASSIVE_ICONS')->{$button};
my $button_name = $self->get_conf('BUTTONS_NAME')->{$button};
if ($passive_icon and $passive_icon ne '') {
$passive = &{$self->{'format_button_icon_img'}}($self, $button_name,
$passive_icon,
$self->_element_direction($self->{'current_element'},
$button, 'string'));
$use_icon = 1;
}
}
if (!$use_icon) {
$passive = '[' . $self->get_conf('BUTTONS_TEXT')->{$button} . ']';
}
}
}
return ($active, $passive);
}
my %html_default_node_directions;
foreach my $node_directions ('NodeNext', 'NodePrev', 'NodeUp') {
$html_default_node_directions{$node_directions} = 1;
}
sub _default_navigation_header_panel($$$$;$)
{
my $self = shift;
my $buttons = shift;
my $cmdname = shift;
my $command = shift;
my $vertical = shift;
# if VERTICAL_HEAD_NAVIGATION, the buttons are in a vertical table which
# is itself in the first column of a table opened in header_navigation
#my $vertical = $self->get_conf('VERTICAL_HEAD_NAVIGATION');
my $first_button = 1;
my $result = '';
if ($self->get_conf('HEADER_IN_TABLE')) {
$result .= $self->_attribute_class('table', 'header')
.' cellpadding="1" cellspacing="1" border="0">'."\n";
$result .= "<tr>" unless $vertical;
} else {
$result .= $self->_attribute_class('div', 'header').">\n<p>\n";
}
foreach my $button (@$buttons) {
if ($self->get_conf('HEADER_IN_TABLE')) {
$result .= qq{<tr valign="top" align="left">\n} if $vertical;
$result .= qq{<td valign="middle" align="left">};
}
my $direction;
if (ref($button) eq 'ARRAY'
and defined($button->[0]) and !ref($button->[0])) {
$direction = $button->[0];
} elsif (defined($button) and !ref($button)) {
$direction = $button;
}
my ($active, $passive) = &{$self->{'format_button'}}($self, $button);
if ($self->get_conf('HEADER_IN_TABLE')) {
if (defined($active)) {
$first_button = 0 if ($first_button);
$result .= $active;
} elsif (defined($passive)) {
$first_button = 0 if ($first_button);
$result .= $passive;
}
$result .= "</td>\n";
$result .= "</tr>\n" if $vertical;
} elsif (defined($active)) {
# only active buttons are print out when not in table
if (defined($direction)
and $html_default_node_directions{$direction} and !$first_button) {
$active = ', ' .$active;
}
$result .= $active;
$first_button = 0 if ($first_button);
}
}
if ($self->get_conf('HEADER_IN_TABLE')) {
$result .= "</tr>" unless $vertical;
$result .= "</table>\n";
} else {
$result .= "</p>\n</div>\n";
}
return $result;
}
sub _default_navigation_header($$$$)
{
my $self = shift;
my $buttons = shift;
my $cmdname = shift;
my $command = shift;
my $result = '';
if ($self->get_conf('VERTICAL_HEAD_NAVIGATION')) {
$result .= '<table border="0" cellpadding="0" cellspacing="0">
<tr valign="top">
<td align="left">
';
}
$result .= &{$self->{'format_navigation_header_panel'}}($self, $buttons,
$cmdname, $command,
$self->get_conf('VERTICAL_HEAD_NAVIGATION'));
if ($self->get_conf('VERTICAL_HEAD_NAVIGATION')) {
$result .= '</td>
<td align="left">
';
} elsif ($self->get_conf('SPLIT') eq 'node') {
$result .= $self->get_conf('DEFAULT_RULE')."\n";
}
return $result;
}
sub _default_element_header($$$$)
{
my $self = shift;
my $cmdname = shift;
my $command = shift;
my $element = shift;
my $result = '';
print STDERR "Element $element (@{$element->{'contents'}}) ".
Texinfo::Structuring::_print_element_command_texi($element) ."\n"
if ($self->get_conf('DEBUG'));
# Do the heading if the command is the first command in the element
if (($element->{'contents'}->[0] eq $command
or (!$element->{'contents'}->[0]->{'cmdname'}
and $element->{'contents'}->[1] eq $command))
# and there is more than one element
and ($element->{'element_next'} or $element->{'element_prev'})) {
my $is_top = $self->element_is_top($element);
my $first_in_page = (defined($element->{'filename'})
and $self->{'counter_in_file'}->{$element->{'filename'}} == 1);
my $previous_is_top = ($element->{'element_prev'}
and $self->element_is_top($element->{'element_prev'}));
print STDERR "Header ($previous_is_top, $is_top, $first_in_page): "
.Texinfo::Structuring::_print_root_command_texi($command)."\n"
if ($self->get_conf('DEBUG'));
if ($is_top) {
# use TOP_BUTTONS for top.
$result .= &{$self->{'format_navigation_header'}}($self,
$self->get_conf('TOP_BUTTONS'), $cmdname, $command)
if ($self->get_conf('SPLIT') or $self->get_conf('HEADERS'));
} else {
if ($first_in_page and !$self->get_conf('HEADERS')) {
if ($self->get_conf('SPLIT') eq 'chapter') {
$result .= &{$self->{'format_navigation_header'}}($self,
$self->get_conf('CHAPTER_BUTTONS'), $cmdname, $command);
$result .= $self->get_conf('DEFAULT_RULE') ."\n"
if (defined($self->get_conf('DEFAULT_RULE'))
and !$self->get_conf('VERTICAL_HEAD_NAVIGATION'));
} elsif ($self->get_conf('SPLIT') eq 'section') {
$result .= &{$self->{'format_navigation_header'}}($self,
$self->get_conf('SECTION_BUTTONS'), $cmdname, $command);
}
}
if (($first_in_page or $previous_is_top)
and $self->get_conf('HEADERS')) {
$result .= &{$self->{'format_navigation_header'}}($self,
$self->get_conf('SECTION_BUTTONS'), $cmdname, $command);
} elsif($self->get_conf('HEADERS') or $self->get_conf('SPLIT') eq 'node') {
# got to do this here, as it isn't done otherwise since
# navigation_header is not called
$result .= &{$self->{'format_navigation_header_panel'}}($self,
$self->get_conf('SECTION_BUTTONS'), $cmdname, $command);
}
}
}
return $result;
}
sub _convert_heading_command($$$$$)
{
my $self = shift;
my $cmdname = shift;
my $command = shift;
my $args = shift;
my $content = shift;
my $result = '';
# not clear that it may really happen
if ($self->in_string) {
$result .= $self->command_string($command) ."\n" if ($cmdname ne 'node');
$result .= $content if (defined($content));
return $result;
}
my $element_id = $self->command_id($command);
$result .= "<a name=\"$element_id\"></a>\n"
if (defined($element_id) and $element_id ne '');
print STDERR "Process $command "
.Texinfo::Structuring::_print_root_command_texi($command)."\n"
if ($self->get_conf('DEBUG'));
my $element;
if ($Texinfo::Common::root_commands{$command->{'cmdname'}}
and $command->{'parent'}
and $command->{'parent'}->{'type'}
and $command->{'parent'}->{'type'} eq 'element') {
$element = $command->{'parent'};
}
if ($element) {
$result .= &{$self->{'format_element_header'}}($self, $cmdname,
$command, $element);
}
my $heading_level;
# node is used as heading if there is nothing else.
if ($cmdname eq 'node') {
if (!$element or (!$element->{'extra'}->{'section'}
and $element->{'extra'}->{'node'}
and $element->{'extra'}->{'node'} eq $command
# bogus node may not have been normalized
and defined($command->{'extra'}->{'normalized'}))) {
if ($command->{'extra'}->{'normalized'} eq 'Top') {
$heading_level = 0;
} else {
$heading_level = 3;
}
}
} else {
$heading_level = $command->{'level'};
}
my $heading = $self->command_text($command);
# $heading not defined may happen if the command is a @node, for example
# if there is an error in the node.
if (defined($heading) and $heading ne '' and defined($heading_level)) {
if ($self->get_conf('TOC_LINKS')
and $Texinfo::Common::root_commands{$cmdname}
and $Texinfo::Common::sectioning_commands{$cmdname}) {
my $content_href = $self->command_contents_href($command, 'contents',
$self->{'current_filename'});
if ($content_href) {
$heading = "<a href=\"$content_href\">$heading</a>";
}
}
if ($self->in_preformatted()) {
$result .= '<strong>'.$heading.'</strong>'."\n";
} else {
# if the level was changed, set the command name right
if ($cmdname ne 'node'
and $heading_level ne $Texinfo::Common::command_structuring_level{$cmdname}) {
$cmdname
= $Texinfo::Common::level_to_structuring_command{$cmdname}->[$heading_level];
}
$result .= &{$self->{'format_heading_text'}}($self, $cmdname, $heading,
$heading_level +$self->get_conf('CHAPTER_HEADER_LEVEL') -1, $command);
}
}
$result .= $content if (defined($content));
return $result;
}
foreach my $command (keys(%sectioning_commands), 'node') {
$default_commands_conversion{$command} = \&_convert_heading_command;
}
sub _convert_raw_command($$$$)
{
my $self = shift;
my $cmdname = shift;
my $command = shift;
my $content = shift;
if ($cmdname eq $self->{'output_format'}) {
return $content;
}
$self->line_warn(sprintf($self->__("raw format %s is not converted"),
$cmdname), $command->{'line_nr'});
return $self->protect_text($content);
}
foreach my $command (keys(%format_raw_commands)) {
$default_commands_conversion{$command} = \&_convert_raw_command;
}
sub _convert_inline_command($$$$)
{
my $self = shift;
my $cmdname = shift;
my $command = shift;
my $args = shift;
my $format_arg = shift @$args;
my $format;
if (defined($format_arg)) {
$format = $format_arg->{'monospacetext'};
}
return '' if (!defined($format) or $format eq '');
my $arg_index = undef;
if ($inline_format_commands{$cmdname}) {
if ($cmdname eq 'inlinefmtifelse'
and ! $self->{'expanded_formats_hash'}->{$format}) {
$arg_index = 1;
} elsif ($self->{'expanded_formats_hash'}->{$format}) {
$arg_index = 0;
}
} elsif (defined($command->{'extra'}->{'expand_index'})) {
$arg_index = 0;
}
if (defined($arg_index) and $arg_index < scalar(@$args)) {
my $text_arg = $args->[$arg_index];
if ($text_arg) {
if ($text_arg->{'normal'}) {
return $text_arg->{'normal'};
} elsif ($text_arg->{'raw'}) {
return $text_arg->{'raw'};
}
}
}
return '';
}
foreach my $command (keys(%inline_commands)) {
$default_commands_conversion{$command} = \&_convert_inline_command;
}
sub _indent_with_table ($)
{
my $content = shift;
return '<table><tr><td>&nbsp;</td><td>'.$content."</td></tr></table>\n";
}
my $html_menu_entry_index = 0;
sub _convert_preformatted_command($$$$)
{
my $self = shift;
my $cmdname = shift;
my $command = shift;
my $content = shift;
if ($cmdname eq 'menu') {
$html_menu_entry_index = 0;
}
if ($content ne '' and !$self->in_string()) {
if ($self->get_conf('COMPLEX_FORMAT_IN_TABLE')) {
if ($indented_preformatted_commands{$cmdname}) {
return _indent_with_table ($content);
} else {
return $content."\n";
}
} else {
return $self->_attribute_class('div', $cmdname).">\n".$content.'</div>'."\n";
}
} else {
return $content;
}
}
foreach my $preformatted_command (keys(%preformatted_commands)) {
$default_commands_conversion{$preformatted_command}
= \&_convert_preformatted_command;
}
sub _convert_indented_command($$$$)
{
my $self = shift;
my $cmdname = shift;
my $command = shift;
my $content = shift;
if ($content ne '' and !$self->in_string()) {
if ($self->get_conf('COMPLEX_FORMAT_IN_TABLE')) {
return _indent_with_table ($content);
} else {
return $self->_attribute_class('blockquote', $cmdname).">\n"
.$content.'</blockquote>'."\n";
}
} else {
return $content;
}
}
$default_commands_conversion{'indentedblock'} = \&_convert_indented_command;
$default_commands_conversion{'smallindentedblock'}
= \&_convert_indented_command;
sub _convert_verbatim_command($$$$)
{
my $self = shift;
my $cmdname = shift;
my $command = shift;
my $content = shift;
if (!$self->in_string) {
return $self->_attribute_class('pre', $cmdname).'>'
.$content . '</pre>';
} else {
return $content;
}
}
$default_commands_conversion{'verbatim'} = \&_convert_verbatim_command;
sub _convert_verbatiminclude_command($$$$)
{
my $self = shift;
my $cmdname = shift;
my $command = shift;
my $args = shift;
my $verbatim_include_verbatim
= $self->Texinfo::Common::expand_verbatiminclude($command);
if (defined($verbatim_include_verbatim)) {
return $self->convert_tree($verbatim_include_verbatim);
} else {
return '';
}
}
$default_commands_conversion{'verbatiminclude'}
= \&_convert_verbatiminclude_command;
sub _convert_command_noop($$$$)
{
my $self = shift;
my $cmdname = shift;
my $command = shift;
my $content = shift;
return $content;
}
$default_commands_conversion{'raggedright'} = \&_convert_command_noop;
$default_commands_conversion{'flushleft'} = \&_convert_command_noop;
$default_commands_conversion{'flushright'} = \&_convert_command_noop;
$default_commands_conversion{'group'} = \&_convert_command_noop;
sub _convert_sp_command($$$$)
{
my $self = shift;
my $cmdname = shift;
my $command = shift;
my $args = shift;
if (defined($command->{'extra'}->{'misc_args'}->[0])) {
my $sp_nr = $command->{'extra'}->{'misc_args'}->[0];
if ($self->in_preformatted() or $self->in_string()) {
return "\n" x $sp_nr;
} else {
return "<br>\n" x $sp_nr;
}
}
}
$default_commands_conversion{'sp'} = \&_convert_sp_command;
sub _convert_exdent_command($$$$)
{
my $self = shift;
my $cmdname = shift;
my $command = shift;
my $args = shift;
# FIXME do something better with css and span?
my $preformatted = $self->in_preformatted();
if ($self->in_preformatted() or $self->in_string()) {
return $self->_convert_preformatted_type($cmdname, $command,
$args->[0]->{'normal'} ."\n");
} else {
# ignore alignment information
return "<p>".$args->[0]->{'normal'} ."\n</p>";
}
}
$default_commands_conversion{'exdent'} = \&_convert_exdent_command;
sub _convert_center_command($$$$)
{
my $self = shift;
my $cmdname = shift;
my $command = shift;
my $args = shift;
if ($self->in_string()) {
return $self->_convert_preformatted_type($cmdname, $command,
$args->[0]->{'normal'}."\n");
} else {
return "<div align=\"center\">".$args->[0]->{'normal'}."\n</div>";
}
}
$default_commands_conversion{'center'} = \&_convert_center_command;
sub _convert_author_command($$$$)
{
my $self = shift;
my $cmdname = shift;
my $command = shift;
my $args = shift;
return '' if (!$args->[0] or !$command->{'extra'}->{'titlepage'});
if (!$self->in_string()) {
return "<strong>$args->[0]->{'normal'}</strong><br>\n";
} else {
return $args->[0]->{'normal'}."\n";
}
}
$default_commands_conversion{'author'} = \&_convert_author_command;
sub _convert_title_command($$$$)
{
my $self = shift;
my $cmdname = shift;
my $command = shift;
my $args = shift;
return '' if (!$args->[0]);
if (!$self->in_string()) {
return "<h1>$args->[0]->{'normal'}</h1>\n";
} else {
return $args->[0]->{'normal'};
}
}
$default_commands_conversion{'title'} = \&_convert_title_command;
sub _convert_subtitle_command($$$$)
{
my $self = shift;
my $cmdname = shift;
my $command = shift;
my $args = shift;
return '' if (!$args->[0]);
if (!$self->in_string()) {
return "<h3 align=\"right\">$args->[0]->{'normal'}</h3>\n";
} else {
return $args->[0]->{'normal'};
}
}
$default_commands_conversion{'subtitle'} = \&_convert_subtitle_command;
sub _convert_insertcopying_command($$$$)
{
my $self = shift;
my $cmdname = shift;
my $command = shift;
if ($self->{'extra'} and $self->{'extra'}->{'copying'}) {
return $self->convert_tree({'contents'
=> $self->{'extra'}->{'copying'}->{'contents'}});
}
return '';
}
$default_commands_conversion{'insertcopying'}
= \&_convert_insertcopying_command;
sub _convert_listoffloats_command($$$$)
{
my $self = shift;
my $cmdname = shift;
my $command = shift;
my $args = shift;
if (!$self->in_string()
and $command->{'extra'} and $command->{'extra'}->{'type'}
and defined($command->{'extra'}->{'type'}->{'normalized'})
and $self->{'floats'}
and $self->{'floats'}->{$command->{'extra'}->{'type'}->{'normalized'}}
and @{$self->{'floats'}->{$command->{'extra'}->{'type'}->{'normalized'}}}) {
my $listoffloats_name = $command->{'extra'}->{'type'}->{'normalized'};
my $result = $self->_attribute_class('dl', 'listoffloats').">\n" ;
foreach my $float (@{$self->{'floats'}->{$listoffloats_name}}) {
my $float_href = $self->command_href($float);
next if (!$float_href);
$result .= '<dt>';
my $float_text = $self->command_text($float);
if (defined($float_text) and $float_text ne '') {
if ($float_href) {
$result .= "<a href=\"$float_href\">$float_text</a>";
} else {
$result .= $float_text;
}
}
$result .= '</dt>';
my $caption;
if ($float->{'extra'}->{'shortcaption'}) {
$caption = $float->{'extra'}->{'shortcaption'};
} elsif ($float->{'extra'}->{'caption'}) {
$caption = $float->{'extra'}->{'caption'};
}
my $caption_text;
if ($caption) {
$caption_text = $self->convert_tree_new_formatting_context(
$caption->{'args'}->[0], $cmdname, 'listoffloats');
} else {
$caption_text = '';
}
$result .= '<dd>'.$caption_text.'</dd>'."\n";
}
return $result . "</dl>\n";
} else {
return '';
}
}
$default_commands_conversion{'listoffloats'} = \&_convert_listoffloats_command;
sub _in_preformatted_in_menu($)
{
my $self = shift;
return 1 if ($self->get_conf('SIMPLE_MENU'));
my @pre_classes = $self->preformatted_classes_stack();
foreach my $pre_class (@pre_classes) {
return 1 if ($preformatted_commands{$pre_class});
}
return 0;
}
sub _convert_menu_command($$$$)
{
my $self = shift;
my $cmdname = shift;
my $command = shift;
my $content = shift;
return $content if ($cmdname eq 'detailmenu');
$html_menu_entry_index = 0;
if ($content !~ /\S/) {
return '';
}
if ($self->in_string()) {
return $content;
}
my $begin_row = '';
my $end_row = '';
if ($self->_in_preformatted_in_menu()) {
$begin_row = '<tr><td>';
$end_row = '</td></tr>';
}
return $self->_attribute_class('table', 'menu')
." border=\"0\" cellspacing=\"0\">${begin_row}\n"
. $content . "${end_row}</table>\n";
}
$default_commands_conversion{'menu'} = \&_convert_menu_command;
$default_commands_conversion{'detailmenu'} = \&_convert_menu_command;
sub _convert_float_command($$$$$)
{
my $self = shift;
my $cmdname = shift;
my $command = shift;
my $args = shift;
my $content = shift;
my ($caption, $prepended) = Texinfo::Common::float_name_caption($self,
$command);
my $caption_text = '';
my $prepended_text;
if ($self->in_string()) {
if ($prepended) {
$prepended_text = $self->convert_tree_new_formatting_context(
$prepended, 'float prepended');
} else {
$prepended_text = '';
}
if ($caption) {
$caption_text = $self->convert_tree_new_formatting_context(
{'contents' => $caption->{'args'}->[0]->{'contents'}},
'float caption');
}
return $prepended.$content.$caption_text;
}
my $id = $self->command_id($command);
my $label;
if (defined($id) and $id ne '') {
$label = "<a name=\"$id\"></a>";
} else {
$label = '';
}
if ($prepended) {
if ($caption) {
# prepend the prepended tree to the first paragraph
my @caption_original_contents = @{$caption->{'args'}->[0]->{'contents'}};
my @caption_contents;
my $new_paragraph;
while (@caption_original_contents) {
my $content = shift @caption_original_contents;
if ($content->{'type'} and $content->{'type'} eq 'paragraph') {
%{$new_paragraph} = %{$content};
$new_paragraph->{'contents'} = [@{$content->{'contents'}}];
unshift (@{$new_paragraph->{'contents'}}, {'cmdname' => 'strong',
'args' => [{'type' => 'brace_command_arg',
'contents' => [$prepended]}]});
push @caption_contents, $new_paragraph;
last;
} else {
push @caption_contents, $content;
}
}
push @caption_contents, @caption_original_contents;
if ($new_paragraph) {
$caption_text = $self->convert_tree_new_formatting_context(
{'contents' => \@caption_contents}, 'float caption');
$prepended_text = '';
}
}
if ($caption_text eq '') {
$prepended_text = $self->convert_tree_new_formatting_context(
$prepended, 'float prepended');
if ($prepended_text ne '') {
$prepended_text = '<p><strong>'.$prepended_text.'</strong></p>';
}
}
} else {
$prepended_text = '';
}
#print STDERR "Float $prepended_text: caption $caption ".
# Texinfo::Parser::_print_current ($caption)."\n";
if ($caption and $caption_text eq '') {
$caption_text = $self->convert_tree_new_formatting_context(
$caption->{'args'}->[0], 'float caption');
}
if ($prepended_text.$caption_text ne '') {
$prepended_text = $self->_attribute_class('div','float-caption'). '>'
. $prepended_text;
$caption_text .= '</div>';
}
return $self->_attribute_class('div','float'). '>' .$label."\n".$content.
$prepended_text.$caption_text . '</div>';
}
$default_commands_conversion{'float'} = \&_convert_float_command;
sub _convert_quotation_command($$$$$)
{
my $self = shift;
my $cmdname = shift;
my $command = shift;
my $args = shift;
my $content = shift;
my $class = '';
$class = $cmdname if ($cmdname ne 'quotation');
my $attribution = '';
if ($command->{'extra'} and $command->{'extra'}->{'authors'}) {
foreach my $author (@{$command->{'extra'}->{'authors'}}) {
my $centered_author = $self->gdt("\@center --- \@emph{{author}}\n",
{'author' => $author->{'extra'}->{'misc_content'}});
$centered_author->{'parent'} = $command;
$attribution .= $self->convert_tree($centered_author);
}
}
if (!$self->in_string()) {
return $self->_attribute_class('blockquote', $class).">\n" .$content
."</blockquote>\n" . $attribution;
} else {
return $content.$attribution;
}
}
$default_commands_conversion{'quotation'} = \&_convert_quotation_command;
$default_commands_conversion{'smallquotation'} = \&_convert_quotation_command;
sub _convert_cartouche_command($$$$)
{
my $self = shift;
my $cmdname = shift;
my $command = shift;
my $content = shift;
if ($content =~ /\S/ and !$self->in_string()) {
return $self->_attribute_class('table', 'cartouche')
." border=\"1\"><tr><td>\n". $content ."</td></tr></table>\n";
}
return $content;
}
$default_commands_conversion{'cartouche'} = \&_convert_cartouche_command;
sub _convert_itemize_command($$$$)
{
my $self = shift;
my $cmdname = shift;
my $command = shift;
my $content = shift;
if ($self->in_string()) {
return $content;
}
if ($command->{'extra'}->{'command_as_argument'}
and $command->{'extra'}->{'command_as_argument'}->{'cmdname'} eq 'bullet') {
return "<ul>\n" . $content. "</ul>\n";
} else {
return $self->_attribute_class('ul',$NO_BULLET_LIST_CLASS).">\n"
. $content . "</ul>\n";
}
}
$default_commands_conversion{'itemize'} = \&_convert_itemize_command;
sub _convert_enumerate_command($$$$)
{
my $self = shift;
my $cmdname = shift;
my $command = shift;
my $content = shift;
if ($self->in_string()) {
return $content;
}
if ($content eq '') {
return '';
}
if ($command->{'extra'}{'enumerate_specification'}
and $command->{'extra'}{'enumerate_specification'} =~ /^\d*$/
and $command->{'extra'}{'enumerate_specification'} ne '1') {
return "<ol start=\"$command->{'extra'}{'enumerate_specification'}\">\n"
. $content . "</ol>\n";
} else {
return "<ol>\n" . $content . "</ol>\n";
}
}
$default_commands_conversion{'enumerate'} = \&_convert_enumerate_command;
sub _convert_multitable_command($$$$)
{
my $self = shift;
my $cmdname = shift;
my $command = shift;
my $content = shift;
if ($self->in_string()) {
return $content;
}
if ($content =~ /\S/) {
return "<table>\n" . $content . "</table>\n";
} else {
return '';
}
}
$default_commands_conversion{'multitable'} = \&_convert_multitable_command;
sub _convert_xtable_command($$$$)
{
my $self = shift;
my $cmdname = shift;
my $command = shift;
my $content = shift;
if ($self->in_string()) {
return $content;
}
if ($content ne '') {
return "<dl compact=\"compact\">\n" . $content . "</dl>\n";
} else {
return '';
}
}
$default_commands_conversion{'table'} = \&_convert_xtable_command;
$default_commands_conversion{'ftable'} = \&_convert_xtable_command;
$default_commands_conversion{'vtable'} = \&_convert_xtable_command;
sub _convert_item_command($$$$)
{
my $self = shift;
my $cmdname = shift;
my $command = shift;
my $content = shift;
if ($self->in_string()) {
return $content;
}
if ($command->{'parent'}->{'cmdname'}
and $command->{'parent'}->{'cmdname'} eq 'itemize') {
my $prepend ;
my $itemize = $command->{'parent'};
if ($itemize->{'extra'}->{'command_as_argument'}
and $itemize->{'extra'}->{'command_as_argument'}->{'cmdname'} eq 'bullet') {
$prepend = '';
} else {
# Setting multiple expansion should not be needed, except in
# case of invalid constructs
$prepend = $self->convert_tree_new_formatting_context(
{'contents' => $itemize->{'extra'}->{'block_command_line_contents'}->[0]},
$command->{'cmdname'}, 'item_prepended');
}
if ($content =~ /\S/) {
return '<li>' . $prepend .' '. $content . '</li>';
} else {
return '';
}
} elsif ($command->{'parent'}->{'cmdname'}
and $command->{'parent'}->{'cmdname'} eq 'enumerate') {
if ($content =~ /\S/) {
return '<li>' . ' ' . $content . '</li>';
} else {
return '';
}
} elsif ($command->{'parent'}->{'type'}
and $command->{'parent'}->{'type'} eq 'table_term') {
# FIXME instead use the code of Plaintext or DocBook.
my $args = $content;
if ($args->[0]) {
my $tree = $self->_table_item_content_tree($command,
[$args->[0]->{'tree'}]);
my $result = $self->convert_tree ($tree);
foreach my $command_name (reverse($self->commands_stack())) {
if ($preformatted_code_commands{$command_name}) {
$result = '<tt>' .$result. '</tt>';
last;
}
}
my $index_id = $self->command_id ($command);
if (defined($index_id) and $index_id ne '') {
$result .= "\n<a name=\"$index_id\"></a>\n";
}
return '<dt>' .$result. '</dt>' . "\n";
} else {
return '';
}
} elsif ($command->{'parent'}->{'type'}
and $command->{'parent'}->{'type'} eq 'row') {
return $self->_convert_tab_command ($cmdname, $command, $content);
}
return '';
}
$default_commands_conversion{'item'} = \&_convert_item_command;
$default_commands_conversion{'headitem'} = \&_convert_item_command;
$default_commands_conversion{'itemx'} = \&_convert_item_command;
sub _convert_tab_command ($$$$)
{
my $self = shift;
my $cmdname = shift;
my $command = shift;
my $content = shift;
my $cell_nr = $command->{'extra'}->{'cell_number'};
my $row = $command->{'parent'};
my $row_cmdname = $row->{'contents'}->[0]->{'cmdname'};
my $multitable = $row->{'parent'}->{'parent'};
my $fractions = '';
if ($multitable->{'extra'}->{'columnfractions'} and
exists($multitable->{'extra'}->{'columnfractions'}->[$cell_nr-1])) {
my $fraction = sprintf('%d', 100*$multitable->{'extra'}->{'columnfractions'}->[$cell_nr-1]);
$fractions = " width=\"$fraction%\"";
}
$content =~ s/^\s*//;
$content =~ s/\s*$//;
if ($self->in_string()) {
return $content;
}
if ($row_cmdname eq 'headitem') {
return "<th${fractions}>" . $content . '</th>';
} else {
return "<td${fractions}>" . $content . '</td>';
}
}
$default_commands_conversion{'tab'} = \&_convert_tab_command;
sub _convert_xref_commands($$$$)
{
my $self = shift;
my $cmdname = shift;
my $root = shift;
my $args = shift;
my $tree;
my $name;
if ($cmdname ne 'inforef'
and defined($args->[2]->{'normal'}) and $args->[2]->{'normal'} ne '') {
$name = $args->[2]->{'normal'};
} elsif (defined($args->[1]->{'normal'}) and $args->[1]->{'normal'} ne '') {
$name = $args->[1]->{'normal'}
}
if ($cmdname eq 'inforef') {
$args->[3] = $args->[2];
$args->[2] = undef;
}
my $file_arg_tree;
my $file = '';
if (defined($args->[3]->{'monospacetext'})
and $args->[3]->{'monospacetext'} ne '') {
$file_arg_tree = $args->[3]->{'tree'};
$file = $args->[3]->{'monospacetext'};
}
my $book = '';
$book = $args->[4]->{'normal'} if (defined($args->[4]->{'normal'}));
# internal reference
if ($cmdname ne 'inforef' and $book eq '' and $file eq ''
and $root->{'extra'}->{'node_argument'}
and defined($root->{'extra'}->{'node_argument'}->{'normalized'})
and !$root->{'extra'}->{'node_argument'}->{'manual_content'}
and $self->{'labels'}
and $self->{'labels'}->{$root->{'extra'}->{'node_argument'}->{'normalized'}}) {
my $node
= $self->label_command($root->{'extra'}->{'node_argument'}->{'normalized'});
# This is the node if USE_NODES, otherwise this may be the sectioning
# command (if the sectioning command is really associated to the node)
my $command = $self->command_element_command($node);
$command = $node if (!$node->{'extra'}->{'associated_section'}
or $node->{'extra'}->{'associated_section'} ne $command);
my $href = $self->command_href($command, undef, $root);
if (!defined($name)) {
if ($self->get_conf('xrefautomaticsectiontitle') eq 'on'
and $node->{'extra'}->{'associated_section'}) {
$command = $node->{'extra'}->{'associated_section'};
$name = $self->command_text($command, 'text_nonumber');
} elsif ($node->{'cmdname'} eq 'float') {
if (!$self->get_conf('XREF_USE_FLOAT_LABEL')) {
$name = $self->command_text($command);
}
if (!defined($name) or $name eq '') {
if (defined($args->[0]->{'monospace'})) {
$name = $args->[0]->{'monospace'};
} else {
$name = '';
}
}
} elsif (!$self->get_conf('XREF_USE_NODE_NAME_ARG')
and (defined($self->get_conf('XREF_USE_NODE_NAME_ARG'))
or !$self->in_preformatted())) {
$name = $self->command_text($command, 'text_nonumber');
#die "$command $command->{'normalized'}" if (!defined($name));
} elsif (defined($args->[0]->{'monospace'})) {
$name = $args->[0]->{'monospace'};
} else {
$name = '';
}
}
my $reference = $name;
$reference = "<a href=\"$href\">$name</a>" if ($href ne ''
and !$self->in_string());
# maybe use {'extra'}->{'node_argument'}?
my $is_section = ($command->{'cmdname'} ne 'node'
and $command->{'cmdname'} ne 'anchor'
and $command->{'cmdname'} ne 'float');
if ($cmdname eq 'pxref') {
if ($is_section) {
$tree = $self->gdt('see section {reference_name}',
{ 'reference_name' => {'type' => '_converted', 'text' => $reference} });
} else {
$tree = $self->gdt('see {reference_name}',
{ 'reference_name' => {'type' => '_converted', 'text' => $reference} });
}
} elsif ($cmdname eq 'xref' or $cmdname eq 'inforef') {
if ($is_section) {
$tree = $self->gdt('See section {reference_name}',
{ 'reference_name' => {'type' => '_converted', 'text' => $reference} });
} else {
$tree = $self->gdt('See {reference_name}',
{ 'reference_name' => {'type' => '_converted', 'text' => $reference} });
}
} elsif ($cmdname eq 'ref') {
$tree = $self->gdt('{reference_name}',
{ 'reference_name' => {'type' => '_converted', 'text' => $reference} });
}
} else {
# external reference
my $node_entry = {};
$node_entry->{'node_content'} = $root->{'extra'}->{'node_argument'}->{'node_content'}
if ($root->{'extra'}->{'node_argument'}
and $root->{'extra'}->{'node_argument'}->{'node_content'});
$node_entry->{'normalized'} = $root->{'extra'}->{'node_argument'}->{'normalized'}
if ($root->{'extra'}->{'node_argument'}
and exists($root->{'extra'}->{'node_argument'}->{'normalized'}));
# file argument takes precedence over the file in the node (file)node entry
if (defined($file_arg_tree) and $file ne '') {
$node_entry->{'manual_content'} = $file_arg_tree->{'contents'};
} elsif ($root->{'extra'}->{'node_argument'}
and $root->{'extra'}->{'node_argument'}->{'manual_content'}) {
$node_entry->{'manual_content'}
= $root->{'extra'}->{'node_argument'}->{'manual_content'};
my $file_with_node_tree = {'type' => '_code',
'contents' => [@{$node_entry->{'manual_content'}}]};
$file = $self->convert_tree($file_with_node_tree, 'node file in ref');
}
my $href = $self->command_href($node_entry, undef, $root);
if ($book eq '') {
if (!defined($name)) {
my $node_name = $self->command_text($node_entry);
$name = $node_name;
} elsif ($file ne '') {
$name = "($file)$name";
}
} elsif (!defined($name) and $node_entry->{'node_content'}) {
my $node_no_file_tree = {'type' => '_code',
'contents' => [@{$node_entry->{'node_content'}}]};
my $node_name = $self->convert_tree($node_no_file_tree, 'node in ref');
if (defined($node_name) and ($self->get_conf('KEEP_TOP_EXTERNAL_REF')
or $node_name ne 'Top')) {
$name = $node_name;
}
}
# not exactly sure when it happens. Something like @ref{(file),,,Manual}?
$name = $args->[0]->{'monospace'}
if (!defined($name)
# FIXME could it really be Top?
and ($self->get_conf('KEEP_TOP_EXTERNAL_REF')
or $args->[0]->{'monospace'} ne 'Top'));
$name = '' if (!defined($name));
my $reference = $name;
my $book_reference = '';
if (!$self->in_string() and $href ne '') {
if ($name ne '') {
$reference = "<a href=\"$href\">$name</a>";
} elsif ($book ne '') {
$book_reference = "<a href=\"$href\">$book</a>";
}
}
if ($cmdname eq 'pxref') {
if (($book ne '') and ($href ne '') and ($reference ne '')) {
$tree = $self->gdt('see {reference} in @cite{{book}}',
{ 'reference' => {'type' => '_converted', 'text' => $reference},
'book' => {'type' => '_converted', 'text' => $book }});
} elsif ($book_reference ne '') {
$tree = $self->gdt('see @cite{{book_reference}}',
{ 'book_reference' => {'type' => '_converted',
'text' => $book_reference }});
} elsif (($book ne '') and ($reference ne '')) {
$tree = $self->gdt('see `{section}\' in @cite{{book}}',
{ 'section' => {'type' => '_converted', 'text' => $reference},
'book' => {'type' => '_converted', 'text' => $book }});
} elsif ($book ne '') { # should seldom or even never happen
$tree = $self->gdt('see @cite{{book}}',
{'book' => {'type' => '_converted', 'text' => $book }});
} elsif ($href ne '') {
$tree = $self->gdt('see {reference}',
{ 'reference' => {'type' => '_converted', 'text' => $reference} });
} elsif ($reference ne '') {
$tree = $self->gdt('see `{section}\'', {
'section' => {'type' => '_converted', 'text' => $reference} });
}
} elsif ($cmdname eq 'xref' or $cmdname eq 'inforef') {
if (($book ne '') and ($href ne '') and ($reference ne '')) {
$tree = $self->gdt('See {reference} in @cite{{book}}',
{ 'reference' => {'type' => '_converted', 'text' => $reference},
'book' => {'type' => '_converted', 'text' => $book }});
} elsif ($book_reference ne '') {
$tree = $self->gdt('See @cite{{book_reference}}',
{ 'book_reference' => {'type' => '_converted',
'text' => $book_reference }});
} elsif (($book ne '') and ($reference ne '')) {
$tree = $self->gdt('See `{section}\' in @cite{{book}}',
{ 'section' => {'type' => '_converted', 'text' => $reference},
'book' => {'type' => '_converted', 'text' => $book }});
} elsif ($book ne '') { # should seldom or even never happen
$tree = $self->gdt('See @cite{{book}}',
{'book' => {'type' => '_converted', 'text' => $book }});
} elsif ($href ne '') {
$tree = $self->gdt('See {reference}',
{ 'reference' => {'type' => '_converted', 'text' => $reference} });
} elsif ($reference ne '') {
$tree = $self->gdt('See `{section}\'', {
'section' => {'type' => '_converted', 'text' => $reference} });
}
} else {
if (($book ne '') and ($href ne '') and ($reference ne '')) {
$tree = $self->gdt('{reference} in @cite{{book}}',
{ 'reference' => {'type' => '_converted', 'text' => $reference},
'book' => {'type' => '_converted', 'text' => $book }});
} elsif ($book_reference ne '') {
$tree = $self->gdt('@cite{{book_reference}}',
{ 'book_reference' => {'type' => '_converted',
'text' => $book_reference }});
} elsif (($book ne '') and ($reference ne '')) {
$tree = $self->gdt('`{section}\' in @cite{{book}}',
{ 'section' => {'type' => '_converted', 'text' => $reference},
'book' => {'type' => '_converted', 'text' => $book }});
} elsif ($book ne '') { # should seldom or even never happen
$tree = $self->gdt('@cite{{book}}',
{'book' => {'type' => '_converted', 'text' => $book }});
} elsif ($href ne '') {
$tree = $self->gdt('{reference}',
{ 'reference' => {'type' => '_converted', 'text' => $reference} });
} elsif ($reference ne '') {
$tree = $self->gdt('`{section}\'', {
'section' => {'type' => '_converted', 'text' => $reference} });
}
}
if (!defined($tree)) {
# May happen if there is no argument
#die "external: $cmdname, ($args), '$name' '$file' '$book' '$href' '$reference'. tree undef";
return '';
}
}
return $self->convert_tree($tree);
}
foreach my $command(keys(%ref_commands)) {
$default_commands_conversion{$command} = \&_convert_xref_commands;
}
sub _convert_index_command($$$$)
{
my $self = shift;
my $cmdname = shift;
my $command = shift;
my $args = shift;
my $index_id = $self->command_id($command);
if (defined($index_id) and $index_id ne ''
and !@{$self->{'multiple_pass'}}
and !$self->in_string()) {
my $result = "<a name=\"$index_id\"></a>";
$result .= "\n" unless ($self->in_preformatted());
return $result;
}
return '';
}
$default_commands_conversion{'cindex'} = \&_convert_index_command;
my %formatted_index_entries;
sub _convert_printindex_command($$$$)
{
my $self = shift;
my $cmdname = shift;
my $command = shift;
my $args = shift;
my $index_name;
if ($command->{'extra'} and $command->{'extra'}->{'misc_args'}
and defined($command->{'extra'}->{'misc_args'}->[0])) {
$index_name = $command->{'extra'}->{'misc_args'}->[0];
} else {
return '';
}
if (!$self->{'index_entries_by_letter'}
or !$self->{'index_entries_by_letter'}->{$index_name}
or !@{$self->{'index_entries_by_letter'}->{$index_name}}) {
return '';
}
#foreach my $letter_entry (@{$self->{'index_entries_by_letter'}->{$index_name}}) {
# print STDERR "IIIIIII $letter_entry->{'letter'}\n";
# foreach my $index_entry (@{$letter_entry->{'entries'}}) {
# print STDERR " ".join('|', keys(%$index_entry))."||| $index_entry->{'key'}\n";
# }
#}
return '' if ($self->in_string());
$self->_new_document_context($cmdname);
my $result = '';
# First do the summary letters linking to the letters done below
my %letter_id;
my @non_alpha = ();
my @alpha = ();
# collect the links
my $symbol_idx = 0;
foreach my $letter_entry (@{$self->{'index_entries_by_letter'}->{$index_name}}) {
my $letter = $letter_entry->{'letter'};
my $index_element_id = $self->_element_direction($self->{'current_element'},
'This', 'id');
if (!defined($index_element_id)) {
$index_element_id = $target_prefix;
}
my $is_symbol = $letter !~ /^[[:alpha:]]/;
my $identifier;
if ($is_symbol) {
$symbol_idx++;
$identifier = $index_element_id . "_${index_name}_symbol-$symbol_idx";
} else {
$identifier = $index_element_id . "_${index_name}_letter-${letter}";
}
$letter_id{$letter} = $identifier;
my $summary_letter_link = $self->_attribute_class('a', 'summary-letter')
." href=\"#$identifier\"><b>".$self->protect_text($letter).'</b></a>';
if ($is_symbol) {
push @non_alpha, $summary_letter_link;
} else {
push @alpha, $summary_letter_link;
}
}
# Format the summary letters
my $join = '';
my $non_alpha_text = '';
my $alpha_text = '';
$join = " &nbsp; \n<br>\n" if (@non_alpha and @alpha);
if (@non_alpha) {
$non_alpha_text = join("\n &nbsp; \n", @non_alpha) . "\n";
}
if (@alpha) {
$alpha_text = join("\n &nbsp; \n", @alpha) . "\n &nbsp; \n";
}
# format the summary
my $summary = "<table><tr><th valign=\"top\">"
. $self->convert_tree($self->gdt('Jump to')) .": &nbsp; </th><td>" .
$non_alpha_text . $join . $alpha_text . "</td></tr></table>\n";
$result .= $summary;
# now format the index entries
$result .= $self->_attribute_class('table', "index-$index_name")
." border=\"0\">\n" . "<tr><td></td><th align=\"left\">"
. $self->convert_tree($self->gdt('Index Entry'))
. "</th><td>&nbsp;</td><th align=\"left\"> "
. $self->convert_tree($self->gdt('Section'))
."</th></tr>\n" . "<tr><td colspan=\"4\"> ".$self->get_conf('DEFAULT_RULE')
."</td></tr>\n";
foreach my $letter_entry (@{$self->{'index_entries_by_letter'}->{$index_name}}) {
my $letter = $letter_entry->{'letter'};
my $entries_text = '';
foreach my $index_entry_ref (@{$letter_entry->{'entries'}}) {
# to avoid double error messages set ignore_notice if an entry was
# already formatted once, for example if there are multiple printindex.
my $already_formatted;
if (!$formatted_index_entries{$index_entry_ref}) {
$formatted_index_entries{$index_entry_ref} = 1;
} else {
$already_formatted = 1;
$self->{'ignore_notice'}++;
}
my $entry;
if ($index_entry_ref->{'in_code'}) {
$entry = $self->convert_tree({'type' => '_code',
'contents' => $index_entry_ref->{'content'}});
} else {
$entry = $self->convert_tree({'contents' => $index_entry_ref->{'content'}});
}
if ($already_formatted) {
$self->{'ignore_notice'}--;
}
next if ($entry !~ /\S/);
$entry = '<code>' .$entry .'</code>' if ($index_entry_ref->{'in_code'});
my $entry_href = $self->command_href($index_entry_ref->{'command'});
my $associated_command;
if ($self->get_conf('NODE_NAME_IN_INDEX')) {
$associated_command = $index_entry_ref->{'node'};
if (!defined($associated_command)) {
$associated_command
= $self->command_node($index_entry_ref->{'command'});
}
}
if (!$associated_command) {
$associated_command
= $self->command_element_command($index_entry_ref->{'command'});
if (!$associated_command) {
# Use Top if not associated command found
$associated_command
= $self->element_command($self->global_element('Top'));
}
}
my ($associated_command_href, $associated_command_text);
if ($associated_command) {
$associated_command_href = $self->command_href($associated_command);
$associated_command_text = $self->command_text($associated_command);
}
$entries_text .= '<tr><td></td><td valign="top">'
. "<a href=\"$entry_href\">$entry</a>" .
$self->get_conf('INDEX_ENTRY_COLON') .
'</td><td>&nbsp;</td><td valign="top">';
$entries_text .= "<a href=\"$associated_command_href\">$associated_command_text</a>"
if ($associated_command_href);
$entries_text .= "</td></tr>\n";
}
# a letter and associated indice entries
$result .= '<tr><th>' .
"<a name=\"$letter_id{$letter}\">".$self->protect_text($letter).'</a>'
. "</th><td></td><td></td></tr>\n" . $entries_text .
"<tr><td colspan=\"4\"> ".$self->get_conf('DEFAULT_RULE')."</td></tr>\n";
}
$result .= "</table>\n";
pop @{$self->{'document_context'}};
return $result .$summary;
}
$default_commands_conversion{'printindex'} = \&_convert_printindex_command;
sub _contents_inline_element($$$)
{
my $self = shift;
my $cmdname = shift;
my $command = shift;
my $content = &{$self->{'format_contents'}}($self, $cmdname, $command);
if ($content) {
my $result = '';
my $element_name = $contents_command_element_name{$cmdname};
my $special_element
= $self->special_element($element_name);
my $heading;
if ($special_element) {
my $id = $self->command_id($special_element);
if ($id ne '') {
$result .= "<a name=\"$id\"></a>\n";
}
$heading = $self->command_text($special_element);
} else {
# happens when called as convert() and not output()
#cluck "$cmdname special element not defined";
$heading
= $self->convert_tree ($self->get_conf('SPECIAL_ELEMENTS_NAME')->{$element_name});
}
my $class = $self->get_conf('SPECIAL_ELEMENTS_CLASS')->{$element_name};
$result .= &{$self->{'format_heading_text'}}($self, $class.'-heading',
$heading, $self->get_conf('CHAPTER_HEADER_LEVEL'))."\n";
$result .= $content . "\n";
return $result;
}
return '';
}
sub _convert_informative_command($$$$)
{
my $self = shift;
my $cmdname = shift;
my $command = shift;
return '' if ($self->in_string());
$cmdname = 'shortcontents' if ($cmdname eq 'summarycontents');
$self->_informative_command($command);
if ($self->get_conf('INLINE_CONTENTS')
and ($cmdname eq 'contents' or $cmdname eq 'shortcontents')
and $self->get_conf($cmdname)
and $self->{'structuring'} and $self->{'structuring'}->{'sectioning_root'}
and scalar(@{$self->{'structuring'}->{'sections_list'}}) > 1
and ! $self->get_conf('set'.$cmdname.'aftertitlepage')) {
return $self->_contents_inline_element($cmdname, $command);
}
if ($cmdname eq 'documentlanguage') {
$self->_translate_names();
}
return '';
}
foreach my $informative_command (@informative_global_commands) {
$default_commands_conversion{$informative_command}
= \&_convert_informative_command;
}
my %default_types_conversion;
sub default_types_conversion($$)
{
my $self = shift;
my $type = shift;
return $default_types_conversion{$type};
}
# Ignored commands
#my %ignored_types;
foreach my $type ('empty_line_after_command', 'preamble',
'preamble_before_setfilename',
'empty_spaces_after_command', 'spaces_at_end',
'empty_spaces_before_argument', 'empty_spaces_before_paragraph',
'empty_spaces_after_close_brace',
'empty_space_at_end_def_bracketed') {
#$ignored_types{$type} = 1;
$default_types_conversion{$type} = undef;
}
my %paragraph_style = (
'center' => 'center',
'flushleft' => 'left',
'flushright' => 'right',
);
sub _quotation_arg_to_prepend($$)
{
my $self = shift;
my $command = shift;
if ($command->{'parent'} and $command->{'parent'}->{'cmdname'}
and ($command->{'parent'}->{'cmdname'} eq 'quotation'
or $command->{'parent'}->{'cmdname'} eq 'smallquotation')
and $command->{'parent'}->{'extra'}
and $command->{'parent'}->{'extra'}->{'block_command_line_contents'}) {
return $self->convert_tree($self->gdt('@b{{quotation_arg}:} ',
{'quotation_arg' =>
$command->{'parent'}->{'extra'}->{'block_command_line_contents'}->[0]}));
}
return undef;
}
sub _convert_paragraph_type($$$$)
{
my $self = shift;
my $type = shift;
my $command = shift;
my $content = shift;
if ($self->paragraph_number() == 1) {
my $in_format = $self->top_format();
if ($in_format) {
# no first paragraph in those environment to avoid extra spacing
if ($in_format eq 'itemize'
or $in_format eq 'enumerate'
or $in_format eq 'multitable') {
return $content;
} else {
my $prepended = $self->_quotation_arg_to_prepend($command);
$content = $prepended.$content if (defined($prepended));
}
}
}
return $content if ($self->in_string());
if ($content =~ /\S/) {
my $align = $self->in_align();
if ($align and $paragraph_style{$align}) {
return "<p align=\"$paragraph_style{$align}\">".$content."</p>";
} else {
return "<p>".$content."</p>";
}
} else {
return '';
}
}
$default_types_conversion{'paragraph'} = \&_convert_paragraph_type;
sub _preformatted_class()
{
my $self = shift;
my $pre_class;
my @pre_classes = $self->preformatted_classes_stack();
foreach my $class (@pre_classes) {
# FIXME maybe add or $pre_class eq 'menu-preformatted' to override
# 'menu-preformatted' with 'menu-comment'?
$pre_class = $class unless ($pre_class
and $preformatted_code_commands{$pre_class}
and !($preformatted_code_commands{$class}
or $class eq 'menu-preformatted'));
}
return $pre_class;
}
sub _convert_preformatted_type($$$$)
{
my $self = shift;
my $type = shift;
my $command = shift;
my $content = shift;
if (!defined($content)) {
cluck "content undef in _convert_preformatted_type "
.Texinfo::Parser::_print_current($command);
}
my $current = $command;
# !defined preformatted_number may happen if there is something before the
# first preformatted. For example an @exdent.
if ($self->preformatted_number() and $self->preformatted_number() == 1) {
my $prepended = $self->_quotation_arg_to_prepend($command);
$content = $prepended.$content if (defined($prepended));
}
return '' if ($content eq '');
return $content if ($type eq 'rawpreformatted');
my $pre_class = $self->_preformatted_class();
if ($self->top_format() eq 'multitable') {
$content =~ s/^\s*//;
$content =~ s/\s*$//;
}
# menu_entry_description is always in a preformatted container
# in the tree, as the whole menu is meant to be an
# environment where spaces and newlines are preserved.
#
# However, if not in preformatted block command (nor in SIMPLE_MENU),
# we don't preserve spaces and newlines in menu_entry_description,
# instead the whole menu_entry is in a table, so here, not <pre>
if ($command->{'parent'}->{'type'}
and $command->{'parent'}->{'type'} eq 'menu_entry_description'
and !$self->_in_preformatted_in_menu()) {
return $content;
}
if ($self->in_string()) {
return $content;
}
$content =~ s/^\n/\n\n/; # a newline immediately after a <pre> is ignored.
my $result = $self->_attribute_class('pre', $pre_class).">".$content."</pre>";
# this may happen with lines without textual content
# between a def* and def*x.
if ($command->{'parent'}->{'cmdname'}
and $command->{'parent'}->{'cmdname'} =~ /^def/) {
$result = '<dd>'.$result.'</dd>';
}
return $result;
}
$default_types_conversion{'preformatted'} = \&_convert_preformatted_type;
$default_types_conversion{'rawpreformatted'} = \&_convert_preformatted_type;
sub _convert_bracketed_type($$$$) {
my $self = shift;
my $type = shift;
my $command = shift;
my $content = shift;
#print STDERR "$self $type $command $content\n";
return '{'.$content.'}';
}
$default_types_conversion{'bracketed'} = \&_convert_bracketed_type;
sub _convert_definfoenclose_type($$$$) {
my $self = shift;
my $type = shift;
my $command = shift;
my $content = shift;
return $self->protect_text($command->{'extra'}->{'begin'}) . $content
.$self->protect_text($command->{'extra'}->{'end'});
}
$default_types_conversion{'definfoenclose_command'}
= \&_convert_definfoenclose_type;
sub _convert_text($$$)
{
my $self = shift;
my $type = shift;
my $command = shift;
my $text = shift;
if ($self->in_verbatim()) {
return $self->protect_text($text);
}
return $text if ($self->in_raw());
$text = uc($text) if ($self->in_upper_case());
$text = $self->protect_text($text);
if ($self->get_conf('ENABLE_ENCODING') and
!$self->get_conf('ENABLE_ENCODING_USE_ENTITY')
and $self->get_conf('OUTPUT_ENCODING_NAME')
and $self->get_conf('OUTPUT_ENCODING_NAME') eq 'utf-8') {
$text = Texinfo::Convert::Unicode::unicode_text($text,
($self->in_code() or $self->in_math()));
} elsif (!$self->in_code() and !$self->in_math()) {
if ($self->get_conf('USE_ISO')) {
$text =~ s/---/\&mdash\;/g;
$text =~ s/--/\&ndash\;/g;
$text =~ s/``/\&ldquo\;/g;
$text =~ s/''/\&rdquo\;/g;
$text =~ s/'/\&rsquo\;/g;
$text =~ s/`/\&lsquo\;/g;
} else {
$text =~ s/``/&quot;/g;
$text =~ s/''/&quot;/g;
$text =~ s/---/\x{1F}/g;
$text =~ s/--/-/g;
$text =~ s/\x{1F}/--/g;
}
}
$text = $self->_protect_space($text);
return $text;
}
$default_types_conversion{'text'} = \&_convert_text;
sub _simplify_text_for_comparison($)
{
my $text = shift;
$text =~ s/[^\w]//g;
return $text;
}
sub _convert_row_type($$$$) {
my $self = shift;
my $type = shift;
my $command = shift;
my $content = shift;
return $content if ($self->in_string());
if ($content =~ /\S/) {
my $row_cmdname = $command->{'contents'}->[0]->{'cmdname'};
if ($row_cmdname eq 'headitem') {
return '<thead><tr>' . $content . '</tr></thead>' . "\n";
} else {
return '<tr>' . $content . '</tr>' . "\n";
}
} else {
return '';
}
}
$default_types_conversion{'row'} = \&_convert_row_type;
sub _convert_menu_entry_type($$$)
{
my $self = shift;
my $type = shift;
my $command = shift;
my $href;
my $node;
my $section;
my $node_entry = $command->{'extra'}->{'menu_entry_node'};
# external node
my $external_node;
if ($node_entry->{'manual_content'}) {
$href = $self->command_href($node_entry, undef, $command);
$external_node = 1;
} else {
$node = $self->label_command($node_entry->{'normalized'});
# if !NODE_NAME_IN_MENU, we pick the associated section, except if
# the node is the element command
if ($node->{'extra'}->{'associated_section'}
and !$self->get_conf('NODE_NAME_IN_MENU')
and !($self->command_element_command($node) eq $node)) {
$section = $node->{'extra'}->{'associated_section'};
$href = $self->command_href($section, undef, $command);
} else {
$href = $self->command_href($node, undef, $command);
}
}
$html_menu_entry_index++;
my $accesskey = '';
$accesskey = " accesskey=\"$html_menu_entry_index\""
if ($self->get_conf('USE_ACCESSKEY') and $html_menu_entry_index < 10);
my $MENU_SYMBOL = $self->get_conf('MENU_SYMBOL');
my $MENU_ENTRY_COLON = $self->get_conf('MENU_ENTRY_COLON');
if ($self->_in_preformatted_in_menu() or $self->in_string()) {
my $result = '';
my $i = 0;
my @args = @{$command->{'args'}};
while (@args) {
last if ($args[0]->{'type'}
and $args[0]->{'type'} eq 'menu_entry_description');
my $arg = shift @args;
if ($arg->{'type'} and $arg->{'type'} eq 'menu_entry_node') {
my $name = $self->convert_tree(
{'type' => '_code', 'contents' => $arg->{'contents'}});
if ($href ne '' and !$self->in_string()) {
$result .= "<a href=\"$href\"$accesskey>".$name."</a>";
} else {
$result .= $name;
}
} elsif ($arg->{'type'} and $arg->{'type'} eq 'menu_entry_leading_text') {
my $text = $arg->{'text'};
$text =~ s/\*/$MENU_SYMBOL/;
$result .= $text;
} else {
$result .= $self->convert_tree($arg, "menu_arg preformatted [$i]");
}
$i++;
}
my $description = '';
foreach my $arg (@args) {
$description .= $self->convert_tree($arg, "menu_arg preformatted [$i]");
$i++;
}
if (!$self->get_conf('SIMPLE_MENU')) {
$description =~ s/^<pre[^>]*>//;
$description =~ s/<\/pre>$//;
}
$result = $result . $description;
if (!$self->get_conf('SIMPLE_MENU')) {
my $pre_class = $self->_preformatted_class();
$result = $self->_attribute_class('pre', $pre_class).">".$result."</pre>";
}
return $result;
}
my $name;
my $name_no_number;
if ($section) {
#my $section_name = $self->command_text($section);
$name = $self->command_text($section);
$name_no_number = $self->command_text($section, 'text_nonumber');
if ($href ne '' and $name ne '') {
#$name = "<a href=\"$href\"$accesskey>".$section_name."</a>";
$name = "<a href=\"$href\"$accesskey>".$name."</a>";
}# else {
# $name = $section_name;
#}
#$name = "$MENU_SYMBOL ".$name if ($section_name eq $name_no_number);
}
if (!defined($name) or $name eq '') {
if ($command->{'extra'}->{'menu_entry_name'}) {
$name = $self->convert_tree($command->{'extra'}->{'menu_entry_name'});
}
if (!defined($name) or $name eq '') {
if ($node_entry->{'manual_content'}) {
$name = $self->command_text($node_entry);
} else {
$name = $self->convert_tree({'type' => '_code',
'contents' => $node_entry->{'node_content'}},
"menu_arg name");
}
}
$name =~ s/^\s*//;
$name_no_number = $name;
if ($href ne '') {
$name = "<a href=\"$href\"$accesskey>".$name."</a>";
}
$name = "$MENU_SYMBOL ".$name;
}
my $description = '';
if ($command->{'extra'}->{'menu_entry_description'}) {
$description = $self->convert_tree ($command->{'extra'}->{'menu_entry_description'},
"menu_arg description");
if ($self->get_conf('AVOID_MENU_REDUNDANCY')) {
$description = '' if (_simplify_text_for_comparison($name_no_number)
eq _simplify_text_for_comparison($description));
}
}
return "<tr><td align=\"left\" valign=\"top\">$name$MENU_ENTRY_COLON</td><td>&nbsp;&nbsp;</td><td align=\"left\" valign=\"top\">$description</td></tr>\n";
}
$default_types_conversion{'menu_entry'} = \&_convert_menu_entry_type;
sub _convert_menu_comment_type($$$$)
{
my $self = shift;
my $type = shift;
my $command = shift;
my $content = shift;
if ($self->_in_preformatted_in_menu() or $self->in_string()) {
return $content;
} else {
return "<tr><th colspan=\"3\" align=\"left\" valign=\"top\">".$content
."</th></tr>";
}
}
$default_types_conversion{'menu_comment'} = \&_convert_menu_comment_type;
sub _convert_before_item_type($$$$)
{
my $self = shift;
my $type = shift;
my $command = shift;
my $content = shift;
return '' if ($content !~ /\S/);
return $content if ($self->in_string());
my $top_format = $self->top_format();
if ($top_format eq 'itemize' or $top_format eq 'enumerate') {
return '<li>'. $content .'</li>';
} elsif ($top_format eq 'table' or $top_format eq 'vtable'
or $top_format eq 'ftable') {
return '<dd>'. $content .'</dd>'."\n";
} elsif ($top_format eq 'multitable') {
$content =~ s/^\s*//;
$content =~ s/\s*$//;
return '<tr><td>'.$content.'</td></tr>'."\n";
}
}
$default_types_conversion{'before_item'} = \&_convert_before_item_type;
sub _convert_def_line_type($$$$)
{
my $self = shift;
my $type = shift;
my $command = shift;
my $content = shift;
if ($self->in_string()) {
return $self->protect_text(Texinfo::Convert::Text::convert(
$command, Texinfo::Common::_convert_text_options($self)));
}
my $index_label = '';
my $index_id = $self->command_id($command);
if (defined($index_id) and $index_id ne '' and !@{$self->{'multiple_pass'}}) {
$index_label = "<a name=\"$index_id\"></a>";
}
my $arguments
= Texinfo::Common::definition_arguments_content($command);
if (!$self->get_conf('DEF_TABLE')) {
my $tree;
my $command_name;
if ($Texinfo::Common::def_aliases{$command->{'extra'}->{'def_command'}}) {
$command_name = $Texinfo::Common::def_aliases{$command->{'extra'}->{'def_command'}};
} else {
$command_name = $command->{'extra'}->{'def_command'};
}
my $name;
if ($command->{'extra'}->{'def_parsed_hash'}->{'name'}) {
$name = $command->{'extra'}->{'def_parsed_hash'}->{'name'};
} else {
$name = '';
}
my $category;
if ($command->{'extra'}->{'def_parsed_hash'}->{'category'}) {
$category = $command->{'extra'}->{'def_parsed_hash'}->{'category'};
} else {
$category = '';
}
if ($command_name eq 'deffn'
or $command_name eq 'defvr'
or $command_name eq 'deftp'
or (($command_name eq 'deftypefn'
or $command_name eq 'deftypevr')
and !$command->{'extra'}->{'def_parsed_hash'}->{'type'})
or (($command_name eq 'defop'
or ($command_name eq 'deftypeop'
and !$command->{'extra'}->{'def_parsed_hash'}->{'type'})
or $command_name eq 'defcv'
or ($command_name eq 'deftypecv'
and !$command->{'extra'}->{'def_parsed_hash'}->{'type'}))
and !$command->{'extra'}->{'def_parsed_hash'}->{'class'})) {
if ($arguments) {
$tree = $self->gdt("{category}: \@strong{{name}} \@emph{{arguments}}", {
'category' => $category,
'name' => $name,
'arguments' => $arguments});
} else {
$tree = $self->gdt("{category}: \@strong{{name}}", {
'category' => $category,
'name' => $name});
}
} elsif ($command_name eq 'deftypefn'
or $command_name eq 'deftypevr'
or (($command_name eq 'deftypeop'
or $command_name eq 'deftypecv')
and !$command->{'extra'}->{'def_parsed_hash'}->{'class'})) {
if ($arguments) {
my $strings = {
'category' => $category,
'name' => $name,
'type' => $command->{'extra'}->{'def_parsed_hash'}->{'type'},
'arguments' => $arguments};
if ($self->get_conf('deftypefnnewline') eq 'on') {
$tree
= $self->gdt("{category}:\@* \@emph{{type}}\@* \@strong{{name}} \@emph{{arguments}}",
$strings);
} else {
$tree
= $self->gdt("{category}: \@emph{{type}} \@strong{{name}} \@emph{{arguments}}",
$strings);
}
} else {
my $strings = {
'category' => $category,
'type' => $command->{'extra'}->{'def_parsed_hash'}->{'type'},
'name' => $name};
if ($self->get_conf('deftypefnnewline') eq 'on') {
$tree = $self->gdt("{category}:\@* \@emph{{type}}\@* \@strong{{name}}",
$strings);
} else {
$tree = $self->gdt("{category}: \@emph{{type}} \@strong{{name}}",
$strings);
}
}
} elsif ($command_name eq 'defcv'
or ($command_name eq 'deftypecv'
and !$command->{'extra'}->{'def_parsed_hash'}->{'type'})) {
if ($arguments) {
$tree = $self->gdt("{category} of {class}: \@strong{{name}} \@emph{{arguments}}", {
'category' => $category,
'name' => $name,
'class' => $command->{'extra'}->{'def_parsed_hash'}->{'class'},
'arguments' => $arguments});
} else {
$tree = $self->gdt("{category} of {class}: \@strong{{name}}", {
'category' => $category,
'class' => $command->{'extra'}->{'def_parsed_hash'}->{'class'},
'name' => $name});
}
} elsif ($command_name eq 'defop'
or ($command_name eq 'deftypeop'
and !$command->{'extra'}->{'def_parsed_hash'}->{'type'})) {
if ($arguments) {
$tree = $self->gdt("{category} on {class}: \@strong{{name}} \@emph{{arguments}}", {
'category' => $category,
'name' => $name,
'class' => $command->{'extra'}->{'def_parsed_hash'}->{'class'},
'arguments' => $arguments});
} else {
$tree = $self->gdt("{category} on {class}: \@strong{{name}}", {
'category' => $category,
'class' => $command->{'extra'}->{'def_parsed_hash'}->{'class'},
'name' => $name});
}
} elsif ($command_name eq 'deftypeop') {
if ($arguments) {
my $strings = {
'category' => $category,
'name' => $name,
'class' => $command->{'extra'}->{'def_parsed_hash'}->{'class'},
'type' => $command->{'extra'}->{'def_parsed_hash'}->{'type'},
'arguments' => $arguments};
if ($self->get_conf('deftypefnnewline') eq 'on') {
$tree
= $self->gdt("{category} on {class}:\@* \@emph{{type}}\@* \@strong{{name}} \@emph{{arguments}}",
$strings);
} else {
$tree
= $self->gdt("{category} on {class}: \@emph{{type}} \@strong{{name}} \@emph{{arguments}}",
$strings);
}
} else {
my $strings = {
'category' => $category,
'type' => $command->{'extra'}->{'def_parsed_hash'}->{'type'},
'class' => $command->{'extra'}->{'def_parsed_hash'}->{'class'},
'name' => $name};
if ($self->get_conf('deftypefnnewline') eq 'on') {
$tree
= $self->gdt("{category} on {class}:\@* \@emph{{type}}\@* \@strong{{name}}",
$strings);
} else {
$tree
= $self->gdt("{category} on {class}: \@emph{{type}} \@strong{{name}}",
$strings);
}
}
} elsif ($command_name eq 'deftypecv') {
if ($arguments) {
my $strings = {
'category' => $category,
'name' => $name,
'class' => $command->{'extra'}->{'def_parsed_hash'}->{'class'},
'type' => $command->{'extra'}->{'def_parsed_hash'}->{'type'},
'arguments' => $arguments};
if ($self->get_conf('deftypefnnewline') eq 'on') {
$tree
= $self->gdt("{category} of {class}:\@* \@emph{{type}}\@* \@strong{{name}} \@emph{{arguments}}",
$strings);
} else {
$tree
= $self->gdt("{category} of {class}: \@emph{{type}} \@strong{{name}} \@emph{{arguments}}",
$strings);
}
} else {
my $strings = {
'category' => $category,
'type' => $command->{'extra'}->{'def_parsed_hash'}->{'type'},
'class' => $command->{'extra'}->{'def_parsed_hash'}->{'class'},
'name' => $name};
if ($self->get_conf('deftypefnnewline') eq 'on') {
$tree
= $self->gdt("{category} of {class}:\@* \@emph{{type}}\@* \@strong{{name}}",
$strings);
} else {
$tree
= $self->gdt("{category} of {class}: \@emph{{type}} \@strong{{name}}",
$strings);
}
}
}
return '<dt>'.$index_label.$self->convert_tree({'type' => '_code',
'contents' => [$tree]}) . "</dt>\n";
} else {
my $category_prepared = '';
if ($command->{'extra'} and $command->{'extra'}->{'def_args'}
and @{$command->{'extra'}->{'def_args'}}) {
my $parsed_definition_category
= Texinfo::Common::definition_category ($self, $command);
if ($parsed_definition_category) {
$category_prepared = $self->convert_tree({'type' => '_code',
'contents' => [$parsed_definition_category]});
}
}
my $arguments_text = '';
if ($arguments) {
$arguments_text = $self->convert_tree({'type' => '_code',
'contents' => $arguments});
$arguments_text = '<em> ' . $arguments_text . '</em>'
if ($arguments_text =~ /\S/);
}
my $def_type = '';
my $type_name = '';
if ($command->{'extra'}->{'def_parsed_hash'}->{'type'}) {
$def_type = $self->convert_tree({'type' => '_code',
'contents' => [$command->{'extra'}->{'def_parsed_hash'}->{'type'}]});
}
$type_name = " <em>$def_type</em>" if ($def_type ne '');
my $name = '';
if ($command->{'extra'}->{'def_parsed_hash'}->{'name'}) {
$name = $self->convert_tree({'type' => '_code',
'contents' => [$command->{'extra'}->{'def_parsed_hash'}->{'name'}]});
}
$type_name .= ' <strong>' . $name . '</strong>' if ($name ne '');
$type_name .= $arguments_text;
return "<tr><td align=\"left\">" . $type_name .
"</td><td align=\"right\">" . $category_prepared .
$index_label . "</td></tr>\n";
}
}
$default_types_conversion{'def_line'} = \&_convert_def_line_type;
sub _convert_def_item_type($$$$)
{
my $self = shift;
my $type = shift;
my $command = shift;
my $content = shift;
return $content if ($self->in_string());
if ($content =~ /\S/) {
if (! $self->get_conf('DEF_TABLE')) {
return '<dd>' . $content . '</dd>';
} else {
return '<tr><td colspan="2">' . $content . '</td></tr>';
}
}
}
$default_types_conversion{'def_item'} = \&_convert_def_item_type;
$default_types_conversion{'inter_def_item'} = \&_convert_def_item_type;
sub _convert_def_command($$$$) {
my $self = shift;
my $cmdname = shift;
my $command = shift;
my $content = shift;
return $content if ($self->in_string());
#print STDERR "IIII $self $cmdname command $command args $args content $content\n";
if (!$self->get_conf('DEF_TABLE')) {
return "<dl>\n". $content ."</dl>\n";
} else {
return "<table width=\"100%\">\n" . $content . "</table>\n";
}
}
foreach my $command (keys(%def_commands)) {
$default_commands_conversion{$command} = \&_convert_def_command;
}
sub _convert_table_item_type($$$$)
{
my $self = shift;
my $type = shift;
my $command = shift;
my $content = shift;
return $content if ($self->in_string());
if ($content =~ /\S/) {
return '<dd>' . $content . '</dd>'."\n";
}
}
$default_types_conversion{'table_item'} = \&_convert_table_item_type;
$default_types_conversion{'inter_item'} = \&_convert_table_item_type;
# This type is the only one present if there are no elements. It is
# therefore used to do the formatting of the element in case there are no
# element.
sub _convert_root_text_type($$$$)
{
my $self = shift;
my $type = shift;
my $command = shift;
my $content = shift;
my $result = $content;
#$result =~ s/^\s*//;
# if there is no element, the parent should not be an element
if (!$command->{'parent'}
or !$command->{'parent'}->{'type'}
or $command->{'parent'}->{'type'} ne 'element') {
$result .= &{$self->{'format_footnotes_text'}}($self);
$result .= $self->get_conf('DEFAULT_RULE') ."\n",
if ($self->get_conf('PROGRAM_NAME_IN_FOOTER')
and defined($self->get_conf('DEFAULT_RULE'))
and !$self->in_string());
}
return $result;
}
$default_types_conversion{'text_root'} = \&_convert_root_text_type;
sub _contents_shortcontents_in_title($)
{
my $self = shift;
my $result = '';
if ($self->{'structuring'} and $self->{'structuring'}->{'sectioning_root'}
and scalar(@{$self->{'structuring'}->{'sections_list'}}) > 1) {
foreach my $command ('contents', 'shortcontents') {
if ($self->get_conf($command)
and $self->get_conf('set'.$command.'aftertitlepage')) {
my $contents_text = $self->_contents_inline_element($command, undef);
if ($contents_text ne '') {
$result .= $contents_text . $self->get_conf('DEFAULT_RULE')."\n";
}
}
}
}
return $result;
}
# Convert @titlepage. Falls back to simpletitle.
sub _default_titlepage($)
{
my $self = shift;
my $titlepage_text;
if ($self->{'extra'}->{'titlepage'}) {
$titlepage_text = $self->convert_tree({'contents'
=> $self->{'extra'}->{'titlepage'}->{'contents'}});
} elsif ($self->{'simpletitle_tree'}) {
my $title_text = $self->convert_tree_new_formatting_context(
$self->{'simpletitle_tree'}, 'simpletitle_string');
$titlepage_text = &{$self->{'format_heading_text'}}($self, 'settitle', $title_text,
0, {'cmdname' => 'settitle',
'contents' => $self->{'simpletitle_tree'}->{'contents'}});
}
my $result = '';
$result .= $titlepage_text.$self->get_conf('DEFAULT_RULE')."\n"
if (defined($titlepage_text));
$result .= $self->_contents_shortcontents_in_title();
return $result;
}
sub _print_title($)
{
my $self = shift;
my $result = '';
if ($self->get_conf('SHOW_TITLE')) {
if ($self->get_conf('USE_TITLEPAGE_FOR_TITLE')) {
$result .= &{$self->{'format_titlepage'}}($self);
} else {
if ($self->{'simpletitle_tree'}) {
my $title_text = $self->convert_tree_new_formatting_context(
$self->{'simpletitle_tree'}, 'simpletitle_string');
$result .= &{$self->{'format_heading_text'}}($self, 'settitle', $title_text,
0, {'cmdname' => 'settitle',
'contents' => $self->{'simpletitle_tree'}->{'contents'}});
}
$result .= $self->_contents_shortcontents_in_title();
}
}
return $result;
}
sub _convert_element_type($$$$)
{
my $self = shift;
my $type = shift;
my $element = shift;
my $content = shift;
#print STDERR "GGGGGGGG $element $element->{'parent'} $element->{'parent'}->{'type'}\n";
#print STDERR "$element->{'extra'}->{'special_element'}\n"
# if ($element->{'extra'}->{'special_element'});
#if (!defined($element->{'parent'})) {
# print STDERR "NO PARENT ".Texinfo::Parser::_print_current($element)."\n";
#}
if ($self->in_string()) {
if (defined($content)) {
return $content;
} else {
return '';
}
}
my $result = '';
my $special_element;
if ($element->{'extra'}->{'special_element'}) {
$special_element = $element->{'extra'}->{'special_element'};
my $id = $self->command_id($element);
if ($id ne '') {
$result .= "<a name=\"$id\"></a>\n";
}
if ($self->get_conf('HEADERS')
# first in page
or $self->{'counter_in_file'}->{$element->{'filename'}} == 1) {
$result .= &{$self->{'format_navigation_header'}}($self,
$self->get_conf('MISC_BUTTONS'), undef, $element);
}
my $heading = $self->command_text($element);
my $element_name = $element->{'extra'}->{'special_element'};
my $class = $self->get_conf('SPECIAL_ELEMENTS_CLASS')->{$element_name};
my $level = $self->get_conf('CHAPTER_HEADER_LEVEL');
if ($element_name eq 'Footnotes') {
$level = $self->get_conf('FOOTNOTE_SEPARATE_HEADER_LEVEL');
}
$result .= &{$self->{'format_heading_text'}}($self, $class.'-heading',
$heading, $level)."\n";
my $special_element_body .= &{$self->{'format_special_element_body'}}($self,
$special_element, $element);
# This may happen with footnotes in regions that are not expanded,
# like @copying or @titlepage
if ($special_element_body eq '') {
return '';
}
$result .= $special_element_body;
} elsif (!$element->{'element_prev'}) {
$result .= $self->_print_title();
if (!$element->{'element_next'}) {
# only one element
my $foot_text = &{$self->{'format_footnotes_text'}}($self);
return $result.$content.$foot_text.$self->get_conf('DEFAULT_RULE')."\n";
}
}
$result .= $content unless ($special_element);
$result .= &{$self->{'format_element_footer'}}($self, $type,
$element, $content);
return $result;
}
sub _default_element_footer($$$$)
{
my $self = shift;
my $type = shift;
my $element = shift;
my $content = shift;
my $result = '';
my $is_top = $self->element_is_top($element);
my $next_is_top = ($element->{'element_next'}
and $self->element_is_top($element->{'element_next'}));
my $next_is_special = (defined($element->{'element_next'})
and $element->{'element_next'}->{'extra'}->{'special_element'});
# no 'parent' defined happens if there are no pages, and there are elements
# which should only happen when called with $self->{'output_file'}
# set to ''.
#print STDERR "$element $element->{'filename'} $self->{'file_counters'}->{$element->{'filename'}}\n";
#print STDERR "next: $element->{'element_next'}->{'filename'}\n" if ($element->{'element_next'});
my $end_page = (!$element->{'element_next'}
or (defined($element->{'filename'})
and $element->{'filename'} ne $element->{'element_next'}->{'filename'}
and $self->{'file_counters'}->{$element->{'filename'}} == 1));
#my $end_page = (!$element->{'element_next'}
# or (defined($element->{'parent'})
# and $element->{'parent'} ne $element->{'element_next'}->{'parent'}));
my $is_special = $element->{'extra'}->{'special_element'};
if (($end_page or $next_is_top or $next_is_special or $is_top)
and $self->get_conf('VERTICAL_HEAD_NAVIGATION')
and ($self->get_conf('SPLIT') ne 'node'
or $self->get_conf('HEADERS') or $is_special or $is_top)) {
$result .= "</td>
</tr>
</table>"."\n";
}
my $rule = '';
my $buttons;
my $maybe_in_page;
if (($is_top or $is_special)
and ($self->get_conf('SPLIT') or !$self->get_conf('MONOLITHIC'))
and ($end_page
and ($self->get_conf('HEADERS')
or ($self->get_conf('SPLIT') and $self->get_conf('SPLIT') ne 'node')))) {
if ($is_top) {
$buttons = $self->get_conf('TOP_BUTTONS');
} else {
$buttons = $self->get_conf('MISC_BUTTONS');
}
} elsif ($end_page and $self->get_conf('SPLIT') eq 'section') {
$buttons = $self->get_conf('SECTION_FOOTER_BUTTONS');
} elsif ($end_page and $self->get_conf('SPLIT') eq 'chapter') {
$buttons = $self->get_conf('CHAPTER_BUTTONS');
} elsif ($self->get_conf('SPLIT') eq 'node') {
if ($self->get_conf('HEADERS')) {
my $no_footer_word_count;
if ($self->get_conf('WORDS_IN_PAGE')) {
my @cnt = split(/\W*\s+\W*/, $content);
if (scalar(@cnt) < $self->get_conf('WORDS_IN_PAGE')) {
$no_footer_word_count = 1;
}
}
$buttons = $self->get_conf('NODE_FOOTER_BUTTONS')
unless ($no_footer_word_count);
}
} else {
$maybe_in_page = 1;
}
if ($maybe_in_page or $is_top or $is_special
or ($end_page and ($self->get_conf('SPLIT') eq 'chapter'
or $self->get_conf('SPLIT') eq 'section'))
or ($self->get_conf('SPLIT') eq 'node' and $self->get_conf('HEADERS'))) {
$rule = $self->get_conf('DEFAULT_RULE');
}
if (!$end_page and ($is_top or $next_is_top or ($next_is_special
and !$is_special))) {
$rule = $self->get_conf('BIG_RULE');
}
# FIXME the following condition is almost a duplication of end_page
# except that the file counter needs not be 1
if ((!$element->{'element_next'}
or (defined($element->{'filename'})
and $element->{'filename'} ne $element->{'element_next'}->{'filename'}))
and $self->get_conf('footnotestyle') eq 'end') {
$result .= &{$self->{'format_footnotes_text'}}($self);
}
if (!$self->get_conf('PROGRAM_NAME_IN_FOOTER')
and !$buttons and !$maybe_in_page) {
# no rule in that case
} else {
$result .= "$rule\n" if ($rule);
}
if ($buttons) {
$result .= &{$self->{'format_navigation_header_panel'}}($self, $buttons,
undef, $element);
}
return $result;
}
$default_types_conversion{'element'} = \&_convert_element_type;
sub _new_document_context($$)
{
my $self = shift;
my $cmdname = shift;
push @{$self->{'document_context'}},
{'cmdname' => $cmdname,
'formatting_context' => [{'cmdname' => $cmdname}],
'composition_context' => ['raggedright'],
'formats' => [],
'monospace' => [0],
};
}
my %default_formatting_references = (
'heading_text' => \&_default_heading_text,
'comment' => \&_default_comment,
'protect_text' => \&_default_protect_text,
'css_lines' => \&_default_css_lines,
'begin_file' => \&_default_begin_file,
'node_redirection_page' => \&_default_node_redirection_page,
'end_file' => \&_default_end_file,
'special_element_body' => \&_default_special_element_body,
'footnotes_text' => \&_default_footnotes_text,
'program_string' => \&_default_program_string,
'titlepage' => \&_default_titlepage,
'navigation_header' => \&_default_navigation_header,
'navigation_header_panel' => \&_default_navigation_header_panel,
'element_header' => \&_default_element_header,
'element_footer' => \&_default_element_footer,
'button' => \&_default_button_formatting,
'button_icon_img' => \&_default_button_icon_img,
'external_href' => \&_default_external_href,
'contents' => \&_default_contents,
'frame_files' => \&_default_frame_files,
);
sub _use_entity_is_entity($$)
{
my $self = shift;
my $text = shift;
return 0 if (!$self->get_conf('ENABLE_ENCODING_USE_ENTITY'));
return 1 if ($text =~ /^&/ and $text =~ /;$/);
}
sub _complete_commands_formatting($$)
{
my $self = shift;
my $command = shift;
if (!defined ($self->{'commands_formatting'}->{'normal'}->{$command})) {
$self->{'commands_formatting'}->{'normal'}->{$command} = '';
}
if (!defined ($self->{'commands_formatting'}->{'preformatted'}->{$command})) {
$self->{'commands_formatting'}->{'preformatted'}->{$command} =
$self->{'commands_formatting'}->{'normal'}->{$command};
}
if (!defined ($self->{'commands_formatting'}->{'string'}->{$command})) {
$self->{'commands_formatting'}->{'string'}->{$command} =
$self->{'commands_formatting'}->{'preformatted'}->{$command};
}
}
sub converter_initialize($)
{
my $self = shift;
if ($self->get_conf('SHORTEXTN')) {
$self->set_conf('EXTENSION', 'htm');
}
$foot_num = 0;
$foot_lines = '';
%formatted_index_entries = ();
%footnote_id_numbers = ();
%{$self->{'css_map'}} = %css_map;
$self->{'htmlxref'} = {};
if ($self->{'htmlxref_files'}) {
$self->{'htmlxref'} = Texinfo::Common::parse_htmlxref_files($self,
$self->{'htmlxref_files'});
}
foreach my $type (keys(%default_types_conversion)) {
if (exists($Texinfo::Config::texinfo_types_conversion{$type})) {
$self->{'types_conversion'}->{$type}
= $Texinfo::Config::texinfo_types_conversion{$type};
} else {
$self->{'types_conversion'}->{$type}
= $default_types_conversion{$type};
}
}
# FIXME API with a function call? Used in cvs.init.
foreach my $type (keys(%default_code_types)) {
$self->{'code_types'}->{$type} = $default_code_types{$type};
}
if ($Texinfo::Config::texinfo_code_types) {
foreach my $type (keys(%$Texinfo::Config::texinfo_code_types)) {
$self->{'code_types'}->{$type}
= $Texinfo::Config::texinfo_code_types->{$type};
}
}
# FIXME put value in a category in Texinfo::Common?
foreach my $command (keys(%misc_commands), keys(%brace_commands),
keys (%block_commands), keys(%no_brace_commands), 'value') {
if (exists($Texinfo::Config::texinfo_commands_conversion{$command})) {
$self->{'commands_conversion'}->{$command}
= $Texinfo::Config::texinfo_commands_conversion{$command};
} else {
if (!$self->get_conf('SHOW_MENU')
and ($command eq 'menu' or $command eq 'detailmenu')) {
$self->{'commands_conversion'}->{$command} = undef;
} elsif ($format_raw_commands{$command}
and !$self->{'expanded_formats_hash'}->{$command}) {
} elsif (exists($default_commands_conversion{$command})) {
$self->{'commands_conversion'}->{$command}
= $default_commands_conversion{$command};
if ($command eq 'menu' and $self->get_conf('SIMPLE_MENU')) {
$self->{'commands_conversion'}->{$command}
= $default_commands_conversion{'example'};
}
}
}
}
foreach my $context ('normal', 'preformatted', 'string') {
foreach my $command (keys(%{$default_commands_formatting{'normal'}})) {
if (exists ($Texinfo::Config::commands_formatting{$context}->{$command})) {
$self->{'commands_formatting'}->{$context}->{$command}
= $Texinfo::Config::commands_formatting{$context}->{$command};
} else {
if (defined($default_commands_formatting{$context}->{$command})) {
if ($self->get_conf('ENABLE_ENCODING')
and Texinfo::Convert::Unicode::unicode_for_brace_no_arg_command(
$command, $self->get_conf('OUTPUT_ENCODING_NAME'))
and !$self->_use_entity_is_entity($default_commands_formatting{$context}->{$command})) {
$self->{'commands_formatting'}->{$context}->{$command}
= Texinfo::Convert::Unicode::unicode_for_brace_no_arg_command(
$command, $self->get_conf('OUTPUT_ENCODING_NAME'))
} else {
$self->{'commands_formatting'}->{$context}->{$command}
= $default_commands_formatting{$context}->{$command};
}
}
}
if (exists ($Texinfo::Config::commands_translation{$context}->{$command})) {
$self->{'commands_translation'}->{$context}->{$command}
= $Texinfo::Config::commands_translation{$context}->{$command};
delete $self->{'translated_commands'}->{$command};
} elsif (defined($default_commands_translation{$context}->{$command})) {
$self->{'commands_translation'}->{$context}->{$command}
= $default_commands_translation{$context}->{$command};
delete $self->{'translated_commands'}->{$command};
}
}
}
# set sane defaults in case there is none and the default formatting
# function is used
foreach my $command (keys(%{$default_commands_formatting{'normal'}})) {
if ($self->{'commands_conversion'}->{$command}
and $self->{'commands_conversion'}->{$command}
eq $default_commands_conversion{$command}) {
$self->_complete_commands_formatting($command);
}
}
foreach my $context (keys(%style_commands_formatting)) {
foreach my $command (keys(%{$style_commands_formatting{$context}})) {
if (exists ($Texinfo::Config::style_commands_formatting{$context}->{$command})) {
$self->{'style_commands_formatting'}->{$context}->{$command}
= $Texinfo::Config::style_commands_formatting{$context}->{$command};
} elsif (exists($style_commands_formatting{$context}->{$command})) {
$self->{'style_commands_formatting'}->{$context}->{$command}
= $style_commands_formatting{$context}->{$command};
}
}
}
foreach my $command (keys %{$self->{'commands_conversion'}}) {
if (exists($Texinfo::Config::commands_args{$command})) {
$self->{'commands_args'}->{$command}
= $Texinfo::Config::commands_args{$command};
} elsif (exists($default_commands_args{$command})) {
$self->{'commands_args'}->{$command} = $default_commands_args{$command};
}
}
foreach my $formatting_reference (keys(%default_formatting_references)) {
$self->{'default_formatting_functions'}->{$formatting_reference}
= $default_formatting_references{$formatting_reference};
if (defined($Texinfo::Config::texinfo_formatting_references{$formatting_reference})) {
$self->{"format_".$formatting_reference}
= $Texinfo::Config::texinfo_formatting_references{$formatting_reference};
} else {
$self->{"format_".$formatting_reference}
= $default_formatting_references{$formatting_reference};
}
}
if ($Texinfo::Config::renamed_nodes) {
%{$self->{'renamed_nodes'}} = %{$Texinfo::Config::renamed_nodes};
}
$self->{'document_context'} = [];
$self->{'multiple_pass'} = [];
$self->_new_document_context('_toplevel_context');
if ($self->get_conf('SPLIT') and $self->get_conf('SPLIT') ne 'chapter'
and $self->get_conf('SPLIT') ne 'section'
and $self->get_conf('SPLIT') ne 'node') {
$self->force_conf('SPLIT', 'node');
}
return $self;
}
# the entry point for _convert
sub convert_tree($$;$)
{
my $self = shift;
my $element = shift;
my $explanation = shift;
return $self->_convert($element, $explanation);
}
sub _normalized_to_id($)
{
my $id = shift;
if (!defined($id)) {
cluck "_normalized_to_id id not defined";
return '';
}
$id =~ s/^([0-9_])/g_t$1/;
return $id;
}
sub _default_css_lines ($)
{
my $self = shift;
return if ($self->get_conf('NO_CSS'));
my $css_refs = $self->get_conf('CSS_REFS');
return if (!@{$self->{'css_import_lines'}} and !@{$self->{'css_rule_lines'}}
and !keys(%{$self->{'css_map'}}) and !@$css_refs);
my $css_text = "<style type=\"text/css\">\n<!--\n";
$css_text .= join('',@{$self->{'css_import_lines'}}) . "\n"
if (@{$self->{'css_import_lines'}});
foreach my $css_rule (sort(keys(%{$self->{'css_map'}}))) {
next unless ($self->{'css_map'}->{$css_rule});
$css_text .= "$css_rule {$self->{'css_map'}->{$css_rule}}\n";
}
$css_text .= join('',@{$self->{'css_rule_lines'}}) . "\n"
if (@{$self->{'css_rule_lines'}});
$css_text .= "-->\n</style>\n";
foreach my $ref (@$css_refs) {
$css_text .= "<link rel=\"stylesheet\" type=\"text/css\" href=\"$ref\">\n";
}
$self->set_conf('CSS_LINES', $css_text);
}
sub _process_css_file($$$)
{
my $self = shift;
my $fh =shift;
my $file = shift;
my $in_rules = 0;
my $in_comment = 0;
my $in_import = 0;
my $in_string = 0;
my $rules = [];
my $imports = [];
my $line_nr = 0;
while (my $line = <$fh>) {
$line_nr++;
#print STDERR "Line: $line";
if ($in_rules) {
push @$rules, $line;
next;
}
my $text = '';
while (1) {
#sleep 1;
#print STDERR "${text}!in_comment $in_comment in_rules $in_rules in_import $in_import in_string $in_string: $line";
if ($in_comment) {
if ($line =~ s/^(.*?\*\/)//) {
$text .= $1;
$in_comment = 0;
} else {
push @$imports, $text . $line;
last;
}
} elsif (!$in_string and $line =~ s/^\///) {
if ($line =~ s/^\*//) {
$text .= '/*';
$in_comment = 1;
} else {
push (@$imports, $text. "\n") if ($text ne '');
push (@$rules, '/' . $line);
$in_rules = 1;
last;
}
} elsif (!$in_string and $in_import and $line =~ s/^([\"\'])//) {
# strings outside of import start rules
$text .= "$1";
$in_string = quotemeta("$1");
} elsif ($in_string and $line =~ s/^(\\$in_string)//) {
$text .= $1;
} elsif ($in_string and $line =~ s/^($in_string)//) {
$text .= $1;
$in_string = 0;
} elsif ((! $in_string and !$in_import)
and ($line =~ s/^([\\]?\@import)$//
or $line =~ s/^([\\]?\@import\s+)//)) {
$text .= $1;
$in_import = 1;
} elsif (!$in_string and $in_import and $line =~ s/^\;//) {
$text .= ';';
$in_import = 0;
} elsif (($in_import or $in_string) and $line =~ s/^(.)//) {
$text .= $1;
} elsif (!$in_import and $line =~ s/^([^\s])//) {
push (@$imports, $text. "\n") if ($text ne '');
push (@$rules, $1 . $line);
$in_rules = 1;
last;
} elsif ($line =~ s/^(\s)//) {
$text .= $1;
} elsif ($line eq '') {
push (@$imports, $text);
last;
}
}
}
#file_line_warn (__("string not closed in css file"), $file) if ($in_string);
#file_line_warn (__("--css-file ended in comment"), $file) if ($in_comment);
#file_line_warn (__("\@import not finished in css file"), $file) if ($in_import and !$in_comment and !$in_string);
$self->file_line_warn(sprintf($self->__("string not closed in css file"),
$file, $line_nr)) if ($in_string);
$self->file_line_warn(sprintf($self->__("--css-include ended in comment"),
$file, $line_nr)) if ($in_comment);
$self->file_line_warn(sprintf($self->__("\@import not finished in css file"),
$file, $line_nr))
if ($in_import and !$in_comment and !$in_string);
return ($imports, $rules);
}
sub _prepare_css($)
{
my $self = shift;
return if ($self->get_conf('NO_CSS'));
my @css_import_lines;
my @css_rule_lines;
my $css_files = $self->get_conf('CSS_FILES');
foreach my $file (@$css_files) {
my $css_file_fh;
my $css_file;
if ($file eq '-') {
$css_file_fh = \*STDIN;
$css_file = '-';
} else {
$css_file = $self->Texinfo::Common::locate_include_file($file);
unless (defined($css_file)) {
$self->document_warn(sprintf(
$self->__("CSS file %s not found"), $file));
next;
}
# FIXME use open_out?
unless (open (CSSFILE, $css_file)) {
$self->document_warn(sprintf($self->__(
"could not open --include-file %s: %s"),
$css_file, $!));
next;
}
$css_file_fh = \*CSSFILE;
}
my ($import_lines, $rules_lines);
($import_lines, $rules_lines)
= $self->_process_css_file ($css_file_fh, $css_file);
if (!close($css_file_fh)) {
$self->document_warn(sprintf($self->__("error on closing CSS file %s: %s"),
$css_file, $!));
}
push @css_import_lines, @$import_lines;
push @css_rule_lines, @$rules_lines;
}
if ($self->get_conf('DEBUG')) {
if (@css_import_lines) {
print STDERR "# css import lines\n";
foreach my $line (@css_import_lines) {
print STDERR "$line";
}
}
if (@css_rule_lines) {
print STDERR "# css rule lines\n";
foreach my $line (@css_rule_lines) {
print STDERR "$line";
}
}
}
$self->{'css_import_lines'} = \@css_import_lines;
$self->{'css_rule_lines'} = \@css_rule_lines;
}
sub _node_id_file($$)
{
my $self = shift;
my $node_info = shift;
my ($target, $id);
my $normalized = $node_info->{'normalized'};
if (defined($normalized)) {
$target = _normalized_to_id($normalized);
} else {
$target = '';
}
if (!$node_info->{'manual_content'}) {
$id = $target;
}
# to find out the Top node, one could check $node_info->{'normalized'}
if (defined($Texinfo::Config::node_target_name)) {
($target, $id) = &$Texinfo::Config::node_target_name($node_info,
$target, $id);
}
my $filename = $self->_node_filename($node_info);
return ($filename, $target, $id);
}
sub _new_sectioning_command_target($$)
{
my $self = shift;
my $command = shift;
my ($normalized_name, $filename)
= $self->_sectioning_command_normalized_filename($command);
my $target_base = _normalized_to_id($normalized_name);
if ($target_base !~ /\S/ and $command->{'cmdname'} eq 'top'
and defined($self->{'misc_elements_targets'}->{'Top'})) {
$target_base = $self->{'misc_elements_targets'}->{'Top'};
}
my $nr=1;
my $target = $target_base;
if ($target ne '') {
while ($self->{'ids'}->{$target}) {
$target = $target_base.'-'.$nr;
$nr++;
# Avoid integer overflow
die if ($nr == 0);
}
}
my $id = $target;
if ($command->{'extra'}->{'associated_node'}
and $self->get_conf('USE_NODE_TARGET')) {
$target
= $self->{'targets'}->{$command->{'extra'}->{'associated_node'}}->{'id'};
}
# These are undefined if the $id is set to ''.
my $target_contents;
my $id_contents;
my $target_shortcontents;
my $id_shortcontents;
if ($Texinfo::Common::sectioning_commands{$command->{'cmdname'}}) {
# NOTE id is used as base for both id and target. In comment an example
# showing how target could have been used.
#my $target_base_contents;
#if ($command->{'extra'}->{'associated_node'}
# and $self->get_conf('USE_NODE_TARGET') {
# $target_base_contents = $target;
#} else {
# $target_base_contents = $target_base;
#}
# $target_content =~ s/^g_t//;
#$target_contents = 'toc-'.$target_base_contents;
if ($id ne '') {
my $id_base_contents = $id;
$id_base_contents =~ s/^g_t//;
$target_contents = 'toc-'.$id_base_contents;
my $target_base_contents = $target_base;
$target_base_contents =~ s/^g_t//;
my $toc_nr = $nr -1;
while ($self->{'ids'}->{$target_contents}) {
$target_contents = 'toc-'.$target_base_contents.'-'.$toc_nr;
$toc_nr++;
# Avoid integer overflow
die if ($toc_nr == 0);
}
$id_contents = $target_contents;
# NOTE id is used as a base for id and target. target could also
# have been used, see above for an example.
$target_shortcontents = 'stoc-'.$id_base_contents;
my $target_base_shortcontents = $target_base;
$target_base_shortcontents =~ s/^g_t//;
my $stoc_nr = $nr -1;
while ($self->{'ids'}->{$target_shortcontents}) {
$target_shortcontents = 'stoc-'.$target_base_shortcontents
.'-'.$stoc_nr;
$stoc_nr++;
# Avoid integer overflow
die if ($stoc_nr == 0);
}
}
$id_shortcontents = $target_shortcontents;
}
if (defined($Texinfo::Config::sectioning_command_target_name)) {
($target, $id, $target_contents, $id_contents,
$target_shortcontents, $id_shortcontents, $filename)
= &$Texinfo::Config::sectioning_command_target_name($self,
$command, $target, $id,
$target_contents, $id_contents,
$target_shortcontents, $id_shortcontents,
$filename);
}
if ($self->get_conf('DEBUG')) {
print STDERR "Register $command->{'cmdname'} $target, $id\n";
}
$self->{'targets'}->{$command} = {
'target' => $target,
'id' => $id,
'section_filename' => $filename,
};
$self->{'ids'}->{$id} = $command;
if (defined($id_contents)) {
$self->{'targets'}->{$command}->{'contents_id'} = $id_contents;
$self->{'ids'}->{$id_contents} = $command;
} else {
$self->{'targets'}->{$command}->{'contents_id'} = '';
}
if (defined($target_contents)) {
$self->{'targets'}->{$command}->{'contents_target'} = $target_contents;
} else {
$self->{'targets'}->{$command}->{'contents_target'} = '';
}
if (defined($id_shortcontents)) {
$self->{'targets'}->{$command}->{'shortcontents_id'} = $id_shortcontents;
$self->{'ids'}->{$id_shortcontents} = $command;
} else {
$self->{'targets'}->{$command}->{'shortcontents_id'} = '';
}
if (defined($target_shortcontents)) {
$self->{'targets'}->{$command}->{'shortcontents_target'}
= $target_shortcontents;
} else {
$self->{'targets'}->{$command}->{'shortcontents_target'} = '';
}
return $self->{'targets'}->{$command};
}
# This set 2 unrelated things.
# * The targets and id of sectioning elements
# * the target, id and normalized filename of 'labels', ie everything that
# may be the target of a ref, like @node, @float, @anchor...
# conversion to HTML is done on-demand, upon call to command_text.
sub _set_root_commands_targets_node_files($$)
{
my $self = shift;
my $elements = shift;
my $no_unidecode;
$no_unidecode = 1 if (defined($self->get_conf('USE_UNIDECODE'))
and !$self->get_conf('USE_UNIDECODE'));
if ($self->{'labels'}) {
foreach my $root_command (values(%{$self->{'labels'}})) {
my ($filename, $target, $id) = $self->_node_id_file($root_command->{'extra'});
$filename .= '.'.$self->get_conf('NODE_FILE_EXTENSION')
if (defined($self->get_conf('NODE_FILE_EXTENSION'))
and $self->get_conf('NODE_FILE_EXTENSION') ne '');
if (defined($Texinfo::Config::node_file_name)) {
$filename = &$Texinfo::Config::node_file_name($self, $root_command,
$filename);
}
if ($self->get_conf('DEBUG')) {
print STDERR "Register label($root_command) $target, $filename\n";
}
$self->{'targets'}->{$root_command} = {'target' => $target,
'id' => $id,
'node_filename' => $filename};
$self->{'ids'}->{$id} = $root_command;
}
}
if ($elements) {
foreach my $element (@$elements) {
foreach my $root_command(@{$element->{'contents'}}) {
# this happens for type 'text_root' which precedes the
# root commands. The target may also already be set for top node.
next if (!defined($root_command->{'cmdname'})
or $self->{'targets'}->{$root_command});
if ($Texinfo::Common::sectioning_commands{$root_command->{'cmdname'}}) {
$self->_new_sectioning_command_target($root_command);
}
}
}
}
}
sub _get_element($$;$);
# If $find_container is set, the element that holds the command is found,
# otherwise the element that holds the command content is found. This is
# mostly relevant for footnote only.
sub _get_element($$;$)
{
my $self = shift;
my $command = shift;
my $find_container = shift;
my $current = $command;
my ($element, $root_command);
while (1) {
if ($current->{'type'}) {
if ($current->{'type'} eq 'element') {
return ($current, $root_command);
}
}
if ($current->{'cmdname'}) {
if ($root_commands{$current->{'cmdname'}}) {
$root_command = $current;
return ($element, $root_command) if defined($element);
} elsif ($region_commands{$current->{'cmdname'}}) {
if ($current->{'cmdname'} eq 'copying'
and $self->{'extra'} and $self->{'extra'}->{'insertcopying'}) {
foreach my $insertcopying(@{$self->{'extra'}->{'insertcopying'}}) {
my ($element, $root_command)
= $self->_get_element($insertcopying, $find_container);
return ($element, $root_command)
if (defined($element) or defined($root_command));
}
} elsif ($current->{'cmdname'} eq 'titlepage'
and $self->get_conf('USE_TITLEPAGE_FOR_TITLE')
and $self->get_conf('SHOW_TITLE')
and $self->{'elements'}->[0]) {
return ($self->{'elements'}->[0],
$self->{'elements'}->[0]->{'extra'}->{'element_command'});
}
die "Problem $element, $root_command" if (defined($element)
or defined($root_command));
return (undef, undef);
} elsif ($current->{'cmdname'} eq 'footnote'
and $self->{'special_elements_types'}->{'Footnotes'}
and $find_container) {
# in that case there is no root_command
$element = $self->{'special_elements_types'}->{'Footnotes'};
return ($element);
}
}
if ($current->{'parent'}) {
$current = $current->{'parent'};
} else {
return ($element, $root_command);
}
}
}
sub _set_pages_files($$)
{
my $self = shift;
my $elements = shift;
my $special_elements = shift;
# Ensure that the document has pages
return undef if (!defined($elements) or !@$elements);
my $extension = '';
$extension = '.'.$self->get_conf('EXTENSION')
if (defined($self->get_conf('EXTENSION'))
and $self->get_conf('EXTENSION') ne '');
if (!$self->get_conf('SPLIT')) {
foreach my $element (@$elements) {
if (!defined($element->{'filename'})) {
$element->{'filename'} = $self->{'output_filename'};
$element->{'out_filename'} = $self->{'output_file'};
}
}
} else {
my $node_top;
#my $section_top;
$node_top = $self->{'labels'}->{'Top'} if ($self->{'labels'});
#$section_top = $self->{'extra'}->{'top'} if ($self->{'extra'});
my $top_node_filename = $self->_top_node_filename();
# first determine the top node file name.
if ($self->get_conf('NODE_FILENAMES') and $node_top
and defined($top_node_filename)) {
my ($node_top_element) = $self->_get_element($node_top);
die "BUG: No element for top node" if (!defined($node_top));
$self->_set_element_file($node_top_element, $top_node_filename);
}
my $file_nr = 0;
my $previous_page;
foreach my $element(@$elements) {
# For Top node.
next if (defined($element->{'filename'}));
if (!$element->{'extra'}->{'first_in_page'}) {
cluck ("No first_in_page for $element\n");
}
if (!defined($element->{'extra'}->{'first_in_page'}->{'filename'})) {
my $file_element = $element->{'extra'}->{'first_in_page'};
if ($self->get_conf('NODE_FILENAMES')) {
foreach my $root_command (@{$file_element->{'contents'}}) {
if ($root_command->{'cmdname'}
and $root_command->{'cmdname'} eq 'node') {
my $node_filename;
# double node are not normalized, they are handled here
if (!defined($root_command->{'extra'}->{'normalized'})
or !defined($self->{'labels'}->{$root_command->{'extra'}->{'normalized'}})) {
$node_filename = 'unknown_node';
$node_filename .= '.'.$self->get_conf('NODE_FILE_EXTENSION')
if (defined($self->get_conf('NODE_FILE_EXTENSION'))
and $self->get_conf('NODE_FILE_EXTENSION') ne '');
} else {
if (!defined($self->{'targets'}->{$root_command})
or !defined($self->{'targets'}->{$root_command}->{'node_filename'})) {
# Could have been a double node, thus use equivalent node.
# However since double nodes are not normalized, in fact it
# never happens.
$root_command
= $self->{'labels'}->{$root_command->{'extra'}->{'normalized'}};
}
$node_filename
= $self->{'targets'}->{$root_command}->{'node_filename'};
}
$self->_set_element_file($file_element, $node_filename);
last;
}
}
if (!defined($file_element->{'filename'})) {
# use section to do the file name if there is no node
my $command = $self->element_command($file_element);
if ($command) {
if ($command->{'cmdname'} eq 'top' and !$node_top
and defined($top_node_filename)) {
$self->_set_element_file($file_element, $top_node_filename);
} else {
$self->_set_element_file($file_element,
$self->{'targets'}->{$command}->{'section_filename'});
}
} else {
# when everything else has failed
if ($file_nr == 0 and !$node_top
and defined($top_node_filename)) {
$self->_set_element_file($file_element, $top_node_filename);
} else {
my $filename = $self->{'document_name'} . "_$file_nr";
$filename .= $extension;
$self->_set_element_file($element, $filename);
}
$file_nr++;
}
}
} else {
my $filename = $self->{'document_name'} . "_$file_nr";
$filename .= '.'.$self->get_conf('EXTENSION')
if (defined($self->get_conf('EXTENSION'))
and $self->get_conf('EXTENSION') ne '');
$self->_set_element_file($file_element, $filename);
$file_nr++;
}
}
$element->{'filename'}
= $element->{'extra'}->{'first_in_page'}->{'filename'};
$element->{'out_filename'}
= $element->{'extra'}->{'first_in_page'}->{'out_filename'};
}
}
foreach my $element (@$elements) {
if (defined($Texinfo::Config::element_file_name)) {
# NOTE the information that it is associated with @top or @node Top
# may be determined with $self->element_is_top($element);
my $filename = &$Texinfo::Config::element_file_name($self, $element,
$element->{'filename'});
$self->_set_element_file($element, $filename) if (defined($filename));
}
$self->{'file_counters'}->{$element->{'filename'}}++;
print STDERR "Page $element ".Texinfo::Structuring::_print_element_command_texi($element).": $element->{'filename'}($self->{'file_counters'}->{$element->{'filename'}})\n"
if ($self->get_conf('DEBUG'));
}
if ($special_elements) {
my $previous_element = $elements->[-1];
foreach my $element (@$special_elements) {
my $filename
= $self->{'targets'}->{$element}->{'misc_filename'};
if (defined($filename)) {
$self->_set_element_file($element, $filename);
$self->{'file_counters'}->{$element->{'filename'}}++;
print STDERR "Special page $element: $element->{'filename'}($self->{'file_counters'}->{$element->{'filename'}})\n"
if ($self->get_conf('DEBUG'));
}
$element->{'element_prev'} = $previous_element;
$previous_element->{'element_next'} = $element;
$previous_element = $element;
}
}
}
sub _prepare_elements($$)
{
my $self = shift;
my $root = shift;
my $elements;
# do that now to have it available for formatting
# NOTE this calls Convert::Converter::_informative_command on all the
# @informative_global commands.
# Thus sets among others language and encodings.
$self->_set_global_multiple_commands(-1);
$self->_translate_names();
if ($self->get_conf('USE_NODES')) {
$elements = Texinfo::Structuring::split_by_node($root);
} else {
$elements = Texinfo::Structuring::split_by_section($root);
}
$self->{'elements'} = $elements
if (defined($elements));
# This may be done as soon as elements are available.
$self->_prepare_global_targets($elements);
# Do that before the other elements, to be sure that special page ids
# are registered before elements id are.
my $special_elements
= $self->_prepare_special_elements($elements);
$self->{'special_elements'} = $special_elements
if (defined($special_elements));
#if ($elements) {
# foreach my $element(@{$elements}) {
# print STDERR "ELEMENT $element->{'type'}: $element\n";
# }
#}
$self->_set_root_commands_targets_node_files($elements);
return ($elements, $special_elements);
}
sub _prepare_special_elements($$)
{
my $self = shift;
my $elements = shift;
my %do_special;
# FIXME let the user decide how @*contents are treated?
if ($self->{'structuring'} and $self->{'structuring'}->{'sectioning_root'}
and scalar(@{$self->{'structuring'}->{'sections_list'}}) > 1) {
foreach my $cmdname ('contents', 'shortcontents') {
my $type = $contents_command_element_name{$cmdname};
if ($self->get_conf($cmdname)) {
if ($self->get_conf('INLINE_CONTENTS')
or ($self->get_conf('set'.$cmdname.'aftertitlepage'))) {
} else {
$do_special{$type} = 1;
}
}
}
}
if ($self->{'extra'}->{'footnote'}
and $self->get_conf('footnotestyle') eq 'separate'
and $elements and scalar(@$elements) > 1) {
$do_special{'Footnotes'} = 1;
}
if ((!defined($self->get_conf('DO_ABOUT'))
and $elements and scalar(@$elements) > 1
and ($self->get_conf('SPLIT') or $self->get_conf('HEADERS')))
or ($self->get_conf('DO_ABOUT'))) {
$do_special{'About'} = 1;
}
my $extension = '';
$extension = $self->get_conf('EXTENSION')
if (defined($self->get_conf('EXTENSION')));
my $special_elements = [];
foreach my $type (@{$self->{'misc_elements_order'}}) {
next unless ($do_special{$type});
my $element = {'type' => 'element',
'extra' => {'special_element' => $type,
}};
$element->{'extra'}->{'directions'}->{'This'} = $element;
$self->{'special_elements_types'}->{$type} = $element;
push @$special_elements, $element;
my $id = $self->{'misc_elements_targets'}->{$type};
my $target = $id;
my $default_filename;
if ($self->get_conf('SPLIT') or !$self->get_conf('MONOLITHIC')) {
$default_filename = $self->{'document_name'}.
$self->{'misc_pages_file_string'}->{$type};
$default_filename .= '.'.$extension if (defined($extension));
} else {
$default_filename = undef;
}
my $filename;
if (defined($Texinfo::Config::special_element_target_file_name)) {
($target, $id, $filename)
= &$Texinfo::Config::special_element_target_file_name(
$self,
$element,
$target, $id,
$default_filename);
}
$filename = $default_filename if (!defined($filename));
if ($self->get_conf('DEBUG')) {
my $fileout = $filename;
$fileout = 'UNDEF' if (!defined($fileout));
print STDERR "Add special $element $type: target $target, id $id,\n".
" filename $fileout\n"
}
if ($self->get_conf('SPLIT') or !$self->get_conf('MONOLITHIC')
or (defined($filename) ne defined($default_filename))
or (defined($filename) and $filename ne $default_filename)) {
$self->_set_element_file($element, $filename);
print STDERR "NEW page for $type ($filename)\n" if ($self->get_conf('DEBUG'));
}
$self->{'targets'}->{$element} = {'id' => $id,
'target' => $target,
'misc_filename' => $filename,
};
$self->{'ids'}->{$id} = $element;
}
if ($self->get_conf('FRAMES')) {
foreach my $type (keys(%{$self->{'frame_pages_file_string'}})) {
my $default_filename;
$default_filename = $self->{'document_name'}.
$self->{'frame_pages_file_string'}->{$type};
$default_filename .= '.'.$extension if (defined($extension));
my $element = {'type' => 'element',
'extra' => {'special_element' => $type,
}};
# only the filename is used
my ($target, $id, $filename);
if (defined($Texinfo::Config::special_element_target_file_name)) {
($target, $id, $filename)
= &$Texinfo::Config::special_element_target_file_name(
$self,
$element,
$target, $id,
$default_filename);
}
$filename = $default_filename if (!defined($filename));
$self->{'frame_pages_filenames'}->{$type} = $filename;
}
}
return $special_elements;
}
sub _prepare_contents_elements($)
{
my $self = shift;
if ($self->{'structuring'} and $self->{'structuring'}->{'sectioning_root'}
and scalar(@{$self->{'structuring'}->{'sections_list'}}) > 1) {
foreach my $cmdname ('contents', 'shortcontents') {
my $type = $contents_command_element_name{$cmdname};
if ($self->get_conf($cmdname)) {
my $default_filename;
if ($self->get_conf('set'.$cmdname.'aftertitlepage')) {
if ($self->{'elements'}) {
$default_filename = $self->{'elements'}->[0]->{'filename'};
}
} elsif ($self->get_conf('INLINE_CONTENTS')) {
if ($self->{'extra'} and $self->{'extra'}->{$cmdname}) {
foreach my $command(@{$self->{'extra'}->{$cmdname}}) {
my ($element, $root_command)
= $self->_get_element($command);
if (defined($element)) {
$default_filename = $element->{'filename'};
last;
}
}
} else {
next;
}
} else { # in this case, there should already be a special element
# if needed, done together with the other special elements.
next;
}
my $element = {'type' => 'element',
'extra' => {'special_element' => $type}};
$self->{'special_elements_types'}->{$type} = $element;
my $id = $self->{'misc_elements_targets'}->{$type};
my $target = $id;
my $filename;
if (defined($Texinfo::Config::special_element_target_file_name)) {
($target, $id, $filename)
= &$Texinfo::Config::special_element_target_file_name(
$self,
$element,
$target, $id,
$default_filename);
}
$filename = $default_filename if (!defined($filename));
print STDERR "Add content $element $type: target $target, id $id,\n".
" filename $filename\n" if ($self->get_conf('DEBUG'));
$self->{'targets'}->{$element} = {'id' => $id,
'target' => $target,
'misc_filename' => $filename,
'filename' => $filename,
};
}
}
}
}
# Associate elements with the global targets, First, Last, Top, Index.
sub _prepare_global_targets($$)
{
my $self = shift;
my $elements = shift;
$self->{'global_target_elements'}->{'First'} = $elements->[0];
$self->{'global_target_elements'}->{'Last'} = $elements->[-1];
# It is always the first printindex, even if it is not output (for example
# it is in @copying and @titlepage, which are certainly wrong constructs).
if ($self->{'extra'} and $self->{'extra'}->{'printindex'}) {
my ($element, $root_command)
= $self->_get_element($self->{'extra'}->{'printindex'}->[0]);
if (defined($element)) {
if ($root_command and $root_command->{'cmdname'} eq 'node'
and $element->{'extra'}->{'section'}) {
$root_command = $element->{'extra'}->{'section'};
}
if ($root_command and $root_command->{'cmdname'} ne 'node') {
while ($root_command->{'level'} > 1
and $root_command->{'section_up'}
and $root_command->{'section_up'}->{'parent'}) {
$root_command = $root_command->{'section_up'};
$element = $root_command->{'parent'};
}
}
$self->{'global_target_elements'}->{'Index'} = $element;
}
}
my $node_top;
$node_top = $self->{'labels'}->{'Top'} if ($self->{'labels'});
my $section_top;
$section_top = $self->{'extra'}->{'top'} if ($self->{'extra'});
if ($section_top) {
$self->{'global_target_elements'}->{'Top'} = $section_top->{'parent'};
} elsif ($node_top) {
my $element_top = $node_top->{'parent'};
if (!$element_top) {
die "No parent for node_top: ".Texinfo::Parser::_print_current($node_top);
}
$self->{'global_target_elements'}->{'Top'} = $element_top;
} else {
$self->{'global_target_elements'}->{'Top'} = $elements->[0];
}
if ($self->get_conf('DEBUG')) {
print STDERR "GLOBAL DIRECTIONS:\n";
foreach my $global_direction ('First', 'Last', 'Index', 'Top') {
if (defined($self->{'global_target_elements'}->{$global_direction})) {
print STDERR "$global_direction($self->{'global_target_elements'}->{$global_direction}): ".
Texinfo::Structuring::_print_element_command_texi(
$self->{'global_target_elements'}->{$global_direction})."\n";
}
}
}
}
sub _prepare_index_entries($)
{
my $self = shift;
if ($self->{'parser'}) {
my $no_unidecode;
$no_unidecode = 1 if (defined($self->get_conf('USE_UNIDECODE'))
and !$self->get_conf('USE_UNIDECODE'));
my ($index_names, $merged_indices)
= $self->{'parser'}->indices_information();
$self->{'index_names'} = $index_names;
#print STDERR "IIII ($index_names, $merged_indices, $index_entries)\n";
my $merged_index_entries
= Texinfo::Structuring::merge_indices($index_names);
$self->{'index_entries_by_letter'}
= $self->Texinfo::Structuring::sort_indices_by_letter($merged_index_entries,
$index_names);
$self->{'index_entries'} = $merged_index_entries;
foreach my $index_name (sort(keys(%$index_names))) {
foreach my $index_entry (@{$index_names->{$index_name}->{'index_entries'}}) {
my $region = '';
$region = "$index_entry->{'region'}->{'cmdname'}-"
if (defined($index_entry->{'region'}));
my @contents = @{$index_entry->{'content_normalized'}};
my $trimmed_contents
= Texinfo::Common::trim_spaces_comment_from_content(\@contents);
my $normalized_index =
Texinfo::Convert::NodeNameNormalization::transliterate_texinfo(
{'contents' => \@contents}, $no_unidecode);
my $target_base = "index-" . $region .$normalized_index;
my $nr=1;
my $target = $target_base;
while ($self->{'ids'}->{$target}) {
$target = $target_base.'-'.$nr;
$nr++;
# Avoid integer overflow
die if ($nr == 0);
}
my $id = $target;
$self->{'ids'}->{$target} = $index_entry->{'command'};
$self->{'targets'}->{$index_entry->{'command'}} = { 'id' => $id,
'target' => $target,
};
#print STDERR "Enter $index_entry $index_entry->{'command'}: $id\n";
}
}
}
}
my $footid_base = 'FOOT';
my $docid_base = 'DOCF';
sub _prepare_footnotes($)
{
my $self = shift;
if ($self->{'extra'}->{'footnote'}) {
my $footnote_nr = 0;
foreach my $footnote (@{$self->{'extra'}->{'footnote'}}) {
$footnote_nr++;
my $nr = $footnote_nr;
my $footid = $footid_base.$nr;
my $docid = $docid_base.$nr;
while ($self->{'ids'}->{$docid} or $self->{'ids'}->{$footid}) {
$nr++;
$footid = $footid_base.$nr;
$docid = $docid_base.$nr;
# Avoid integer overflow
die if ($nr == 0);
}
$self->{'ids'}->{$footid} = $footnote;
$self->{'ids'}->{$docid} = $footnote;
$self->{'targets'}->{$footnote} = { 'id' => $docid,
'target' => $footid,
};
print STDERR "Enter footnote $footnote: id $docid, target $footid, nr $footnote_nr\n"
.Texinfo::Convert::Texinfo::convert($footnote)."\n"
if ($self->get_conf('DEBUG'));
}
}
}
# TODO this encapsulates some information.
# The encapsulation and API should be more consistent for
# the overall module.
sub _htmlxref($$)
{
my $self = shift;
my $file = shift;
return $self->{'htmlxref'}->{$file};
}
my %htmlxref_entries = %Texinfo::Common::htmlxref_entries;
sub _external_node_href($$$$)
{
my $self = shift;
my $external_node = shift;
my $filename = shift;
my $link_command = shift;
# This only overrides an implicit pointer to (dir) as the Top node's Up.
# It has no effect if a pointer is explicitly specified,
# or if implicit pointers aren't being created (e.g., just a Top node).
if ($external_node->{'top_node_up'}
and defined($self->get_conf('TOP_NODE_UP_URL'))) {
return $self->get_conf('TOP_NODE_UP_URL');
}
# In addition to that implicit (dir) as the Top node's Up, replace all
# other references to external file "dir" with the same TOP_NODE_UP_URL.
if (defined($self->get_conf('TOP_NODE_UP_URL'))
and $external_node->{'manual_content'}[0]->{'text'} eq "dir") {
return $self->get_conf('TOP_NODE_UP_URL');
}
#print STDERR "external_node: ".join('|', keys(%$external_node))."\n";
my ($target_filebase, $target, $id) = $self->_node_id_file($external_node);
my $xml_target = _normalized_to_id($target);
my $default_target_split = $self->get_conf('EXTERNAL_CROSSREF_SPLIT');
my $extension = '';
$extension = "." . $self->get_conf('NODE_FILE_EXTENSION')
if (defined($self->get_conf('NODE_FILE_EXTENSION'))
and $self->get_conf('NODE_FILE_EXTENSION') ne '');
my $target_split;
my $file;
if ($external_node->{'manual_content'}) {
my $manual_name = Texinfo::Convert::Text::convert(
{'contents' => $external_node->{'manual_content'}},
{ 'code' => 1,
Texinfo::Common::_convert_text_options($self)});
my $manual_base = $manual_name;
$manual_base =~ s/\.[^\.]*$//;
$manual_base =~ s/^.*\///;
my $document_split = $self->get_conf('SPLIT');
$document_split = 'mono' if (!$document_split);
my $split_found;
my $href;
my $htmlxref_info = $self->_htmlxref($manual_base);
if ($htmlxref_info) {
foreach my $split_ordered (@{$htmlxref_entries{$document_split}}) {
if (defined($htmlxref_info->{$split_ordered})) {
$split_found = $split_ordered;
$href = $htmlxref_info->{$split_ordered};
last;
}
}
}
if (defined($split_found)) {
$target_split = 1 unless ($split_found eq 'mono');
} else { # nothing specified for that manual, use default
$target_split = $default_target_split;
if ($self->get_conf('CHECK_HTMLXREF')
and !$external_node->{'top_node_up'}) {
if (defined($link_command) and $link_command->{'line_nr'}) {
$self->line_warn(sprintf($self->__(
"no htmlxref.cnf entry found for `%s'"), $manual_name),
$link_command->{'line_nr'});
} elsif (!$self->{'check_htmlxref_already_warned'}->{$manual_name}) {
$self->document_warn(sprintf($self->__(
"no htmlxref.cnf entry found for `%s'"), $manual_name),
);
}
$self->{'check_htmlxref_already_warned'}->{$manual_name} = 1;
}
}
if ($target_split) {
if (defined($href)) {
$file = $href;
} elsif (defined($self->get_conf('EXTERNAL_DIR'))) {
$file = $self->get_conf('EXTERNAL_DIR')."/$manual_base";
} elsif ($self->get_conf('SPLIT')) {
$file = "../$manual_base";
}
$file .= "/";
} else {# target not split
if (defined($href)) {
$file = $href;
} else {
if (defined($self->get_conf('EXTERNAL_DIR'))) {
$file = $self->get_conf('EXTERNAL_DIR')."/$manual_base";
} elsif ($self->get_conf('SPLIT')) {
$file = "../$manual_base";
} else {
$file = $manual_base;
}
$file .= $extension;
}
}
} else {
$file = '';
$target_split = $default_target_split;
}
if ($target eq '') {
if ($target_split) {
if (defined($self->get_conf('TOP_NODE_FILE_TARGET'))) {
return $file . $self->get_conf('TOP_NODE_FILE_TARGET')
. $extension;# . '#Top';
} else {
return $file;# . '#Top';
}
} else {
return $file . '#Top';
}
}
if (! $target_split) {
return $file . '#' . $xml_target;
} else {
my $file_basename;
if ($target eq 'Top' and defined($self->get_conf('TOP_NODE_FILE_TARGET'))) {
$file_basename = $self->get_conf('TOP_NODE_FILE_TARGET');
} else {
$file_basename = $target_filebase;
}
return $file . $file_basename . $extension . '#' . $xml_target;
}
}
my %valid_types = (
'href' => 1,
'string' => 1,
'text' => 1,
'tree' => 1,
'target' => 1,
'id' => 1,
'node' => 1,
);
foreach my $no_number_type ('text', 'tree', 'string') {
$valid_types{$no_number_type .'_nonumber'} = 1;
}
sub _element_direction($$$$;$)
{
my $self = shift;
my $element = shift;
my $direction = shift;
my $type = shift;
my $filename = shift;
my $element_target;
my $command;
my $target;
$filename = $self->{'current_filename'} if (!defined($filename));
if (!$valid_types{$type}) {
print STDERR "Incorrect type $type in _element_direction call\n";
return undef;
}
if ($self->{'global_target_elements'}->{$direction}) {
$element_target = $self->{'global_target_elements'}->{$direction};
} elsif ($element and $element->{'extra'}
and $element->{'extra'}->{'directions'}
and $element->{'extra'}->{'directions'}->{$direction}) {
$element_target
= $element->{'extra'}->{'directions'}->{$direction};
}
if ($element_target) {
######## debug
if (!$element_target->{'type'}) {
die "No type for element_target $direction $element_target: "
. Texinfo::Parser::_print_current_keys($element_target)
. "directions :". Texinfo::Structuring::_print_directions($element);
}
########
if ($element_target->{'type'} eq 'external_node'
or $element_target->{'type'} eq 'top_node_up') {
my $external_node = $element_target->{'extra'};
if ($type eq 'href') {
return $self->command_href($external_node, $filename);
} elsif ($type eq 'text' or $type eq 'node') {
return $self->command_text($external_node);
} elsif ($type eq 'string') {
return $self->command_text($external_node, $type);
}
} elsif ($type eq 'node') {
$command = $element_target->{'extra'}->{'node'};
$target = $self->{'targets'}->{$command} if ($command);
$type = 'text';
} else {
if ($element_target->{'extra'}->{'special_element'}) {
$command = $element_target;
} else {
$command = $element_target->{'extra'}->{'element_command'};
}
if ($type eq 'href') {
if (defined($command)) {
return $self->command_href($command, $filename);
} else {
return '';
}
}
$target = $self->{'targets'}->{$command} if ($command);
}
} elsif ($self->special_element($direction)) {
$element_target = $self->special_element($direction);
$command = $element_target;
if ($type eq 'href') {
return $self->command_href($element_target, $filename);
}
$target = $self->{'targets'}->{$element_target};
} else {
return undef;
}
if (exists($target->{$type})) {
return $target->{$type};
} elsif ($type eq 'id' or $type eq 'target') {
return undef;
} elsif ($command) {
return $self->command_text($command, $type);
}
}
sub _default_contents($$;$$)
{
my $self = shift;
my $cmdname = shift;
my $command = shift;
my $filename = shift;
$filename = $self->{'current_filename'} if (!defined($filename));
return ''
if (!$self->{'structuring'} or !$self->{'structuring'}->{'sectioning_root'});
my $section_root = $self->{'structuring'}->{'sectioning_root'};
my $contents;
$contents = 1 if ($cmdname eq 'contents');
my $min_root_level = $section_root->{'section_childs'}->[0]->{'level'};
my $max_root_level = $section_root->{'section_childs'}->[0]->{'level'};
foreach my $top_section(@{$section_root->{'section_childs'}}) {
$min_root_level = $top_section->{'level'}
if ($top_section->{'level'} < $min_root_level);
$max_root_level = $top_section->{'level'}
if ($top_section->{'level'} > $max_root_level);
}
# chapter level elements are considered top-level here.
$max_root_level = 1 if ($max_root_level < 1);
#print STDERR "ROOT_LEVEL Max: $max_root_level, Min: $min_root_level\n";
my $ul_class = '';
$ul_class = $NO_BULLET_LIST_CLASS if ($self->get_conf('NUMBER_SECTIONS'));
my $result = '';
if ($contents and !defined($self->get_conf('BEFORE_TOC_LINES'))
or (!$contents and !defined($self->get_conf('BEFORE_OVERVIEW')))) {
$result .= $self->_attribute_class('div', $cmdname).">\n";
} elsif($contents) {
$result .= $self->get_conf('BEFORE_TOC_LINES');
} else {
$result .= $self->get_conf('BEFORE_OVERVIEW');
}
my $toplevel_contents;
if (@{$section_root->{'section_childs'}} > 1) {
# or $section_root->{'section_childs'}->[0]->{'cmdname'} ne 'top') {
$result .= $self->_attribute_class('ul', $ul_class) .">\n";
$toplevel_contents = 1;
}
foreach my $top_section (@{$section_root->{'section_childs'}}) {
my $section = $top_section;
SECTION:
while ($section) {
if ($section->{'cmdname'} ne 'top') {
my $text = $self->command_text($section);
my $href;
if (!$contents and $self->get_conf('OVERVIEW_LINK_TO_TOC')) {
$href = $self->command_contents_href($section, 'contents', $filename);
} else {
$href = $self->command_href($section, $filename);
}
my $toc_id = $self->command_contents_id($section, $cmdname);
if ($text ne '') {
# no indenting for shortcontents
$result .= (' ' x (2*($section->{'level'} - $min_root_level)))
if ($contents);
if ($toc_id ne '' or $href ne '') {
my $toc_name_attribute = '';
if ($toc_id ne '') {
$toc_name_attribute = "name=\"$toc_id\" ";
}
my $href_attribute = '';
if ($href ne '') {
$href_attribute = "href=\"$href\"";
}
$result .= "<li><a ${toc_name_attribute}${href_attribute}>$text</a>";
} else {
$result .= "<li>$text";
}
}
} elsif ($section->{'section_childs'} and @{$section->{'section_childs'}}
and $toplevel_contents) {
$result .= "<li>";
}
# for shortcontents don't do child if child is not toplevel
if ($section->{'section_childs'}
and ($contents or $section->{'level'} < $max_root_level)) {
# no indenting for shortcontents
$result .= "\n". ' ' x (2*($section->{'level'} - $min_root_level))
if ($contents);
$result .= $self->_attribute_class('ul', $ul_class) .">\n";
$section = $section->{'section_childs'}->[0];
} elsif ($section->{'section_next'} and $section->{'cmdname'} ne 'top') {
$result .= "</li>\n";
last if ($section eq $top_section);
$section = $section->{'section_next'};
} else {
#last if ($section eq $top_section);
if ($section eq $top_section) {
$result .= "</li>\n" unless ($section->{'cmdname'} eq 'top');
last;
}
while ($section->{'section_up'}) {
$section = $section->{'section_up'};
$result .= "</li>\n". ' ' x (2*($section->{'level'} - $min_root_level))
. "</ul>";
if ($section eq $top_section) {
$result .= "</li>\n" if ($toplevel_contents);
last SECTION;
}
if ($section->{'section_next'}) {
$result .= "</li>\n";
$section = $section->{'section_next'};
last;
}
}
}
}
}
if (@{$section_root->{'section_childs'}} > 1) {
# or $section_root->{'section_childs'}->[0]->{'cmdname'} ne 'top') {
$result .= "\n</ul>";
}
if ($contents and !defined($self->get_conf('AFTER_TOC_LINES'))
or (!$contents and !defined($self->get_conf('AFTER_OVERVIEW')))) {
$result .= "\n</div>\n";
} elsif($contents) {
$result .= $self->get_conf('AFTER_TOC_LINES');
} else {
$result .= $self->get_conf('AFTER_OVERVIEW');
}
return $result;
}
sub _default_program_string($)
{
my $self = shift;
if (defined($self->get_conf('PROGRAM'))
and $self->get_conf('PROGRAM') ne ''
and defined($self->get_conf('PACKAGE_URL'))) {
return $self->convert_tree(
$self->gdt('This document was generated on @emph{@today{}} using @uref{{program_homepage}, @emph{{program}}}.',
{ 'program_homepage' => $self->get_conf('PACKAGE_URL'),
'program' => $self->get_conf('PROGRAM') }));
} else {
return $self->convert_tree(
$self->gdt('This document was generated on @emph{@today{}}.'));
}
}
sub _default_end_file($)
{
my $self = shift;
my $program_text = '';
if ($self->get_conf('PROGRAM_NAME_IN_FOOTER')) {
my $program_string = &{$self->{'format_program_string'}}($self);
$program_text = "<p><font size=\"-1\">
$program_string
</font></p>";
}
my $pre_body_close = $self->get_conf('PRE_BODY_CLOSE');
$pre_body_close = '' if (!defined($pre_body_close));
return "$program_text
$pre_body_close
</body>
</html>
";
}
# This is used for normal output files and other files, like renamed
# nodes file headers, or redirection file headers.
sub _file_header_informations($$)
{
my $self = shift;
my $command = shift;
my $title;
if ($command) {
my $command_string =
$self->command_text($command, 'string');
if (defined($command_string)
and $command_string ne $self->{'title_string'}) {
print STDERR "DO <title>\n"
if ($self->get_conf('DEBUG'));
my $title_tree = $self->gdt('{title}: {element_text}',
{ 'title' => $self->{'title_tree'},
'element_text' => $self->command_text($command, 'tree')});
$title = $self->convert_tree_new_formatting_context(
{'type' => '_string', 'contents' => [$title_tree]},
$command->{'cmdname'}, 'element_title');
}
}
$title = $self->{'title_string'} if (!defined($title));
my $description;
if ($self->{'documentdescription_string'}) {
$description = $self->{'documentdescription_string'};
} else {
$description = $title;
}
$description = "<meta name=\"description\" content=\"$description\">"
if ($description ne '');
my $encoding = '';
$encoding
= "<meta http-equiv=\"Content-Type\" content=\"text/html; charset=".
$self->get_conf('OUTPUT_ENCODING_NAME')."\">"
if (defined($self->get_conf('OUTPUT_ENCODING_NAME'))
and ($self->get_conf('OUTPUT_ENCODING_NAME') ne ''));
my $date = '';
if ($self->get_conf('DATE_IN_HEADER')) {
my $today = $self->convert_tree_new_formatting_context(
{'cmdname' => 'today'}, 'DATE_IN_HEADER');
$date = "\n<meta name=\"date\" content=\"$today\">";
}
my $css_lines;
if (defined($self->get_conf('CSS_LINES'))) {
$css_lines = $self->get_conf('CSS_LINES');
} else {
$css_lines = '';
}
my $doctype = $self->get_conf('DOCTYPE');
my $bodytext = $self->get_conf('BODYTEXT');
my $copying_comment = '';
$copying_comment = $self->{'copying_comment'}
if (defined($self->{'copying_comment'}));
my $after_body_open = '';
$after_body_open = $self->get_conf('AFTER_BODY_OPEN')
if (defined($self->get_conf('AFTER_BODY_OPEN')));
my $extra_head = '';
$extra_head = $self->get_conf('EXTRA_HEAD')
if (defined($self->get_conf('EXTRA_HEAD')));
my $program_and_version = $self->get_conf('PACKAGE_AND_VERSION');
my $program_homepage = $self->get_conf('PACKAGE_URL');
my $program = $self->get_conf('PROGRAM');
my $generator = '';
if (defined($program) and $program ne '') {
$generator = "\n<meta name=\"Generator\" content=\"$program\">";
}
return ($title, $description, $encoding, $date, $css_lines,
$doctype, $bodytext, $copying_comment, $after_body_open,
$extra_head, $program_and_version, $program_homepage,
$program, $generator);
}
sub _get_links ($$$)
{
my $self = shift;
my $filename = shift;
my $element = shift;
my $links = '';
if ($self->get_conf('USE_LINKS')) {
my $link_buttons = $self->get_conf('LINKS_BUTTONS');
foreach my $link (@$link_buttons) {
my $link_href = $self->_element_direction($element,
$link, 'href', $filename);
#print STDERR "$title: $link -> $link_href \n";
if ($link_href and $link_href ne '') {
my $link_string = $self->_element_direction($element,
$link, 'string');
my $link_title = '';
$link_title = " title=\"$link_string\"" if (defined($link_string));
my $rel = '';
$rel = " rel=\"".$self->get_conf('BUTTONS_REL')->{$link}.'"'
if (defined($self->get_conf('BUTTONS_REL')->{$link}));
$links .= "<link href=\"$link_href\"${rel}${link_title}>\n";
}
}
}
return $links;
}
sub _default_begin_file($$$)
{
my $self = shift;
my $filename = shift;
my $element = shift;
my $command;
if ($element and $self->get_conf('SPLIT')) {
$command = $self->element_command($element);
}
my ($title, $description, $encoding, $date, $css_lines,
$doctype, $bodytext, $copying_comment, $after_body_open,
$extra_head, $program_and_version, $program_homepage,
$program, $generator) = $self->_file_header_informations($command);
my $links = $self->_get_links ($filename, $element);
my $result = "$doctype
<html>
$copying_comment<!-- Created by $program_and_version, $program_homepage -->
<head>
<title>$title</title>
$description
<meta name=\"keywords\" content=\"$title\">
<meta name=\"resource-type\" content=\"document\">
<meta name=\"distribution\" content=\"global\">${generator}$date
$encoding
${links}$css_lines
$extra_head
</head>
<body $bodytext>
$after_body_open";
return $result;
}
sub _default_node_redirection_page($$)
{
my $self = shift;
my $command = shift;
my ($title, $description, $encoding, $date, $css_lines,
$doctype, $bodytext, $copying_comment, $after_body_open,
$extra_head, $program_and_version, $program_homepage,
$program, $generator) = $self->_file_header_informations($command);
my $name = $self->command_text($command);
my $href = $self->command_href($command);
my $direction = "<a href=\"$href\">$name</a>";
my $string = $self->convert_tree (
$self->gdt('The node you are looking for is at {href}.',
{ 'href' => {'type' => '_converted', 'text' => $direction }}));
my $result = "$doctype
<html>
$copying_comment<!-- Created by $program_and_version, $program_homepage -->
<!-- This file redirects to the location of a node or anchor -->
<head>
<title>$title</title>
$description
<meta name=\"keywords\" content=\"$title\">
<meta name=\"resource-type\" content=\"document\">
<meta name=\"distribution\" content=\"global\">${generator}$date
$encoding
$css_lines
<meta http-equiv=\"Refresh\" content=\"0; url=$href\">
$extra_head
</head>
<body $bodytext>
$after_body_open
<p>$string</p>
</body>
";
return $result;
}
sub _default_footnotes_text($)
{
my $self = shift;
return '' if (!$foot_lines);
my $result = $self->_attribute_class('div', 'footnote').">\n";
$result .= $self->get_conf('DEFAULT_RULE') . "\n"
if (defined($self->get_conf('DEFAULT_RULE'))
and $self->get_conf('DEFAULT_RULE') ne '');
my $footnote_heading
= $self->convert_tree ($self->get_conf('SPECIAL_ELEMENTS_NAME')->{'Footnotes'});
my $class = $self->get_conf('SPECIAL_ELEMENTS_CLASS')->{'Footnotes'};
my $level = $self->get_conf('FOOTNOTE_END_HEADER_LEVEL');
$result .= &{$self->{'format_heading_text'}}($self, $class.'-heading',
$footnote_heading, $level)."\n";
$result .= &{$self->{'format_special_element_body'}}($self, 'Footnotes',
$self->{'current_element'});
$result .= "</div>\n";
return $result;
}
sub _default_special_element_body($$$)
{
my $self = shift;
my $special_type = shift;
my $element = shift;
if ($special_type eq 'About') {
my $about = "<p>\n";
my $PRE_ABOUT = $self->get_conf('PRE_ABOUT');
if (defined($PRE_ABOUT)) {
if (ref($PRE_ABOUT) eq 'CODE') {
$about .= &$PRE_ABOUT($self, $element);
} else {
$about .= $PRE_ABOUT;
}
} else {
$about .= ' '.&{$self->{'format_program_string'}}($self) ."\n";
}
$about .= <<EOT;
</p>
<p>
EOT
$about .= $self->convert_tree($self->gdt(' The buttons in the navigation panels have the following meaning:')) . "\n";
$about .= <<EOT;
</p>
<table border="1">
<tr>
EOT
$about .= ' <th> ' . $self->convert_tree($self->gdt('Button')) . " </th>\n" .
' <th> ' . $self->convert_tree($self->gdt('Name')) . " </th>\n" .
' <th> ' . $self->convert_tree($self->gdt('Go to')) . " </th>\n" .
' <th> ' . $self->convert_tree($self->gdt('From 1.2.3 go to')) . "</th>\n" . " </tr>\n";
foreach my $button (@{$self->get_conf('SECTION_BUTTONS')}) {
next if ($button eq ' ' or ref($button) eq 'CODE' or ref($button) eq 'SCALAR'
or ref($button) eq 'ARRAY');
my $button_name = $self->get_conf('BUTTONS_NAME')->{$button};
$about .= " <tr>\n <td align=\"center\">";
$about .=
($self->get_conf('ICONS') && $self->get_conf('ACTIVE_ICONS')->{$button} ?
&{$self->{'format_button_icon_img'}}($self, $button_name,
$self->get_conf('ACTIVE_ICONS')->{$button}) :
' [' . $self->get_conf('BUTTONS_TEXT')->{$button} . '] ');
$about .= "</td>\n";
$about .=
" <td align=\"center\">".$button_name."</td>
<td>".$self->get_conf('BUTTONS_GOTO')->{$button}."</td>
<td>".$self->get_conf('BUTTONS_EXAMPLE')->{$button}."</td>
</tr>
";
}
$about .= <<EOT;
</table>
<p>
EOT
$about .= $self->convert_tree($self->gdt(' where the @strong{ Example } assumes that the current position is at @strong{ Subsubsection One-Two-Three } of a document of the following structure:')) . "\n";
# where the <strong> Example </strong> assumes that the current position
# is at <strong> Subsubsection One-Two-Three </strong> of a document of
# the following structure:
$about .= <<EOT;
</p>
<ul>
EOT
$about .= ' <li> 1. ' . $self->convert_tree($self->gdt('Section One')) . "\n" .
" <ul>\n" .
' <li>1.1 ' . $self->convert_tree($self->gdt('Subsection One-One')) . "\n";
$about .= <<EOT;
<ul>
<li>...</li>
</ul>
</li>
EOT
$about .= ' <li>1.2 ' . $self->convert_tree($self->gdt('Subsection One-Two')) . "\n" .
" <ul>\n" .
' <li>1.2.1 ' . $self->convert_tree($self->gdt('Subsubsection One-Two-One')) . "</li>\n" .
' <li>1.2.2 ' . $self->convert_tree($self->gdt('Subsubsection One-Two-Two')) . "</li>\n" .
' <li>1.2.3 ' . $self->convert_tree($self->gdt('Subsubsection One-Two-Three')) . " &nbsp; &nbsp;\n"
.
' <strong>&lt;== ' . $self->convert_tree($self->gdt('Current Position')) . " </strong></li>\n" .
' <li>1.2.4 ' . $self->convert_tree($self->gdt('Subsubsection One-Two-Four')) . "</li>\n" .
" </ul>\n" .
" </li>\n" .
' <li>1.3 ' . $self->convert_tree($self->gdt('Subsection One-Three')) . "\n";
$about .= <<EOT;
<ul>
<li>...</li>
</ul>
</li>
EOT
$about .= ' <li>1.4 ' . $self->convert_tree($self->gdt('Subsection One-Four')) . "</li>\n";
my $AFTER_ABOUT = '';
if (defined($self->get_conf('AFTER_ABOUT'))) {
$AFTER_ABOUT = $self->get_conf('AFTER_ABOUT');
}
$about .= <<EOT;
</ul>
</li>
</ul>
$AFTER_ABOUT
EOT
return $about;
} elsif ($special_type eq 'Contents') {
return &{$self->{'format_contents'}}($self, 'contents', undef);
} elsif ($special_type eq 'Overview') {
return &{$self->{'format_contents'}}($self, 'shortcontents', undef);
} elsif ($special_type eq 'Footnotes') {
my $result = $foot_lines;
$foot_lines = '';
return $result;
}
}
sub _default_frame_files($)
{
my $self = shift;
my $frame_file = $self->{'frame_pages_filenames'}->{'Frame'};
my $frame_outfile;
if (defined($self->{'destination_directory'})
and $self->{'destination_directory'} ne '') {
$frame_outfile = File::Spec->catfile($self->{'destination_directory'},
$frame_file);
} else {
$frame_outfile = $frame_file;
}
my $toc_frame_file = $self->{'frame_pages_filenames'}->{'Toc_Frame'};
my $toc_frame_outfile;
if (defined($self->{'destination_directory'})
and $self->{'destination_directory'} ne '') {
$toc_frame_outfile = File::Spec->catfile($self->{'destination_directory'},
$toc_frame_file);
} else {
$toc_frame_outfile = $toc_frame_file;
}
my $frame_fh = $self->Texinfo::Common::open_out($frame_outfile);
if (defined($frame_fh)) {
my $doctype = $self->get_conf('FRAMESET_DOCTYPE');
my $top_file = '';
if ($self->global_element('Top')) {
my $top_element = $self->global_element('Top');
$top_file = $top_element->{'filename'};
}
my $title = $self->{'title_string'};
print $frame_fh <<EOT;
$doctype
<html>
<head><title>$title</title></head>
<frameset cols="140,*">
<frame name="toc" src="$toc_frame_file">
<frame name="main" src="$top_file">
</frameset>
</html>
EOT
$self->register_close_file($frame_outfile);
if (!close ($frame_fh)) {
$self->document_error(sprintf($self->__("error on closing frame file %s: %s"),
$frame_outfile, $!));
return 0;
}
} else {
$self->document_error(sprintf($self->__("could not open %s for writing: %s"),
$frame_outfile, $!));
return 0;
}
my $toc_frame_fh = $self->Texinfo::Common::open_out($toc_frame_outfile);
if (defined($toc_frame_fh)) {
my $header = &{$self->{'format_begin_file'}}($self, $toc_frame_file, undef);
print $toc_frame_fh $header;
print $toc_frame_fh '<h2>Content</h2>'."\n";
my $shortcontents =
&{$self->{'format_contents'}}($self, 'shortcontents', undef);
$shortcontents =~ s/\bhref=/target="main" href=/g;
print $toc_frame_fh $shortcontents;
print $toc_frame_fh "</body></html>\n";
$self->register_close_file($toc_frame_outfile);
if (!close ($toc_frame_fh)) {
$self->document_error(sprintf($self->__("error on closing TOC frame file %s: %s"),
$toc_frame_outfile, $!));
return 0;
}
} else {
$self->document_error(sprintf($self->__("could not open %s for writing: %s"),
$toc_frame_outfile, $!));
return 0;
}
return 1;
}
sub convert($$)
{
my $self = shift;
my $root = shift;
my $result = '';
# This should return undef if called on a tree without node or sections.
my ($elements, $special_elements)
= $self->_prepare_elements($root);
$self->_prepare_index_entries();
$self->_prepare_footnotes();
if (!defined($elements)) {
$result = $self->_convert($root);
} else {
foreach my $element (@$elements) {
my $element_text = $self->_convert($element);
$result .= $element_text;
}
}
return $result;
}
# This is called from the main program on the converter.
sub output_internal_links($)
{
my $self = shift;
my $out_string = '';
if ($self->{'elements'}) {
foreach my $element (@{$self->{'elements'}}) {
my $text;
my $href;
my $command = $self->element_command($element);
if (defined($command)) {
# Use '' for filename, to force a filename in href.
$href = $self->command_href($command, '');
my $tree = $self->command_text($command, 'tree');
if ($tree) {
$text = Texinfo::Convert::Text::convert($tree,
{Texinfo::Common::_convert_text_options($self)});
}
}
if (defined($href) or defined($text)) {
$out_string .= $href if (defined($href));
$out_string .= "\ttoc\t";
$out_string .= $text if (defined($text));
$out_string .= "\n";
}
}
}
if ($self->{'parser'}) {
foreach my $index_name (sort(keys (%{$self->{'index_entries_by_letter'}}))) {
foreach my $letter_entry (@{$self->{'index_entries_by_letter'}->{$index_name}}) {
foreach my $index_entry (@{$letter_entry->{'entries'}}) {
my $href;
my $key;
$href = $self->command_href($index_entry->{'command'}, '');
$key = $index_entry->{'key'};
if (defined($key) and $key =~ /\S/) {
$out_string .= $href if (defined($href));
$out_string .= "\t$index_name\t";
$out_string .= $key;
$out_string .= "\n";
}
}
}
}
}
if ($out_string ne '') {
return $out_string;
} else {
return undef;
}
}
my @possible_stages = ('setup', 'structure', 'init', 'finish');
my %possible_stages;
foreach my $stage (@possible_stages) {
$possible_stages{$stage} = 1;
}
sub run_stage_handlers($$)
{
my $converter = shift;
my $stage = shift;
die if (!$possible_stages{$stage});
return 1 if (!defined($Texinfo::Config::texinfo_default_stage_handlers{$stage}));
my @sorted_priorities = sort keys(%{$Texinfo::Config::texinfo_default_stage_handlers{$stage}});
foreach my $priority (@sorted_priorities) {
foreach my $handler (@{$Texinfo::Config::texinfo_default_stage_handlers{$stage}->{$priority}}) {
if ($converter->get_conf('DEBUG')) {
print STDERR "HANDLER($stage) , priority $priority: $handler\n";
}
my $status = &{$handler}($converter, $stage);
if (!$status) {
#if ($converter->get_conf('VERBOSE')) {
# print STDERR "Handler $handler of $stage($priority) failed\n";
#}
$converter->document_error(sprintf($converter->__(
"handler %s of stage %s priority %s failed"),
$handler, $stage, $priority));
return $status;
}
}
}
return 1;
}
my $default_priority = 'default';
{
package Texinfo::Config;
use vars qw(%texinfo_default_stage_handlers %texinfo_formatting_references
%texinfo_commands_conversion %texinfo_types_conversion);
sub texinfo_register_handler($$;$)
{
my $stage = shift;
my $handler = shift;
my $priority = shift;
if (!$possible_stages{$stage}) {
carp ("Unknown stage $stage\n");
return 0;
}
$priority = $default_priority if (!defined($priority));
push @{$texinfo_default_stage_handlers{$stage}->{$priority}}, $handler;
return 1;
}
sub texinfo_register_formatting_function($$)
{
my $thing = shift;
my $handler = shift;
if (!$default_formatting_references{$thing}) {
carp ("Unknown formatting type $thing\n");
return 0;
}
$texinfo_formatting_references{$thing} = $handler;
}
sub texinfo_register_command_formatting($$)
{
my $command = shift;
my $reference = shift;
$texinfo_commands_conversion{$command} = $reference;
}
sub texinfo_register_type_formatting($$)
{
my $command = shift;
my $reference = shift;
$texinfo_types_conversion{$command} = $reference;
}
}
sub output($$)
{
my $self = shift;
my $root = shift;
# no splitting when writing to the null device or to stdout or returning
# a string
if (defined($self->get_conf('OUTFILE'))
and ($Texinfo::Common::null_device_file{$self->get_conf('OUTFILE')}
or $self->get_conf('OUTFILE') eq '-'
or $self->get_conf('OUTFILE') eq '')) {
$self->force_conf('SPLIT', 0);
$self->force_conf('MONOLITHIC', 1);
$self->force_conf('FRAMES', 0);
}
if ($self->get_conf('SPLIT')) {
$self->set_conf('NODE_FILES', 1);
}
if ($self->get_conf('NODE_FILES') or $self->get_conf('SPLIT') eq 'node') {
$self->set_conf('NODE_FILENAMES', 1);
}
if ($self->get_conf('FRAMES')) {
$self->set_conf('shortcontents', 1);
}
$self->set_conf('EXTERNAL_CROSSREF_SPLIT', $self->get_conf('SPLIT'));
my $setup_status = $self->run_stage_handlers('setup');
return undef unless($setup_status);
$self->_prepare_css();
# this sets OUTFILE, to be used if not split, but also
# 'destination_directory' and 'output_filename' that are useful when split.
$self->_set_outfile();
return undef unless $self->_create_destination_directory();
# collect renamed nodes
($self->{'renamed_nodes'}, $self->{'renamed_nodes_lines'},
$self->{'renamed_nodes_file'})
= Texinfo::Common::collect_renamed_nodes($self, $self->{'input_basename_name'},
$self->{'renamed_nodes'});
# This should return undef if called on a tree without node or sections.
my ($elements, $special_elements)
= $self->_prepare_elements($root);
Texinfo::Structuring::split_pages($elements, $self->get_conf('SPLIT'));
# determine file names associated with the different pages, and setup
# the counters for special element pages.
if ($self->{'output_file'} ne '') {
$self->_set_pages_files($elements, $special_elements);
}
$self->_prepare_contents_elements();
# do element directions.
Texinfo::Structuring::elements_directions($self, $elements);
# do element directions related to files.
# FIXME do it here or before? Here it means that
# PrevFile and NextFile can be set.
Texinfo::Structuring::elements_file_directions($self, $elements);
# associate the special elements that have no page to the main page.
# This may only happen if not split.
if ($special_elements
and $elements and $elements->[0]
and defined($elements->[0]->{'filename'})) {
foreach my $special_element (@$special_elements) {
if (!defined($special_element->{'filename'})) {
$special_element->{'filename'} = $elements->[0]->{'filename'};
$special_element->{'out_filename'} = $elements->[0]->{'out_filename'};
$self->{'file_counters'}->{$special_element->{'filename'}}++;
print STDERR "Special page $special_element: $special_element->{'filename'}($self->{'file_counters'}->{$special_element->{'filename'}})\n"
if ($self->get_conf('DEBUG'));
}
}
}
$self->_prepare_index_entries();
$self->_prepare_footnotes();
my $structure_status = $self->run_stage_handlers('structure');
return undef unless($structure_status);
&{$self->{'format_css_lines'}}($self);
$self->set_conf('BODYTEXT',
'lang="' . $self->get_conf('documentlanguage') . '"');
# prepare title. fulltitle uses more possibility than simpletitle for
# title, including @-commands found in @titlepage only. Therefore
# simpletitle is more in line with what makeinfo in C does.
my $fulltitle;
foreach my $fulltitle_command('settitle', 'title',
'shorttitlepage', 'top') {
if ($self->{'extra'}->{$fulltitle_command}) {
my $command = $self->{'extra'}->{$fulltitle_command};
next if (!$command->{'extra'}
or (!$command->{'extra'}->{'misc_content'}
or $command->{'extra'}->{'missing_argument'}));
print STDERR "Using $fulltitle_command as title\n"
if ($self->get_conf('DEBUG'));
$fulltitle = {'contents' => $command->{'extra'}->{'misc_content'}};
last;
}
}
if (!$fulltitle and $self->{'extra'}->{'titlefont'}
and $self->{'extra'}->{'titlefont'}->[0]->{'extra'}
and $self->{'extra'}->{'titlefont'}->[0]->{'extra'}->{'brace_command_contents'}
and defined($self->{'extra'}->{'titlefont'}->[0]->{'extra'}->{'brace_command_contents'}->[0])) {
$fulltitle = $self->{'extra'}->{'titlefont'}->[0];
}
# prepare simpletitle
foreach my $simpletitle_command('settitle', 'shorttitlepage') {
if ($self->{'extra'}->{$simpletitle_command}) {
my $command = $self->{'extra'}->{$simpletitle_command};
next if ($command->{'extra'}
and $command->{'extra'}->{'missing_argument'});
$self->{'simpletitle_tree'} =
{'contents' => $command->{'extra'}->{'misc_content'}};
last;
}
}
my $html_title_string;
if ($fulltitle) {
$self->{'title_tree'} = $fulltitle;
print STDERR "DO fulltitle_string\n" if ($self->get_conf('DEBUG'));
$html_title_string = $self->convert_tree_new_formatting_context(
{'type' => '_string', 'contents' => [$self->{'title_tree'}]},
'title_string');
}
if (!defined($html_title_string) or $html_title_string !~ /\S/) {
my $default_title = $self->gdt('Untitled Document');
$self->{'title_tree'} = $default_title;
$self->{'title_string'} = $self->convert_tree_new_formatting_context(
{'type' => '_string', 'contents' => [$self->{'title_tree'}]},
'title_string');
$self->file_line_warn($self->__(
"must specify a title with a title command or \@top"),
$self->{'info'}->{'input_file_name'});
} else {
$self->{'title_string'} = $html_title_string;
}
# copying comment
if ($self->{'extra'}->{'copying'}) {
print STDERR "DO copying_comment\n" if ($self->get_conf('DEBUG'));
my $copying_comment = Texinfo::Convert::Text::convert(
{'contents' => $self->{'extra'}->{'copying'}->{'contents'}},
{Texinfo::Common::_convert_text_options($self)});
if ($copying_comment ne '') {
$self->{'copying_comment'} = &{$self->{'format_comment'}}($self, $copying_comment);
}
}
# documentdescription
if (defined($self->get_conf('documentdescription'))) {
$self->{'documentdescription_string'}
= $self->get_conf('documentdescription');
} elsif ($self->{'extra'}->{'documentdescription'}) {
print STDERR "DO documentdescription\n" if ($self->get_conf('DEBUG'));
$self->{'documentdescription_string'}
= $self->convert_tree_new_formatting_context(
{'type' => '_string',
'contents' => $self->{'extra'}->{'documentdescription'}->{'contents'}},
'documentdescription');
chomp($self->{'documentdescription_string'});
}
my $init_status = $self->run_stage_handlers('init');
return undef unless($init_status);
if ($self->get_conf('FRAMES')) {
my $status = &{$self->{'format_frame_files'}}($self);
return undef if (!$status);
}
# FIXME here call _unset_global_multiple_commands? Problem is
# that some conversion, for instance for page header requires
# that the correct language is set, for instance. The @-command
# will necessarily appear later on -- even if it appears a the
# beginning of the file.
#
# Now do the output
my $fh;
my $output = '';
if (!$elements or !defined($elements->[0]->{'filename'})) {
# no page
my $outfile;
if ($self->{'output_file'} ne '') {
if ($self->get_conf('SPLIT')) {
$outfile = $self->_top_node_filename();
if (defined($self->{'destination_directory'})
and $self->{'destination_directory'} ne '') {
$outfile = File::Spec->catfile($self->{'destination_directory'},
$outfile);
}
} else {
$outfile = $self->{'output_file'};
}
print STDERR "DO No pages, output in $outfile\n"
if ($self->get_conf('DEBUG'));
$fh = $self->Texinfo::Common::open_out($outfile);
if (!$fh) {
$self->document_error(sprintf($self->__("could not open %s for writing: %s"),
$outfile, $!));
return undef;
}
} else {
print STDERR "DO No pages, string output\n"
if ($self->get_conf('DEBUG'));
}
$self->{'current_filename'} = $self->{'output_filename'};
my $header = &{$self->{'format_begin_file'}}($self,
$self->{'output_filename'}, undef);
$output .= $self->_output_text($header, $fh);
if ($elements and @$elements) {
foreach my $element (@$elements) {
my $element_text = $self->_convert($element);
$output .= $self->_output_text($element_text, $fh);
}
} else {
$output .= $self->_output_text($self->_print_title(), $fh);
$output .= $self->_output_text($self->_convert($root), $fh);
}
$output .= $self->_output_text(&{$self->{'format_end_file'}}($self), $fh);
# NOTE do not close STDOUT now to avoid a perl warning.
if ($fh and $outfile ne '-') {
$self->register_close_file($outfile);
if (!close($fh)) {
$self->document_error(sprintf($self->__("error on closing %s: %s"),
$outfile, $!));
}
}
return $output if ($self->{'output_file'} eq '');
} else {
# output with pages
print STDERR "DO Elements with filenames\n"
if ($self->get_conf('DEBUG'));
my %files;
$special_elements = [] if (!defined($special_elements));
foreach my $element (@$elements, @$special_elements) {
my $file_fh;
$self->{'current_filename'} = $element->{'filename'};
$self->{'counter_in_file'}->{$element->{'filename'}}++;
#print STDERR "TTTTTTT($element) $element->{'filename'}: $self->{'file_counters'}->{$element->{'filename'}} (out_filename $element->{'out_filename'})\n";
# First do the special pages, to avoid outputting these if they are
# empty.
my $special_element_content;
if ($element->{'extra'} and $element->{'extra'}->{'special_element'}) {
$special_element_content .= $self->_convert($element);
#print STDERR "Special element converter: $element->{'extra'}->{'special_element'}\n";
if ($special_element_content eq '') {
$self->{'file_counters'}->{$element->{'filename'}}--;
next ;
}
}
# Then open the file and output the elements or the special_page_content
if (!$files{$element->{'filename'}}->{'fh'}) {
$file_fh = $self->Texinfo::Common::open_out($element->{'out_filename'});
if (!$file_fh) {
$self->document_error(sprintf($self->__("could not open %s for writing: %s"),
$element->{'out_filename'}, $!));
return undef;
}
print $file_fh "".&{$self->{'format_begin_file'}}($self,
$element->{'filename'},
$element);
$files{$element->{'filename'}}->{'fh'} = $file_fh;
} else {
$file_fh = $files{$element->{'filename'}}->{'fh'};
}
if (defined($special_element_content)) {
print $file_fh $special_element_content;
} else {
my $element_text = $self->_convert($element);
print $file_fh $element_text;
}
$self->{'file_counters'}->{$element->{'filename'}}--;
if ($self->{'file_counters'}->{$element->{'filename'}} == 0) {
# end file
print $file_fh "". &{$self->{'format_end_file'}}($self);
# NOTE do not close STDOUT here to avoid a perl warning
if ($element->{'out_filename'} ne '-') {
$self->register_close_file($element->{'out_filename'});
if (!close($file_fh)) {
$self->document_error(sprintf($self->__("error on closing %s: %s"),
$element->{'out_filename'}, $!));
return undef;
}
}
}
}
}
my $finish_status = $self->run_stage_handlers('finish');
return undef unless($finish_status);
# do node redirection pages
$self->{'current_filename'} = undef;
if ($self->get_conf('NODE_FILES')
and $self->{'labels'} and $self->{'output_file'} ne '') {
foreach my $label (sort(keys (%{$self->{'labels'}}))) {
my $node = $self->{'labels'}->{$label};
my $target = $self->_get_target($node);
# filename may not be defined in case of an @anchor or similar in
# @titlepage, and @titlepage is not used.
my $filename = $self->command_filename($node);
my $node_filename;
# NOTE 'node_filename' is not used for Top, so the other manual
# must use the same convention to get it right. We avoid doing
# also 'node_filename' to avoid unneeded redirection files.
if ($node->{'extra'}->{'normalized'} eq 'Top'
and defined($self->get_conf('TOP_NODE_FILE_TARGET'))) {
my $extension = '';
$extension = "." . $self->get_conf('NODE_FILE_EXTENSION')
if (defined($self->get_conf('NODE_FILE_EXTENSION'))
and $self->get_conf('NODE_FILE_EXTENSION') ne '');
$node_filename = $self->get_conf('TOP_NODE_FILE_TARGET')
.$extension;
} else {
$node_filename = $target->{'node_filename'};
}
if (defined($filename) and $node_filename ne $filename) {
my $redirection_page
= &{$self->{'format_node_redirection_page'}}($self, $node);
my $out_filename;
if (defined($self->{'destination_directory'})
and $self->{'destination_directory'} ne '') {
$out_filename = File::Spec->catfile($self->{'destination_directory'},
$node_filename);
} else {
$out_filename = $node_filename;
}
my $file_fh = $self->Texinfo::Common::open_out($out_filename);
if (!$file_fh) {
$self->document_error(sprintf($self->__(
"could not open %s for writing: %s"),
$out_filename, $!));
} else {
print $file_fh $redirection_page;
$self->register_close_file($out_filename);
if (!close ($file_fh)) {
$self->document_error(sprintf($self->__(
"error on closing redirection node file %s: %s"),
$out_filename, $!));
return undef;
}
}
}
}
}
if ($self->{'renamed_nodes'}
and $self->{'labels'} and $self->{'output_file'} ne '') {
# do a fresh parser, to avoid, for example adding new labels if renamed
# nodes incorrectly define anchors...
my $parser_for_renamed_nodes;
if ($self->{'parser'}) {
$parser_for_renamed_nodes = $self->{'parser'}->parser();
}
my %warned_new_node;
foreach my $old_node_name (sort(keys(%{$self->{'renamed_nodes'}}))) {
my $parsed_old_node = $self->_parse_node_and_warn_external(
$old_node_name, $parser_for_renamed_nodes,
$self->{'renamed_nodes_lines'}->{$old_node_name},
$self->{'renamed_nodes_file'});
if ($parsed_old_node) {
if ($self->label_command($parsed_old_node->{'normalized'})) {
$self->file_line_error(sprintf($self->__(
"old name for `%s' is a node of the document"), $old_node_name),
$self->{'renamed_nodes_file'},
$self->{'renamed_nodes_lines'}->{$old_node_name});
$parsed_old_node = undef;
} elsif ($parsed_old_node->{'normalized'} !~ /[^-]/) {
$self->file_line_error(sprintf($self->__(
"file empty for renamed node `%s'"), $old_node_name),
$self->{'renamed_nodes_file'},
$self->{'renamed_nodes_lines'}->{$old_node_name});
$parsed_old_node = undef;
}
}
my $new_node_name = $self->{'renamed_nodes'}->{$old_node_name};
my $parsed_new_node = $self->_parse_node_and_warn_external(
$new_node_name, $parser_for_renamed_nodes,
$self->{'renamed_nodes_lines'}->{$new_node_name},
$self->{'renamed_nodes_file'});
if (!$self->label_command($parsed_new_node->{'normalized'})) {
if (!$warned_new_node{$new_node_name}) {
$self->file_line_warn(sprintf($self->__(
"target node (new name for `%s') not in document: %s"),
$old_node_name, $new_node_name), $self->{'renamed_nodes_file'},
$self->{'renamed_nodes_lines'}->{$new_node_name});
$warned_new_node{$new_node_name} = 1;
}
$parsed_new_node = undef;
}
if ($parsed_new_node and $parsed_old_node) {
my ($filename, $target, $id) = $self->_node_id_file($parsed_old_node);
$filename .= '.'.$self->get_conf('NODE_FILE_EXTENSION')
if (defined($self->get_conf('NODE_FILE_EXTENSION'))
and $self->get_conf('NODE_FILE_EXTENSION') ne '');
my $redirection_page
= &{$self->{'format_node_redirection_page'}}($self,
$self->label_command($parsed_new_node->{'normalized'}));
my $out_filename;
if (defined($self->{'destination_directory'})
and $self->{'destination_directory'} ne '') {
$out_filename = File::Spec->catfile($self->{'destination_directory'},
$filename);
} else {
$out_filename = $filename;
}
my $file_fh = $self->Texinfo::Common::open_out($out_filename);
if (!$file_fh) {
$self->document_error(sprintf($self->__("could not open %s for writing: %s"),
$out_filename, $!));
} else {
print $file_fh $redirection_page;
$self->register_close_file($out_filename);
if (!close ($file_fh)) {
$self->document_error(sprintf($self->__(
"error on closing renamed node redirection file %s: %s"),
$out_filename, $!));
return undef;
}
}
}
}
}
}
sub _parse_node_and_warn_external($$$$$)
{
my $self = shift;
my $node_texi = shift;
my $parser = shift;
my $line_number = shift;
my $file = shift;
# NOTE nothing to check that there is an invalid nesting. Indeed, there
# is no information given to the parser stating that we are in a label
# command.
# A possibility would be to consider
# 'root_line' type as a $simple_text_command, or, to avoid spurious
# messages, $full_text_command. This would imply really using
# the gdt 4th argument to pass 'translated_paragraph' when in a
# less constrained environment, for instance @center in @quotation for
# @author
#
# it is unlikely, however that invalid nesting does much harm, since
# the tree is mostly used to be normalized and this converter should
# be rather foolproof.
my $node_tree = Texinfo::Parser::parse_texi_line($parser,
$node_texi, $line_number, $file);
if ($node_tree) {
my $node_normalized_result = Texinfo::Parser::_parse_node_manual(
$node_tree);
my $line_nr = {'line_nr' => $line_number, 'file_name' => $file };
if (!$node_normalized_result) {
$self->line_warn($self->__('empty node name'), $line_nr);
} elsif ($node_normalized_result->{'manual_content'}) {
$self->line_error(sprintf($self->__("syntax for an external node used for `%s'"),
$node_texi), $line_nr);
} else {
return $node_normalized_result;
}
}
return undef;
}
sub _convert_contents($$$)
{
my $self = shift;
my $root = shift;
my $command_type = shift;
my $content_formatted = '';
if (ref($root->{'contents'}) ne 'ARRAY') {
cluck "for $root contents not an array: $root->{'contents'}";
print STDERR Texinfo::Parser::_print_current($root);
}
my $content_idx = 0;
foreach my $content (@{$root->{'contents'}}) {
my $new_content = $self->_convert($content, "$command_type [$content_idx]");
if (!defined($new_content)) {
cluck "content not defined for $command_type [$content_idx]\n";
print STDERR "root is: ".Texinfo::Parser::_print_current ($root);
print STDERR "content is: ".Texinfo::Parser::_print_current ($content);
} else {
$content_formatted .= $new_content;
}
$content_idx++;
}
return $content_formatted;
}
sub _attribute_class($$$)
{
my $self = shift;
my $element = shift;
my $class = shift;
if (!defined($class) or $class eq '' or $self->get_conf('NO_CSS')) {
if ($element eq 'span') {
return '';
} else {
return "<$element";
}
}
my $style = '';
if ($self->get_conf('INLINE_CSS_STYLE')
and defined($self->{'css_map'}->{"$element.$class"})) {
$style = ' style="'.$self->{'css_map'}->{"$element.$class"}.'"';
}
return "<$element class=\"$class\"$style";
}
sub _protect_space($$)
{
my $self = shift;
my $text = shift;
return $text if ($self->in_preformatted());
if ($self->in_space_protected()) {
my $open = $self->_attribute_class('span', 'nolinebreak');
if ($open ne '') {
$open .= '>';
# protect spaces in the html leading attribute in case we are in 'w'
$open =~ s/ /\x{1F}/g;
# special span to avoid breaking at _-
$text =~ s/(\S*[_-]\S*)/${open}$1<\/span>/g;
}
$text .= '&nbsp;' if (chomp($text));
# protect spaces within text
$text =~ s/ /&nbsp;/g;
# revert protected spaces in leading html attribute
$text =~ s/\x{1F}/ /g;
}
return $text;
}
sub _convert($$;$);
sub _convert($$;$)
{
my $self = shift;
my $root = shift;
# only used for debug
my $explanation = shift;
# to help debug and trace
my $command_type = '';
if ($root->{'cmdname'}) {
$command_type = "\@$root->{'cmdname'} ";
}
if (defined($root->{'type'})) {
$command_type .= $root->{'type'};
}
if ($self->get_conf('DEBUG')) {
$explanation = 'NO EXPLANATION' if (!defined($explanation));
print STDERR "ROOT($explanation):$root (".join('|',@{$self->{'document_context'}->[-1]->{'formatting_context'}})."), ->";
print STDERR " cmd: $root->{'cmdname'}," if ($root->{'cmdname'});
print STDERR " type: $root->{'type'}" if ($root->{'type'});
my $text = $root->{'text'};
if (defined($text)) {
$text =~ s/\n/\\n/;
print STDERR " text: $text";
}
print STDERR "\n";
#print STDERR " Special def_command: $root->{'extra'}->{'def_command'}\n"
# if (defined($root->{'extra'}) and $root->{'extra'}->{'def_command'});
}
if (ref($root) ne 'HASH') {
cluck "_convert: root not a HASH\n";
return '';
}
if (($root->{'type'}
and exists ($self->{'types_conversion'}->{$root->{'type'}})
and !defined($self->{'types_conversion'}->{$root->{'type'}}))
or ($root->{'cmdname'}
and exists($self->{'commands_conversion'}->{$root->{'cmdname'}})
and !defined($self->{'commands_conversion'}->{$root->{'cmdname'}}))) {
if ($self->get_conf('DEBUG')) {
my $string = 'IGNORED';
$string .= " \@$root->{'cmdname'}" if ($root->{'cmdname'});
$string .= " $root->{'type'}" if ($root->{'type'});
print STDERR "$string\n";
}
return '';
}
# process text
if (defined($root->{'text'})) {
# already converted to html, keep it as is
if ($root->{'type'} and $root->{'type'} eq '_converted') {
return $root->{'text'};
}
my $result = &{$self->{'types_conversion'}->{'text'}} ($self,
$root->{'type'},
$root,
$root->{'text'});
print STDERR "DO TEXT => `$result'\n" if ($self->get_conf('DEBUG'));
return $result;
}
if ($root->{'extra'}) {
#if ($root->{'extra'}->{'invalid_nesting'}) {
# print STDERR "INVALID_NESTING\n" if ($self->get_conf('DEBUG'));
# return '';
#} elsif ($root->{'extra'}->{'missing_argument'}
if ($root->{'extra'}->{'missing_argument'}
and (!$root->{'contents'} or !@{$root->{'contents'}})) {
print STDERR "MISSING_ARGUMENT\n" if ($self->get_conf('DEBUG'));
return '';
}
}
# commands like @deffnx have both a cmdname and a def_line type. It is
# better to consider them as a def_line type, as the whole point of the
# def_line type is to handle the same the def*x and def* line formatting.
if ($root->{'cmdname'}
and !($root->{'type'} and $root->{'type'} eq 'def_line'
or $root->{'type'} and $root->{'type'} eq 'definfoenclose_command')) {
my $command_name = $root->{'cmdname'};
# use the same command name for all the index entry commands
if ($root->{'extra'} and $root->{'extra'}->{'index_entry'}
and $root->{'cmdname'} and $root->{'cmdname'} =~ /index$/) {
$command_name = 'cindex';
}
if ($root_commands{$command_name}) {
$self->{'current_root_command'} = $root;
}
if (exists($self->{'commands_conversion'}->{$command_name})) {
if (exists($context_brace_commands{$command_name})) {
$self->_new_document_context($command_name);
}
push @{$self->{'document_context'}->[-1]->{'commands'}},
$root->{'cmdname'};
if (exists($format_context_commands{$command_name})) {
push @{$self->{'document_context'}->[-1]->{'formatting_context'}},
{'cmdname' => $command_name};
}
if (exists($block_commands{$command_name})) {
push @{$self->{'document_context'}->[-1]->{'formats'}}, $command_name;
}
if (exists ($composition_context_commands{$command_name})) {
push @{$self->{'document_context'}->[-1]->{'composition_context'}}, $command_name;
}
if ($pre_class_commands{$command_name}) {
push @{$self->{'document_context'}->[-1]->{'preformatted_classes'}},
$pre_class_commands{$command_name};
}
if ($format_raw_commands{$command_name}) {
$self->{'document_context'}->[-1]->{'raw'}++;
} elsif ($command_name eq 'verb' or $command_name eq 'verbatim') {
$self->{'document_context'}->[-1]->{'verbatim'}++;
}
if ($code_style_commands{$command_name} or
$preformatted_code_commands{$command_name}) {
push @{$self->{'document_context'}->[-1]->{'monospace'}}, 1;
} elsif ($regular_font_style_commands{$command_name}) {
push @{$self->{'document_context'}->[-1]->{'monospace'}}, 0;
} elsif ($upper_case_commands{$command_name}) {
$self->{'document_context'}->[-1]->{'formatting_context'}->[-1]->{'upper_case'}++;
} elsif ($command_name eq 'math') {
$self->{'document_context'}->[-1]->{'math'}++;
} elsif ($command_name eq 'w') {
$self->{'document_context'}->[-1]->{'formatting_context'}->[-1]->{'space_protected'}++;
}
my $content_formatted;
if ($root->{'contents'}) {
$content_formatted = $self->_convert_contents($root, $command_type);
}
my $args_formatted;
if ($brace_commands{$command_name}
or ($misc_commands{$command_name}
and $misc_commands{$command_name} eq 'line')
or (($command_name eq 'item' or $command_name eq 'itemx')
and ($root->{'parent'}->{'type'}
and $root->{'parent'}->{'type'} eq 'table_term'))
or ($command_name eq 'quotation'
or $command_name eq 'smallquotation')
or ($command_name eq 'float')) {
$args_formatted = [];
if ($root->{'args'}) {
my @args_specification;
@args_specification = @{$self->{'commands_args'}->{$command_name}}
if (defined($self->{'commands_args'}->{$command_name}));
my $arg_idx = 0;
foreach my $arg (@{$root->{'args'}}) {
my $arg_spec = shift @args_specification;
$arg_spec = ['normal'] if (!defined($arg_spec));
my $arg_formatted = {'tree' => $arg};
foreach my $arg_type (@$arg_spec) {
my $explanation = "$command_type \[$arg_idx\]$arg_type";
if ($arg_type eq 'normal') {
$arg_formatted->{'normal'} = $self->_convert($arg, $explanation);
} elsif ($arg_type eq 'monospace') {
push @{$self->{'document_context'}->[-1]->{'monospace'}}, 1;
#$self->{'document_context'}->[-1]->{'code'}++;
$arg_formatted->{$arg_type} = $self->_convert($arg, $explanation);
#$self->{'document_context'}->[-1]->{'code'}--;
pop @{$self->{'document_context'}->[-1]->{'monospace'}};
} elsif ($arg_type eq 'string') {
$self->_new_document_context($command_type);
$self->{'document_context'}->[-1]->{'string'}++;
$arg_formatted->{$arg_type} = $self->_convert($arg, $explanation);
pop @{$self->{'document_context'}};
} elsif ($arg_type eq 'monospacestring') {
$self->_new_document_context($command_type);
$self->{'document_context'}->[-1]->{'monospace'}->[-1] = 1;
$self->{'document_context'}->[-1]->{'string'}++;
$arg_formatted->{$arg_type} = $self->_convert($arg, $explanation);
pop @{$self->{'document_context'}};
} elsif ($arg_type eq 'monospacetext') {
$arg_formatted->{$arg_type}
= Texinfo::Convert::Text::convert($arg, {'code' => 1,
Texinfo::Common::_convert_text_options($self)});
} elsif ($arg_type eq 'raw') {
$self->{'document_context'}->[-1]->{'raw'}++;
$arg_formatted->{$arg_type} = $self->_convert($arg, $explanation);
$self->{'document_context'}->[-1]->{'raw'}--;
}
}
push @$args_formatted, $arg_formatted;
$arg_idx++;
}
}
}
if (exists ($composition_context_commands{$command_name})) {
pop @{$self->{'document_context'}->[-1]->{'composition_context'}};
}
if ($pre_class_commands{$command_name}) {
pop @{$self->{'document_context'}->[-1]->{'preformatted_classes'}};
}
if ($code_style_commands{$command_name}
or $preformatted_code_commands{$command_name}
or $regular_font_style_commands{$command_name}) {
#$self->{'document_context'}->[-1]->{'code'}--;
pop @{$self->{'document_context'}->[-1]->{'monospace'}};
} elsif ($upper_case_commands{$command_name}) {
$self->{'document_context'}->[-1]->{'formatting_context'}->[-1]->{'upper_case'}--;
} elsif ($command_name eq 'math') {
$self->{'document_context'}->[-1]->{'math'}--;
} elsif ($command_name eq 'w') {
$self->{'document_context'}->[-1]->{'formatting_context'}->[-1]->{'space_protected'}--;
}
if ($format_raw_commands{$command_name}) {
$self->{'document_context'}->[-1]->{'raw'}--;
} elsif ($command_name eq 'verb' or $command_name eq 'verbatim') {
$self->{'document_context'}->[-1]->{'verbatim'}--;
}
if (exists($block_commands{$command_name})) {
pop @{$self->{'document_context'}->[-1]->{'formats'}};
}
if (exists($format_context_commands{$command_name})) {
pop @{$self->{'document_context'}->[-1]->{'formatting_context'}};
}
pop @{$self->{'document_context'}->[-1]->{'commands'}};
if (exists($context_brace_commands{$command_name})) {
pop @{$self->{'document_context'}};
}
# args are formatted, now format the command itself
my $result;
if ($args_formatted) {
if (!defined($self->{'commands_conversion'}->{$command_name})) {
print STDERR "No command_conversion for $command_name\n";
$result = '';
} else {
$result = &{$self->{'commands_conversion'}->{$command_name}}($self,
$command_name, $root, $args_formatted, $content_formatted);
}
} else {
$result = &{$self->{'commands_conversion'}->{$command_name}}($self,
$command_name, $root, $content_formatted);
}
return $result;
} else {
print STDERR "Unknown command `$command_name'\n"
if ($self->get_conf('VERBOSE') or $self->get_conf('DEBUG'));
return '';
}
if ($root_commands{$command_name}) {
delete $self->{'current_root_command'};
}
} elsif ($root->{'type'}) {
push @{$self->{'document_context'}->[-1]->{'commands'}},
$root->{'cmdname'}
if ($root->{'cmdname'});
if ($root->{'type'} eq 'paragraph') {
$self->{'document_context'}->[-1]->{'formatting_context'}->[-1]->{'paragraph_number'}++;
} elsif ($root->{'type'} eq 'preformatted'
or $root->{'type'} eq 'rawpreformatted') {
$self->{'document_context'}->[-1]->{'formatting_context'}->[-1]->{'preformatted_number'}++;
} elsif ($root->{'type'} eq 'element') {
$self->{'current_element'} = $root;
$self->{'current_filename'} = $root->{'filename'};
} elsif ($pre_class_types{$root->{'type'}}) {
push @{$self->{'document_context'}->[-1]->{'preformatted_classes'}},
$pre_class_types{$root->{'type'}};
push @{$self->{'document_context'}->[-1]->{'composition_context'}},
$root->{'type'};
}
if ($self->{'code_types'}->{$root->{'type'}}) {
#$self->{'document_context'}->[-1]->{'code'}++;
push @{$self->{'document_context'}->[-1]->{'monospace'}}, 1;
}
if ($root->{'type'} eq '_string') {
$self->{'document_context'}->[-1]->{'string'}++;
}
my $content_formatted;
if ($root->{'type'} eq 'definfoenclose_command') {
if ($root->{'args'}) {
$content_formatted = $self->_convert($root->{'args'}->[0]);
}
} elsif ($root->{'contents'}) {
$content_formatted = $self->_convert_contents($root, $command_type);
}
my $result = '';
if (exists($self->{'types_conversion'}->{$root->{'type'}})) {
$result = &{$self->{'types_conversion'}->{$root->{'type'}}} ($self,
$root->{'type'},
$root,
$content_formatted);
#print STDERR "Converting type $root->{'type'} -> $result\n";
} elsif (defined($content_formatted)) {
$result = $content_formatted;
}
if ($self->{'code_types'}->{$root->{'type'}}) {
#$self->{'document_context'}->[-1]->{'code'}--;
pop @{$self->{'document_context'}->[-1]->{'monospace'}};
}
if ($root->{'type'} eq '_string') {
$self->{'document_context'}->[-1]->{'string'}--;
}
if ($root->{'type'} eq 'element') {
delete $self->{'current_element'};
delete $self->{'current_filename'};
} elsif ($pre_class_types{$root->{'type'}}) {
pop @{$self->{'document_context'}->[-1]->{'preformatted_classes'}};
pop @{$self->{'document_context'}->[-1]->{'composition_context'}};
}
print STDERR "DO type ($root->{'type'}) => `$result'\n"
if ($self->get_conf('DEBUG'));
pop @{$self->{'document_context'}->[-1]->{'commands'}}
if ($root->{'cmdname'});
return $result;
# no type, no cmdname, but contents.
} elsif ($root->{'contents'}) {
# this happens inside accents, for section/node names, for @images.
my $content_formatted = '';
my $i = 0;
foreach my $content (@{$root->{'contents'}}) {
$content_formatted .= $self->_convert($content, "$command_type [$i]");
$i++;
}
print STDERR "UNNAMED HOLDER => `$content_formatted'\n"
if ($self->get_conf('DEBUG'));
return $content_formatted;
} else {
print STDERR "UNNAMED empty\n" if ($self->get_conf('DEBUG'));
if ($self->{'types_conversion'}->{''}) {
return &{$self->{'types_conversion'}->{''}} ($self, $root);
} else {
return '';
}
}
print STDERR "DEBUG: HERE!($root)\n";
}
sub _set_variables_texi2html()
{
my @texi2html_options = (
['NO_USE_SETFILENAME', 1],
['USE_SETFILENAME_EXTENSION', 0],
['footnotestyle', 'separate'],
['INLINE_CONTENTS', 0],
['FORCE', 1],
['AVOID_MENU_REDUNDANCY', 1],
['TOP_FILE', ''],
['USE_ACCESSKEY', 0],
['NODE_NAME_IN_MENU', 0],
['OVERVIEW_LINK_TO_TOC', 0],
['USE_UP_NODE_FOR_ELEMENT_UP', 1],
['USE_REL_REV', 0],
['USE_LINKS', 0],
['USE_NODES', 0],
['NODE_FILENAMES', 0],
['USE_NUMERIC_ENTITY', 1],
['SPLIT', ''],
['SPLIT_INDEX', 100],
['PROGRAM_NAME_IN_FOOTER', 1],
['HEADER_IN_TABLE', 1],
['USE_TITLEPAGE_FOR_TITLE', 1],
['MENU_ENTRY_COLON', ''],
['INDEX_ENTRY_COLON', ''],
['ENABLE_ENCODING_USE_ENTITY', 1],
['DO_ABOUT', undef],
['NODE_NAME_IN_INDEX', 0],
['CHAPTER_HEADER_LEVEL', 1],
['BIG_RULE', '<hr size="6">'],
['FOOTNOTE_END_HEADER_LEVEL', 3],
['FOOTNOTE_SEPARATE_HEADER_LEVEL', 1],
['KEEP_TOP_EXTERNAL_REF', 1],
['SECTION_BUTTONS', ['FastBack', 'Back', 'Up', 'Forward', 'FastForward',
' ', ' ', ' ', ' ',
'Top', 'Contents', 'Index', 'About' ]],
['TOP_BUTTONS', ['Back', 'Forward', ' ',
'Contents', 'Index', 'About']],
['MISC_BUTTONS', [ 'Top', 'Contents', 'Index', 'About' ]],
['CHAPTER_BUTTONS', [ 'FastBack', 'FastForward', ' ',
' ', ' ', ' ', ' ',
'Top', 'Contents', 'Index', 'About', ]],
['SECTION_FOOTER_BUTTONS', [ 'FastBack', 'Back', 'Up',
'Forward', 'FastForward' ]],
['NODE_FOOTER_BUTTONS', [ 'FastBack', 'Back',
'Up', 'Forward', 'FastForward',
' ', ' ', ' ', ' ',
'Top', 'Contents', 'Index', 'About' ]],
);
foreach my $option (@texi2html_options) {
#no warnings 'once';
$defaults{$option->[0]} = $option->[1];
}
}
1;
__END__
# $Id: HTML.pm 6991 2016-02-06 12:16:13Z gavin $
# Automatically generated from maintain/template.pod
=head1 NAME
Texinfo::Convert::HTML - Convert Texinfo tree to HTML
=head1 SYNOPSIS
my $converter
= Texinfo::Convert::HTML->converter({'parser' => $parser});
$converter->output($tree);
$converter->convert($tree);
$converter->convert_tree($tree);
$converter->output_internal_links(); # HTML only
=head1 DESCRIPTION
Texinfo::Convert::HTML converts a Texinfo tree to HTML.
=head1 METHODS
=over
=item $converter = Texinfo::Convert::HTML->converter($options)
Initialize converter from Texinfo to HTML.
The I<$options> hash reference holds options for the converter. In
this option hash reference a parser object may be associated with the
I<parser> key. The other options should be configuration options
described in the Texinfo manual. Those options, when appropriate,
override the document content.
See L<Texinfo::Convert::Converter> for more informations.
=item $converter->output($tree)
Convert a Texinfo tree I<$tree> and output the result in files as
described in the Texinfo manual.
=item $result = $converter->convert($tree)
Convert a Texinfo tree I<$tree> or tree portion and return
the resulting output.
=item $result = $converter->convert_tree($tree)
Convert a Texinfo tree portion I<$tree> and return the resulting
output. This function does not try to output a full document but only
portions. For a full document use C<convert>.
=item $result = $converter->output_internal_links()
Returns text representing the links in the document. The format should
follow the C<--internal-links> option of the texi2any/makeinfo
specification. This is only supported in (and relevant for) HTML.
=back
=head1 AUTHOR
Patrice Dumas, E<lt>pertusus@free.frE<gt>
=head1 COPYRIGHT AND LICENSE
Copyright 2015 Free Software Foundation, Inc.
This library 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.
=cut