'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',
};
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";
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 . '';
}
}
$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 "$text";
}
}
$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_result;
my $explanation_string;
my $normalized_type
= Texinfo::Convert::NodeNameNormalization::normalize_node(
{'contents' => $command->{'args'}->[0]->{'contents'}});
if ($args->[1] and defined($args->[1]->{'string'})
and $args->[1]->{'string'} =~ /\S/) {
$with_explanation = 1;
$explanation_string = $args->[1]->{'string'};
# Convert the expanation of the acronym. Must do this before we save
# the explanation for the future, otherwise we get infinite recursion
# for recursively-defined acronyms.
$explanation_result = $self->convert_tree( $args->[1]->{'tree'} );
$self->{'explained_commands'}->{$cmdname}->{$normalized_type} =
$command->{'args'}->[1]->{'contents'};
} elsif ($command->{'extra'}->{'explanation_contents'}) {
if (@{$command->{'extra'}->{'explanation_contents'}}) {
$explanation_string = $self->convert_tree_new_formatting_context(
{'type' => '_string',
'contents' => $command->{'extra'}->{'explanation_contents'}},
$cmdname, $cmdname);
}
} elsif ($self->{'explained_commands'}->{$cmdname}->{$normalized_type}) {
$explanation_string = $self->convert_tree_new_formatting_context(
{'type' => '_string',
'contents' => $self->{'explained_commands'}
->{$cmdname}->{$normalized_type}},
$cmdname, $cmdname);
$command->{'extra'}->{'explanation_contents'}
= $self->{'explained_commands'}->{$cmdname}->{$normalized_type};
} else {
# Avoid ever giving an explanation for this element. This prevents
# infinite recursion for a recursively-defined acronym, when an
# @acronym within the explanation could end up referring to the
# containing @acronym.
$command->{'extra'}->{'explanation_contents'} = [];
}
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' => {'type' => '_converted',
'text' => $explanation_result}}));
}
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 "";
}
return '';
}
$default_commands_conversion{'anchor'} = \&_convert_anchor_command;
my $foot_num;
my $foot_lines;
my $NO_NUMBER_FOOTNOTE_SYMBOL = '*';
my $footid_base = 'FOOT';
my $docid_base = 'DOCF';
# 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 $footid = $self->command_target($command);
# happens for bogus footnotes
if (!defined($footid)) {
return '';
}
# ID for linking back to the main text from the footnote.
my $docid = $footid;
$docid =~ s/^$footid_base/$docid_base/;
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 .= '
\n"
. $footnote_text;
my $footnote_number_text;
if ($self->in_preformatted()) {
$footnote_number_text = "($number_in_doc)";
} else {
$footnote_number_text = "$number_in_doc";
}
return "$footnote_number_text";
}
$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 "$text";
}
$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(
__("\@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;
}
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 "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;
my $arg = $args->[0]->{'normal'};
my $math_type = $self->get_conf('HTML_MATH');
if ($math_type and $math_type eq 'mathjax') {
# MathJax won't handle tags in code
# TODO: instead convert inside $command to LaTeX, when such a conversion
# becomes possible
if ($arg !~ /) {
$self->{'element_math'} = 1;
return "\\($arg\\)";
}
}
return "$arg";
}
$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
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 .'';;
} else {
my $open = $self->_attribute_class('span', $class);
if ($open ne '') {
return $open.'>'.$text.'';
} 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
return '';
}
if (!$self->in_string()) {
return $self->get_conf('OPEN_QUOTE_SYMBOL').'' .$text
.''.$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_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
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 = "$arg;";
} else {
$res = '';
}
return $res;
}
$default_commands_conversion{'U'} = \&_convert_U_command;
sub _default_format_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_format_protect_text($$) {
my $self = shift;
my $text = shift;
my $result = $self->xml_protect_text($text);
$result =~ s/\f//g;
return $result;
}
sub _default_format_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";
# 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. Return text to use for a link in button bar.
# Depending on USE_NODE_DIRECTIONS and xrefautomaticsectiontitle
# use section or node for link direction and string.
sub _default_panel_button_dynamic_direction($$;$$)
{
my $self = shift;
my $direction = shift;
my $omit_rel = shift;
my $use_first_element_in_file_directions = shift;
my $result = undef;
if ((defined($self->get_conf('USE_NODE_DIRECTIONS'))
and $self->get_conf('USE_NODE_DIRECTIONS'))
or (not defined($self->get_conf('USE_NODE_DIRECTIONS'))
and $self->get_conf('USE_NODES'))) {
$direction = 'Node'.$direction;
}
if ($use_first_element_in_file_directions) {
$direction = 'FirstInFile'.$direction;
}
my $href = $self->_element_direction($self->{'current_element'},
$direction, 'href');
my $node;
if ($self->get_conf('xrefautomaticsectiontitle') eq 'on') {
$node = $self->_element_direction($self->{'current_element'},
$direction, 'section');
}
if (!defined($node)) {
$node = $self->_element_direction($self->{'current_element'},
$direction, 'node');
}
my $anchor;
if (defined($href) and defined($node) and $node =~ /\S/) {
my $anchor_attributes = $omit_rel ? ''
: $self->_direction_href_attributes($direction);
$anchor = "$node";
}
if (defined($anchor)) {
# i18n
$result = $self->get_conf('BUTTONS_TEXT')->{$direction}.": $anchor";
}
# 1 to communicate that a delimiter is needed for that button
return ($result, 1);
}
# Used for button bar at the foot of a node, with "rel" and "accesskey"
# attributes omitted.
sub _default_panel_button_dynamic_direction_node_footer($$)
{
my $self = shift;
my $direction = shift;
return _default_panel_button_dynamic_direction($self, $direction, 1);
}
# used for button bar at the foot of a section or chapter with
# directions of first element in file used instead of the last
# element directions.
sub _default_panel_button_dynamic_direction_section_footer($$) {
my $self = shift;
my $direction = shift;
return _default_panel_button_dynamic_direction($self, $direction, undef, 1);
}
# how to create IMG tag
# this is only used in html, and only if ICONS is set and the button
# is active.
sub _default_format_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{};
}
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;
}
my %html_default_node_directions;
foreach my $node_directions ('NodeNext', 'NodePrev', 'NodeUp') {
$html_default_node_directions{$node_directions} = 1;
}
sub _default_format_button($$)
{
my $self = shift;
my $button = shift;
my ($active, $passive, $need_delimiter);
if (ref($button) eq 'CODE') {
($active, $need_delimiter) = &$button($self);
} elsif (ref($button) eq 'SCALAR') {
$active = "$$button" if defined($$button);
$need_delimiter = 1;
} elsif (ref($button) eq 'ARRAY' and scalar(@$button == 2)) {
my $text = $button->[1];
my $button_href = $button->[0];
# $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 = "$$text";
} else {
$passive = $$text;
}
$need_delimiter = 1;
# $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, $need_delimiter) = &$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 = "$text_formatted";
} else {
$passive = $text_formatted;
}
}
$need_delimiter = 1;
}
} 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};
}
$need_delimiter = 0;
} 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 = "".
&{$self->{'format_button_icon_img'}}($self, $button_name, $active_icon,
$self->_element_direction($self->{'current_element'},
$button, 'string')) ."";
$use_icon = 1;
}
}
if (!$use_icon) {
# use text
$active = '[' . "".
$self->get_conf('BUTTONS_TEXT')->{$button}."" . ']';
}
} 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} . ']';
}
}
$need_delimiter = 0;
}
# FIXME chose another option among those proposed in comments below?
if (not defined($need_delimiter)) {
# option 1: be forgiving if $need_delimiter is not set
# if ($html_default_node_directions{$button}) {
# $need_delimiter = 1;
# } else {
# $need_delimiter = 0;
# }
# option 2: be somewhat forgiving but show a backtrace
#cluck ("need_delimiter not defined");
# $need_delimiter = 0;
# option3: no pity
confess ("need_delimiter not defined");
}
return ($active, $passive, $need_delimiter);
}
sub _default_format_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 .= "
\n";
}
foreach my $button (@$buttons) {
if ($self->get_conf('HEADER_IN_TABLE')) {
$result .= qq{
\n} if $vertical;
$result .= qq{
};
}
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, $need_delimiter) = &{$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 .= "
\n";
$result .= "
\n" if $vertical;
} elsif (defined($active)) {
# only active buttons are print out when not in table
if ($need_delimiter and !$first_button) {
$active = ', ' .$active;
}
$result .= $active;
$first_button = 0 if ($first_button);
}
}
if ($self->get_conf('HEADER_IN_TABLE')) {
$result .= "" unless $vertical;
$result .= "\n";
} else {
$result .= "
\n\n";
}
return $result;
}
sub _default_format_navigation_header($$$$)
{
my $self = shift;
my $buttons = shift;
my $cmdname = shift;
my $command = shift;
my $result = '';
if ($self->get_conf('VERTICAL_HEAD_NAVIGATION')) {
$result .= '
';
} elsif ($self->get_conf('SPLIT') eq 'node') {
$result .= $self->get_conf('DEFAULT_RULE')."\n";
}
return $result;
}
sub _default_format_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 register_opened_section_level($$$)
{
my $self = shift;
my $level = shift;
my $close = shift;
while (@{$self->{'pending_closes'}} < $level) {
push(@{$self->{'pending_closes'}}, "");
}
push(@{$self->{'pending_closes'}}, $close);
}
sub close_registered_sections_level($$)
{
my $self = shift;
my $level = shift;
if (not defined($level)) {
cluck 'close_registered_sections_level $level not defined';
}
my @closed_elements;
my $result = '';
while (@{$self->{'pending_closes'}} > $level) {
my $close = pop @{$self->{'pending_closes'}};
push(@closed_elements, $close)
if ($close);
}
return @closed_elements;
}
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);
my $section;
if ($cmdname eq 'node' and $command->{'extra'}->{'associated_section'}) {
$section = $command->{'extra'}->{'associated_section'};
} elsif ($cmdname ne 'node'
and not $command->{'extra'}->{'associated_node'}
# to avoid *heading* @-commands
and $Texinfo::Common::root_commands{$cmdname}) {
$section = $command;
}
if ($section) {
my $level = $section->{'level'};
$result .= join('', $self->close_registered_sections_level($level));
$self->register_opened_section_level($level, "\n");
$result .= '
"
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;
}
}
} elsif (defined $command->{'level'}) {
$heading_level = $command->{'level'};
} else {
# for *heading* @-commands which do not have a level
# in the document as they are not associated with the
# sectioning tree, but still have a $heading_level
$heading_level = Texinfo::Structuring::section_level($command);
}
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 = "$heading";
}
}
if ($self->in_preformatted()) {
$result .= ''.$heading.''."\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));
my $table_of_contents_was_output = 0.;
if ($self->get_conf('CONTENTS_OUTPUT_LOCATION') eq 'after_top'
and $cmdname eq 'top'
and $self->{'structuring'} and $self->{'structuring'}->{'sectioning_root'}
and scalar(@{$self->{'structuring'}->{'sections_list'}}) > 1) {
foreach my $content_command_name ('contents', 'shortcontents') {
if ($self->get_conf($content_command_name)) {
my $contents_text
= $self->_contents_inline_element($content_command_name, undef);
if ($contents_text ne '') {
$result .= $contents_text;
#$result .= $contents_text . $self->get_conf('DEFAULT_RULE')."\n";
$table_of_contents_was_output = 1;
}
}
}
}
if (not $table_of_contents_was_output
and $self->get_conf('FORMAT_MENU') eq 'sectiontoc'
and $sectioning_commands{$cmdname}
and ($cmdname ne 'top'
or (not ($self->_has_contents_or_shortcontents()
and $self->get_conf('CONTENTS_OUTPUT_LOCATION')
eq 'inline')))) {
$result .= _mini_toc($self, $command);
}
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(__("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 '
'.$content."
\n";
}
my $html_menu_entry_index = 0;
sub _convert_preformatted_command($$$$)
{
my $self = shift;
my $cmdname = shift;
my $command = shift;
my $content = shift;
my $extra_classes;
# this is mainly for classes as there are purprosely no classes
# for small*
my $main_cmdname;
if ($small_alias{$cmdname}) {
$main_cmdname = $small_alias{$cmdname};
} else {
$main_cmdname = $cmdname;
}
if ($cmdname eq 'menu') {
$html_menu_entry_index = 0;
} elsif ($cmdname eq 'example') {
if ($command->{'args'}) {
$extra_classes = [];
for my $example_arg (@{$command->{'args'}}) {
# convert or remove all @-commands, using simple ascii and unicode
# characters
my $converted_arg = Texinfo::Convert::NodeNameNormalization::convert($example_arg);
if ($converted_arg ne '') {
push @$extra_classes, $converted_arg;
}
}
}
} elsif ($main_cmdname eq 'lisp') {
$main_cmdname = 'example';
$extra_classes = ['lisp'];
}
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', $main_cmdname, $extra_classes).">\n".$content.'
'."\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;
# no class for @small* variants
$cmdname = $small_alias{$cmdname}
if $small_alias{$cmdname};
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.''."\n";
}
} else {
return $content;
}
}
$default_commands_conversion{'indentedblock'} = \&_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 . '';
} else {
return $content;
}
}
$default_commands_conversion{'verbatim'} = \&_convert_verbatim_command;
sub _convert_displaymath_command($$$$)
{
my $self = shift;
my $cmdname = shift;
my $command = shift;
my $content = shift;
if ($self->in_string) {
return $content;
}
my $result = '';
$result .= $self->_attribute_class('div', 'displaymath').'>';
if ($self->get_conf('HTML_MATH')
and $self->get_conf('HTML_MATH') eq 'mathjax') {
$self->{'element_math'} = 1;
$result .= $self->_attribute_class('em', 'tex2jax_process').'>'
."\\[$content\\]".'';
} else {
$result .= $self->_attribute_class('em').'>'."$content".'';
}
$result .= '';
return $result;
}
$default_commands_conversion{'displaymath'} = \&_convert_displaymath_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 " \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 "
".$args->[0]->{'normal'} ."\n
";
}
}
$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 "
".$args->[0]->{'normal'}."\n
";
}
}
$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 "$args->[0]->{'normal'} \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 "
$args->[0]->{'normal'}
\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 "
$args->[0]->{'normal'}
\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 .= '
';
my $float_text = $self->command_text($float);
if (defined($float_text) and $float_text ne '') {
if ($float_href) {
$result .= "$float_text";
} else {
$result .= $float_text;
}
}
$result .= '
';
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 .= '
'.$caption_text.'
'."\n";
}
return $result . "\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 '';
}
# This can probably only happen with incorrect input,
# for instance menu in copying
# FIXME check?
if ($self->in_string()) {
return $content;
}
my $begin_row = '';
my $end_row = '';
if ($self->_in_preformatted_in_menu()) {
$begin_row = '
\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'}});
}
$entry .= $self->convert_index_subentries($index_entry_ref);
if ($already_formatted) {
$self->{'ignore_notice'}--;
}
next if ($entry !~ /\S/);
$entry = '' .$entry .'' 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 .= '
';
$entries_text .= "$associated_command_text"
if ($associated_command_href);
$entries_text .= "
\n";
}
# a letter and associated indice entries
$result .= '
' .
"
".$self->protect_text($letter)
. "
\n" . $entries_text .
"
".$self->get_conf('DEFAULT_RULE')."
\n";
}
$result .= "\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 $element_name = $contents_command_element_name{$cmdname};
my $special_element
= $self->special_element($element_name);
my $heading;
my $result = "
command_id($special_element);
if ($id ne '') {
$result .= " id=\"$id\"";
}
$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});
}
$result .= ">\n";
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('CONTENTS_OUTPUT_LOCATION') eq 'inline'
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) {
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;
}
# associate same formatting function for @small* command
# as for the associated @-command
foreach my $small_command (keys(%small_alias)) {
$default_commands_conversion{$small_command}
= $default_commands_conversion{$small_alias{$small_command}};
}
# Keys are tree element types, values are function references to convert
# elements of that type. Can be overridden with
# Texinfo::Config::texinfo_types_conversion, setup by
# Texinfo::Config::texinfo_register_type_formatting()
my %default_types_conversion;
sub default_types_conversion($$)
{
my $self = shift;
my $type = shift;
return $default_types_conversion{$type};
}
# Ignored commands
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') {
$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'}->{'args'}
and $command->{'parent'}->{'args'}->[0]
and $command->{'parent'}->{'args'}->[0]->{'contents'}
and @{$command->{'parent'}->{'args'}->[0]->{'contents'}}) {
return $self->convert_tree($self->gdt('@b{{quotation_arg}:} ',
{'quotation_arg' =>
$command->{'parent'}->{'args'}->[0]->{'contents'}}));
}
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 "
".$content."
";
} else {
return "
".$content."
";
}
} 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::Common::_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
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
is ignored.
my $result = $self->_attribute_class('pre', $pre_class).">".$content."
";
# this may happen with lines without textual content
# between a def* and def*x.
if ($command->{'parent'}->{'cmdname'}
and $command->{'parent'}->{'cmdname'} =~ /^def/) {
$result = '
'.$result.'
';
}
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/``/"/g;
$text =~ s/''/"/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 '
' . $content . '
' . "\n";
} else {
return '
' . $content . '
' . "\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 $rel = '';
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 {
my $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);
}
if ($node->{'extra'}->{'isindex'}) {
# Mark the target as an index. See
# http://microformats.org/wiki/existing-rel-values#HTML5_link_type_extensions
$rel = ' rel="index"';
}
}
$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 .= "".$name."";
} 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/^
";
}
return $result;
}
my $name;
my $name_no_number;
if ($section) {
$name = $self->command_text($section);
$name_no_number = $self->command_text($section, 'text_nonumber');
if ($href ne '' and $name ne '') {
$name = "".$name."";
}
}
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 = "".$name."";
}
$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 "
$name$MENU_ENTRY_COLON
$description
\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 "
".$content
."
";
}
}
$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 '
'. $content .'
';
} elsif ($top_format eq 'table' or $top_format eq 'vtable'
or $top_format eq 'ftable') {
return '
\n";
} else {
my $category_prepared = '';
if ($command->{'extra'} and $command->{'extra'}->{'def_parsed_hash'}
and %{$command->{'extra'}->{'def_parsed_hash'}}) {
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 = ' ' . $arguments_text . ''
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 = " $def_type" 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 .= ' ' . $name . '' if ($name ne '');
$type_name .= $arguments_text;
return "
" . $type_name .
"
" . $category_prepared . "
\n";
}
}
sub _get_copiable_anchor {
my ($self, $id) = @_;
my $result = '';
if ($id and $self->get_conf('COPIABLE_ANCHORS')) {
$result = " ¶";
}
return $result;
}
$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 '
' . $content . '
';
} else {
return '
' . $content . '
';
}
}
}
$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());
if (!$self->get_conf('DEF_TABLE')) {
return $self->_attribute_class('dl', 'def').">\n". $content ."\n";
} else {
return "
\n" . $content . "
\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 '
' . $content . '
'."\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
and $self->get_conf('CONTENTS_OUTPUT_LOCATION') eq 'after_title') {
foreach my $command ('contents', 'shortcontents') {
if ($self->get_conf($command)) {
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_format_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;
}
# Function for converting the top-level elements in the conversion: a section
# or a node. $ELEMENT was created in this module (in _prepare_elements), with
# type 'element' (it's not a tree element created by the parser). $CONTENT
# is the contents of the node/section, already converted.
sub _convert_element_type($$$$)
{
my $self = shift;
my $type = shift;
my $element = shift;
my $content = shift;
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'};
$result .= join('', $self->close_registered_sections_level(0));
my $id = $self->command_id($element);
$result .= "
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
$result .= $content;
$result .= &{$self->{'format_footnotes_text'}}($self);
$result .= $self->get_conf('DEFAULT_RULE');
$result .= join('', $self->close_registered_sections_level(0));
return $result;
}
}
$result .= $content unless ($special_element);
$result .= &{$self->{'format_element_footer'}}($self, $type,
$element, $content);
return $result;
}
sub _default_format_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'});
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 $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 .= "
"."\n";
}
my $rule = '';
my $buttons;
if ($end_page) {
$result .= join('', $self->close_registered_sections_level(0));
# setup buttons for navigation footer
if (($is_top or $is_special)
and ($self->get_conf('SPLIT') or !$self->get_conf('MONOLITHIC'))
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 ($self->get_conf('SPLIT') eq 'section') {
$buttons = $self->get_conf('SECTION_FOOTER_BUTTONS');
} elsif ($self->get_conf('SPLIT') eq 'chapter') {
$buttons = $self->get_conf('CHAPTER_FOOTER_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);
}
}
}
# 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 (!$buttons 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');
}
if ($buttons or !$end_page or $self->get_conf('PROGRAM_NAME_IN_FOOTER')) {
$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],
};
}
# Functions accessed with e.g. 'format_heading_text'.
my %default_formatting_references = (
'format_heading_text' => \&_default_format_heading_text,
'format_comment' => \&_default_format_comment,
'format_protect_text' => \&_default_format_protect_text,
'format_css_lines' => \&_default_format_css_lines,
'format_begin_file' => \&_default_format_begin_file,
'format_node_redirection_page' => \&_default_format_node_redirection_page,
'format_end_file' => \&_default_format_end_file,
'format_special_element_body' => \&_default_format_special_element_body,
'format_footnotes_text' => \&_default_format_footnotes_text,
'format_program_string' => \&_default_format_program_string,
'format_titlepage' => \&_default_format_titlepage,
'format_navigation_header' => \&_default_format_navigation_header,
'format_navigation_header_panel' => \&_default_format_navigation_header_panel,
'format_element_header' => \&_default_format_element_header,
'format_element_footer' => \&_default_format_element_footer,
'format_button' => \&_default_format_button,
'format_button_icon_img' => \&_default_format_button_icon_img,
'format_contents' => \&_default_format_contents,
'format_frame_files' => \&_default_format_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};
}
}
my %htmlxref_entries = (
'node' => [ 'node', 'section', 'chapter', 'mono' ],
'section' => [ 'section', 'chapter','node', 'mono' ],
'chapter' => [ 'chapter', 'section', 'node', 'mono' ],
'mono' => [ 'mono', 'chapter', 'section', 'node' ],
);
sub _parse_htmlxref_files($$)
{
my $self = shift;
my $files = shift;
my $htmlxref;
foreach my $file (@$files) {
my ($fname) = $file;
if ($self->get_conf('TEST')) {
$fname =~ s/([^\/]+\/)*//; # strip directories for out-of-source builds
}
print STDERR "html refs config file: $file\n" if ($self->get_conf('DEBUG'));
unless (open (HTMLXREF, $file)) {
$self->document_warn(
sprintf(__("could not open html refs config file %s: %s"),
$file, $!));
next;
}
my $line_nr = 0;
my %variables;
while (my $hline = ) {
my $line = $hline;
$line_nr++;
next if $hline =~ /^\s*#/;
#$hline =~ s/[#]\s.*//;
$hline =~ s/^\s*//;
next if $hline =~ /^\s*$/;
chomp ($hline);
if ($hline =~ s/^\s*(\w+)\s*=\s*//) {
# handle variables
my $var = $1;
my $re = join '|', map { quotemeta $_ } keys %variables;
$hline =~ s/\$\{($re)\}/defined $variables{$1} ? $variables{$1}
: "\${$1}"/ge;
$variables{$var} = $hline;
next;
}
my @htmlxref = split /\s+/, $hline;
my $manual = shift @htmlxref;
my $split_or_mono = shift @htmlxref;
#print STDERR "$split_or_mono $Texi2HTML::Config::htmlxref_entries{$split_or_mono} $line_nr\n";
if (!defined($split_or_mono)) {
$self->file_line_warn(__("missing type"), $fname, $line_nr);
next;
} elsif (!defined($htmlxref_entries{$split_or_mono})) {
$self->file_line_warn(sprintf(__("unrecognized type: %s"),
$split_or_mono), $fname, $line_nr);
next;
}
my $href = shift @htmlxref;
next if (exists($htmlxref->{$manual}->{$split_or_mono}));
if (defined($href)) { # substitute 'variables'
my $re = join '|', map { quotemeta $_ } keys %variables;
$href =~ s/\$\{($re)\}/defined $variables{$1} ? $variables{$1}
: "\${$1}"/ge;
$href =~ s/\/*$// if ($split_or_mono ne 'mono');
}
$htmlxref->{$manual}->{$split_or_mono} = $href;
}
if (!close (HTMLXREF)) {
$self->document_warn(sprintf(__(
"error on closing html refs config file %s: %s"),
$file, $!));
}
}
return $htmlxref;
}
sub _load_htmlxref_files {
my ($self) = @_;
my @htmlxref_dirs = ();
if ($self->get_conf('TEST')) {
my $curdir = File::Spec->curdir();
# to have reproducible tests, do not use system or user
# directories if TEST is set.
@htmlxref_dirs = File::Spec->catdir($curdir, '.texinfo');
my $input_directory = $self->{'info'}->{'input_directory'};
if (defined($input_directory)
and $input_directory ne '.' and $input_directory ne '') {
unshift @htmlxref_dirs, $input_directory;
}
} elsif ($self->{'language_config_dirs'}
and @{$self->{'language_config_dirs'}}) {
@htmlxref_dirs = @{$self->{'language_config_dirs'}};
}
unshift @htmlxref_dirs, '.';
my @texinfo_htmlxref_files;
my $init_file_from_conf = $self->get_conf('HTMLXREF');
if ($init_file_from_conf) {
if (!$self->get_conf('TEST')) {
@texinfo_htmlxref_files = ( $init_file_from_conf );
} else {
@texinfo_htmlxref_files
= Texinfo::Common::locate_init_file ($init_file_from_conf,
\@htmlxref_dirs, 1);
}
} elsif (!$self->get_conf('TEST')) {
@texinfo_htmlxref_files
= Texinfo::Common::locate_init_file ('htmlxref.cnf',
\@htmlxref_dirs, 1);
}
$self->{'htmlxref_files'} = \@texinfo_htmlxref_files;
$self->{'htmlxref'} = {};
if ($self->{'htmlxref_files'}) {
$self->{'htmlxref'} = _parse_htmlxref_files($self,
$self->{'htmlxref_files'});
}
}
sub converter_initialize($)
{
my $self = shift;
$foot_num = 0;
$foot_lines = '';
%formatted_index_entries = ();
%footnote_id_numbers = ();
%{$self->{'css_map'}} = %css_map;
_load_htmlxref_files($self);
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('FORMAT_MENU') ne '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->{$formatting_reference}
= $Texinfo::Config::texinfo_formatting_references{$formatting_reference};
} else {
$self->{$formatting_reference}
= $default_formatting_references{$formatting_reference};
}
}
$self->{'document_context'} = [];
$self->{'multiple_pass'} = [];
$self->{'pending_closes'} = [];
$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_format_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 = "\n";
foreach my $ref (@$css_refs) {
$css_text .= "\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(__("string not closed in css file"),
$file, $line_nr)) if ($in_string);
$self->file_line_warn(sprintf(__("--css-include ended in comment"),
$file, $line_nr)) if ($in_comment);
$self->file_line_warn(sprintf(__("\@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(
__("CSS file %s not found"), $file));
next;
}
# FIXME use open_out?
unless (open (CSSFILE, $css_file)) {
$self->document_warn(sprintf(__(
"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(__("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;
}
# Get the name of a file containing a node, as well as the anchor within
# that file to link to that node. Argument is the 'extra' value on
# an element hash, or something that looks like it.
sub _node_id_file($$)
{
my $self = shift;
my $node_info = shift;
my $target;
my $normalized;
if ($node_info->{'normalized'}) {
$normalized = $node_info->{'normalized'};
} elsif ($node_info->{'node_content'}) {
$normalized = Texinfo::Convert::NodeNameNormalization::normalize_node (
{ 'contents' => $node_info->{'node_content'} });
}
if (defined($normalized)) {
$target = _normalized_to_id($normalized);
} else {
$target = '';
}
# to find out the Top node, one could check $node_info->{'normalized'}
if (defined($Texinfo::Config::node_target_name)) {
$target = &$Texinfo::Config::node_target_name($node_info, $target);
}
my $filename = $self->_node_filename($node_info);
return ($filename, $target);
}
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->{'seen_ids'}->{$target}) {
$target = $target_base.'-'.$nr;
$nr++;
# Avoid integer overflow
die if ($nr == 0);
}
}
# These are undefined if the $target is set to ''.
my $target_contents;
my $target_shortcontents;
if ($Texinfo::Common::sectioning_commands{$command->{'cmdname'}}) {
if ($target ne '') {
my $target_base_contents = $target;
$target_base_contents =~ s/^g_t//;
$target_contents = 'toc-'.$target_base_contents;
my $toc_nr = $nr -1;
while ($self->{'seen_ids'}->{$target_contents}) {
$target_contents = 'toc-'.$target_base_contents.'-'.$toc_nr;
$toc_nr++;
# Avoid integer overflow
die if ($toc_nr == 0);
}
$target_shortcontents = 'stoc-'.$target_base_contents;
my $target_base_shortcontents = $target_base;
$target_base_shortcontents =~ s/^g_t//;
my $stoc_nr = $nr -1;
while ($self->{'seen_ids'}->{$target_shortcontents}) {
$target_shortcontents = 'stoc-'.$target_base_shortcontents
.'-'.$stoc_nr;
$stoc_nr++;
# Avoid integer overflow
die if ($stoc_nr == 0);
}
}
}
if (defined($Texinfo::Config::sectioning_command_target_name)) {
($target, $target_contents,
$target_shortcontents, $filename)
= &$Texinfo::Config::sectioning_command_target_name($self,
$command, $target,
$target_contents,
$target_shortcontents,
$filename);
}
if ($self->get_conf('DEBUG')) {
print STDERR "Register $command->{'cmdname'} $target\n";
}
$self->{'targets'}->{$command} = {
'target' => $target,
'section_filename' => $filename,
};
$self->{'seen_ids'}->{$target} = 1;
if (defined($target_contents)) {
$self->{'targets'}->{$command}->{'contents_target'} = $target_contents;
} else {
$self->{'targets'}->{$command}->{'contents_target'} = '';
}
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.
# Note that 'node_filename', which is set here for Top too, is not
# used later for Top, see the NOTE below.
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'));
my $extension = '';
$extension = '.'.$self->get_conf('EXTENSION')
if (defined($self->get_conf('EXTENSION'))
and $self->get_conf('EXTENSION') ne '');
if ($self->{'labels'}) {
foreach my $root_command (values(%{$self->{'labels'}})) {
my ($filename, $target) = $self->_node_id_file($root_command->{'extra'});
$filename .= $extension;
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,
'node_filename' => $filename};
$self->{'seen_ids'}->{$target} = 1;
}
}
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 ($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'};
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 .= $extension;
} 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++;
}
}
}
$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;
}
}
}
# $ROOT is a parsed Texinfo tree. Return a list of the "elements" we need to
# output in the HTML file(s). Each "element" is what can go in one HTML file,
# such as the content between @node lines in the Texinfo source.
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('CONTENTS_OUTPUT_LOCATION')
eq 'separate_element') {
$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 $target = $self->{'misc_elements_targets'}->{$type};
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, $filename)
= &$Texinfo::Config::special_element_target_file_name(
$self,
$element,
$target,
$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,\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} = {'target' => $target,
'misc_filename' => $filename,
};
$self->{'seen_ids'}->{$target} = 1;
}
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, $filename);
if (defined($Texinfo::Config::special_element_target_file_name)) {
($target, $filename)
= &$Texinfo::Config::special_element_target_file_name(
$self,
$element,
$target,
$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('CONTENTS_OUTPUT_LOCATION') eq 'after_title') {
if ($self->{'elements'}) {
$default_filename = $self->{'elements'}->[0]->{'filename'};
}
} elsif ($self->get_conf('CONTENTS_OUTPUT_LOCATION') eq 'after_top') {
my $section_top = undef;
if ($self->{'extra'} and $self->{'extra'}->{'top'}) {
$section_top = $self->{'extra'}->{'top'};
$default_filename = $self->command_filename($section_top)
}
} elsif ($self->get_conf('CONTENTS_OUTPUT_LOCATION') eq 'inline') {
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 $target = $self->{'misc_elements_targets'}->{$type};
my $filename;
if (defined($Texinfo::Config::special_element_target_file_name)) {
($target, $filename)
= &$Texinfo::Config::special_element_target_file_name(
$self,
$element,
$target,
$default_filename);
}
$filename = $default_filename if (!defined($filename));
print STDERR "Add content $element $type: target $target,\n".
" filename $filename\n" if ($self->get_conf('DEBUG'));
$self->{'targets'}->{$element} = {'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::Common::_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 (@global_directions) {
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 = $self->{'parser'}->indices_information();
$self->{'index_names'} = $index_names;
my $merged_index_entries
= Texinfo::Structuring::merge_indices($index_names);
$self->{'index_entries_by_letter'}
= Texinfo::Structuring::sort_indices_by_letter ($self->{'parser'},
$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->{'seen_ids'}->{$target}) {
$target = $target_base.'-'.$nr;
$nr++;
# Avoid integer overflow
die if ($nr == 0);
}
$self->{'seen_ids'}->{$target} = 1;
$self->{'targets'}->{$index_entry->{'command'}} = {'target' => $target,
};
}
}
}
}
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->{'seen_ids'}->{$docid} or $self->{'seen_ids'}->{$footid}) {
$nr++;
$footid = $footid_base.$nr;
$docid = $docid_base.$nr;
# Avoid integer overflow
die if ($nr == 0);
}
$self->{'seen_ids'}->{$footid} = 1;
$self->{'seen_ids'}->{$docid} = 1;
$self->{'targets'}->{$footnote} = { 'target' => $footid };
print STDERR "Enter footnote $footnote: 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};
}
sub _external_node_href($$$$)
{
my $self = shift;
my $external_node = shift;
my $filename = shift;
my $link_command = shift;
#print STDERR "external_node: ".join('|', keys(%$external_node))."\n";
my ($target_filebase, $target) = $self->_node_id_file($external_node);
my $xml_target = _normalized_to_id($target);
my $default_target_split = $self->get_conf('EXTERNAL_CROSSREF_SPLIT');
my $external_file_extension = '.html';
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/\.info*$//;
$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')) {
if (defined($link_command) and $link_command->{'line_nr'}) {
$self->line_warn(sprintf(__(
"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(__(
"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 .= $external_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');
} else {
return $file;# . '#Top';
}
} else {
return $file . '#Top';
}
}
if (! $target_split) {
return $file . '#' . $xml_target;
} else {
my $file_name;
if ($target eq 'Top' and defined($self->get_conf('TOP_NODE_FILE_TARGET'))) {
$file_name = $self->get_conf('TOP_NODE_FILE_TARGET');
} else {
$file_name = $target_filebase . $external_file_extension;
}
return $file . $file_name . '#' . $xml_target;
}
}
my %valid_types = (
'href' => 1,
'string' => 1,
'text' => 1,
'tree' => 1,
'target' => 1,
'node' => 1,
'section' => 1
);
foreach my $no_number_type ('text', 'tree', 'string') {
$valid_types{$no_number_type .'_nonumber'} = 1;
}
# sub _element_direction($SELF, $ELEMENT, $DIRECTION, $TYPE, $FILENAME)
#
# Return text used for linking from $ELEMENT in direction $DIRECTION. The
# text returned depends on $TYPE.
#
# $element can be undef.
# $element undef happens at least when there is no output file, or for
# the table of content when frames are used. That call would result
# for instance from _element_direction being called from _get_links,
# itself called from 'format_begin_file' which, in the default case
# points to _default_format_begin_file.
# TODO are there other cases?
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};
# output TOP_NODE_UP related infos even if element is not
# defined which should mostly correspond to cases when there is no
# output file, for example in the tests.
} elsif ((not defined($element)
or ($element and $self->element_is_top($element)))
and defined($self->get_conf('TOP_NODE_UP_URL'))
and ($direction eq 'Up' or $direction eq 'NodeUp')) {
if ($type eq 'href') {
return $self->get_conf('TOP_NODE_UP_URL');
} elsif ($type eq 'text' or $type eq 'node' or $type eq 'string' or $type eq 'section') {
return $self->get_conf('TOP_NODE_UP');
} else {
cluck("type $type not available for TOP_NODE_UP\n");
return '';
}
}
if ($element_target) {
######## debug
if (!$element_target->{'type'}) {
die "No type for element_target $direction $element_target: "
. Texinfo::Common::_print_current_keys($element_target)
. "directions :". Texinfo::Structuring::_print_directions($element);
}
########
if ($element_target->{'type'} eq 'external_node') {
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';
} elsif ($type eq 'section') {
$command = $element_target->{'extra'}->{'section'};
$target = $self->{'targets'}->{$command} if ($command);
$type = 'text_nonumber';
} 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 'target') {
return undef;
} elsif ($command) {
return $self->command_text($command, $type);
}
}
# Output a list of the nodes immediately below this one
sub _mini_toc
{
my ($self, $command) = @_;
my $filename = $self->{'current_filename'};
my $result = '';
my $entry_index = 0;
my $accesskey;
if ($command->{'section_childs'} and @{$command->{'section_childs'}}) {
$result .= $self->_attribute_class('ul', 'section-toc').">\n";
foreach my $section (@{$command->{'section_childs'}}) {
my $tree = $self->command_text($section, 'tree_nonumber');
my $text = $self->_convert($tree);
$entry_index++;
$accesskey = '';
$accesskey = " accesskey=\"$entry_index\""
if ($self->get_conf('USE_ACCESSKEY') and $entry_index < 10);
my $href = $self->command_href($section, $filename);
if ($text ne '') {
if ($href ne '') {
my $href_attribute = '';
if ($href ne '') {
$href_attribute = " href=\"$href\"";
}
$result .= "
\n";
}
}
$result .= "\n";
}
return $result;
}
sub _default_format_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_target($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 = " id=\"$toc_id\"";
}
my $href_attribute = '';
if ($href ne '') {
$href_attribute = " href=\"$href\"";
}
my $rel = '';
if ($section->{'extra'}
and $section->{'extra'}->{'associated_node'}
and $section->{'extra'}->{'associated_node'}->{'extra'}->{'isindex'}) {
$rel = ' rel="index"';
}
$result .= "
$text";
}
}
} elsif ($section->{'section_childs'} and @{$section->{'section_childs'}}
and $toplevel_contents) {
$result .= "
";
}
# 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 .= "
\n";
last if ($section eq $top_section);
$section = $section->{'section_next'};
} else {
#last if ($section eq $top_section);
if ($section eq $top_section) {
$result .= "\n" unless ($section->{'cmdname'} eq 'top');
last;
}
while ($section->{'section_up'}) {
$section = $section->{'section_up'};
$result .= "\n". ' ' x (2*($section->{'level'} - $min_root_level))
. "";
if ($section eq $top_section) {
$result .= "\n" if ($toplevel_contents);
last SECTION;
}
if ($section->{'section_next'}) {
$result .= "\n";
$section = $section->{'section_next'};
last;
}
}
}
}
}
if (@{$section_root->{'section_childs'}} > 1) {
# or $section_root->{'section_childs'}->[0]->{'cmdname'} ne 'top') {
$result .= "\n";
}
if ($contents and !defined($self->get_conf('AFTER_TOC_LINES'))
or (!$contents and !defined($self->get_conf('AFTER_OVERVIEW')))) {
$result .= "\n\n";
} elsif($contents) {
$result .= $self->get_conf('AFTER_TOC_LINES');
} else {
$result .= $self->get_conf('AFTER_OVERVIEW');
}
return $result;
}
sub _default_format_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_format_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 .= "
$program_string
";
}
my $pre_body_close = $self->get_conf('PRE_BODY_CLOSE');
$pre_body_close = '' if (!defined($pre_body_close));
my $setting = $self->get_conf('JS_WEBLABELS');
my $path = $self->get_conf('JS_WEBLABELS_FILE');
if ($setting and $path
and ($setting eq 'generate' or $setting eq 'reference')
and %{$self->{'jslicenses_element'}}) {
$pre_body_close .=
""
.$self->convert_tree($self->gdt('JavaScript license information'))
.'';
}
return "${program_text}
$pre_body_close