diff options
Diffstat (limited to 'macros/texinfo/texinfo/tp/Texinfo/Convert')
14 files changed, 1208 insertions, 601 deletions
diff --git a/macros/texinfo/texinfo/tp/Texinfo/Convert/Converter.pm b/macros/texinfo/texinfo/tp/Texinfo/Convert/Converter.pm index b093a96c47..258ef30626 100644 --- a/macros/texinfo/texinfo/tp/Texinfo/Convert/Converter.pm +++ b/macros/texinfo/texinfo/tp/Texinfo/Convert/Converter.pm @@ -1,6 +1,6 @@ # Converter.pm: Common code for Converters. # -# Copyright 2011-2019 Free Software Foundation, Inc. +# Copyright 2011-2020 Free Software Foundation, Inc. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -49,7 +49,7 @@ xml_accents @EXPORT = qw( ); -$VERSION = '6.7'; +$VERSION = '6.8'; my %defaults = ( 'ENABLE_ENCODING' => 1, @@ -244,20 +244,6 @@ sub converter(;$) $converter->set_conf('OUTPUT_PERL_ENCODING', $perl_encoding); } } - if (!defined($converter->{'expanded_formats'})) { - if ($converter->{'parser'}) { - $converter->{'expanded_formats'} = $converter->{'parser'}->{'expanded_formats'}; - } else { - $converter->{'expanded_formats'} = []; - } - } - if (!defined($converter->{'include_directories'})) { - if ($converter->{'parser'}) { - $converter->{'include_directories'} = $converter->{'parser'}->{'include_directories'}; - } else { - $converter->{'include_directories'} = [ '.' ]; - } - } # turn the array to a hash for speed. Not sure it really matters for such # a small array. diff --git a/macros/texinfo/texinfo/tp/Texinfo/Convert/DocBook.pm b/macros/texinfo/texinfo/tp/Texinfo/Convert/DocBook.pm index 3b30955a53..a6899761b5 100644 --- a/macros/texinfo/texinfo/tp/Texinfo/Convert/DocBook.pm +++ b/macros/texinfo/texinfo/tp/Texinfo/Convert/DocBook.pm @@ -1,6 +1,6 @@ # DocBook.pm: output tree as DocBook. # -# Copyright 2011-2019 Free Software Foundation, Inc. +# Copyright 2011-2020 Free Software Foundation, Inc. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -47,7 +47,7 @@ use vars qw($VERSION @ISA @EXPORT @EXPORT_OK %EXPORT_TAGS); @EXPORT = qw( ); -$VERSION = '6.7'; +$VERSION = '6.8'; my $nbsp = '&#'.hex('00A0').';'; my $mdash = '&#'.hex('2014').';'; @@ -59,7 +59,7 @@ my $rsquo = '&#'.hex('2019').';'; my %defaults = ( #'ENABLE_ENCODING' => 0, - 'SHOW_MENU' => 0, + 'FORMAT_MENU' => 'nomenu', 'EXTENSION' => 'xml', # dbk? 'OUTPUT_ENCODING_NAME' => 'utf-8', 'OUTFILE' => undef, @@ -427,15 +427,14 @@ sub _index_entry($$) $result .= "</$level>"; $level = "tertiary"; } - if ($index_entry->{'command'}->{'extra'}->{'seeentry'}) { $result .= "<see>"; - $result .= $index_entry->{'command'}->{'extra'}->{'seeentry'}; + $result .= $self->_convert({'contents' => $index_entry->{'command'}->{'extra'}->{'seeentry'}->{'args'}}); $result .= "</see>"; } if ($index_entry->{'command'}->{'extra'}->{'seealso'}) { $result .= "<seealso>"; - $result .= $index_entry->{'command'}->{'extra'}->{'seealso'}; + $result .= $self->_convert({'contents' => $index_entry->{'command'}->{'extra'}->{'seealso'}->{'args'}}); $result .= "</seealso>"; } @@ -1260,6 +1259,9 @@ sub _convert($$;$) } } elsif ($root->{'cmdname'} eq 'verbatim') { push @elements, 'screen'; + } elsif ($root->{'cmdname'} eq 'displaymath') { + push @elements, 'informalequation'; + push @elements, 'mathphrase'; } elsif ($root->{'cmdname'} eq 'quotation' or $root->{'cmdname'} eq 'smallquotation') { my $element; @@ -1377,7 +1379,8 @@ sub _convert($$;$) #warn " have contents $root->{'contents'}\n"; my $in_code; if ($root->{'cmdname'} - and $Texinfo::Common::preformatted_code_commands{$root->{'cmdname'}}) { + and ($Texinfo::Common::preformatted_code_commands{$root->{'cmdname'}} + or $Texinfo::Common::math_commands{$root->{'cmdname'}})) { $in_code = 1; } push @{$self->{'document_context'}->[-1]->{'monospace'}}, 1 diff --git a/macros/texinfo/texinfo/tp/Texinfo/Convert/HTML.pm b/macros/texinfo/texinfo/tp/Texinfo/Convert/HTML.pm index 80dc1a9d8c..476384ea5c 100644 --- a/macros/texinfo/texinfo/tp/Texinfo/Convert/HTML.pm +++ b/macros/texinfo/texinfo/tp/Texinfo/Convert/HTML.pm @@ -1,6 +1,6 @@ # HTML.pm: output tree as HTML. # -# Copyright 2011-2019 Free Software Foundation, Inc. +# Copyright 2011-2020 Free Software Foundation, Inc. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -16,12 +16,22 @@ # along with this program. If not, see <http://www.gnu.org/licenses/>. # # -# Formatting functions, registered in %default_types_conversion -# should not have side effects, such that users can overrides -# them independently without risking unwanted results. Also in -# formatting functions, the state of the converter should only -# be accessed through functions, such as in_math, in_preformatted, -# preformatted_classes_stack and similar functions. +# There are three categories of formatting functions that can be +# replaced by the user, together with the hash with default functions: +# * command tree element formatting functions registered in +# %default_commands_conversion +# * type tree element (element without @-command) formatting +# functions, registered in %default_types_conversion +# * other formatting functions, registered in +# %default_formatting_references +# +# The functions used in the default case for all the functions +# that may be replaced should not have side effects, such that +# users can overrides them independently without risking unwanted +# results. Also in formatting functions, the state of the +# converter should only be accessed through functions, such +# as in_math, in_preformatted, preformatted_classes_stack and +# similar functions. # # In most formatting functions, the case where $self->in_string() is # true should be handled explicitely and the simplest formatting should be @@ -53,7 +63,9 @@ use Texinfo::Convert::Text; use Texinfo::Convert::Unicode; use Texinfo::Convert::NodeNameNormalization; -use Carp qw(cluck); +use Carp qw(cluck confess); + +use File::Copy qw(copy); require Exporter; use vars qw($VERSION @ISA @EXPORT @EXPORT_OK %EXPORT_TAGS); @@ -71,7 +83,7 @@ use vars qw($VERSION @ISA @EXPORT @EXPORT_OK %EXPORT_TAGS); @EXPORT = qw( ); -$VERSION = '6.7'; +$VERSION = '6.8'; # misc commands that are of use for formatting. my %formatting_misc_commands = %Texinfo::Convert::Text::formatting_misc_commands; @@ -86,6 +98,7 @@ my %block_commands = %Texinfo::Common::block_commands; my %menu_commands = %Texinfo::Common::menu_commands; my %root_commands = %Texinfo::Common::root_commands; my %preformatted_commands = %Texinfo::Common::preformatted_commands; +my %math_commands = %Texinfo::Common::math_commands; my %explained_commands = %Texinfo::Common::explained_commands; my %item_container_commands = %Texinfo::Common::item_container_commands; my %raw_commands = %Texinfo::Common::raw_commands; @@ -102,6 +115,12 @@ my %region_commands = %Texinfo::Common::region_commands; my %context_brace_commands = %Texinfo::Common::context_brace_commands; my %letter_no_arg_commands = %Texinfo::Common::letter_no_arg_commands; +my %small_alias; +for my $cmd ('example', 'display', 'format', 'lisp', 'quotation', + 'indentedblock') { + $small_alias{'small'.$cmd} = $cmd; +}; + foreach my $def_command (keys(%def_commands)) { $formatting_misc_commands{$def_command} = 1 if ($misc_commands{$def_command}); } @@ -235,7 +254,7 @@ sub in_align($) # # Returns a hash that may have these keys set: # 'target': A unique string representing the target. Used as argument to -# 'name' attribute inside <a>. +# 'id' attribute. # 'node_filename', 'section_filename', # 'misc_filename', 'filename'. Possibly others. # @@ -428,9 +447,14 @@ sub command_href($$;$$) $href .= $target_filename; # omit target if the command is an element command, there is only # one element in file and there is a file in the href + my $command_element_command = $self->command_element_command($command); if (defined($filename) - and defined($self->command_element_command($command)) - and $self->command_element_command($command) eq $command) { + and defined($command_element_command) + and ($command_element_command eq $command + or (defined($command_element_command->{'extra'}) + and defined($command_element_command->{'extra'}->{'associated_section'}) + and $command_element_command->{'extra'}->{'associated_section'} + eq $command))) { my $count_elements_in_file = $self->count_elements_in_filename($target_filename); if (defined($count_elements_in_file) and $count_elements_in_file == 1) { $target = ''; @@ -448,6 +472,8 @@ my %contents_command_element_name = ( 'summarycontents' => 'Overview', ); +# Return string for linking to $CONTENTS_OR_SHORTCONTENTS associated +# element from $COMMAND with <a href> sub command_contents_href($$$$) { my $self = shift; @@ -649,6 +675,7 @@ sub default_formatting_function($$) return $self->{'default_formatting_functions'}->{$format}; } +# used for customization only (in t2h_singular.init) sub get_value($$) { my $self = shift; @@ -661,6 +688,8 @@ sub get_value($$) } } +# This function should be used in formatting functions when some +# Texinfo tree need to be converted. sub convert_tree_new_formatting_context($$;$$) { my $self = shift; @@ -765,143 +794,6 @@ my %BUTTONS_EXAMPLE = ); -my (%BUTTONS_TEXT, %BUTTONS_GOTO, %BUTTONS_NAME, %SPECIAL_ELEMENTS_NAME); - -sub _translate_names($) -{ - my $self = shift; - #print STDERR "encoding_name: ".$self->get_conf('OUTPUT_ENCODING_NAME')." documentlanguage: ".$self->get_conf('documentlanguage')."\n"; - - - %BUTTONS_TEXT = ( - 'Top', $self->gdt('Top'), - 'Contents', $self->gdt('Contents'), - 'Overview', $self->gdt('Overview'), - 'Index', $self->gdt('Index'), - ' ', ' ', - 'This', $self->gdt('current'), - 'Back', ' < ', - 'FastBack', ' << ', - 'Prev', $self->gdt('Prev'), - 'Up', $self->gdt(' Up '), - 'Next', $self->gdt('Next'), - #'NodeUp', $self->gdt('Node up'), - 'NodeUp', $self->gdt('Up'), - #'NodeNext', $self->gdt('Next node'), - 'NodeNext', $self->gdt('Next'), - #'NodePrev', $self->gdt('Previous node'), - 'NodePrev', $self->gdt('Previous'), - 'NodeForward', $self->gdt('Forward node'), - 'NodeBack', $self->gdt('Back node'), - 'Forward', ' > ', - 'FastForward', ' >> ', - 'About', ' ? ', - 'First', ' |< ', - 'Last', ' >| ', - 'NextFile', $self->gdt('Next file'), - 'PrevFile', $self->gdt('Previous file'), - ); - - #%BUTTONS_TEXT = %NAVIGATION_TEXT; - - %BUTTONS_GOTO = ( - 'Top', $self->gdt('Cover (top) of document'), - 'Contents', $self->gdt('Table of contents'), - 'Overview', $self->gdt('Short table of contents'), - 'Index', $self->gdt('Index'), - 'This', $self->gdt('Current section'), - 'Back', $self->gdt('Previous section in reading order'), - 'FastBack', $self->gdt('Beginning of this chapter or previous chapter'), - 'Prev', $self->gdt('Previous section on same level'), - 'Up', $self->gdt('Up section'), - 'Next', $self->gdt('Next section on same level'), - 'NodeUp', $self->gdt('Up node'), - 'NodeNext', $self->gdt('Next node'), - 'NodePrev', $self->gdt('Previous node'), - 'NodeForward', $self->gdt('Next node in node reading order'), - 'NodeBack', $self->gdt('Previous node in node reading order'), - 'Forward', $self->gdt('Next section in reading order'), - 'FastForward', $self->gdt('Next chapter'), - 'About' , $self->gdt('About (help)'), - 'First', $self->gdt('First section in reading order'), - 'Last', $self->gdt('Last section in reading order'), - 'NextFile', $self->gdt('Forward section in next file'), - 'PrevFile', $self->gdt('Back section in previous file'), - ); - - %BUTTONS_NAME = ( - 'Top', $self->gdt('Top'), - 'Contents', $self->gdt('Contents'), - 'Overview', $self->gdt('Overview'), - 'Index', $self->gdt('Index'), - ' ', ' ', - 'This', $self->gdt('This'), - 'Back', $self->gdt('Back'), - 'FastBack', $self->gdt('FastBack'), - 'Prev', $self->gdt('Prev'), - 'Up', $self->gdt('Up'), - 'Next', $self->gdt('Next'), - 'NodeUp', $self->gdt('NodeUp'), - 'NodeNext', $self->gdt('NodeNext'), - 'NodePrev', $self->gdt('NodePrev'), - 'NodeForward', $self->gdt('NodeForward'), - 'NodeBack', $self->gdt('NodeBack'), - 'Forward', $self->gdt('Forward'), - 'FastForward', $self->gdt('FastForward'), - 'About', $self->gdt('About'), - 'First', $self->gdt('First'), - 'Last', $self->gdt('Last'), - 'NextFile', $self->gdt('NextFile'), - 'PrevFile', $self->gdt('PrevFile'), - ); - - %SPECIAL_ELEMENTS_NAME = ( - 'About' => $self->gdt('About This Document'), - 'Contents' => $self->gdt('Table of Contents'), - 'Overview' => $self->gdt('Short Table of Contents'), - 'Footnotes' => $self->gdt('Footnotes'), - ); - - # delete the tree and formatted results for special elements - # such that they are redone with the new tree when needed. - foreach my $special_element (keys (%SPECIAL_ELEMENTS_NAME)) { - if ($self->{'special_elements_types'}->{$special_element} and - $self->{'targets'}->{$self->{'special_elements_types'}->{$special_element}}) { - my $target - = $self->{'targets'}->{$self->{'special_elements_types'}->{$special_element}}; - foreach my $key ('text', 'string', 'tree') { - delete $target->{$key}; - } - } - } - - foreach my $hash (\%BUTTONS_TEXT, \%BUTTONS_GOTO, \%BUTTONS_NAME) { - foreach my $button (keys (%$hash)) { - if (ref($hash->{$button})) { - $hash->{$button} = $self->convert_tree_new_formatting_context( - $hash->{$button}, "button $button"); - } - } - } - if ($self->{'commands_translation'}) { - my %translated_commands; - foreach my $context ('normal', 'preformatted', 'string') { - foreach my $command (keys(%{$self->{'commands_translation'}->{$context}})) { - $translated_commands{$command} = 1; - delete $self->{'commands_formatting'}->{$context}->{$command}; - if (defined($self->{'commands_translation'}->{$context}->{$command})) { - $self->{'commands_formatting'}->{$context}->{$command} - = $self->gdt($self->{'commands_translation'}->{$context}->{$command}, - undef, 'translated_text'); - } - } - } - foreach my $command(keys(%translated_commands)) { - $self->_complete_commands_formatting($command); - } - } -} - # insert here name of icon images for buttons # Icons are used, if ICONS and resp. value are set my %ACTIVE_ICONS = ( @@ -956,22 +848,24 @@ my %PASSIVE_ICONS = ( 'PrevFile', '', ); +my (%BUTTONS_TEXT, %BUTTONS_GOTO, %BUTTONS_NAME, %SPECIAL_ELEMENTS_NAME); my %defaults = ( 'ENABLE_ENCODING' => 0, - 'SHOW_MENU' => 1, + 'FORMAT_MENU' => 'sectiontoc', 'OUTPUT_ENCODING_NAME' => 'utf-8', 'OUTFILE' => undef, 'SUBDIR' => undef, 'USE_NODES' => 1, - 'INLINE_CONTENTS' => 1, + 'USE_NODE_DIRECTIONS' => undef, + 'CONTENTS_OUTPUT_LOCATION' => 'after_top', 'SPLIT' => 'node', # if set style is added in attribute. 'INLINE_CSS_STYLE' => 0, # if set, no css is used. 'NO_CSS' => 0, -# if set, use node anchors for sections targets - 'USE_NODE_TARGET' => 1, + 'JS_WEBLABELS' => 'generate', + 'JS_WEBLABELS_FILE' => 'js_licenses.html', # no clash with node name 'OPEN_QUOTE_SYMBOL' => '‘', 'CLOSE_QUOTE_SYMBOL' => '’', 'USE_ISO' => 1, @@ -995,21 +889,22 @@ my %defaults = ( 'OVERVIEW_LINK_TO_TOC' => 1, 'COMPLEX_FORMAT_IN_TABLE' => 0, 'WORDS_IN_PAGE' => 300, - 'SECTION_BUTTONS' => [[ 'NodeNext', \&_default_node_direction ], - [ 'NodePrev', \&_default_node_direction ], - [ 'NodeUp', \&_default_node_direction ], ' ', + # _default_panel_button_dynamic_direction use nodes direction based on USE_NODE_DIRECTIONS + # or USE_NODES if USE_NODE_DIRECTIONS is undefined + 'SECTION_BUTTONS' => [[ 'Next', \&_default_panel_button_dynamic_direction ], + [ 'Prev', \&_default_panel_button_dynamic_direction ], + [ 'Up', \&_default_panel_button_dynamic_direction ], ' ', 'Contents', 'Index'], + 'SECTION_FOOTER_BUTTONS' => [[ 'Next', \&_default_panel_button_dynamic_direction_section_footer ], + [ 'Prev', \&_default_panel_button_dynamic_direction_section_footer ], + [ 'Up', \&_default_panel_button_dynamic_direction_section_footer ], ' ', + 'Contents', 'Index'], 'LINKS_BUTTONS' => ['Top', 'Index', 'Contents', 'About', - 'NodeUp', 'NextFile', 'PrevFile'], -# 'TOP_BUTTONS' => ['Back', 'Forward', ' ', -# 'Contents', 'Index', 'About'], -# -# 'MISC_BUTTONS' => [ 'Top', 'Contents', 'Index', 'About' ], -# 'CHAPTER_BUTTONS' => [ 'FastBack', 'FastForward', ' ', -# ' ', ' ', ' ', ' ', -# 'Top', 'Contents', 'Index', 'About', ], -# 'SECTION_FOOTER_BUTTONS' => [ 'FastBack', 'Back', 'Up', 'Forward', 'FastForward' ], -# 'NODE_FOOTER_BUTTONS' => [ 'FastBack', 'Back', 'Up', 'Forward', 'FastForward' ], + 'NodeUp', 'NodeNext', 'NodePrev'], + 'NODE_FOOTER_BUTTONS' => [[ 'Next', \&_default_panel_button_dynamic_direction_node_footer ], + [ 'Prev', \&_default_panel_button_dynamic_direction_node_footer ], + [ 'Up', \&_default_panel_button_dynamic_direction_node_footer ], + ' ', 'Contents', 'Index'], 'misc_elements_targets' => { 'Overview' => 'SEC_Overview', 'Contents' => 'SEC_Contents', @@ -1033,11 +928,11 @@ my %defaults = ( 'DEFAULT_RULE' => '<hr>', 'BIG_RULE' => '<hr>', 'MENU_SYMBOL' => '•', - 'MENU_ENTRY_COLON' => '', + 'MENU_ENTRY_COLON' => ':', 'INDEX_ENTRY_COLON' => ':', 'BODYTEXT' => undef, 'documentlanguage' => 'en', - 'xrefautomaticsectiontitle' => 'off', + 'xrefautomaticsectiontitle' => 'on', 'SHOW_TITLE' => 1, 'SECTION_NAME_IN_TITLE' => 0, 'USE_TITLEPAGE_FOR_TITLE' => 0, @@ -1061,16 +956,190 @@ my %defaults = ( 'Contents' => 'contents', 'Overview' => 'shortcontents', 'Footnotes' => 'footnotes', - }, - + }, + 'jslicenses' => {}, # for outputting licences file + 'jslicenses_element' => {}, # scripts used in current output file + 'jslicenses_math' => {}, # MathJax scripts + 'jslicenses_infojs' => {}, # info.js scripts + 'element_math' => 0, # whether math has been seen in current file + 'COPIABLE_ANCHORS' => 1, + 'output_format' => 'html', ); -foreach my $buttons ('CHAPTER_BUTTONS', 'SECTION_FOOTER_BUTTONS', 'NODE_FOOTER_BUTTONS', - 'MISC_BUTTONS', 'TOP_BUTTONS') { +foreach my $buttons ('CHAPTER_BUTTONS', 'MISC_BUTTONS', 'TOP_BUTTONS') { $defaults{$buttons} = [@{$defaults{'SECTION_BUTTONS'}}]; } +foreach my $buttons ('SECTION_FOOTER_BUTTONS', 'CHAPTER_FOOTER_BUTTONS') { + $defaults{$buttons} = [@{$defaults{'SECTION_FOOTER_BUTTONS'}}]; +} + + +my @global_directions = ('First', 'Last', 'Index', 'Top'); +my %global_and_special_directions; +foreach my $global_direction (@global_directions) { + $global_and_special_directions{$global_direction} = 1; +} +foreach my $special_element (keys %{$defaults{'SPECIAL_ELEMENTS_CLASS'}}) { + $global_and_special_directions{$special_element} = 1; +} + +foreach my $hash (\%BUTTONS_REL, \%BUTTONS_ACCESSKEY, + \%ACTIVE_ICONS, \%PASSIVE_ICONS) { + foreach my $button (grep {not exists($global_and_special_directions{$_}) and $_ ne ' '} keys %$hash) { + $hash->{'FirstInFile'.$button} = $hash->{$button}; + } +} + +sub _translate_names($) +{ + my $self = shift; + #print STDERR "encoding_name: ".$self->get_conf('OUTPUT_ENCODING_NAME')." documentlanguage: ".$self->get_conf('documentlanguage')."\n"; + + + %BUTTONS_TEXT = ( + 'Top', $self->gdt('Top'), + 'Contents', $self->gdt('Contents'), + 'Overview', $self->gdt('Overview'), + 'Index', $self->gdt('Index'), + ' ', ' ', + 'This', $self->gdt('current'), + 'Back', ' < ', + 'FastBack', ' << ', + 'Prev', $self->gdt('Prev'), + 'Up', $self->gdt(' Up '), + 'Next', $self->gdt('Next'), + #'NodeUp', $self->gdt('Node up'), + 'NodeUp', $self->gdt('Up'), + #'NodeNext', $self->gdt('Next node'), + 'NodeNext', $self->gdt('Next'), + #'NodePrev', $self->gdt('Previous node'), + 'NodePrev', $self->gdt('Previous'), + 'NodeForward', $self->gdt('Forward node'), + 'NodeBack', $self->gdt('Back node'), + 'Forward', ' > ', + 'FastForward', ' >> ', + 'About', ' ? ', + 'First', ' |< ', + 'Last', ' >| ', + 'NextFile', $self->gdt('Next file'), + 'PrevFile', $self->gdt('Previous file'), + ); + + foreach my $button (grep {not exists($global_and_special_directions{$_}) and $_ ne ' '} keys %BUTTONS_TEXT) { + $BUTTONS_TEXT{'FirstInFile'.$button} = $BUTTONS_TEXT{$button}; + } + + #%BUTTONS_TEXT = %NAVIGATION_TEXT; + + %BUTTONS_GOTO = ( + 'Top', $self->gdt('Cover (top) of document'), + 'Contents', $self->gdt('Table of contents'), + 'Overview', $self->gdt('Short table of contents'), + 'Index', $self->gdt('Index'), + 'This', $self->gdt('Current section'), + 'Back', $self->gdt('Previous section in reading order'), + 'FastBack', $self->gdt('Beginning of this chapter or previous chapter'), + 'Prev', $self->gdt('Previous section on same level'), + 'Up', $self->gdt('Up section'), + 'Next', $self->gdt('Next section on same level'), + 'NodeUp', $self->gdt('Up node'), + 'NodeNext', $self->gdt('Next node'), + 'NodePrev', $self->gdt('Previous node'), + 'NodeForward', $self->gdt('Next node in node reading order'), + 'NodeBack', $self->gdt('Previous node in node reading order'), + 'Forward', $self->gdt('Next section in reading order'), + 'FastForward', $self->gdt('Next chapter'), + 'About' , $self->gdt('About (help)'), + 'First', $self->gdt('First section in reading order'), + 'Last', $self->gdt('Last section in reading order'), + 'NextFile', $self->gdt('Forward section in next file'), + 'PrevFile', $self->gdt('Back section in previous file'), + ); + + foreach my $button (grep {not exists($global_and_special_directions{$_}) and $_ ne ' '} keys %BUTTONS_GOTO) { + $BUTTONS_GOTO{'FirstInFile'.$button} = $BUTTONS_GOTO{$button}; + } + + %BUTTONS_NAME = ( + 'Top', $self->gdt('Top'), + 'Contents', $self->gdt('Contents'), + 'Overview', $self->gdt('Overview'), + 'Index', $self->gdt('Index'), + ' ', ' ', + 'This', $self->gdt('This'), + 'Back', $self->gdt('Back'), + 'FastBack', $self->gdt('FastBack'), + 'Prev', $self->gdt('Prev'), + 'Up', $self->gdt('Up'), + 'Next', $self->gdt('Next'), + 'NodeUp', $self->gdt('NodeUp'), + 'NodeNext', $self->gdt('NodeNext'), + 'NodePrev', $self->gdt('NodePrev'), + 'NodeForward', $self->gdt('NodeForward'), + 'NodeBack', $self->gdt('NodeBack'), + 'Forward', $self->gdt('Forward'), + 'FastForward', $self->gdt('FastForward'), + 'About', $self->gdt('About'), + 'First', $self->gdt('First'), + 'Last', $self->gdt('Last'), + 'NextFile', $self->gdt('NextFile'), + 'PrevFile', $self->gdt('PrevFile'), + ); + + foreach my $button (grep {not exists($global_and_special_directions{$_}) and $_ ne ' '} keys %BUTTONS_NAME) { + $BUTTONS_NAME{'FirstInFile'.$button} = $BUTTONS_NAME{$button}; + } + + %SPECIAL_ELEMENTS_NAME = ( + 'About' => $self->gdt('About This Document'), + 'Contents' => $self->gdt('Table of Contents'), + 'Overview' => $self->gdt('Short Table of Contents'), + 'Footnotes' => $self->gdt('Footnotes'), + ); + + # delete the tree and formatted results for special elements + # such that they are redone with the new tree when needed. + foreach my $special_element (keys (%SPECIAL_ELEMENTS_NAME)) { + if ($self->{'special_elements_types'}->{$special_element} and + $self->{'targets'}->{$self->{'special_elements_types'}->{$special_element}}) { + my $target + = $self->{'targets'}->{$self->{'special_elements_types'}->{$special_element}}; + foreach my $key ('text', 'string', 'tree') { + delete $target->{$key}; + } + } + } + + foreach my $hash (\%BUTTONS_TEXT, \%BUTTONS_GOTO, \%BUTTONS_NAME) { + foreach my $button (keys (%$hash)) { + if (ref($hash->{$button})) { + $hash->{$button} = $self->convert_tree_new_formatting_context( + $hash->{$button}, "button $button"); + } + } + } + if ($self->{'commands_translation'}) { + my %translated_commands; + foreach my $context ('normal', 'preformatted', 'string') { + foreach my $command (keys(%{$self->{'commands_translation'}->{$context}})) { + $translated_commands{$command} = 1; + delete $self->{'commands_formatting'}->{$context}->{$command}; + if (defined($self->{'commands_translation'}->{$context}->{$command})) { + $self->{'commands_formatting'}->{$context}->{$command} + = $self->gdt($self->{'commands_translation'}->{$context}->{$command}, + undef, 'translated_text'); + } + } + } + foreach my $command(keys(%translated_commands)) { + $self->_complete_commands_formatting($command); + } + } +} + + sub converter_defaults($$) { my $self = shift; @@ -1097,6 +1166,15 @@ my %css_map = ( 'span.roman' => 'font-family: initial; font-weight: normal', 'span.nolinebreak' => 'white-space: nowrap', 'kbd' => 'font-style: oblique', + + # The anchor element is wrapped in a <span> rather than a block level + # element to avoid it appearing unless the mouse pointer is directly + # over the text, as it is annoying for anchors to flicker when + # you are moving your pointer elsewhere. "line-height: 0em" stops the + # invisible text from changing vertical spacing. + 'a.copiable-anchor' => 'visibility: hidden; ' + .'text-decoration: none; line-height: 0em', + 'span:hover a.copiable-anchor' => 'visibility: visible', ); $css_map{'pre.format'} = $css_map{'pre.display'}; @@ -1106,7 +1184,12 @@ $preformatted_commands_context{'verbatim'} = 1; my %pre_class_commands; foreach my $preformatted_command (keys(%preformatted_commands_context)) { - $pre_class_commands{$preformatted_command} = $preformatted_command; + # no class for the @small* variants + if ($small_alias{$preformatted_command}) { + $pre_class_commands{$preformatted_command} = $small_alias{$preformatted_command}; + } else { + $pre_class_commands{$preformatted_command} = $preformatted_command; + } } $pre_class_commands{'menu'} = 'menu-preformatted'; $pre_class_types{'menu_comment'} = 'menu-comment'; @@ -1118,10 +1201,13 @@ foreach my $indented_format ('example', 'display', 'lisp') { $css_map{"div.$indented_format"} = 'margin-left: 3.2em'; } +delete $css_map{"div.lisp"}; # output as div.example instead $css_map{"blockquote.indentedblock"} = 'margin-right: 0em'; -# types that are in code style in the default case +# types that are in code style in the default case. '_code' is not +# a type that can appear in the tree built from Texinfo code, it is used +# to format a tree fragment as if it was in a @code @-command. my %default_code_types = ( '_code' => 1, ); @@ -1154,8 +1240,11 @@ foreach my $explained_command (keys(%explained_commands)) { = [['normal'], ['string']]; } -# Default for the function references used for the formatting -# of commands. +# Return the default for the function references used for +# the formatting of commands, in case a user still wants to call +# default @-commands formatting functions when replacing functions, +# using code along +# &{$self->default_commands_conversion($cmdname)}($self, $cmdname, $command, $content) my %default_commands_conversion; sub default_commands_conversion($$) @@ -1187,27 +1276,18 @@ sub converter_global_commands($) return @informative_global_commands; } -my %contents_commands = ( - 'contents' => 1, - 'shortcontents' => 1, - 'summarycontents' => 1, -); - -#my %ignored_misc_commands; foreach my $misc_command (keys(%misc_commands)) { -# $ignored_misc_commands{$misc_command} = 1 $default_commands_conversion{$misc_command} = undef unless ($kept_misc_commands{$misc_command}); } foreach my $ignored_brace_commands ('caption', 'shortcaption', 'hyphenation', 'sortas') { - #$ignored_commands{$ignored_brace_commands} = 1; $default_commands_conversion{$ignored_brace_commands} = undef; } # commands that leads to advancing the paragraph number. This is mostly -#used to determine the first line, in fact. +# used to determine the first line, in fact. my %advance_paragraph_count_commands; foreach my $command (keys(%block_commands)) { next if ($menu_commands{$command} @@ -1217,14 +1297,16 @@ foreach my $command (keys(%block_commands)) { foreach my $ignored_block_commands ('ignore', 'macro', 'rmacro', 'copying', 'documentdescription', 'titlepage', 'direntry') { - #$ignored_commands{$ignored_block_commands} = 1; $default_commands_conversion{$ignored_block_commands} = undef; }; # Formatting of commands without args # The hash holding the defaults for the formatting of -# most commands without args +# most commands without args. It has three contexts as keys, +# 'normal' in normal text, 'preformatted' in @example and similar +# commands, and 'string' for contexts where HTML elements should not +# be used. my %default_commands_formatting; foreach my $command (keys(%{$Texinfo::Convert::Converter::default_xml_commands_formatting{'normal'}})) { @@ -1246,13 +1328,6 @@ my %default_commands_translation; # $not_existing->gdt('error-->'); #} -#foreach my $command (keys(%{$default_commands_formatting{'normal'}})) { -# $default_commands_formatting{'preformatted'}->{$command} = -# $default_commands_formatting{'normal'}->{$command}; -# $default_commands_formatting{'string'}->{$command} = -# $default_commands_formatting{'normal'}->{$command}; -#} - $default_commands_formatting{'normal'}->{'enddots'} = '<small class="enddots">...</small>'; $default_commands_formatting{'preformatted'}->{'enddots'} = '...'; @@ -1346,7 +1421,6 @@ $style_attribute_commands{'normal'} = { 't' => 'tt', 'var' => 'var', 'verb' => 'tt', - 'math' => 'em', }; my %style_commands_formatting; @@ -1674,7 +1748,7 @@ sub _convert_footnote_command($$$$) } $foot_lines .= '<h5>' . - "<a id=\"$footid\" href=\"$document_filename#$docid\">($number_in_doc)</a></h3>\n" + "<a id=\"$footid\" href=\"$document_filename#$docid\">($number_in_doc)</a></h5>\n" . $footnote_text; my $footnote_number_text; @@ -1743,7 +1817,7 @@ sub _convert_image_command($$$$) } if (!defined($image_file) or $image_file eq '') { if (defined($extension) and $extension ne '') { - $image_file = "$basefile.$extension"; + $image_file = $basefile.$extension; } else { $image_file = "$basefile.jpg"; } @@ -1755,42 +1829,43 @@ sub _convert_image_command($$$$) if (defined($self->get_conf('IMAGE_LINK_PREFIX'))) { $image_file = $self->get_conf('IMAGE_LINK_PREFIX') . $image_file; } - if ($self->in_preformatted()) { - my $alt_text; - if (defined($args->[3]) and defined($args->[3]->{'normal'})) { - $alt_text = $args->[3]->{'normal'}; - } - if (!defined($alt_text) or ($alt_text eq '')) { - $alt_text = $self->protect_text($basefile); - } - return "[ $alt_text ]"; - } else { - my $alt_string; - if (defined($args->[3]) and defined($args->[3]->{'string'})) { - $alt_string = $args->[3]->{'string'}; - } - if (!defined($alt_string) or ($alt_string eq '')) { - $alt_string = $self->protect_text($basefile); - } - return "<img src=\"".$self->protect_text($image_file)."\" alt=\"$alt_string\">"; + my $alt_string; + if (defined($args->[3]) and defined($args->[3]->{'string'})) { + $alt_string = $args->[3]->{'string'}; } + if (!defined($alt_string) or ($alt_string eq '')) { + $alt_string = $self->protect_text($basefile); + } + return "<img src=\"".$self->protect_text($image_file)."\" alt=\"$alt_string\">"; } return ''; } $default_commands_conversion{'image'} = \&_convert_image_command; -#sub _convert_math_command($$$$) -#{ -# my $self = shift; -# my $cmdname = shift; -# my $command = shift; -# my $args = shift; -# -# return $args->[0]->{'normal'}; -#} +sub _convert_math_command($$$$) +{ + my $self = shift; + my $cmdname = shift; + my $command = shift; + my $args = shift; -#$default_commands_conversion{'math'} = \&_convert_math_command; + 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 "<em class=\'tex2jax_process\'>\\($arg\\)</em>"; + } + } + return "<em class=\'math\'>$arg</em>"; +} + +$default_commands_conversion{'math'} = \&_convert_math_command; sub _convert_accent_command($$$$) { @@ -1898,7 +1973,7 @@ sub _convert_U_command($$$$) } $default_commands_conversion{'U'} = \&_convert_U_command; -sub _default_comment($$) { +sub _default_format_comment($$) { my $self = shift; my $text = shift; return $self->xml_comment(' '.$text); @@ -1910,7 +1985,7 @@ sub protect_text($$) { return &{$self->{'format_protect_text'}}($self, $text); } -sub _default_protect_text($$) { +sub _default_format_protect_text($$) { my $self = shift; my $text = shift; my $result = $self->xml_protect_text($text); @@ -1918,7 +1993,7 @@ sub _default_protect_text($$) { return $result; } -sub _default_heading_text($$$$$) +sub _default_format_heading_text($$$$$) { my $self = shift; my $cmdname = shift; @@ -1959,36 +2034,82 @@ sub _default_heading_text($$$$$) return $result; } -# Associated to a button -sub _default_node_direction($$) +# 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 = $self->_element_direction($self->{'current_element'}, - $direction, 'node'); + 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 = $self->_direction_href_attributes($direction); + my $anchor_attributes = $omit_rel ? '' + : $self->_direction_href_attributes($direction); $anchor = "<a href=\"$href\"${anchor_attributes}>$node</a>"; - #} elsif (defined($node) and $node =~ /\S/) { - # $anchor = $node; - #} else { } if (defined($anchor)) { # i18n $result = $self->get_conf('BUTTONS_TEXT')->{$direction}.": $anchor"; } - return $result; + # 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_button_icon_img($$$;$) +sub _default_format_button_icon_img($$$;$) { my $self = shift; my $button = shift; @@ -2032,20 +2153,26 @@ sub _direction_href_attributes($$) return $href_attributes; } -sub _default_button_formatting($$) +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); + my ($active, $passive, $need_delimiter); if (ref($button) eq 'CODE') { - $active = &$button($self); + ($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]; - # verify that $button_href is simple text and text is a reference + # $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 @@ -2057,11 +2184,12 @@ sub _default_button_formatting($$) } else { $passive = $$text; } - # button_href is simple text and text is a reference on code + $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 = &$text($self, $button_href); - # button_href is simple text and text is also a simple text + ($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*//) { @@ -2079,6 +2207,7 @@ sub _default_button_formatting($$) $passive = $text_formatted; } } + $need_delimiter = 1; } } elsif ($button eq ' ') { # handle space button @@ -2091,6 +2220,7 @@ sub _default_button_formatting($$) } else { $active = $self->get_conf('BUTTONS_TEXT')->{$button}; } + $need_delimiter = 0; } else { my $href = $self->_element_direction($self->{'current_element'}, $button, 'href'); @@ -2152,16 +2282,26 @@ sub _default_button_formatting($$) $passive = '[' . $self->get_conf('BUTTONS_TEXT')->{$button} . ']'; } } + $need_delimiter = 0; } - return ($active, $passive); -} - -my %html_default_node_directions; -foreach my $node_directions ('NodeNext', 'NodePrev', 'NodeUp') { - $html_default_node_directions{$node_directions} = 1; + # 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_navigation_header_panel($$$$;$) +sub _default_format_navigation_header_panel($$$$;$) { my $self = shift; my $buttons = shift; @@ -2195,7 +2335,7 @@ sub _default_navigation_header_panel($$$$;$) $direction = $button; } - my ($active, $passive) = &{$self->{'format_button'}}($self, $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); @@ -2208,8 +2348,7 @@ sub _default_navigation_header_panel($$$$;$) $result .= "</tr>\n" if $vertical; } elsif (defined($active)) { # only active buttons are print out when not in table - if (defined($direction) - and $html_default_node_directions{$direction} and !$first_button) { + if ($need_delimiter and !$first_button) { $active = ', ' .$active; } $result .= $active; @@ -2225,7 +2364,7 @@ sub _default_navigation_header_panel($$$$;$) return $result; } -sub _default_navigation_header($$$$) +sub _default_format_navigation_header($$$$) { my $self = shift; my $buttons = shift; @@ -2252,7 +2391,7 @@ sub _default_navigation_header($$$$) return $result; } -sub _default_element_header($$$$) +sub _default_format_element_header($$$$) { my $self = shift; my $cmdname = shift; @@ -2315,6 +2454,34 @@ sub _default_element_header($$$$) 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; @@ -2333,8 +2500,30 @@ sub _convert_heading_command($$$$$) } my $element_id = $self->command_id($command); - $result .= "<span id=\"$element_id\"></span>" - if (defined($element_id) and $element_id ne ''); + 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, "</div>\n"); + + $result .= '<div class="' . $section->{'cmdname'} . '"'; + + $result .= " id=\"$element_id\"" + if (defined($element_id) and $element_id ne ''); + $result .= ">\n"; + } else { + $result .= "<span id=\"$element_id\"></span>" + if (defined($element_id) and $element_id ne ''); + } print STDERR "Process $command " .Texinfo::Structuring::_print_root_command_texi($command)."\n" @@ -2368,6 +2557,9 @@ sub _convert_heading_command($$$$$) } 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); } @@ -2401,6 +2593,32 @@ sub _convert_heading_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; } @@ -2484,9 +2702,34 @@ sub _convert_preformatted_command($$$$) 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()) { @@ -2497,7 +2740,7 @@ sub _convert_preformatted_command($$$$) return $content."\n"; } } else { - return $self->_attribute_class('div', $cmdname).">\n".$content.'</div>'."\n"; + return $self->_attribute_class('div', $main_cmdname, $extra_classes).">\n".$content.'</div>'."\n"; } } else { return $content; @@ -2516,6 +2759,10 @@ sub _convert_indented_command($$$$) 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); @@ -2547,6 +2794,33 @@ sub _convert_verbatim_command($$$$) $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\\]".'</em>'; + } else { + $result .= $self->_attribute_class('em').'>'."$content".'</em>'; + } + $result .= '</div>'; + return $result; +} + +$default_commands_conversion{'displaymath'} = \&_convert_displaymath_command; + sub _convert_verbatiminclude_command($$$$) { my $self = shift; @@ -2890,6 +3164,9 @@ sub _convert_quotation_command($$$$$) my $args = shift; my $content = shift; + #$cmdname = $small_alias{$cmdname} + # if $small_alias{$cmdname}; + my $attribution = ''; if ($command->{'extra'} and $command->{'extra'}->{'authors'}) { foreach my $author (@{$command->{'extra'}->{'authors'}}) { @@ -3070,11 +3347,16 @@ sub _convert_item_command($$$$) } } my $index_id = $self->command_id ($command); - if (defined($index_id) and $index_id ne '') { - $result .= "\n<span id=\"$index_id\"></span>\n"; + my $anchor; + if (defined($index_id)) { + $anchor = $self->_get_copiable_anchor($index_id); + $index_id = " id='$index_id'"; + } else { + $anchor = ''; + $index_id = ''; } - return '<dt>' .$result. '</dt>' . "\n"; + return "<dt${index_id}><span>$result$anchor</span></dt>\n"; } else { return ''; } @@ -3209,21 +3491,11 @@ sub _convert_xref_commands($$$$) and $command->{'cmdname'} ne 'anchor' and $command->{'cmdname'} ne 'float'); if ($cmdname eq 'pxref') { - if ($is_section) { - $tree = $self->gdt('see section {reference_name}', - { 'reference_name' => {'type' => '_converted', 'text' => $reference} }); - } else { - $tree = $self->gdt('see {reference_name}', - { 'reference_name' => {'type' => '_converted', 'text' => $reference} }); - } + $tree = $self->gdt('see {reference_name}', + { 'reference_name' => {'type' => '_converted', 'text' => $reference} }); } elsif ($cmdname eq 'xref' or $cmdname eq 'inforef') { - if ($is_section) { - $tree = $self->gdt('See section {reference_name}', - { 'reference_name' => {'type' => '_converted', 'text' => $reference} }); - } else { - $tree = $self->gdt('See {reference_name}', - { 'reference_name' => {'type' => '_converted', 'text' => $reference} }); - } + $tree = $self->gdt('See {reference_name}', + { 'reference_name' => {'type' => '_converted', 'text' => $reference} }); } elsif ($cmdname eq 'ref') { $tree = $self->gdt('{reference_name}', { 'reference_name' => {'type' => '_converted', 'text' => $reference} }); @@ -3279,10 +3551,16 @@ sub _convert_xref_commands($$$$) my $reference = $name; my $book_reference = ''; if (!$self->in_string() and $href ne '') { + # attribute to distiguish links to Texinfo manuals from other links + # and to provide manual name of target + my $attribute = ''; + if ($file) { + $attribute = "data-manual=\"".$self->protect_text($file)."\" "; + } if ($name ne '') { - $reference = "<a href=\"$href\">$name</a>"; + $reference = "<a ${attribute}href=\"$href\">$name</a>"; } elsif ($book ne '') { - $book_reference = "<a href=\"$href\">$book</a>"; + $book_reference = "<a ${attribute}href=\"$href\">$book</a>"; } } if ($cmdname eq 'pxref') { @@ -3560,15 +3838,15 @@ sub _contents_inline_element($$$) my $content = &{$self->{'format_contents'}}($self, $cmdname, $command); if ($content) { - my $result = ''; my $element_name = $contents_command_element_name{$cmdname}; my $special_element = $self->special_element($element_name); my $heading; + my $result = "<div class=\"${element_name}_element\""; if ($special_element) { my $id = $self->command_id($special_element); if ($id ne '') { - $result .= "<span id=\"$id\"></span>\n"; + $result .= " id=\"$id\""; } $heading = $self->command_text($special_element); } else { @@ -3577,10 +3855,11 @@ sub _contents_inline_element($$$) $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"; + $result .= $content . "</div>\n"; return $result; } return ''; @@ -3596,7 +3875,7 @@ sub _convert_informative_command($$$$) $cmdname = 'shortcontents' if ($cmdname eq 'summarycontents'); $self->_informative_command($command); - if ($self->get_conf('INLINE_CONTENTS') + 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'} @@ -3614,9 +3893,17 @@ foreach my $informative_command (@informative_global_commands) { = \&_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. +# Texinfo::Config::texinfo_types_conversion, setup by +# Texinfo::Config::texinfo_register_type_formatting() my %default_types_conversion; sub default_types_conversion($$) @@ -4075,12 +4362,19 @@ sub _convert_def_line_type($$$$) } else { $name = ''; } - my $category; - if ($command->{'extra'}->{'def_parsed_hash'}->{'category'}) { - $category = $command->{'extra'}->{'def_parsed_hash'}->{'category'}; + my $category = $command->{'extra'}->{'def_parsed_hash'}->{'category'}; + my $category_result = ''; + my $category_tree; + if ($category) { + $category_tree + = {'type' => '_code', + 'contents'=>[$self->gdt("{category}: ", {'category' => $category})] + }; + # NB perhaps the category shouldn't be in_code. } else { $category = ''; } + # no type if ($command_name eq 'deffn' or $command_name eq 'defvr' or $command_name eq 'deftp' @@ -4094,16 +4388,15 @@ sub _convert_def_line_type($$$$) or ($command_name eq 'deftypecv' and !$command->{'extra'}->{'def_parsed_hash'}->{'type'})) and !$command->{'extra'}->{'def_parsed_hash'}->{'class'})) { + $category_result = $self->convert_tree($category_tree); if ($arguments) { - $tree = $self->gdt("{category}: \@strong{{name}} \@emph{{arguments}}", { - 'category' => $category, + $tree = $self->gdt("\@strong{{name}} \@emph{{arguments}}", { 'name' => $name, 'arguments' => $arguments}); } else { - $tree = $self->gdt("{category}: \@strong{{name}}", { - 'category' => $category, - 'name' => $name}); + $tree = $self->gdt("\@strong{{name}}", {'name' => $name}); } + # with a type } elsif ($command_name eq 'deftypefn' or $command_name eq 'deftypevr' or (($command_name eq 'deftypeop' @@ -4111,32 +4404,40 @@ sub _convert_def_line_type($$$$) and !$command->{'extra'}->{'def_parsed_hash'}->{'class'})) { if ($arguments) { my $strings = { - 'category' => $category, 'name' => $name, 'type' => $command->{'extra'}->{'def_parsed_hash'}->{'type'}, 'arguments' => $arguments}; if ($self->get_conf('deftypefnnewline') eq 'on') { + $category_tree + = {'type' => '_code', + 'contents' + => [$self->gdt("{category}:\@* ", {'category' => $category})] + }; $tree - = $self->gdt("{category}:\@* \@emph{{type}}\@* \@strong{{name}} \@emph{{arguments}}", + = $self->gdt("\@emph{{type}}\@* \@strong{{name}} \@emph{{arguments}}", $strings); } else { $tree - = $self->gdt("{category}: \@emph{{type}} \@strong{{name}} \@emph{{arguments}}", + = $self->gdt("\@emph{{type}} \@strong{{name}} \@emph{{arguments}}", $strings); } } else { my $strings = { - 'category' => $category, 'type' => $command->{'extra'}->{'def_parsed_hash'}->{'type'}, 'name' => $name}; if ($self->get_conf('deftypefnnewline') eq 'on') { - $tree = $self->gdt("{category}:\@* \@emph{{type}}\@* \@strong{{name}}", - $strings); + $category_tree + = {'type' => '_code', + 'contents' + => [$self->gdt("{category}:\@* ", {'category' => $category})] + }; } else { - $tree = $self->gdt("{category}: \@emph{{type}} \@strong{{name}}", + $tree = $self->gdt("\@emph{{type}} \@strong{{name}}", $strings); } } + $category_result = $self->convert_tree($category_tree); + # with a class, no type } elsif ($command_name eq 'defcv' or ($command_name eq 'deftypecv' and !$command->{'extra'}->{'def_parsed_hash'}->{'type'})) { @@ -4167,6 +4468,7 @@ sub _convert_def_line_type($$$$) 'class' => $command->{'extra'}->{'def_parsed_hash'}->{'class'}, 'name' => $name}); } + # with a class and a type } elsif ($command_name eq 'deftypeop') { if ($arguments) { my $strings = { @@ -4235,8 +4537,14 @@ sub _convert_def_line_type($$$$) } } - return "<dt$index_label>".$self->convert_tree({'type' => '_code', - 'contents' => [$tree]}) . "</dt>\n"; + if ($category_result ne '') { + $category_result = $self->_attribute_class('span', 'category') + .">$category_result</span>"; + } + my $anchor = $self->_get_copiable_anchor($index_id); + return "<dt$index_label>".$category_result + ."<span>".$self->convert_tree({'type' => '_code', + 'contents' => [$tree]}) . "$anchor</span></dt>\n"; } else { my $category_prepared = ''; if ($command->{'extra'} and $command->{'extra'}->{'def_parsed_hash'} @@ -4278,6 +4586,15 @@ sub _convert_def_line_type($$$$) } } +sub _get_copiable_anchor { + my ($self, $id) = @_; + my $result = ''; + if ($id and $self->get_conf('COPIABLE_ANCHORS')) { + $result = "<a href='#$id' class='copiable-anchor'> ¶</a>"; + } + return $result; +} + $default_types_conversion{'def_line'} = \&_convert_def_line_type; sub _convert_def_item_type($$$$) @@ -4307,9 +4624,8 @@ sub _convert_def_command($$$$) { my $content = shift; return $content if ($self->in_string()); - #print STDERR "IIII $self $cmdname command $command args $args content $content\n"; if (!$self->get_conf('DEF_TABLE')) { - return "<dl>\n". $content ."</dl>\n"; + return $self->_attribute_class('dl', 'def').">\n". $content ."</dl>\n"; } else { return "<table width=\"100%\">\n" . $content . "</table>\n"; } @@ -4362,8 +4678,29 @@ sub _convert_root_text_type($$$$) $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_titlepage($) +sub _default_format_titlepage($) { my $self = shift; @@ -4381,6 +4718,7 @@ sub _default_titlepage($) my $result = ''; $result .= $titlepage_text.$self->get_conf('DEFAULT_RULE')."\n" if (defined($titlepage_text)); + $result .= $self->_contents_shortcontents_in_title(); return $result; } @@ -4400,6 +4738,7 @@ sub _print_title($) 0, {'cmdname' => 'settitle', 'contents' => $self->{'simpletitle_tree'}->{'contents'}}); } + $result .= $self->_contents_shortcontents_in_title(); } } return $result; @@ -4429,16 +4768,18 @@ sub _convert_element_type($$$$) 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 .= "<div class=\"${special_element}_element\""; if ($id ne '') { - $result .= "<span id=\"$id\"></span>\n"; + $result .= " id=\"$id\""; } + $result .= ">\n"; if ($self->get_conf('HEADERS') # first in page or $self->{'counter_in_file'}->{$element->{'filename'}} == 1) { $result .= &{$self->{'format_navigation_header'}}($self, $self->get_conf('MISC_BUTTONS'), undef, $element); - } my $heading = $self->command_text($element); my $element_name = $element->{'extra'}->{'special_element'}; @@ -4458,13 +4799,16 @@ sub _convert_element_type($$$$) if ($special_element_body eq '') { return ''; } - $result .= $special_element_body; + $result .= $special_element_body . '</div>'; } elsif (!$element->{'element_prev'}) { $result .= $self->_print_title(); if (!$element->{'element_next'}) { # only one element - my $foot_text = &{$self->{'format_footnotes_text'}}($self); - return $result.$content.$foot_text.$self->get_conf('DEFAULT_RULE')."\n"; + $result .= $content; + $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); @@ -4473,7 +4817,7 @@ sub _convert_element_type($$$$) return $result; } -sub _default_element_footer($$$$) +sub _default_format_element_footer($$$$) { my $self = shift; my $type = shift; @@ -4486,18 +4830,12 @@ sub _default_element_footer($$$$) and $self->element_is_top($element->{'element_next'})); my $next_is_special = (defined($element->{'element_next'}) and $element->{'element_next'}->{'extra'}->{'special_element'}); - # no 'parent' defined happens if there are no pages, and there are elements - # which should only happen when called with $self->{'output_file'} - # set to ''. - #print STDERR "$element $element->{'filename'} $self->{'file_counters'}->{$element->{'filename'}}\n"; - #print STDERR "next: $element->{'element_next'}->{'filename'}\n" if ($element->{'element_next'}); + my $end_page = (!$element->{'element_next'} or (defined($element->{'filename'}) and $element->{'filename'} ne $element->{'element_next'}->{'filename'} and $self->{'file_counters'}->{$element->{'filename'}} == 1)); - #my $end_page = (!$element->{'element_next'} - # or (defined($element->{'parent'}) - # and $element->{'parent'} ne $element->{'element_next'}->{'parent'})); + my $is_special = $element->{'extra'}->{'special_element'}; if (($end_page or $next_is_top or $next_is_special or $is_top) @@ -4511,38 +4849,48 @@ sub _default_element_footer($$$$) my $rule = ''; my $buttons; - my $maybe_in_page; - if (($is_top or $is_special) - and ($self->get_conf('SPLIT') or !$self->get_conf('MONOLITHIC')) - and ($end_page - and ($self->get_conf('HEADERS') - or ($self->get_conf('SPLIT') and $self->get_conf('SPLIT') ne 'node')))) { - if ($is_top) { - $buttons = $self->get_conf('TOP_BUTTONS'); - } else { - $buttons = $self->get_conf('MISC_BUTTONS'); - } - } elsif ($end_page and $self->get_conf('SPLIT') eq 'section') { - $buttons = $self->get_conf('SECTION_FOOTER_BUTTONS'); - } elsif ($end_page and $self->get_conf('SPLIT') eq 'chapter') { - $buttons = $self->get_conf('CHAPTER_BUTTONS'); - } elsif ($self->get_conf('SPLIT') eq 'node') { - if ($self->get_conf('HEADERS')) { - my $no_footer_word_count; - if ($self->get_conf('WORDS_IN_PAGE')) { - my @cnt = split(/\W*\s+\W*/, $content); - if (scalar(@cnt) < $self->get_conf('WORDS_IN_PAGE')) { - $no_footer_word_count = 1; + + 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); } - $buttons = $self->get_conf('NODE_FOOTER_BUTTONS') - unless ($no_footer_word_count); } - } else { - $maybe_in_page = 1; + } + # 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 ($maybe_in_page or $is_top or $is_special + 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'))) { @@ -4554,18 +4902,7 @@ sub _default_element_footer($$$$) $rule = $self->get_conf('BIG_RULE'); } - # FIXME the following condition is almost a duplication of end_page - # except that the file counter needs not be 1 - if ((!$element->{'element_next'} - or (defined($element->{'filename'}) - and $element->{'filename'} ne $element->{'element_next'}->{'filename'})) - and $self->get_conf('footnotestyle') eq 'end') { - $result .= &{$self->{'format_footnotes_text'}}($self); - } - if (!$self->get_conf('PROGRAM_NAME_IN_FOOTER') - and !$buttons and !$maybe_in_page) { - # no rule in that case - } else { + if ($buttons or !$end_page or $self->get_conf('PROGRAM_NAME_IN_FOOTER')) { $result .= "$rule\n" if ($rule); } if ($buttons) { @@ -4592,28 +4929,27 @@ sub _new_document_context($$) }; } -# Functions accessed with e.g. 'format_heading_text' for 'heading_text'. +# Functions accessed with e.g. 'format_heading_text'. my %default_formatting_references = ( - 'heading_text' => \&_default_heading_text, - 'comment' => \&_default_comment, - 'protect_text' => \&_default_protect_text, - 'css_lines' => \&_default_css_lines, - 'begin_file' => \&_default_begin_file, - 'node_redirection_page' => \&_default_node_redirection_page, - 'end_file' => \&_default_end_file, - 'special_element_body' => \&_default_special_element_body, - 'footnotes_text' => \&_default_footnotes_text, - 'program_string' => \&_default_program_string, - 'titlepage' => \&_default_titlepage, - 'navigation_header' => \&_default_navigation_header, - 'navigation_header_panel' => \&_default_navigation_header_panel, - 'element_header' => \&_default_element_header, - 'element_footer' => \&_default_element_footer, - 'button' => \&_default_button_formatting, - 'button_icon_img' => \&_default_button_icon_img, - 'external_href' => \&_default_external_href, - 'contents' => \&_default_contents, - 'frame_files' => \&_default_frame_files, + '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($$) @@ -4726,16 +5062,16 @@ sub _load_htmlxref_files { # 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'}}; } - my $input_directory = $self->{'info'}->{'input_directory'} - if $self->{'info'}->{'input_directory'}; - if (defined($input_directory) - and $input_directory ne '.' and $input_directory ne '') { - unshift @htmlxref_dirs, $input_directory; - } unshift @htmlxref_dirs, '.'; my @texinfo_htmlxref_files; @@ -4803,7 +5139,7 @@ sub converter_initialize($) $self->{'commands_conversion'}->{$command} = $Texinfo::Config::texinfo_commands_conversion{$command}; } else { - if (!$self->get_conf('SHOW_MENU') + 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} @@ -4886,16 +5222,17 @@ sub converter_initialize($) $self->{'default_formatting_functions'}->{$formatting_reference} = $default_formatting_references{$formatting_reference}; if (defined($Texinfo::Config::texinfo_formatting_references{$formatting_reference})) { - $self->{"format_".$formatting_reference} + $self->{$formatting_reference} = $Texinfo::Config::texinfo_formatting_references{$formatting_reference}; } else { - $self->{"format_".$formatting_reference} + $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' @@ -4928,7 +5265,7 @@ sub _normalized_to_id($) return $id; } -sub _default_css_lines ($) +sub _default_format_css_lines($) { my $self = shift; @@ -5521,7 +5858,8 @@ sub _prepare_special_elements($$) foreach my $cmdname ('contents', 'shortcontents') { my $type = $contents_command_element_name{$cmdname}; if ($self->get_conf($cmdname)) { - if (not $self->get_conf('INLINE_CONTENTS')) { + if ($self->get_conf('CONTENTS_OUTPUT_LOCATION') + eq 'separate_element') { $do_special{$type} = 1; } } @@ -5631,7 +5969,17 @@ sub _prepare_contents_elements($) my $type = $contents_command_element_name{$cmdname}; if ($self->get_conf($cmdname)) { my $default_filename; - if ($self->get_conf('INLINE_CONTENTS')) { + 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) @@ -5722,7 +6070,7 @@ sub _prepare_global_targets($$) if ($self->get_conf('DEBUG')) { print STDERR "GLOBAL DIRECTIONS:\n"; - foreach my $global_direction ('First', 'Last', 'Index', 'Top') { + 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( @@ -5746,8 +6094,8 @@ sub _prepare_index_entries($) my $merged_index_entries = Texinfo::Structuring::merge_indices($index_names); $self->{'index_entries_by_letter'} - = $self->Texinfo::Structuring::sort_indices_by_letter($merged_index_entries, - $index_names); + = 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))) { @@ -5935,18 +6283,24 @@ my %valid_types = ( '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_begin_file. +# points to _default_format_begin_file. # TODO are there other cases? sub _element_direction($$$$;$) { @@ -5982,7 +6336,7 @@ sub _element_direction($$$$;$) 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') { + } 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"); @@ -6011,6 +6365,10 @@ sub _element_direction($$$$;$) $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; @@ -6046,7 +6404,48 @@ sub _element_direction($$$$;$) } } -sub _default_contents($$;$$) +# 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 .= "<li><a${href_attribute}$accesskey>$text</a>"; + } else { + $result .= "<li>$text"; + } + $result .= "</li>\n"; + } + } + $result .= "</ul>\n"; + } + return $result; +} + +sub _default_format_contents($$;$$) { my $self = shift; my $cmdname = shift; @@ -6182,7 +6581,7 @@ sub _default_contents($$;$$) return $result; } -sub _default_program_string($) +sub _default_format_program_string($) { my $self = shift; if (defined($self->get_conf('PROGRAM')) @@ -6198,19 +6597,31 @@ sub _default_program_string($) } } -sub _default_end_file($) +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 = "<p><font size=\"-1\"> + $program_text .= "<p><font size=\"-1\"> $program_string </font></p>"; } my $pre_body_close = $self->get_conf('PRE_BODY_CLOSE'); $pre_body_close = '' if (!defined($pre_body_close)); - return "$program_text + + 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 .= +"<a href='$path' rel='jslicense'><small>" +.$self->convert_tree($self->gdt('JavaScript license information')) +.'</small></a>'; + } + + return "${program_text} $pre_body_close </body> @@ -6232,9 +6643,6 @@ sub _file_header_informations($$) $self->command_text($command, 'string'); if (defined($command_string) and $command_string ne $self->{'title_string'}) { - print STDERR "DO <title>\n" - if ($self->get_conf('DEBUG')); - my $element_tree; if ($self->get_conf('SECTION_NAME_IN_TITLE') and $command->{'extra'} @@ -6285,6 +6693,11 @@ sub _file_header_informations($$) } my $doctype = $self->get_conf('DOCTYPE'); my $bodytext = $self->get_conf('BODYTEXT'); + if ($self->{'element_math'} and $self->get_conf('HTML_MATH')) { + if ($self->get_conf('HTML_MATH') eq 'mathjax') { + $bodytext .= ' class="tex2jax_ignore"'; + } + } my $copying_comment = ''; $copying_comment = $self->{'copying_comment'} if (defined($self->{'copying_comment'})); @@ -6302,6 +6715,52 @@ sub _file_header_informations($$) $generator = "\n<meta name=\"Generator\" content=\"$program\">"; } + if (defined($self->get_conf('INFO_JS_DIR'))) { + if (!$self->get_conf('SPLIT')) { + $self->document_error( + sprintf(__("%s not meaningful for non-split output"), + 'INFO_JS_DIR')); + } else { + my $jsdir = $self->get_conf('INFO_JS_DIR'); + if ($jsdir eq '.') { + $jsdir = ''; + } else { + $jsdir =~ s,/*$,/,; # append a single slash + } + + $extra_head .= +'<link rel="stylesheet" type="text/css" href="'.$jsdir.'info.css"/> +<script src="'.$jsdir.'modernizr.js" type="text/javascript"></script> +<script src="'.$jsdir.'info.js" type="text/javascript"></script>'; + } + for my $key (keys %{$self->{'jslicenses_infojs'}}) { + $self->{'jslicenses_element'}->{$key} = $self->{'jslicenses_infojs'}->{$key}; + } + } + if (($self->{'element_math'} or !$self->get_conf('SPLIT')) + and defined($self->get_conf('HTML_MATH')) + and $self->get_conf('HTML_MATH') eq 'mathjax') { + my $mathjax_script = $self->get_conf('MATHJAX_SCRIPT'); + + $extra_head .= +"<script type='text/javascript'> +MathJax = { + options: { + skipHtmlTags: {'[-]': ['pre']}, + ignoreHtmlClass: 'tex2jax_ignore', + processHtmlClass: 'tex2jax_process' + }, +}; +</script>" +.'<script type="text/javascript" id="MathJax-script" async + src="'.$mathjax_script.'"> +</script>'; + + for my $key (keys %{$self->{'jslicenses_math'}}) { + $self->{'jslicenses_element'}->{$key} = $self->{'jslicenses_math'}->{$key}; + } + } + return ($title, $description, $encoding, $date, $css_lines, $doctype, $bodytext, $copying_comment, $after_body_open, $extra_head, $program_and_version, $program_homepage, @@ -6336,7 +6795,7 @@ sub _get_links ($$$) return $links; } -sub _default_begin_file($$$) +sub _default_format_begin_file($$$) { my $self = shift; my $filename = shift; @@ -6356,15 +6815,17 @@ sub _default_begin_file($$$) my $result = "$doctype <html> -$copying_comment<!-- Created by $program_and_version, $program_homepage --> +<!-- Created by $program_and_version, $program_homepage --> <head> $encoding -<title>$title</title> +$copying_comment<title>$title</title> $description <meta name=\"keywords\" content=\"$title\"> <meta name=\"resource-type\" content=\"document\"> <meta name=\"distribution\" content=\"global\">${generator}$date +<meta name=\"viewport\" content=\"width=device-width,initial-scale=1\"> + ${links}$css_lines $extra_head </head> @@ -6375,7 +6836,7 @@ $after_body_open"; return $result; } -sub _default_node_redirection_page($$) +sub _default_format_node_redirection_page($$) { my $self = shift; my $command = shift; @@ -6393,11 +6854,11 @@ sub _default_node_redirection_page($$) { 'href' => {'type' => '_converted', 'text' => $direction }})); my $result = "$doctype <html> -$copying_comment<!-- Created by $program_and_version, $program_homepage --> +<!-- Created by $program_and_version, $program_homepage --> <!-- This file redirects to the location of a node or anchor --> <head> $encoding -<title>$title</title> +$copying_comment<title>$title</title> $description <meta name=\"keywords\" content=\"$title\"> @@ -6405,6 +6866,7 @@ $description <meta name=\"distribution\" content=\"global\">${generator}$date $css_lines <meta http-equiv=\"Refresh\" content=\"0; url=$href\"> +<meta name=\"viewport\" content=\"width=device-width,initial-scale=1\"> $extra_head </head> @@ -6416,7 +6878,7 @@ $after_body_open return $result; } -sub _default_footnotes_text($) +sub _default_format_footnotes_text($) { my $self = shift; return '' if (!$foot_lines); @@ -6436,7 +6898,7 @@ sub _default_footnotes_text($) return $result; } -sub _default_special_element_body($$$) +sub _default_format_special_element_body($$$) { my $self = shift; my $special_type = shift; @@ -6553,7 +7015,58 @@ EOT } } -sub _default_frame_files($) +sub _do_jslicenses_file { + my $self = shift; + + my $setting = $self->get_conf('JS_WEBLABELS'); + my $path = $self->get_conf('JS_WEBLABELS_FILE'); + + # Possible settings: + # 'generate' - create file at JS_WEBLABELS_FILE + # 'reference' - reference file at JS_WEBLABELS_FILE but do not create it + # 'omit' - do nothing + return if (!$setting or $setting ne 'generate'); + + my $a = ''; + $a .= '<!DOCTYPE html> +<html><head><title>jslicense labels</title></head> +<body> +<table id="jslicense-labels1"> +'; + my $h = $self->{'jslicenses'}; + + for my $file (keys %{$self->{'jslicenses'}}) { + $a .= "<tr>\n"; + $a .= "<td><a href=\"$file\">$file</a></td>\n"; + $a .= "<td><a href=\"$h->{$file}->[1]\">$h->{$file}->[0]</a></td>\n"; + $a .= "<td><a href=\"$h->{$file}->[2]\">$h->{$file}->[2]</a></td>\n"; + $a .= "</tr>\n"; + } + + $a .= "</body></html>\n"; + + if (File::Spec->file_name_is_absolute($path) or $path =~ /^[A-Za-z]*:/) { + $self->document_warn(sprintf( +__("cannot use absolute path or URL `%s' for JS_WEBLABELS_FILE when generating web labels file"), $path)); + return; + } + my $license_file = File::Spec->catdir($self->{'destination_directory'}, + $path); + my $fh = $self->Texinfo::Common::open_out($license_file); + if (defined($fh)) { + print $fh $a; + $self->register_close_file($license_file); + if (!close ($fh)) { + $self->document_error(sprintf(__("error on closing %s: %s"), + $license_file, $!)); + } + } else { + $self->document_error(sprintf(__("could not open %s for writing: %s"), + $license_file, $!)); + } +} + +sub _default_format_frame_files($) { my $self = shift; @@ -6635,6 +7148,17 @@ EOT return 1; } +sub _has_contents_or_shortcontents($) +{ + my $self = shift; + foreach my $cmdname ('contents', 'shortcontents') { + if ($self->{'extra'} and $self->{'extra'}->{$cmdname}) { + return 1; + } + } + return 0; +} + sub convert($$) { my $self = shift; @@ -6738,7 +7262,7 @@ sub run_stage_handlers($$$) #if ($converter->get_conf('VERBOSE')) { # print STDERR "Handler $handler of $stage($priority) failed\n"; #} - $converter->document_error(sprintf($converter->__( + $converter->document_error(sprintf(__( "handler %s of stage %s priority %s failed"), $handler, $stage, $priority)); return $status; @@ -6753,6 +7277,11 @@ my $default_priority = 'default'; { package Texinfo::Config; +# Note that these variables are available for the Texinfo modules +# but, in general should not be accessed directly by the users who +# customize formatting and should use the associated functions, +# such as texinfo_register_handler(), texinfo_register_formatting_function(), +# texinfo_register_command_formatting() or texinfo_register_type_formatting(). use vars qw(%texinfo_default_stage_handlers %texinfo_formatting_references %texinfo_commands_conversion %texinfo_types_conversion); @@ -6867,8 +7396,6 @@ sub output($$) $special_element->{'filename'} = $elements->[0]->{'filename'}; $special_element->{'out_filename'} = $elements->[0]->{'out_filename'}; $self->{'file_counters'}->{$special_element->{'filename'}}++; - print STDERR "Special page $special_element: $special_element->{'filename'}($self->{'file_counters'}->{$special_element->{'filename'}})\n" - if ($self->get_conf('DEBUG')); } } } @@ -6928,7 +7455,6 @@ sub output($$) my $html_title_string; if ($fulltitle) { $self->{'title_tree'} = $fulltitle; - print STDERR "DO fulltitle_string\n" if ($self->get_conf('DEBUG')); $html_title_string = $self->convert_tree_new_formatting_context( {'type' => '_string', 'contents' => [$self->{'title_tree'}]}, 'title_string'); @@ -6948,7 +7474,6 @@ sub output($$) # copying comment if ($self->{'extra'}->{'copying'}) { - print STDERR "DO copying_comment\n" if ($self->get_conf('DEBUG')); my $copying_comment = Texinfo::Convert::Text::convert( {'contents' => $self->{'extra'}->{'copying'}->{'contents'}}, {Texinfo::Common::_convert_text_options($self)}); @@ -6962,7 +7487,6 @@ sub output($$) $self->{'documentdescription_string'} = $self->get_conf('documentdescription'); } elsif ($self->{'extra'}->{'documentdescription'}) { - print STDERR "DO documentdescription\n" if ($self->get_conf('DEBUG')); $self->{'documentdescription_string'} = $self->convert_tree_new_formatting_context( {'type' => '_string', @@ -6979,6 +7503,51 @@ sub output($$) return undef if (!$status); } + if ($self->get_conf('HTML_MATH') + and $self->get_conf('HTML_MATH') eq 'mathjax') { + # See https://www.gnu.org/licenses/javascript-labels.html + # + # The link to the source for mathjax does not strictly follow the advice + # there: instead we link to instructions for obtaining the full source in + # its preferred form of modification. + + my ($mathjax_script, $mathjax_source); + + $mathjax_script = $self->get_conf('MATHJAX_SCRIPT'); + if (!$mathjax_script) { + $mathjax_script = 'https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-svg.js'; + $self->set_conf('MATHJAX_SCRIPT', $mathjax_script); + } + + $mathjax_source = $self->get_conf('MATHJAX_SOURCE'); + if (!$mathjax_source) { + $mathjax_source = 'http://docs.mathjax.org/en/latest/web/hosting.html#getting-mathjax-via-git'; + $self->set_conf('MATHJAX_SOURCE', $mathjax_source); + } + + $self->{'jslicenses_math'}->{$mathjax_script} = + [ 'Apache License, Version 2.0.', + 'https://www.apache.org/licenses/LICENSE-2.0', + $mathjax_source ]; + # append to hash + %{$self->{'jslicenses'}} = ( %{$self->{'jslicenses'}}, + %{$self->{'jslicenses_math'}} ); + + } + if ($self->get_conf('INFO_JS_DIR')) { + $self->{'jslicenses_infojs'}->{'js/info.js'} = + [ 'GNU General Public License 3.0 or later', + 'http://www.gnu.org/licenses/gpl-3.0.html', + 'js/info.js' ]; + + $self->{'jslicenses_infojs'}->{'js/modernizr.js'} = + [ 'Expat', + 'http://www.jclark.com/xml/copying.txt', + 'js/modernizr.js' ]; + %{$self->{'jslicenses'}} = ( %{$self->{'jslicenses'}}, + %{$self->{'jslicenses_infojs'}} ); + } + # FIXME here call _unset_global_multiple_commands? Problem is # that some conversion, for instance for page header requires # that the correct language is set, for instance. The @-command @@ -7002,32 +7571,32 @@ sub output($$) } else { $outfile = $self->{'output_file'}; } - print STDERR "DO No pages, output in $outfile\n" - if ($self->get_conf('DEBUG')); $fh = $self->Texinfo::Common::open_out($outfile); if (!$fh) { $self->document_error(sprintf(__("could not open %s for writing: %s"), $outfile, $!)); return undef; } - } else { - print STDERR "DO No pages, string output\n" - if ($self->get_conf('DEBUG')); } $self->{'current_filename'} = $self->{'output_filename'}; - my $header = &{$self->{'format_begin_file'}}($self, - $self->{'output_filename'}, undef); - $output .= $self->_output_text($header, $fh); + + my $body = ''; if ($elements and @$elements) { foreach my $element (@$elements) { my $element_text = $self->_convert($element); - $output .= $self->_output_text($element_text, $fh); + $body .= $element_text; } } else { - $output .= $self->_output_text($self->_print_title(), $fh); - $output .= $self->_output_text($self->_convert($root), $fh); + $body .= $self->_print_title(); + $body .= $self->_convert($root); } + + my $header = &{$self->{'format_begin_file'}}($self, + $self->{'output_filename'}, undef); + $output .= $self->_output_text($header, $fh); + $output .= $self->_output_text($body, $fh); $output .= $self->_output_text(&{$self->{'format_end_file'}}($self), $fh); + # NOTE do not close STDOUT now to avoid a perl warning. if ($fh and $outfile ne '-') { $self->register_close_file($outfile); @@ -7049,19 +7618,30 @@ sub output($$) my $file_fh; $self->{'current_filename'} = $element->{'filename'}; $self->{'counter_in_file'}->{$element->{'filename'}}++; - #print STDERR "TTTTTTT($element) $element->{'filename'}: $self->{'file_counters'}->{$element->{'filename'}} (out_filename $element->{'out_filename'})\n"; + if ($self->{'counter_in_file'}->{$element->{'filename'}} == 1) { + $self->{'jslicenses_element'} = {}; + $self->{'element_math'} = 0; + } + # First do the special pages, to avoid outputting these if they are # empty. my $special_element_content; if ($element->{'extra'} and $element->{'extra'}->{'special_element'}) { $special_element_content .= $self->_convert($element); - #print STDERR "Special element converter: $element->{'extra'}->{'special_element'}\n"; if ($special_element_content eq '') { $self->{'file_counters'}->{$element->{'filename'}}--; next ; } } - # Then open the file and output the elements or the special_page_content + + # convert body before header in case this affects the header + my $body = ''; + if (defined($special_element_content)) { + $body = $special_element_content; + } else { + $body = $self->_convert($element); + } + if (!$files{$element->{'filename'}}->{'fh'}) { $file_fh = $self->Texinfo::Common::open_out($element->{'out_filename'}); if (!$file_fh) { @@ -7076,12 +7656,7 @@ sub output($$) } else { $file_fh = $files{$element->{'filename'}}->{'fh'}; } - if (defined($special_element_content)) { - print $file_fh $special_element_content; - } else { - my $element_text = $self->_convert($element); - print $file_fh $element_text; - } + print $file_fh $body; $self->{'file_counters'}->{$element->{'filename'}}--; if ($self->{'file_counters'}->{$element->{'filename'}} == 0) { # end file @@ -7098,6 +7673,40 @@ sub output($$) } } } + if ($self->get_conf('INFO_JS_DIR')) { + my $jsdir = File::Spec->catdir($self->{'destination_directory'}, + $self->get_conf('INFO_JS_DIR')); + if (!-d $jsdir) { + if (-f $jsdir) { + $self->document_error( + sprintf(__("%s already exists but is not a directory"), $jsdir)); + } else { + mkdir $jsdir; + } + } + if (-d $jsdir) { + my $jssrcdir; + if (!$Texinfo::ModulePath::texinfo_uninstalled) { + $jssrcdir = File::Spec->catdir( + $Texinfo::ModulePath::lib_dir, 'js'); + } else { + $jssrcdir = File::Spec->catdir( + $Texinfo::ModulePath::top_srcdir, 'js'); + } + for my $f ('info.js', 'modernizr.js', 'info.css') { + my $from = File::Spec->catfile($jssrcdir, $f); + + if (!copy($from, $jsdir)) { + $self->document_error( + sprintf(__("error on copying %s into %s"), $from, $jsdir)); + } + } + } + } + } + + if (%{$self->{'jslicenses'}}) { + $self->_do_jslicenses_file(); } my $finish_status = $self->run_stage_handlers($root, 'finish'); @@ -7188,11 +7797,24 @@ sub _convert_contents($$$) return $content_formatted; } -sub _attribute_class($$$) +#my $characters_replaced_from_class_names = quotemeta('[](),~#:/\\@+=!;.,?* '); +# FIXME not clear what character should be allowed and which ones replaced besides space +my $characters_replaced_from_class_names = quotemeta(' '); +sub _protect_class_name($$) +{ + my $self = shift; + my $class_name = shift; + $class_name =~ s/[$characters_replaced_from_class_names]/-/g; + return $self->protect_text($class_name); +} + +# $extra_classes should be an array reference or undef +sub _attribute_class($$$;$) { my $self = shift; my $element = shift; my $class = shift; + my $extra_classes = shift; if (!defined($class) or $class eq '' or $self->get_conf('NO_CSS')) { if ($element eq 'span') { @@ -7208,7 +7830,14 @@ sub _attribute_class($$$) and defined($self->{'css_map'}->{"$element.$class"})) { $style = ' style="'.$self->{'css_map'}->{"$element.$class"}.'"'; } - return "<$element class=\"$class\"$style"; + my $extra_class_str = ''; + if (defined($extra_classes)) { + my $extra_class_conversion = join(' ', map {$self->_protect_class_name($_)} @$extra_classes); + if ($extra_class_conversion ne '') { + $extra_class_str = ' '.$extra_class_conversion; + } + } + return "<$element class=\"$class$extra_class_str\"$style"; } sub _protect_space($$) @@ -7239,12 +7868,6 @@ sub _protect_space($$) # Convert tree element $ROOT, and return HTML text for the output files. sub _convert($$;$); -my %small_alias; -for my $cmd ('example', 'display', 'format', 'lisp', 'quotation', - 'indentedblock') { - $small_alias{'small'.$cmd} = $cmd; -}; - sub _convert($$;$) { my $self = shift; @@ -7331,8 +7954,6 @@ sub _convert($$;$) and $root->{'cmdname'} and $root->{'cmdname'} =~ /index$/) { $command_name = 'cindex'; } - $command_name = $small_alias{$command_name} - if $small_alias{$command_name}; if ($root_commands{$command_name}) { $self->{'current_root_command'} = $root; } @@ -7368,7 +7989,7 @@ sub _convert($$;$) push @{$self->{'document_context'}->[-1]->{'monospace'}}, 0; } elsif ($upper_case_commands{$command_name}) { $self->{'document_context'}->[-1]->{'formatting_context'}->[-1]->{'upper_case'}++; - } elsif ($command_name eq 'math') { + } elsif ($math_commands{$command_name}) { $self->{'document_context'}->[-1]->{'math'}++; } elsif ($command_name eq 'w') { $self->{'document_context'}->[-1]->{'formatting_context'}->[-1]->{'space_protected'}++; @@ -7447,7 +8068,7 @@ sub _convert($$;$) pop @{$self->{'document_context'}->[-1]->{'monospace'}}; } elsif ($upper_case_commands{$command_name}) { $self->{'document_context'}->[-1]->{'formatting_context'}->[-1]->{'upper_case'}--; - } elsif ($command_name eq 'math') { + } elsif ($math_commands{$command_name}) { $self->{'document_context'}->[-1]->{'math'}--; } elsif ($command_name eq 'w') { $self->{'document_context'}->[-1]->{'formatting_context'}->[-1]->{'space_protected'}--; @@ -7488,24 +8109,6 @@ sub _convert($$;$) $result = &{$self->{'commands_conversion'}->{$command_name}}($self, $command_name, $root, $content_formatted); } - # FIXME add a configuration variable to be able turn automatically - # adding menus off? - if ($sectioning_commands{$command_name} - and $self->{'current_node'} - and !$self->{'seenmenus'}->{$self->{'current_node'}}) { - $self->{'seenmenus'}->{$self->{'current_node'}} = 1; - # Generate automatically a menu for this node. - my $menu_text; - my $menu_node = Texinfo::Structuring::section_menu_of_node (undef, - $root->{'extra'}->{'associated_node'}); - if ($menu_node) { - $menu_text = _convert ($self, $menu_node); - if ($menu_text) { - $result .= $menu_text; - $result .= "\n"; - } - } - } return $result; } else { print STDERR "Unknown command `$command_name'\n" @@ -7607,10 +8210,11 @@ sub _convert($$;$) sub _set_variables_texi2html() { my @texi2html_options = ( + ['FORMAT_MENU', 'menu'], ['NO_USE_SETFILENAME', 1], ['USE_SETFILENAME_EXTENSION', 0], ['footnotestyle', 'separate'], - ['INLINE_CONTENTS', 0], + ['CONTENTS_OUTPUT_LOCATION', 'separate_element'], ['FORCE', 1], ['AVOID_MENU_REDUNDANCY', 1], ['USE_ACCESSKEY', 0], @@ -7622,7 +8226,6 @@ sub _set_variables_texi2html() ['USE_NODES', 0], ['USE_NUMERIC_ENTITY', 1], ['SPLIT', ''], - ['SPLIT_INDEX', 100], ['PROGRAM_NAME_IN_FOOTER', 1], ['HEADER_IN_TABLE', 1], ['USE_TITLEPAGE_FOR_TITLE', 1], @@ -7646,8 +8249,11 @@ sub _set_variables_texi2html() ['CHAPTER_BUTTONS', [ 'FastBack', 'FastForward', ' ', ' ', ' ', ' ', ' ', 'Top', 'Contents', 'Index', 'About', ]], - ['SECTION_FOOTER_BUTTONS', [ 'FastBack', 'Back', 'Up', + ['SECTION_FOOTER_BUTTONS', [ 'FastBack', 'FirstInFileBack', 'FirstInFileUp', 'Forward', 'FastForward' ]], + ['CHAPTER_FOOTER_BUTTONS', [ 'FastBack', 'FastForward', ' ', + ' ', ' ', ' ', ' ', + 'Top', 'Contents', 'Index', 'About', ]], ['NODE_FOOTER_BUTTONS', [ 'FastBack', 'Back', 'Up', 'Forward', 'FastForward', ' ', ' ', ' ', ' ', diff --git a/macros/texinfo/texinfo/tp/Texinfo/Convert/IXIN.pm b/macros/texinfo/texinfo/tp/Texinfo/Convert/IXIN.pm index d89c32c896..664705930e 100644 --- a/macros/texinfo/texinfo/tp/Texinfo/Convert/IXIN.pm +++ b/macros/texinfo/texinfo/tp/Texinfo/Convert/IXIN.pm @@ -1,6 +1,6 @@ # IXIN.pm: output tree as IXIN. # -# Copyright 2013, 2014, 2015, 2016, 2017, 2018 Free Software Foundation, Inc. +# Copyright 2013-2020 Free Software Foundation, Inc. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -47,7 +47,7 @@ use vars qw($VERSION @ISA @EXPORT @EXPORT_OK %EXPORT_TAGS); @EXPORT = qw( ); -$VERSION = '6.7'; +$VERSION = '6.8'; my $ixin_version = 1; @@ -616,8 +616,8 @@ sub output_ixin($$) my $merged_index_entries = Texinfo::Structuring::merge_indices($index_names); my $entries - = $self->Texinfo::Structuring::sort_indices($merged_index_entries, - $index_names); + = $self->Texinfo::Structuring::sort_indices($self->{'parser'}, + $merged_index_entries, $index_names); # first do the dts_text as the counts are needed for the dts index foreach my $index_name (sort(keys(%$entries))) { my $dts_text_result = ''; diff --git a/macros/texinfo/texinfo/tp/Texinfo/Convert/IXINSXML.pm b/macros/texinfo/texinfo/tp/Texinfo/Convert/IXINSXML.pm index 3b536db5a8..e2f5135767 100644 --- a/macros/texinfo/texinfo/tp/Texinfo/Convert/IXINSXML.pm +++ b/macros/texinfo/texinfo/tp/Texinfo/Convert/IXINSXML.pm @@ -46,11 +46,11 @@ use vars qw($VERSION @ISA @EXPORT @EXPORT_OK %EXPORT_TAGS); @EXPORT = qw( ); -$VERSION = '6.7'; +$VERSION = '6.8'; my %defaults = ( 'ENABLE_ENCODING' => 0, - 'SHOW_MENU' => 1, + 'FORMAT_MENU' => 'menu', 'EXTENSION' => 'ixin', #'output_perl_encoding' => 'utf8', 'OUTPUT_ENCODING_NAME' => 'utf-8', diff --git a/macros/texinfo/texinfo/tp/Texinfo/Convert/Info.pm b/macros/texinfo/texinfo/tp/Texinfo/Convert/Info.pm index b1ea027c12..af4fbeea5d 100644 --- a/macros/texinfo/texinfo/tp/Texinfo/Convert/Info.pm +++ b/macros/texinfo/texinfo/tp/Texinfo/Convert/Info.pm @@ -43,12 +43,12 @@ use vars qw($VERSION @ISA @EXPORT @EXPORT_OK %EXPORT_TAGS); @EXPORT = qw( ); -$VERSION = '6.7'; +$VERSION = '6.8'; my $STDIN_DOCU_NAME = 'stdin'; my %defaults = Texinfo::Convert::Plaintext::converter_defaults(undef, undef); -$defaults{'SHOW_MENU'} = 1; +$defaults{'FORMAT_MENU'} = 'menu'; $defaults{'EXTENSION'} = 'info'; $defaults{'USE_SETFILENAME_EXTENSION'} = 1; $defaults{'OUTFILE'} = undef; diff --git a/macros/texinfo/texinfo/tp/Texinfo/Convert/Paragraph.pm b/macros/texinfo/texinfo/tp/Texinfo/Convert/Paragraph.pm index 0f2e90cb71..e32891fefc 100644 --- a/macros/texinfo/texinfo/tp/Texinfo/Convert/Paragraph.pm +++ b/macros/texinfo/texinfo/tp/Texinfo/Convert/Paragraph.pm @@ -1,4 +1,4 @@ -# Copyright 2014, 2015, 2016, 2017, 2018 Free Software Foundation, Inc. +# Copyright 2014-2019 Free Software Foundation, Inc. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -20,7 +20,7 @@ use 5.00405; use strict; use warnings; -our $VERSION = '6.7'; +our $VERSION = '6.8'; use Texinfo::XSLoader; @@ -57,9 +57,9 @@ BEGIN { my $package = Texinfo::XSLoader::init ( "Texinfo::Convert::Paragraph", - "Texinfo::XS::XSParagraph", "Texinfo::Convert::ParagraphNonXS", "XSParagraph", + undef, 0, $warning_message, $fatal_message diff --git a/macros/texinfo/texinfo/tp/Texinfo/Convert/ParagraphNonXS.pm b/macros/texinfo/texinfo/tp/Texinfo/Convert/ParagraphNonXS.pm index 7108a3f701..e26b18a771 100644 --- a/macros/texinfo/texinfo/tp/Texinfo/Convert/ParagraphNonXS.pm +++ b/macros/texinfo/texinfo/tp/Texinfo/Convert/ParagraphNonXS.pm @@ -1,7 +1,6 @@ # ParagraphNonXS.pm: handle paragraph text. # -# Copyright 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018 Free Software -# Foundation, Inc. +# Copyright 2010-2019 Free Software Foundation, Inc. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -22,7 +21,7 @@ # modules Text::Wrap, Text::Format, it keeps a state of the paragraph # and waits for text to be fed into it. -package Texinfo::Convert::ParagraphNonXS; +package Texinfo::Convert::Paragraph; use 5.006; use strict; @@ -201,22 +200,6 @@ sub _add_next($;$$$) } $word =~ s/\x08//g; - if (!defined($paragraph->{'word'})) { - $paragraph->{'word'} = ''; - $paragraph->{'last_char'} = ''; - if ($paragraph->{'end_sentence'} - and $paragraph->{'end_sentence'} > 0 - and !$paragraph->{'frenchspacing'} - and $paragraph->{'counter'} != 0 and $paragraph->{'space'}) { - # do not double space if there are leading spaces in word - if ($word !~ /^\s/) { - #$paragraph->{'space'} = ' '; - $paragraph->{'space'} .= ' ' x (2 - length($paragraph->{'space'})); - } - delete $paragraph->{'end_sentence'}; - } - } - $paragraph->{'word'} .= $word; if (!$transparent) { @@ -312,7 +295,6 @@ sub set_space_protection($$;$$$$) if ($space_protection) { _add_next($paragraph, ''); } - return ''; } # Wrap $TEXT, returning the wrapped text, taking into account the current state @@ -375,19 +357,7 @@ sub add_text($$) and $paragraph->{'end_sentence'} > 0 and !$paragraph->{'frenchspacing'} and !$paragraph->{'unfilled'}) { - if (length($paragraph->{'space'}) >= 1 or length($spaces) > 1) { - # more than one space, we can make sure tht there are only - # 2 spaces - my $all_spaces = substr($paragraph->{'space'} . $spaces, 0, 2); - $all_spaces =~ s/[\n\r]/ /g; - $all_spaces .= ' ' x (2 - length($all_spaces)); - $paragraph->{'space'} = $all_spaces; - } else { - # if there is only one space, we let it accumulate - my $new_space = $spaces; - $new_space =~ s/^[\n\r]/ /; - $paragraph->{'space'} = $new_space; - } + $paragraph->{'space'} = ' '; } else { # Only save the first space if ($paragraph->{'unfilled'} diff --git a/macros/texinfo/texinfo/tp/Texinfo/Convert/Plaintext.pm b/macros/texinfo/texinfo/tp/Texinfo/Convert/Plaintext.pm index 8b72c239dc..7402eb9f99 100644 --- a/macros/texinfo/texinfo/tp/Texinfo/Convert/Plaintext.pm +++ b/macros/texinfo/texinfo/tp/Texinfo/Convert/Plaintext.pm @@ -1,6 +1,6 @@ # Plaintext.pm: output tree as text with filling. # -# Copyright 2010-2019 Free Software Foundation, Inc. +# Copyright 2010-2020 Free Software Foundation, Inc. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -61,7 +61,7 @@ sub import { @EXPORT = qw( ); -$VERSION = '6.7'; +$VERSION = '6.8'; # misc commands that are of use for formatting. my %formatting_misc_commands = %Texinfo::Convert::Text::formatting_misc_commands; @@ -92,6 +92,7 @@ my %block_commands = %Texinfo::Common::block_commands; my %menu_commands = %Texinfo::Common::menu_commands; my %root_commands = %Texinfo::Common::root_commands; my %preformatted_commands = %Texinfo::Common::preformatted_commands; +my %math_commands = %Texinfo::Common::math_commands; my %explained_commands = %Texinfo::Common::explained_commands; my %inline_format_commands = %Texinfo::Common::inline_format_commands; my %inline_commands = %Texinfo::Common::inline_commands; @@ -157,6 +158,13 @@ foreach my $preformatted_command ('verbatim', keys(%menu_commands)) { $default_preformatted_context_commands{$preformatted_command} = 1; } +my %block_math_commands; +foreach my $block_math_command (keys(%math_commands)) { + if (exists($block_commands{$block_math_command})) { + $block_math_commands{$block_math_command} = 1; + } +} + my %ignored_misc_commands; foreach my $misc_command (keys(%misc_commands)) { $ignored_misc_commands{$misc_command} = 1 @@ -193,6 +201,8 @@ foreach my $non_indented('format', 'smallformat') { delete $indented_commands{$non_indented}; } +# FIXME should keys(%math_brace_commands) be added here? +# How can this be tested? foreach my $format_context_command (keys(%menu_commands), 'verbatim', 'flushleft', 'flushright', 'multitable', 'float') { $default_format_context_commands{$format_context_command} = 1; @@ -315,7 +325,7 @@ foreach my $command ('var', 'cite', 'dmn', keys(%code_style_commands)) { my %defaults = ( 'ENABLE_ENCODING' => 1, - 'SHOW_MENU' => 0, + 'FORMAT_MENU' => 'nomenu', #'EXTENSION' => 'info', 'EXTENSION' => 'txt', #'USE_SETFILENAME_EXTENSION' => 1, @@ -652,10 +662,12 @@ sub new_formatter($$;$) if ($menu_commands{$context}) { last; } elsif ($preformatted_code_commands{$context} - or $format_raw_commands{$context}) { + or $format_raw_commands{$context} + or $math_commands{$context}) { $formatter->{'font_type_stack'}->[-1]->{'monospace'} = 1; $formatter->{'font_type_stack'}->[-1]->{'code_command'} = 1 - if ($preformatted_code_commands{$context}); + if ($preformatted_code_commands{$context} + or $math_commands{$context}); last; } } @@ -1167,8 +1179,8 @@ sub _printindex_formatted($$;$) my $merged_index_entries = Texinfo::Structuring::merge_indices($index_names); $self->{'index_entries'} - = $self->Texinfo::Structuring::sort_indices($merged_index_entries, - $index_names); + = Texinfo::Structuring::sort_indices($self->{'parser'}, + $merged_index_entries, $index_names); $self->{'index_names'} = $index_names; } if (!$self->{'index_entries'} or !$self->{'index_entries'}->{$index_name} @@ -1562,6 +1574,10 @@ sub _convert($$) } elsif ($root->{'text'} =~ /\S/) { $self->_bug_message("ignored text not empty `$root->{'text'}'", $root); return ''; + } else { + # miscellaneous top-level whitespace - possibly after an @image + return _count_added($self, $formatter->{'container'}, + add_text($formatter->{'container'}, $root->{'text'})); } } else { my $tree = $self->gdt($root->{'text'}); @@ -1677,14 +1693,12 @@ sub _convert($$) add_end_sentence($formatter->{'container'}, 1); } elsif ($command eq 'tie') { $formatter->{'w'}++; - $result .= _count_added($self, $formatter->{'container'}, - set_space_protection($formatter->{'container'}, 1, undef)) + set_space_protection($formatter->{'container'}, 1, undef) if ($formatter->{'w'} == 1); $result .= _count_added($self, $formatter->{'container'}, add_text($formatter->{'container'}, $text)); $formatter->{'w'}--; - $result .= _count_added($self, $formatter->{'container'}, - set_space_protection($formatter->{'container'}, 0, undef)) + set_space_protection($formatter->{'container'}, 0, undef) if ($formatter->{'w'} == 0); } else { $result .= _count_added($self, $formatter->{'container'}, @@ -1765,8 +1779,7 @@ sub _convert($$) } if ($command eq 'w') { $formatter->{'w'}++; - $result .= _count_added($self, $formatter->{'container'}, - set_space_protection($formatter->{'container'}, 1,undef)) + set_space_protection($formatter->{'container'}, 1,undef) if ($formatter->{'w'} == 1); } my ($text_before, $text_after); @@ -1811,8 +1824,7 @@ sub _convert($$) if ($text_after ne ''); if ($command eq 'w') { $formatter->{'w'}--; - $result .= _count_added($self, $formatter->{'container'}, - set_space_protection($formatter->{'container'},0,undef)) + set_space_protection($formatter->{'container'},0,undef) if ($formatter->{'w'} == 0); } if ($code_style_commands{$command}) { @@ -2008,15 +2020,14 @@ sub _convert($$) if ($self->{'document_context'}->[-1]->{'in_multitable'}) { $in_multitable = 1; $formatter->{'w'}++; - $result .= _count_added($self, $formatter->{'container'}, - set_space_protection($formatter->{'container'},1,undef)) - if ($formatter->{'w'} == 1); + set_space_protection($formatter->{'container'}, 1, undef) + if ($formatter->{'w'} == 1); } # Disallow breaks in runs of Chinese text in node names, because a # break would be normalized to a single space by the Info reader, and # the node wouldn't be found. set_space_protection($formatter->{'container'}, - undef,undef,undef,undef,1); # double_width_no_break + undef, undef, undef, undef, 1); if ($command eq 'xref') { $result = _convert($self, {'contents' => [{'text' => '*Note '}]}); @@ -2187,8 +2198,7 @@ sub _convert($$) if ($in_multitable) { $formatter->{'w'}--; - $result .= _count_added($self, $formatter->{'container'}, - set_space_protection($formatter->{'container'},0,undef)) + set_space_protection($formatter->{'container'}, 0, undef) if ($formatter->{'w'} == 0); } set_space_protection($formatter->{'container'}, @@ -2243,15 +2253,16 @@ sub _convert($$) unshift @{$self->{'current_contents'}->[-1]}, ($argument); } return ''; - } elsif ($command eq 'math') { - push @{$self->{'context'}}, 'math'; + # condition should actually be that the $command is inline + } elsif ($math_commands{$command} and not exists($block_commands{$command})) { + push @{$self->{'context'}}, $command; if ($root->{'args'}) { $result .= _convert($self, {'type' => 'frenchspacing', 'contents' => [{'type' => '_code', 'contents' => [$root->{'args'}->[0]]}]}); } my $old_context = pop @{$self->{'context'}}; - die if ($old_context ne 'math'); + die if ($old_context ne $command); return $result; } elsif ($command eq 'titlefont') { push @{$self->{'count_context'}}, {'lines' => 0, 'bytes' => 0}; @@ -2262,9 +2273,10 @@ sub _convert($$) 'cmdname' => 'titlefont'}, $result, $self, $self->get_conf('NUMBER_SECTIONS'), ($self->{'format_context'}->[-1]->{'indent_level'}) * $indent_length); + $result =~ s/\n$//; # final newline has its own tree element $self->{'empty_lines_count'} = 0 unless ($result eq ''); _add_text_count($self, $result); - _add_lines_count($self, 2); + _add_lines_count($self, 1); return $result; } elsif ($command eq 'U') { @@ -2345,7 +2357,7 @@ sub _convert($$) # remark: # cartouche group and raggedright -> nothing on format stack - if ($menu_commands{$command} and !$self->get_conf('SHOW_MENU')) { + if ($menu_commands{$command} and $self->get_conf('FORMAT_MENU') eq 'nomenu') { return ''; } if ($self->{'preformatted_context_commands'}->{$command} @@ -2360,7 +2372,7 @@ sub _convert($$) push @{$self->{'context'}}, $command; } elsif ($flush_commands{$command}) { push @{$self->{'context'}}, $command; - } elsif ($raw_commands{$command}) { + } elsif ($raw_commands{$command} or $block_math_commands{$command}) { if (!$self->{'formatters'}->[-1]->{'_top_formatter'}) { # reuse the current formatter if not in top level $result .= _count_added($self, $formatter->{'container'}, @@ -2942,7 +2954,9 @@ sub _convert($$) my $def_paragraph = $self->new_formatter('paragraph', { 'indent_length' => ($self->{'format_context'}->[-1]->{'indent_level'} -1) *$indent_length, - 'indent_length_next' => (1+$self->{'format_context'}->[-1]->{'indent_level'})*$indent_length}); + 'indent_length_next' => (1+$self->{'format_context'}->[-1]->{'indent_level'})*$indent_length, + 'suppress_styles' => 1 + }); push @{$self->{'formatters'}}, $def_paragraph; $result .= _convert($self, {'type' => '_code', 'contents' => [$tree]}); @@ -3038,8 +3052,7 @@ sub _convert($$) } elsif ($root->{'type'} eq 'frenchspacing') { push @{$formatter->{'frenchspacing_stack'}}, 'on'; - set_space_protection($formatter->{'container'}, undef, - undef,undef,1); + set_space_protection($formatter->{'container'}, undef, undef, undef, 1); } elsif ($root->{'type'} eq '_code') { if (!$formatter->{'font_type_stack'}->[-1]->{'monospace'}) { push @{$formatter->{'font_type_stack'}}, {'monospace' => 1}; @@ -3047,8 +3060,7 @@ sub _convert($$) $formatter->{'font_type_stack'}->[-1]->{'monospace'}++; } push @{$formatter->{'frenchspacing_stack'}}, 'on'; - set_space_protection($formatter->{'container'},undef, - undef,undef,1); + set_space_protection($formatter->{'container'}, undef, undef, undef, 1); } elsif ($root->{'type'} eq 'bracketed') { $result .= _count_added($self, $formatter->{'container'}, add_text($formatter->{'container'}, '{')); @@ -3077,8 +3089,8 @@ sub _convert($$) pop @{$formatter->{'frenchspacing_stack'}}; my $frenchspacing = 0; $frenchspacing = 1 if ($formatter->{'frenchspacing_stack'}->[-1] eq 'on'); - set_space_protection($formatter->{'container'},undef, - undef, undef, $frenchspacing); + set_space_protection($formatter->{'container'}, undef, + undef, undef, $frenchspacing); } elsif ($root->{'type'} eq '_code') { $formatter->{'font_type_stack'}->[-1]->{'monospace'}--; pop @{$formatter->{'font_type_stack'}} @@ -3086,8 +3098,8 @@ sub _convert($$) pop @{$formatter->{'frenchspacing_stack'}}; my $frenchspacing = 0; $frenchspacing = 1 if ($formatter->{'frenchspacing_stack'}->[-1] eq 'on'); - set_space_protection($formatter->{'container'},undef, - undef, undef, $frenchspacing); + set_space_protection($formatter->{'container'}, undef, + undef, undef, $frenchspacing); } elsif ($root->{'type'} eq 'bracketed') { $result .= _count_added($self, $formatter->{'container'}, add_text($formatter->{'container'}, '}')); @@ -3267,16 +3279,19 @@ sub _convert($$) and $command ne 'part') { # add menu if missing my $node = $self->{'node'}; - if ($node) { - if (!$self->{'seenmenus'}->{$node}) { - $self->{'seenmenus'}->{$node} = 1; - my $menu_node - = Texinfo::Structuring::node_menu_of_node (undef, $node); - if ($menu_node) { - my $menu_text = $self->_convert ($menu_node); - if ($menu_text) { - $result .= $menu_text; - } + my $automatic_directions; + if ($node and defined($node->{'extra'}->{'nodes_manuals'})) { + $automatic_directions = + (scalar(@{$node->{'extra'}->{'nodes_manuals'}}) == 1); + } + if ($node and $automatic_directions + and !$self->{'seenmenus'}->{$node}) { + $self->{'seenmenus'}->{$node} = 1; + my $menu_node = Texinfo::Structuring::new_complete_node_menu(undef, $node); + if ($menu_node) { + my $menu_text = $self->_convert($menu_node); + if ($menu_text) { + $result .= $menu_text; } } } diff --git a/macros/texinfo/texinfo/tp/Texinfo/Convert/Texinfo.pm b/macros/texinfo/texinfo/tp/Texinfo/Convert/Texinfo.pm index 5ba330d9a3..0e01fe9814 100644 --- a/macros/texinfo/texinfo/tp/Texinfo/Convert/Texinfo.pm +++ b/macros/texinfo/texinfo/tp/Texinfo/Convert/Texinfo.pm @@ -40,7 +40,7 @@ use vars qw($VERSION @ISA @EXPORT @EXPORT_OK %EXPORT_TAGS); @EXPORT = qw( ); -$VERSION = '6.7'; +$VERSION = '6.8'; my %misc_commands = %Texinfo::Common::misc_commands; my %brace_commands = %Texinfo::Common::brace_commands; diff --git a/macros/texinfo/texinfo/tp/Texinfo/Convert/TexinfoSXML.pm b/macros/texinfo/texinfo/tp/Texinfo/Convert/TexinfoSXML.pm index 2166664e89..5098a90e2d 100644 --- a/macros/texinfo/texinfo/tp/Texinfo/Convert/TexinfoSXML.pm +++ b/macros/texinfo/texinfo/tp/Texinfo/Convert/TexinfoSXML.pm @@ -43,12 +43,12 @@ use vars qw($VERSION @ISA @EXPORT @EXPORT_OK %EXPORT_TAGS); @EXPORT = qw( ); -$VERSION = '6.7'; +$VERSION = '6.8'; # SXML specific my %defaults = ( 'ENABLE_ENCODING' => 0, - 'SHOW_MENU' => 1, + 'FORMAT_MENU' => 'menu', 'EXTENSION' => 'sxml', #'output_perl_encoding' => 'utf8', 'OUTPUT_ENCODING_NAME' => 'utf-8', diff --git a/macros/texinfo/texinfo/tp/Texinfo/Convert/TexinfoXML.pm b/macros/texinfo/texinfo/tp/Texinfo/Convert/TexinfoXML.pm index b065ffca2e..5a962a4649 100644 --- a/macros/texinfo/texinfo/tp/Texinfo/Convert/TexinfoXML.pm +++ b/macros/texinfo/texinfo/tp/Texinfo/Convert/TexinfoXML.pm @@ -45,12 +45,12 @@ use vars qw($VERSION @ISA @EXPORT @EXPORT_OK %EXPORT_TAGS); @EXPORT = qw( ); -$VERSION = '6.7'; +$VERSION = '6.8'; # XML specific my %defaults = ( 'ENABLE_ENCODING' => 0, - 'SHOW_MENU' => 1, + 'FORMAT_MENU' => 'menu', 'EXTENSION' => 'xml', #'output_perl_encoding' => 'utf8', 'OUTPUT_ENCODING_NAME' => 'utf-8', @@ -107,6 +107,13 @@ our %commands_formatting = ( 'rbracechar' => 'rbracechar', 'backslashchar' => 'backslashchar', 'hashchar' => 'hashchar', + # in Texinfo::Convert::Converter::default_xml_commands_formatting, + # guillemotleft and guillemotright are mapped to laquo and raquo, + # but guillemetleft and guillemetright are also mapped to those + # entities. To make sure that it is possible to go back to the + # @-command from the entity, we use specific entities here + 'guillemotleft' => 'guillemotleft', + 'guillemotright' => 'guillemotright', ); # use default XML formatting to complete the hash, removing XML @@ -328,6 +335,8 @@ our %commands_args_elements = ( 'inforef' => ['inforefnodename', 'inforefrefname', 'inforefinfoname'], 'image' => ['imagefile', 'imagewidth', 'imageheight', 'alttext', 'imageextension'], + # * means that the previous element is variadic, ie can appear indefinitely + 'example' => ['examplelanguage', 'examplearg', '*'], 'quotation' => ['quotationtype'], 'float' => ['floattype', 'floatname'], 'itemize' => ['itemprepend'], @@ -1321,7 +1330,20 @@ sub _convert($$;$) if ($root->{'args'}) { if ($commands_args_elements{$root->{'cmdname'}}) { my $arg_index = 0; - foreach my $element (@{$commands_args_elements{$root->{'cmdname'}}}) { + my $variadic_element = undef; + while (defined($commands_args_elements{$root->{'cmdname'}}->[$arg_index]) + or defined($variadic_element)) { + my $element; + if (defined($variadic_element)) { + $element = $variadic_element; + } else { + if ($commands_args_elements{$root->{'cmdname'}}->[$arg_index] eq '*') { + $variadic_element = $commands_args_elements{$root->{'cmdname'}}->[$arg_index-1]; + $element = $variadic_element; + } else { + $element = $commands_args_elements{$root->{'cmdname'}}->[$arg_index]; + } + } if (defined($root->{'args'}->[$arg_index])) { my $in_code; $in_code = 1 @@ -1548,7 +1570,8 @@ sub _convert($$;$) if ($root->{'contents'}) { my $in_code; if ($root->{'cmdname'} - and $Texinfo::Common::preformatted_code_commands{$root->{'cmdname'}}) { + and ($Texinfo::Common::preformatted_code_commands{$root->{'cmdname'}} + or $Texinfo::Common::math_commands{$root->{'cmdname'}})) { $in_code = 1; } push @{$self->{'document_context'}->[-1]->{'monospace'}}, 1 diff --git a/macros/texinfo/texinfo/tp/Texinfo/Convert/Text.pm b/macros/texinfo/texinfo/tp/Texinfo/Convert/Text.pm index 989f81688c..22a21464bc 100644 --- a/macros/texinfo/texinfo/tp/Texinfo/Convert/Text.pm +++ b/macros/texinfo/texinfo/tp/Texinfo/Convert/Text.pm @@ -1,6 +1,6 @@ # Text.pm: output tree as simple text. # -# Copyright 2010-2018 Free Software Foundation, Inc., +# Copyright 2010-2020 Free Software Foundation, Inc. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -22,6 +22,7 @@ package Texinfo::Convert::Text; use 5.00405; use strict; +use Texinfo::Convert::Converter; # accent commands list. use Texinfo::Common; use Texinfo::Convert::Unicode; @@ -47,7 +48,7 @@ use vars qw($VERSION @ISA @EXPORT @EXPORT_OK %EXPORT_TAGS); @EXPORT = qw( ); -$VERSION = '6.7'; +$VERSION = '6.8'; # this is in fact not needed for 'footnote', 'shortcaption', 'caption' # when they have no brace_command_arg, see below. @@ -252,7 +253,7 @@ sub brace_no_arg_command($;$) $result = Texinfo::Convert::Unicode::unicode_for_brace_no_arg_command( $command, $encoding); } - if (!defined($result and $options and $options->{'converter'})) { + if (!defined($result) and $options and $options->{'converter'}) { my $tree = Texinfo::Common::translated_command_tree( $options->{'converter'}, $command); if ($tree) { @@ -475,12 +476,12 @@ sub _convert($;$) } elsif ($root->{'args'} and $root->{'args'}->[0] and (($root->{'args'}->[0]->{'type'} and $root->{'args'}->[0]->{'type'} eq 'brace_command_arg') - or $root->{'cmdname'} eq 'math')) { + or $Texinfo::Common::math_commands{$root->{'cmdname'}})) { my $result; if ($root->{'cmdname'} eq 'sc') { $options = {%$options, 'sc' => 1}; } elsif ($Texinfo::Common::code_style_commands{$root->{'cmdname'}} - or $root->{'cmdname'} eq 'math') { + or $Texinfo::Common::math_commands{$root->{'cmdname'}}) { $options = _code_options($options); } $result = _convert($root->{'args'}->[0], $options); @@ -575,7 +576,10 @@ sub _convert($;$) } if ($root->{'contents'}) { if ($root->{'cmdname'} - and $Texinfo::Common::preformatted_code_commands{$root->{'cmdname'}}) { + and ($Texinfo::Common::preformatted_code_commands{$root->{'cmdname'}} + or $Texinfo::Common::math_commands{$root->{'cmdname'}} + or (defined($Texinfo::Common::block_commands{$root->{'cmdname'}}) + and $Texinfo::Common::block_commands{$root->{'cmdname'}} eq 'raw'))) { $options = _code_options($options); } if (ref($root->{'contents'}) ne 'ARRAY') { @@ -822,7 +826,7 @@ L<Texinfo::Report> objet. See also L<Texinfo::Convert::Converter>. =item expanded_formats_hash A reference on a hash. The keys should be format names (like C<html>, -C<tex>), and if thecorresponding value is set, the format is expanded. +C<tex>), and if the corresponding value is set, the format is expanded. =back diff --git a/macros/texinfo/texinfo/tp/Texinfo/Convert/TextContent.pm b/macros/texinfo/texinfo/tp/Texinfo/Convert/TextContent.pm index 2b4c8d44e6..9bac7fd4d5 100644 --- a/macros/texinfo/texinfo/tp/Texinfo/Convert/TextContent.pm +++ b/macros/texinfo/texinfo/tp/Texinfo/Convert/TextContent.pm @@ -49,7 +49,7 @@ foreach my $type ('empty_line_after_command', 'preamble', } my %defaults = ( - 'SHOW_MENU' => 1, + 'FORMAT_MENU' => 'menu', 'OUTFILE' => '-', ); @@ -104,7 +104,7 @@ sub _convert($$) or ($Texinfo::Common::inline_format_commands{$root->{'cmdname'}} and (!$root->{'extra'}->{'format'} or !$self->{'expanded_formats_hash'}->{$root->{'extra'}->{'format'}})) - or ($root->{'cmdname'} eq 'menu' and !$self->get_conf('SHOW_MENU')) + or ($root->{'cmdname'} eq 'menu' and $self->get_conf('FORMAT_MENU') eq 'nomenu') # here ignore most of the misc commands or ($root->{'args'} and $root->{'args'}->[0] and $root->{'args'}->[0]->{'type'} |