1# HTML.pm: output tree as HTML. 2# 3# Copyright 2011-2020 Free Software Foundation, Inc. 4# 5# This program is free software; you can redistribute it and/or modify 6# it under the terms of the GNU General Public License as published by 7# the Free Software Foundation; either version 3 of the License, 8# or (at your option) any later version. 9# 10# This program is distributed in the hope that it will be useful, 11# but WITHOUT ANY WARRANTY; without even the implied warranty of 12# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13# GNU General Public License for more details. 14# 15# You should have received a copy of the GNU General Public License 16# along with this program. If not, see <http://www.gnu.org/licenses/>. 17# 18# 19# There are three categories of formatting functions that can be 20# replaced by the user, together with the hash with default functions: 21# * command tree element formatting functions registered in 22# %default_commands_conversion 23# * type tree element (element without @-command) formatting 24# functions, registered in %default_types_conversion 25# * other formatting functions, registered in 26# %default_formatting_references 27# 28# The functions used in the default case for all the functions 29# that may be replaced should not have side effects, such that 30# users can overrides them independently without risking unwanted 31# results. Also in formatting functions, the state of the 32# converter should only be accessed through functions, such 33# as in_math, in_preformatted, preformatted_classes_stack and 34# similar functions. 35# 36# In most formatting functions, the case where $self->in_string() is 37# true should be handled explicitely and the simplest formatting should be 38# done in that case, without any HTML element such that the result 39# can be in an attribute or in a comment. 40# 41# FIXME: there is already a case with a side effect, with the 42# variable $html_menu_entry_index. 43# 44# Original author: Patrice Dumas <pertusus@free.fr> 45 46package Texinfo::Convert::HTML; 47 48use 5.00405; 49 50# See 'The "Unicode Bug"' under 'perlunicode' man page. This means 51# that regular expressions will treat characters 128-255 in a Perl string 52# the same regardless of whether the string is using a UTF-8 encoding. 53# For older Perls, you can use utf8::upgrade on the strings, where the 54# difference matters. 55use if $] >= 5.012, feature => 'unicode_strings'; 56 57use strict; 58 59use Texinfo::Convert::Converter; 60use Texinfo::Common; 61use Texinfo::Convert::Texinfo; 62use Texinfo::Convert::Text; 63use Texinfo::Convert::Unicode; 64use Texinfo::Convert::NodeNameNormalization; 65 66use Carp qw(cluck confess); 67 68use File::Copy qw(copy); 69 70require Exporter; 71use vars qw($VERSION @ISA @EXPORT @EXPORT_OK %EXPORT_TAGS); 72@ISA = qw(Exporter Texinfo::Convert::Converter); 73 74%EXPORT_TAGS = ( 'all' => [ qw( 75 convert 76 convert_tree 77 output 78 output_internal_links 79) ] ); 80 81@EXPORT_OK = ( @{ $EXPORT_TAGS{'all'} } ); 82 83@EXPORT = qw( 84); 85 86$VERSION = '6.8'; 87 88# misc commands that are of use for formatting. 89my %formatting_misc_commands = %Texinfo::Convert::Text::formatting_misc_commands; 90my %no_brace_commands = %Texinfo::Common::no_brace_commands; 91my %accent_commands = %Texinfo::Common::accent_commands; 92my %misc_commands = %Texinfo::Common::misc_commands; 93my %sectioning_commands = %Texinfo::Common::sectioning_commands; 94my %def_commands = %Texinfo::Common::def_commands; 95my %ref_commands = %Texinfo::Common::ref_commands; 96my %brace_commands = %Texinfo::Common::brace_commands; 97my %block_commands = %Texinfo::Common::block_commands; 98my %menu_commands = %Texinfo::Common::menu_commands; 99my %root_commands = %Texinfo::Common::root_commands; 100my %preformatted_commands = %Texinfo::Common::preformatted_commands; 101my %math_commands = %Texinfo::Common::math_commands; 102my %explained_commands = %Texinfo::Common::explained_commands; 103my %item_container_commands = %Texinfo::Common::item_container_commands; 104my %raw_commands = %Texinfo::Common::raw_commands; 105my %format_raw_commands = %Texinfo::Common::format_raw_commands; 106my %inline_commands = %Texinfo::Common::inline_commands; 107my %inline_format_commands = %Texinfo::Common::inline_format_commands; 108my %code_style_commands = %Texinfo::Common::code_style_commands; 109my %regular_font_style_commands = %Texinfo::Common::regular_font_style_commands; 110my %preformatted_code_commands = %Texinfo::Common::preformatted_code_commands; 111my %default_index_commands = %Texinfo::Common::default_index_commands; 112my %style_commands = %Texinfo::Common::style_commands; 113my %align_commands = %Texinfo::Common::align_commands; 114my %region_commands = %Texinfo::Common::region_commands; 115my %context_brace_commands = %Texinfo::Common::context_brace_commands; 116my %letter_no_arg_commands = %Texinfo::Common::letter_no_arg_commands; 117 118my %small_alias; 119for my $cmd ('example', 'display', 'format', 'lisp', 'quotation', 120 'indentedblock') { 121 $small_alias{'small'.$cmd} = $cmd; 122}; 123 124foreach my $def_command (keys(%def_commands)) { 125 $formatting_misc_commands{$def_command} = 1 if ($misc_commands{$def_command}); 126} 127 128# FIXME remove raw commands? 129my %format_context_commands = (%block_commands, %root_commands); 130 131foreach my $misc_context_command('tab', 'item', 'itemx', 'headitem') { 132 $format_context_commands{$misc_context_command} = 1; 133} 134 135my %composition_context_commands = (%preformatted_commands, %root_commands, 136 %menu_commands, %align_commands); 137$composition_context_commands{'float'} = 1; 138 139my %pre_class_types; 140 141# FIXME allow customization? (also in DocBook) 142my %upper_case_commands = ( 'sc' => 1 ); 143 144sub in_math($) 145{ 146 my $self = shift; 147 return $self->{'document_context'}->[-1]->{'math'}; 148} 149 150# set if in menu or preformatted command 151sub in_preformatted($) 152{ 153 my $self = shift; 154 my $context = $self->{'document_context'}->[-1]->{'composition_context'}->[-1]; 155 if ($preformatted_commands{$context} 156 or $pre_class_types{$context} 157 or ($menu_commands{$context} and $self->_in_preformatted_in_menu())) { 158 return $context; 159 } else { 160 return undef; 161 } 162} 163 164sub in_upper_case($) 165{ 166 my $self = shift; 167 return $self->{'document_context'}->[-1]->{'formatting_context'}->[-1]->{'upper_case'}; 168} 169 170sub in_space_protected($) 171{ 172 my $self = shift; 173 return $self->{'document_context'}->[-1]->{'formatting_context'}->[-1]->{'space_protected'}; 174} 175 176sub in_code($) 177{ 178 my $self = shift; 179 return $self->{'document_context'}->[-1]->{'monospace'}->[-1]; 180} 181 182sub in_string($) 183{ 184 my $self = shift; 185 return $self->{'document_context'}->[-1]->{'string'}; 186} 187 188sub in_verbatim($) 189{ 190 my $self = shift; 191 return $self->{'document_context'}->[-1]->{'verbatim'}; 192} 193 194sub in_raw($) 195{ 196 my $self = shift; 197 return $self->{'document_context'}->[-1]->{'raw'}; 198} 199 200sub paragraph_number($) 201{ 202 my $self = shift; 203 return $self->{'document_context'}->[-1]->{'formatting_context'}->[-1]->{'paragraph_number'}; 204} 205 206sub preformatted_number($) 207{ 208 my $self = shift; 209 return $self->{'document_context'}->[-1]->{'formatting_context'}->[-1]->{'preformatted_number'}; 210} 211 212sub count_elements_in_filename($$) 213{ 214 my $self = shift; 215 my $filename = shift; 216 217 if (defined($self->{'elements_in_file_count'}->{$filename})) { 218 return $self->{'elements_in_file_count'}->{$filename}; 219 } 220 return undef; 221} 222 223sub top_format($) 224{ 225 my $self = shift; 226 return $self->{'document_context'}->[-1]->{'formats'}->[-1]; 227} 228 229sub commands_stack($) 230{ 231 my $self = shift; 232 return @{$self->{'document_context'}->[-1]->{'commands'}}; 233} 234 235sub preformatted_classes_stack($) 236{ 237 my $self = shift; 238 return @{$self->{'document_context'}->[-1]->{'preformatted_classes'}}; 239} 240 241sub in_align($) 242{ 243 my $self = shift; 244 my $context 245 = $self->{'document_context'}->[-1]->{'composition_context'}->[-1]; 246 if ($align_commands{$context}) { 247 return $context; 248 } else { 249 return undef; 250 } 251} 252 253# $COMMAND should be a tree element which is a possible target of a link. 254# 255# Returns a hash that may have these keys set: 256# 'target': A unique string representing the target. Used as argument to 257# 'id' attribute. 258# 'node_filename', 'section_filename', 259# 'misc_filename', 'filename'. Possibly others. 260# 261# Some functions cache their results in these hashes. 262sub _get_target($$) 263{ 264 my $self = shift; 265 my $command = shift; 266 my $target; 267 if (!defined($command)) { 268 cluck("_get_target command not defined"); 269 } 270 if ($self->{'targets'}->{$command}) { 271 $target = $self->{'targets'}->{$command}; 272 } elsif ($command->{'cmdname'} 273 # This should only happen for @*heading*, root_commands targets should 274 # already be set. 275 and $sectioning_commands{$command->{'cmdname'}} 276 and !$root_commands{$command->{'cmdname'}}) { 277 $target = $self->_new_sectioning_command_target($command); 278 } 279 return $target; 280} 281 282# API for the elements formatting 283sub command_id($$) 284{ 285 my $self = shift; 286 my $command = shift; 287 my $target = $self->_get_target($command); 288 if ($target) { 289 return $target->{'target'}; 290 } else { 291 return undef; 292 } 293} 294 295sub command_contents_target($$$) 296{ 297 my $self = shift; 298 my $command = shift; 299 my $contents_or_shortcontents = shift; 300 $contents_or_shortcontents = 'shortcontents' 301 if ($contents_or_shortcontents eq 'summarycontents'); 302 303 my $target = $self->_get_target($command); 304 if ($target) { 305 return $target->{$contents_or_shortcontents .'_target'}; 306 } else { 307 return undef; 308 } 309} 310 311# Return href target for linking to this command 312sub command_target($$) 313{ 314 my $self = shift; 315 my $command = shift; 316 317 if ($command->{'extra'} 318 and $command->{'extra'}->{'associated_node'}) { 319 $command = $command->{'extra'}->{'associated_node'}; 320 } 321 my $target = $self->_get_target($command); 322 if ($target) { 323 return $target->{'target'}; 324 } else { 325 return undef; 326 } 327} 328 329sub command_filename($$) 330{ 331 my $self = shift; 332 my $command = shift; 333 334 my $target = $self->_get_target($command); 335 if ($target) { 336 if (defined($target->{'filename'})) { 337 return $target->{'filename'}; 338 } 339 my ($element, $root_command) = $self->_get_element($command, 1); 340 341 if (defined($root_command)) { 342 $target->{'root_command'} = $root_command; 343 } 344 if (defined($element)) { 345 $target->{'element'} = $element; 346 $target->{'filename'} = $element->{'filename'}; 347 return $element->{'filename'}; 348 } 349 } 350 return undef; 351} 352 353sub command_element($$) 354{ 355 my $self = shift; 356 my $command = shift; 357 358 my $target = $self->_get_target($command); 359 if ($target) { 360 $self->command_filename($command); 361 return $target->{'element'}; 362 } 363 return undef; 364} 365 366sub command_element_command($$) 367{ 368 my $self = shift; 369 my $command = shift; 370 371 my ($element, $root_command) = $self->_get_element($command); 372 #my $element = $self->command_element($command); 373 if ($element and $element->{'extra'}) { 374 return $element->{'extra'}->{'element_command'}; 375 } 376 return undef; 377} 378 379sub element_command($$) 380{ 381 my $self = shift; 382 my $element = shift; 383 384 if ($element and $element->{'extra'}) { 385 if ($element->{'extra'}->{'element_command'}) { 386 return $element->{'extra'}->{'element_command'}; 387 } elsif ($element->{'extra'}->{'special_element'}) { 388 return $element; 389 } 390 } 391 return undef; 392} 393 394sub command_node($$) 395{ 396 my $self = shift; 397 my $command = shift; 398 399 my $target = $self->_get_target($command); 400 if ($target) { 401 $self->command_filename($command); 402 my $root_command = $target->{'root_command'}; 403 if (defined($root_command)) { 404 if ($root_command->{'cmdname'} and $root_command->{'cmdname'} eq 'node') { 405 return $root_command; 406 } 407 if ($root_command->{'extra'} and $root_command->{'extra'}->{'associated_node'}) { 408 return $root_command->{'extra'}->{'associated_node'}; 409 } 410 } 411 } 412 return undef; 413} 414 415# Return string for linking to $COMMAND with <a href> 416sub command_href($$;$$) 417{ 418 my $self = shift; 419 my $command = shift; 420 my $filename = shift; 421 my $link_command = shift; 422 423 $filename = $self->{'current_filename'} if (!defined($filename)); 424 425 if ($command->{'manual_content'}) { 426 return $self->_external_node_href($command, $filename, $link_command); 427 } 428 429 my $target = $self->command_target($command); 430 return '' if (!defined($target)); 431 my $href = ''; 432 433 my $target_filename = $self->command_filename($command); 434 if (!defined($target_filename)) { 435 # Happens if there are no pages, for example if OUTPUT is set to '' 436 # as in the test cases. Also for things in @titlepage when 437 # titlepage is not output. 438 if ($self->{'elements'} and $self->{'elements'}->[0] 439 and defined($self->{'elements'}->[0]->{'filename'})) { 440 # In that case use the first page. 441 $target_filename = $self->{'elements'}->[0]->{'filename'}; 442 } 443 } 444 if (defined($target_filename)) { 445 if (!defined($filename) 446 or $filename ne $target_filename) { 447 $href .= $target_filename; 448 # omit target if the command is an element command, there is only 449 # one element in file and there is a file in the href 450 my $command_element_command = $self->command_element_command($command); 451 if (defined($filename) 452 and defined($command_element_command) 453 and ($command_element_command eq $command 454 or (defined($command_element_command->{'extra'}) 455 and defined($command_element_command->{'extra'}->{'associated_section'}) 456 and $command_element_command->{'extra'}->{'associated_section'} 457 eq $command))) { 458 my $count_elements_in_file = $self->count_elements_in_filename($target_filename); 459 if (defined($count_elements_in_file) and $count_elements_in_file == 1) { 460 $target = ''; 461 } 462 } 463 } 464 } 465 $href .= '#' . $target if ($target ne ''); 466 return $href; 467} 468 469my %contents_command_element_name = ( 470 'contents' => 'Contents', 471 'shortcontents' => 'Overview', 472 'summarycontents' => 'Overview', 473); 474 475# Return string for linking to $CONTENTS_OR_SHORTCONTENTS associated 476# element from $COMMAND with <a href> 477sub command_contents_href($$$$) 478{ 479 my $self = shift; 480 my $command = shift; 481 my $contents_or_shortcontents = shift; 482 my $filename = shift; 483 484 my $href; 485 my $name = $contents_command_element_name{$contents_or_shortcontents}; 486 487 my $target = $self->command_contents_target($command, $contents_or_shortcontents); 488 489 my $target_element = $self->special_element($name); 490 my $target_filename; 491 # !defined happens when called as convert() and not output() 492 if (defined($target_element)) { 493 $target_filename = $self->command_filename($target_element); 494 } 495 if (defined($target_filename) and 496 (!defined($filename) 497 or $filename ne $target_filename)) { 498 $href .= $target_filename; 499 } 500 $href .= '#' . $target if ($target ne ''); 501 return $href; 502} 503 504# Return text to be used for a hyperlink to $COMMAND. 505# $TYPE refers to the type of value returned from this function: 506# 'text' - return text 507# 'tree' - return a tree 508# 'tree_nonumber' - return tree representing text without a chapter number 509# being included. 510# 'string' - return simpler text that can be used in element attributes 511sub command_text($$;$) 512{ 513 my $self = shift; 514 my $command = shift; 515 my $type = shift; 516 517 if (!defined($type)) { 518 $type = 'text'; 519 } 520 if (!defined($command)) { 521 cluck "in command_text($type) command not defined"; 522 } 523 524 if ($command->{'manual_content'}) { 525 my $node_content = []; 526 $node_content = $command->{'node_content'} 527 if (defined($command->{'node_content'})); 528 my $tree; 529 if ($command->{'manual_content'}) { 530 $tree = {'type' => '_code', 531 'contents' => [{'text' => '('}, @{$command->{'manual_content'}}, 532 {'text' => ')'}, @$node_content]}; 533 } else { 534 $tree = {'type' => '_code', 535 'contents' => $node_content}; 536 } 537 if ($type eq 'tree') { 538 return $tree; 539 } else { 540 if ($type eq 'string') { 541 $tree = {'type' => '_string', 542 'contents' => [$tree]}; 543 } 544 my $result = $self->convert_tree_new_formatting_context( 545 $tree, $command->{'cmdname'}); 546 return $result; 547 } 548 } 549 550 my $target = $self->_get_target($command); 551 if ($target) { 552 my $explanation; 553 $explanation = "command_text \@$command->{'cmdname'}" 554 if ($command->{'cmdname'}); 555 if (defined($target->{$type})) { 556 return $target->{$type}; 557 } 558 my $tree; 559 if (!$target->{'tree'}) { 560 if ($command->{'extra'} 561 and $command->{'extra'}->{'special_element'}) { 562 my $special_element = $command->{'extra'}->{'special_element'}; 563 $tree = $self->get_conf('SPECIAL_ELEMENTS_NAME')->{$special_element}; 564 $explanation = "command_text $special_element"; 565 } elsif ($command->{'cmdname'} and ($command->{'cmdname'} eq 'node' 566 or $command->{'cmdname'} eq 'anchor')) { 567 $tree = {'type' => '_code', 568 'contents' => $command->{'extra'}->{'node_content'}}; 569 } elsif ($command->{'cmdname'} and ($command->{'cmdname'} eq 'float')) { 570 $tree = $self->_float_type_number($command); 571 } elsif ($command->{'extra'}->{'missing_argument'}) { 572 if ($type eq 'tree' or $type eq 'tree_nonumber') { 573 return {}; 574 } else { 575 return ''; 576 } 577 } else { 578 if (!$command->{'args'}->[0]->{'contents'}) { 579 cluck "No misc_content: " 580 .Texinfo::Common::_print_current($command); 581 } 582 if (defined($command->{'number'}) 583 and ($self->get_conf('NUMBER_SECTIONS') 584 or !defined($self->get_conf('NUMBER_SECTIONS')))) { 585 if ($command->{'cmdname'} eq 'appendix' and $command->{'level'} == 1) { 586 $tree = $self->gdt('Appendix {number} {section_title}', 587 {'number' => {'text' => $command->{'number'}}, 588 'section_title' 589 => {'contents' 590 => $command->{'args'}->[0]->{'contents'}}}); 591 } else { 592 $tree = $self->gdt('{number} {section_title}', 593 {'number' => {'text' => $command->{'number'}}, 594 'section_title' 595 => {'contents' 596 => $command->{'args'}->[0]->{'contents'}}}); 597 } 598 } else { 599 $tree = {'contents' => [@{$command->{'args'}->[0]->{'contents'}}]}; 600 } 601 602 $target->{'tree_nonumber'} 603 = {'contents' => $command->{'args'}->[0]->{'contents'}}; 604 } 605 $target->{'tree'} = $tree; 606 } else { 607 $tree = $target->{'tree'}; 608 } 609 return $target->{'tree_nonumber'} if ($type eq 'tree_nonumber' 610 and $target->{'tree_nonumber'}); 611 return $tree if ($type eq 'tree' or $type eq 'tree_nonumber'); 612 613 $self->_new_document_context($command->{'cmdname'}); 614 615 if ($type eq 'string') { 616 $tree = {'type' => '_string', 617 'contents' => [$tree]}; 618 } 619 620 if ($type =~ /^(.*)_nonumber$/) { 621 $tree = $target->{'tree_nonumber'} 622 if (defined($target->{'tree_nonumber'})); 623 } 624 $self->{'ignore_notice'}++; 625 $target->{$type} = $self->_convert($tree, $explanation); 626 $self->{'ignore_notice'}--; 627 628 pop @{$self->{'document_context'}}; 629 return $target->{$type}; 630 } 631 return undef; 632} 633 634# Return the element in the tree that $LABEL refers to. 635sub label_command($$) 636{ 637 my $self = shift; 638 my $label = shift; 639 return $self->{'labels'}->{$label}; 640} 641 642sub special_element($$) 643{ 644 my $self = shift; 645 my $type = shift; 646 return $self->{'special_elements_types'}->{$type}; 647} 648 649sub global_element($$) 650{ 651 my $self = shift; 652 my $type = shift; 653 return $self->{'global_target_elements'}->{$type}; 654} 655 656# it is considered 'top' only if element corresponds to @top or 657# element is a node 658sub element_is_top($$) 659{ 660 my $self = shift; 661 my $element = shift; 662 return ($self->{'global_target_elements'}->{'Top'} 663 and $self->{'global_target_elements'}->{'Top'} eq $element 664 and $element->{'extra'} 665 and (($element->{'extra'}->{'section'} 666 and $element->{'extra'}->{'section'}->{'cmdname'} eq 'top') 667 or ($element->{'extra'}->{'element_command'} 668 and $element->{'extra'}->{'element_command'}->{'cmdname'} eq 'node'))); 669} 670 671sub default_formatting_function($$) 672{ 673 my $self = shift; 674 my $format = shift; 675 return $self->{'default_formatting_functions'}->{$format}; 676} 677 678# used for customization only (in t2h_singular.init) 679sub get_value($$) 680{ 681 my $self = shift; 682 my $value = shift; 683 if (defined($self->{'parser'}) 684 and exists ($self->{'parser'}->{'values'}->{$value})) { 685 return $self->{'parser'}->{'values'}->{$value}; 686 } else { 687 return undef; 688 } 689} 690 691# This function should be used in formatting functions when some 692# Texinfo tree need to be converted. 693sub convert_tree_new_formatting_context($$;$$) 694{ 695 my $self = shift; 696 my $tree = shift; 697 my $context_string = shift; 698 my $multiple_pass = shift; 699 if (defined($context_string)) { 700 $self->_new_document_context($context_string); 701 } 702 if ($multiple_pass) { 703 $self->{'ignore_notice'}++; 704 push @{$self->{'multiple_pass'}}, $multiple_pass; 705 } 706 my $result = $self->convert_tree($tree); 707 if (defined($context_string)) { 708 pop @{$self->{'document_context'}}; 709 } 710 if ($multiple_pass) { 711 $self->{'ignore_notice'}--; 712 pop @{$self->{'multiple_pass'}}; 713 } 714 return $result; 715} 716 717# see http://www.w3.org/TR/REC-html40/types.html#type-links 718my %BUTTONS_REL = 719( 720 'Top', 'start', 721 'Contents', 'contents', 722 'Overview', '', 723 'Index', 'index', 724 'This', '', 725 'Back', 'prev', 726 'FastBack', '', 727 'Prev', 'prev', 728 'Up', 'up', 729 'Next', 'next', 730 'NodeUp', 'up', 731 'NodeNext', 'next', 732 'NodePrev', 'prev', 733 'NodeForward', '', 734 'NodeBack', '', 735 'Forward', 'next', 736 'FastForward', '', 737 'About' , 'help', 738 'First', '', 739 'Last', '', 740 'NextFile', 'next', 741 'PrevFile', 'prev', 742); 743 744my %BUTTONS_ACCESSKEY = 745( 746 'Top', '', 747 'Contents', '', 748 'Overview', '', 749 'Index', '', 750 'This', '', 751 'Back', 'p', 752 'FastBack', '', 753 'Prev', 'p', 754 'Up', 'u', 755 'Next', 'n', 756 'NodeUp', 'u', 757 'NodeNext', 'n', 758 'NodePrev', 'p', 759 'NodeForward', '', 760 'NodeBack', '', 761 'Forward', 'n', 762 'FastForward', '', 763 'About' , '', 764 'First', '', 765 'Last', '', 766 'NextFile', '', 767 'PrevFile', '', 768); 769 770my %BUTTONS_EXAMPLE = 771 ( 772 'Top', ' ', 773 'Contents', ' ', 774 'Overview', ' ', 775 'Index', ' ', 776 'This', '1.2.3', 777 'Back', '1.2.2', 778 'FastBack', '1', 779 'Prev', '1.2.2', 780 'Up', '1.2', 781 'Next', '1.2.4', 782 'NodeUp', '1.2', 783 'NodeNext', '1.2.4', 784 'NodePrev', '1.2.2', 785 'NodeForward', '1.2.4', 786 'NodeBack', '1.2.2', 787 'Forward', '1.2.4', 788 'FastForward', '2', 789 'About', ' ', 790 'First', '1.', 791 'Last', '1.2.4', 792 'NextFile', ' ', 793 'PrevFile', ' ', 794 ); 795 796 797# insert here name of icon images for buttons 798# Icons are used, if ICONS and resp. value are set 799my %ACTIVE_ICONS = ( 800 'Top', '', 801 'Contents', '', 802 'Overview', '', 803 'Index', '', 804 'This', '', 805 'Back', '', 806 'FastBack', '', 807 'Prev', '', 808 'Up', '', 809 'Next', '', 810 'NodeUp', '', 811 'NodeNext', '', 812 'NodePrev', '', 813 'NodeForward', '', 814 'NodeBack', '', 815 'Forward', '', 816 'FastForward', '', 817 'About' , '', 818 'First', '', 819 'Last', '', 820 'NextFile', '', 821 'PrevFile', '', 822 ' ', '', 823); 824 825# insert here name of icon images for these, if button is inactive 826my %PASSIVE_ICONS = ( 827 'Top', '', 828 'Contents', '', 829 'Overview', '', 830 'Index', '', 831 'This', '', 832 'Back', '', 833 'FastBack', '', 834 'Prev', '', 835 'Up', '', 836 'Next', '', 837 'NodeUp', '', 838 'NodeNext', '', 839 'NodePrev', '', 840 'NodeForward', '', 841 'NodeBack', '', 842 'Forward', '', 843 'FastForward', '', 844 'About', '', 845 'First', '', 846 'Last', '', 847 'NextFile', '', 848 'PrevFile', '', 849); 850 851my (%BUTTONS_TEXT, %BUTTONS_GOTO, %BUTTONS_NAME, %SPECIAL_ELEMENTS_NAME); 852 853my %defaults = ( 854 'ENABLE_ENCODING' => 0, 855 'FORMAT_MENU' => 'sectiontoc', 856 'OUTPUT_ENCODING_NAME' => 'utf-8', 857 'OUTFILE' => undef, 858 'SUBDIR' => undef, 859 'USE_NODES' => 1, 860 'USE_NODE_DIRECTIONS' => undef, 861 'CONTENTS_OUTPUT_LOCATION' => 'after_top', 862 'SPLIT' => 'node', 863# if set style is added in attribute. 864 'INLINE_CSS_STYLE' => 0, 865# if set, no css is used. 866 'NO_CSS' => 0, 867 'JS_WEBLABELS' => 'generate', 868 'JS_WEBLABELS_FILE' => 'js_licenses.html', # no clash with node name 869 'OPEN_QUOTE_SYMBOL' => '‘', 870 'CLOSE_QUOTE_SYMBOL' => '’', 871 'USE_ISO' => 1, 872 'TOP_FILE' => 'index.html', 873 'EXTENSION' => 'html', 874 'TOP_NODE_FILE_TARGET' => 'index.html', 875 'TRANSLITERATE_FILE_NAMES' => 1, 876 'USE_LINKS' => 1, 877 'USE_NUMERIC_ENTITY' => 1, 878 'ENABLE_ENCODING_USE_ENTITY' => 1, 879 'DATE_IN_HEADER' => 0, 880 'AVOID_MENU_REDUNDANCY' => 0, 881 'HEADERS' => 1, 882 'DO_ABOUT' => 0, 883 'USE_ACCESSKEY' => 1, 884 'USE_REL_REV' => 1, 885 'NODE_NAME_IN_MENU' => 1, 886 'NODE_NAME_IN_INDEX' => 1, 887 'XREF_USE_NODE_NAME_ARG' => undef, 888 'XREF_USE_FLOAT_LABEL' => 0, 889 'OVERVIEW_LINK_TO_TOC' => 1, 890 'COMPLEX_FORMAT_IN_TABLE' => 0, 891 'WORDS_IN_PAGE' => 300, 892 # _default_panel_button_dynamic_direction use nodes direction based on USE_NODE_DIRECTIONS 893 # or USE_NODES if USE_NODE_DIRECTIONS is undefined 894 'SECTION_BUTTONS' => [[ 'Next', \&_default_panel_button_dynamic_direction ], 895 [ 'Prev', \&_default_panel_button_dynamic_direction ], 896 [ 'Up', \&_default_panel_button_dynamic_direction ], ' ', 897 'Contents', 'Index'], 898 'SECTION_FOOTER_BUTTONS' => [[ 'Next', \&_default_panel_button_dynamic_direction_section_footer ], 899 [ 'Prev', \&_default_panel_button_dynamic_direction_section_footer ], 900 [ 'Up', \&_default_panel_button_dynamic_direction_section_footer ], ' ', 901 'Contents', 'Index'], 902 'LINKS_BUTTONS' => ['Top', 'Index', 'Contents', 'About', 903 'NodeUp', 'NodeNext', 'NodePrev'], 904 'NODE_FOOTER_BUTTONS' => [[ 'Next', \&_default_panel_button_dynamic_direction_node_footer ], 905 [ 'Prev', \&_default_panel_button_dynamic_direction_node_footer ], 906 [ 'Up', \&_default_panel_button_dynamic_direction_node_footer ], 907 ' ', 'Contents', 'Index'], 908 'misc_elements_targets' => { 909 'Overview' => 'SEC_Overview', 910 'Contents' => 'SEC_Contents', 911 'Footnotes' => 'SEC_Foot', 912 'About' => 'SEC_About', 913 'Top' => 'SEC_Top', 914 }, 915 'misc_pages_file_string' => { 916 'Contents' => '_toc', 917 'Overview' => '_ovr', 918 'Footnotes' => '_fot', 919 'About' => '_abt', 920 }, 921 'frame_pages_file_string' => { 922 'Frame' => '_frame', 923 'Toc_Frame' => '_toc_frame', 924 }, 925 'misc_elements_order' => ['Footnotes', 'Contents', 'Overview', 'About'], 926 'DOCTYPE' => '<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">', 927 'FRAMESET_DOCTYPE' => '<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Frameset//EN" "http://www.w3.org/TR/html4/frameset.dtd">', 928 'DEFAULT_RULE' => '<hr>', 929 'BIG_RULE' => '<hr>', 930 'MENU_SYMBOL' => '•', 931 'MENU_ENTRY_COLON' => ':', 932 'INDEX_ENTRY_COLON' => ':', 933 'BODYTEXT' => undef, 934 'documentlanguage' => 'en', 935 'xrefautomaticsectiontitle' => 'on', 936 'SHOW_TITLE' => 1, 937 'SECTION_NAME_IN_TITLE' => 0, 938 'USE_TITLEPAGE_FOR_TITLE' => 0, 939 'MONOLITHIC' => 1, 940 'CHAPTER_HEADER_LEVEL' => 2, 941 'MAX_HEADER_LEVEL' => 4, 942 'FOOTNOTE_END_HEADER_LEVEL' => 4, 943 'FOOTNOTE_SEPARATE_HEADER_LEVEL' => 4, 944 945 'BUTTONS_REL' => \%BUTTONS_REL, 946 'BUTTONS_ACCESSKEY' => \%BUTTONS_ACCESSKEY, 947 'BUTTONS_EXAMPLE' => \%BUTTONS_EXAMPLE, 948 'BUTTONS_GOTO' => \%BUTTONS_GOTO, 949 'BUTTONS_NAME' => \%BUTTONS_NAME, 950 'BUTTONS_TEXT' => \%BUTTONS_TEXT, 951 'ACTIVE_ICONS' => \%ACTIVE_ICONS, 952 'PASSIVE_ICONS' => \%PASSIVE_ICONS, 953 'SPECIAL_ELEMENTS_NAME' => \%SPECIAL_ELEMENTS_NAME, 954 'SPECIAL_ELEMENTS_CLASS' => { 955 'About' => 'about', 956 'Contents' => 'contents', 957 'Overview' => 'shortcontents', 958 'Footnotes' => 'footnotes', 959 }, 960 'jslicenses' => {}, # for outputting licences file 961 'jslicenses_element' => {}, # scripts used in current output file 962 'jslicenses_math' => {}, # MathJax scripts 963 'jslicenses_infojs' => {}, # info.js scripts 964 'element_math' => 0, # whether math has been seen in current file 965 'COPIABLE_ANCHORS' => 1, 966 967 'output_format' => 'html', 968); 969 970foreach my $buttons ('CHAPTER_BUTTONS', 'MISC_BUTTONS', 'TOP_BUTTONS') { 971 $defaults{$buttons} = [@{$defaults{'SECTION_BUTTONS'}}]; 972} 973 974foreach my $buttons ('SECTION_FOOTER_BUTTONS', 'CHAPTER_FOOTER_BUTTONS') { 975 $defaults{$buttons} = [@{$defaults{'SECTION_FOOTER_BUTTONS'}}]; 976} 977 978 979my @global_directions = ('First', 'Last', 'Index', 'Top'); 980my %global_and_special_directions; 981foreach my $global_direction (@global_directions) { 982 $global_and_special_directions{$global_direction} = 1; 983} 984foreach my $special_element (keys %{$defaults{'SPECIAL_ELEMENTS_CLASS'}}) { 985 $global_and_special_directions{$special_element} = 1; 986} 987 988foreach my $hash (\%BUTTONS_REL, \%BUTTONS_ACCESSKEY, 989 \%ACTIVE_ICONS, \%PASSIVE_ICONS) { 990 foreach my $button (grep {not exists($global_and_special_directions{$_}) and $_ ne ' '} keys %$hash) { 991 $hash->{'FirstInFile'.$button} = $hash->{$button}; 992 } 993} 994 995sub _translate_names($) 996{ 997 my $self = shift; 998 #print STDERR "encoding_name: ".$self->get_conf('OUTPUT_ENCODING_NAME')." documentlanguage: ".$self->get_conf('documentlanguage')."\n"; 999 1000 1001 %BUTTONS_TEXT = ( 1002 'Top', $self->gdt('Top'), 1003 'Contents', $self->gdt('Contents'), 1004 'Overview', $self->gdt('Overview'), 1005 'Index', $self->gdt('Index'), 1006 ' ', ' ', 1007 'This', $self->gdt('current'), 1008 'Back', ' < ', 1009 'FastBack', ' << ', 1010 'Prev', $self->gdt('Prev'), 1011 'Up', $self->gdt(' Up '), 1012 'Next', $self->gdt('Next'), 1013 #'NodeUp', $self->gdt('Node up'), 1014 'NodeUp', $self->gdt('Up'), 1015 #'NodeNext', $self->gdt('Next node'), 1016 'NodeNext', $self->gdt('Next'), 1017 #'NodePrev', $self->gdt('Previous node'), 1018 'NodePrev', $self->gdt('Previous'), 1019 'NodeForward', $self->gdt('Forward node'), 1020 'NodeBack', $self->gdt('Back node'), 1021 'Forward', ' > ', 1022 'FastForward', ' >> ', 1023 'About', ' ? ', 1024 'First', ' |< ', 1025 'Last', ' >| ', 1026 'NextFile', $self->gdt('Next file'), 1027 'PrevFile', $self->gdt('Previous file'), 1028 ); 1029 1030 foreach my $button (grep {not exists($global_and_special_directions{$_}) and $_ ne ' '} keys %BUTTONS_TEXT) { 1031 $BUTTONS_TEXT{'FirstInFile'.$button} = $BUTTONS_TEXT{$button}; 1032 } 1033 1034 #%BUTTONS_TEXT = %NAVIGATION_TEXT; 1035 1036 %BUTTONS_GOTO = ( 1037 'Top', $self->gdt('Cover (top) of document'), 1038 'Contents', $self->gdt('Table of contents'), 1039 'Overview', $self->gdt('Short table of contents'), 1040 'Index', $self->gdt('Index'), 1041 'This', $self->gdt('Current section'), 1042 'Back', $self->gdt('Previous section in reading order'), 1043 'FastBack', $self->gdt('Beginning of this chapter or previous chapter'), 1044 'Prev', $self->gdt('Previous section on same level'), 1045 'Up', $self->gdt('Up section'), 1046 'Next', $self->gdt('Next section on same level'), 1047 'NodeUp', $self->gdt('Up node'), 1048 'NodeNext', $self->gdt('Next node'), 1049 'NodePrev', $self->gdt('Previous node'), 1050 'NodeForward', $self->gdt('Next node in node reading order'), 1051 'NodeBack', $self->gdt('Previous node in node reading order'), 1052 'Forward', $self->gdt('Next section in reading order'), 1053 'FastForward', $self->gdt('Next chapter'), 1054 'About' , $self->gdt('About (help)'), 1055 'First', $self->gdt('First section in reading order'), 1056 'Last', $self->gdt('Last section in reading order'), 1057 'NextFile', $self->gdt('Forward section in next file'), 1058 'PrevFile', $self->gdt('Back section in previous file'), 1059 ); 1060 1061 foreach my $button (grep {not exists($global_and_special_directions{$_}) and $_ ne ' '} keys %BUTTONS_GOTO) { 1062 $BUTTONS_GOTO{'FirstInFile'.$button} = $BUTTONS_GOTO{$button}; 1063 } 1064 1065 %BUTTONS_NAME = ( 1066 'Top', $self->gdt('Top'), 1067 'Contents', $self->gdt('Contents'), 1068 'Overview', $self->gdt('Overview'), 1069 'Index', $self->gdt('Index'), 1070 ' ', ' ', 1071 'This', $self->gdt('This'), 1072 'Back', $self->gdt('Back'), 1073 'FastBack', $self->gdt('FastBack'), 1074 'Prev', $self->gdt('Prev'), 1075 'Up', $self->gdt('Up'), 1076 'Next', $self->gdt('Next'), 1077 'NodeUp', $self->gdt('NodeUp'), 1078 'NodeNext', $self->gdt('NodeNext'), 1079 'NodePrev', $self->gdt('NodePrev'), 1080 'NodeForward', $self->gdt('NodeForward'), 1081 'NodeBack', $self->gdt('NodeBack'), 1082 'Forward', $self->gdt('Forward'), 1083 'FastForward', $self->gdt('FastForward'), 1084 'About', $self->gdt('About'), 1085 'First', $self->gdt('First'), 1086 'Last', $self->gdt('Last'), 1087 'NextFile', $self->gdt('NextFile'), 1088 'PrevFile', $self->gdt('PrevFile'), 1089 ); 1090 1091 foreach my $button (grep {not exists($global_and_special_directions{$_}) and $_ ne ' '} keys %BUTTONS_NAME) { 1092 $BUTTONS_NAME{'FirstInFile'.$button} = $BUTTONS_NAME{$button}; 1093 } 1094 1095 %SPECIAL_ELEMENTS_NAME = ( 1096 'About' => $self->gdt('About This Document'), 1097 'Contents' => $self->gdt('Table of Contents'), 1098 'Overview' => $self->gdt('Short Table of Contents'), 1099 'Footnotes' => $self->gdt('Footnotes'), 1100 ); 1101 1102 # delete the tree and formatted results for special elements 1103 # such that they are redone with the new tree when needed. 1104 foreach my $special_element (keys (%SPECIAL_ELEMENTS_NAME)) { 1105 if ($self->{'special_elements_types'}->{$special_element} and 1106 $self->{'targets'}->{$self->{'special_elements_types'}->{$special_element}}) { 1107 my $target 1108 = $self->{'targets'}->{$self->{'special_elements_types'}->{$special_element}}; 1109 foreach my $key ('text', 'string', 'tree') { 1110 delete $target->{$key}; 1111 } 1112 } 1113 } 1114 1115 foreach my $hash (\%BUTTONS_TEXT, \%BUTTONS_GOTO, \%BUTTONS_NAME) { 1116 foreach my $button (keys (%$hash)) { 1117 if (ref($hash->{$button})) { 1118 $hash->{$button} = $self->convert_tree_new_formatting_context( 1119 $hash->{$button}, "button $button"); 1120 } 1121 } 1122 } 1123 if ($self->{'commands_translation'}) { 1124 my %translated_commands; 1125 foreach my $context ('normal', 'preformatted', 'string') { 1126 foreach my $command (keys(%{$self->{'commands_translation'}->{$context}})) { 1127 $translated_commands{$command} = 1; 1128 delete $self->{'commands_formatting'}->{$context}->{$command}; 1129 if (defined($self->{'commands_translation'}->{$context}->{$command})) { 1130 $self->{'commands_formatting'}->{$context}->{$command} 1131 = $self->gdt($self->{'commands_translation'}->{$context}->{$command}, 1132 undef, 'translated_text'); 1133 } 1134 } 1135 } 1136 foreach my $command(keys(%translated_commands)) { 1137 $self->_complete_commands_formatting($command); 1138 } 1139 } 1140} 1141 1142 1143sub converter_defaults($$) 1144{ 1145 my $self = shift; 1146 my $conf = shift; 1147 if (defined($conf->{'TEXI2HTML'})) { 1148 _set_variables_texi2html(); 1149 } 1150 return %defaults; 1151} 1152 1153my $NO_BULLET_LIST_STYLE = 'list-style: none'; 1154my $NO_BULLET_LIST_CLASS = 'no-bullet'; 1155my $NO_BULLET_LIST_ATTRIBUTE = ' class="'.$NO_BULLET_LIST_CLASS.'"'; 1156 1157my $MENU_PRE_STYLE = 'font-family: serif'; 1158 1159my %css_map = ( 1160 "ul.$NO_BULLET_LIST_CLASS" => "$NO_BULLET_LIST_STYLE", 1161 'pre.menu-comment' => "$MENU_PRE_STYLE", 1162 'pre.menu-preformatted' => "$MENU_PRE_STYLE", 1163 'a.summary-letter' => 'text-decoration: none', 1164 'pre.display' => 'font-family: inherit', 1165 'span.sansserif' => 'font-family: sans-serif; font-weight: normal', 1166 'span.roman' => 'font-family: initial; font-weight: normal', 1167 'span.nolinebreak' => 'white-space: nowrap', 1168 'kbd' => 'font-style: oblique', 1169 1170 # The anchor element is wrapped in a <span> rather than a block level 1171 # element to avoid it appearing unless the mouse pointer is directly 1172 # over the text, as it is annoying for anchors to flicker when 1173 # you are moving your pointer elsewhere. "line-height: 0em" stops the 1174 # invisible text from changing vertical spacing. 1175 'a.copiable-anchor' => 'visibility: hidden; ' 1176 .'text-decoration: none; line-height: 0em', 1177 'span:hover a.copiable-anchor' => 'visibility: visible', 1178); 1179 1180$css_map{'pre.format'} = $css_map{'pre.display'}; 1181 1182my %preformatted_commands_context = %preformatted_commands; 1183$preformatted_commands_context{'verbatim'} = 1; 1184 1185my %pre_class_commands; 1186foreach my $preformatted_command (keys(%preformatted_commands_context)) { 1187 # no class for the @small* variants 1188 if ($small_alias{$preformatted_command}) { 1189 $pre_class_commands{$preformatted_command} = $small_alias{$preformatted_command}; 1190 } else { 1191 $pre_class_commands{$preformatted_command} = $preformatted_command; 1192 } 1193} 1194$pre_class_commands{'menu'} = 'menu-preformatted'; 1195$pre_class_types{'menu_comment'} = 'menu-comment'; 1196 1197my %indented_preformatted_commands; 1198foreach my $indented_format ('example', 'display', 'lisp') { 1199 $indented_preformatted_commands{$indented_format} = 1; 1200 $indented_preformatted_commands{"small$indented_format"} = 1; 1201 1202 $css_map{"div.$indented_format"} = 'margin-left: 3.2em'; 1203} 1204delete $css_map{"div.lisp"}; # output as div.example instead 1205 1206$css_map{"blockquote.indentedblock"} = 'margin-right: 0em'; 1207 1208# types that are in code style in the default case. '_code' is not 1209# a type that can appear in the tree built from Texinfo code, it is used 1210# to format a tree fragment as if it was in a @code @-command. 1211my %default_code_types = ( 1212 '_code' => 1, 1213); 1214 1215# default specification of arguments formatting 1216my %default_commands_args = ( 1217 'email' => [['monospace', 'monospacestring'], ['normal']], 1218 'anchor' => [['monospacestring']], 1219 'uref' => [['monospacestring'], ['normal'], ['normal']], 1220 'url' => [['monospacestring'], ['normal'], ['normal']], 1221 'printindex' => [[]], 1222 'sp' => [[]], 1223 'inforef' => [['monospace'],['normal'],['monospacetext']], 1224 'xref' => [['monospace'],['normal'],['normal'],['monospacetext'],['normal']], 1225 'pxref' => [['monospace'],['normal'],['normal'],['monospacetext'],['normal']], 1226 'ref' => [['monospace'],['normal'],['normal'],['monospacetext'],['normal']], 1227 'image' => [['monospacetext'],['monospacetext'],['monospacetext'],['string', 'normal'],['monospacetext']], 1228 # FIXME shouldn't it better not to convert if later ignored? 1229 'inlinefmt' => [['monospacetext'],['normal']], 1230 'inlinefmtifelse' => [['monospacetext'],['normal'],['normal']], 1231 'inlineraw' => [['monospacetext'],['raw']], 1232 'inlineifclear' => [['monospacetext'],['normal']], 1233 'inlineifset' => [['monospacetext'],['normal']], 1234 'item' => [[]], 1235 'itemx' => [[]], 1236); 1237 1238foreach my $explained_command (keys(%explained_commands)) { 1239 $default_commands_args{$explained_command} 1240 = [['normal'], ['string']]; 1241} 1242 1243# Return the default for the function references used for 1244# the formatting of commands, in case a user still wants to call 1245# default @-commands formatting functions when replacing functions, 1246# using code along 1247# &{$self->default_commands_conversion($cmdname)}($self, $cmdname, $command, $content) 1248my %default_commands_conversion; 1249 1250sub default_commands_conversion($$) 1251{ 1252 my $self = shift; 1253 my $command = shift; 1254 return $default_commands_conversion{$command}; 1255} 1256 1257my %kept_misc_commands; 1258 1259my @informative_global_commands = ('contents', 'shortcontents', 1260 'summarycontents', 'allowcodebreaks', 'documentlanguage', 1261 'footnotestyle', 'documentencoding', 1262 'xrefautomaticsectiontitle', 'deftypefnnewline'); 1263# taken from global 1264# 'documentencoding' 1265# 'novalidate' 1266foreach my $misc_command(@informative_global_commands, 1267 'verbatiminclude', 'insertcopying', 'printindex', 'listoffloats', 1268 'author', 'subtitle', 1269 'title', keys(%default_index_commands), 1270 keys(%formatting_misc_commands)) { 1271 $kept_misc_commands{$misc_command} = 1; 1272} 1273 1274sub converter_global_commands($) 1275{ 1276 return @informative_global_commands; 1277} 1278 1279foreach my $misc_command (keys(%misc_commands)) { 1280 $default_commands_conversion{$misc_command} = undef 1281 unless ($kept_misc_commands{$misc_command}); 1282} 1283 1284foreach my $ignored_brace_commands ('caption', 'shortcaption', 1285 'hyphenation', 'sortas') { 1286 $default_commands_conversion{$ignored_brace_commands} = undef; 1287} 1288 1289# commands that leads to advancing the paragraph number. This is mostly 1290# used to determine the first line, in fact. 1291my %advance_paragraph_count_commands; 1292foreach my $command (keys(%block_commands)) { 1293 next if ($menu_commands{$command} 1294 or $block_commands{$command} eq 'raw'); 1295 $advance_paragraph_count_commands{$command} = 1; 1296} 1297 1298foreach my $ignored_block_commands ('ignore', 'macro', 'rmacro', 'copying', 1299 'documentdescription', 'titlepage', 'direntry') { 1300 $default_commands_conversion{$ignored_block_commands} = undef; 1301}; 1302 1303# Formatting of commands without args 1304 1305# The hash holding the defaults for the formatting of 1306# most commands without args. It has three contexts as keys, 1307# 'normal' in normal text, 'preformatted' in @example and similar 1308# commands, and 'string' for contexts where HTML elements should not 1309# be used. 1310my %default_commands_formatting; 1311 1312foreach my $command (keys(%{$Texinfo::Convert::Converter::default_xml_commands_formatting{'normal'}})) { 1313 $default_commands_formatting{'normal'}->{$command} = 1314 $Texinfo::Convert::Converter::default_xml_commands_formatting{'normal'}->{$command}; 1315} 1316 1317$default_commands_formatting{'normal'}->{' '} = ' '; 1318$default_commands_formatting{'normal'}->{"\t"} = ' '; 1319$default_commands_formatting{'normal'}->{"\n"} = ' '; 1320 1321my %default_commands_translation; 1322# possible example of use, right now not used, as the generic 1323# translated command with gdt tree is used. 1324#$default_commands_translation{'normal'}->{'error'} = 'error-->'; 1325## This is used to have gettext pick up the chain to be translated 1326#if (0) { 1327# my $not_existing; 1328# $not_existing->gdt('error-->'); 1329#} 1330 1331$default_commands_formatting{'normal'}->{'enddots'} 1332 = '<small class="enddots">...</small>'; 1333$default_commands_formatting{'preformatted'}->{'enddots'} = '...'; 1334$default_commands_formatting{'normal'}->{'*'} = '<br>'; 1335$default_commands_formatting{'preformatted'}->{'*'} = "\n"; 1336 1337 1338sub _convert_no_arg_command($$$) 1339{ 1340 my $self = shift; 1341 my $cmdname = shift; 1342 my $command = shift; 1343 1344 if ($cmdname eq 'click' and $command->{'extra'} 1345 and exists($command->{'extra'}->{'clickstyle'})) { 1346 my $click_cmdname = $command->{'extra'}->{'clickstyle'}; 1347 if (($self->in_preformatted() or $self->in_math() 1348 and $self->{'commands_formatting'}->{'preformatted'}->{$click_cmdname}) 1349 or ($self->in_string() and 1350 $self->{'commands_formatting'}->{'string'}->{$click_cmdname}) 1351 or ($self->{'commands_formatting'}->{'normal'}->{$click_cmdname})) { 1352 $cmdname = $click_cmdname; 1353 } 1354 } 1355 if ($self->in_upper_case() and $letter_no_arg_commands{$cmdname} 1356 and $self->{'commands_formatting'}->{'normal'}->{uc($cmdname)}) { 1357 $cmdname = uc($cmdname); 1358 } 1359 1360 my $result; 1361 if ($self->{'translated_commands'}->{$cmdname}) { 1362 return $self->convert_tree( 1363 $self->gdt($self->{'translated_commands'}->{$cmdname})); 1364 } 1365 if ($self->in_preformatted() or $self->in_math()) { 1366 $result = $self->{'commands_formatting'}->{'preformatted'}->{$cmdname}; 1367 } elsif ($self->in_string()) { 1368 $result = $self->{'commands_formatting'}->{'string'}->{$cmdname}; 1369 } else { 1370 $result = $self->{'commands_formatting'}->{'normal'}->{$cmdname}; 1371 } 1372 return $result; 1373} 1374 1375foreach my $command(keys(%{$default_commands_formatting{'normal'}})) { 1376 $default_commands_conversion{$command} = \&_convert_no_arg_command; 1377} 1378 1379sub _convert_today_command($$$) 1380{ 1381 my $self = shift; 1382 my $cmdname = shift; 1383 my $command = shift; 1384 1385 my $tree = $self->Texinfo::Common::expand_today(); 1386 return $self->convert_tree($tree); 1387} 1388 1389$default_commands_conversion{'today'} = \&_convert_today_command; 1390 1391# style commands 1392 1393my %quoted_style_commands; 1394foreach my $quoted_command ('samp') { 1395 $quoted_style_commands{$quoted_command} = 1; 1396} 1397 1398my %style_attribute_commands; 1399$style_attribute_commands{'normal'} = { 1400 'b' => 'b', 1401 'cite' => 'cite', 1402 'code' => 'code', 1403 'command' => 'code', 1404 'dfn' => 'em', 1405 'emph' => 'em', 1406 'env' => 'code', 1407 'file' => 'samp', 1408 'headitemfont' => 'b', # not really that, in fact it is 1409 # in <th> rather than <td> 1410 'i' => 'i', 1411 'slanted' => 'i', 1412 'sansserif' => 'span class="sansserif"', 1413 'kbd' => 'kbd', 1414 'option' => 'samp', 1415 'r' => 'span class="roman"', 1416 'samp' => 'samp', 1417 'sc' => 'small', 1418 'strong' => 'strong', 1419 'sub' => 'sub', 1420 'sup' => 'sup', 1421 't' => 'tt', 1422 'var' => 'var', 1423 'verb' => 'tt', 1424}; 1425 1426my %style_commands_formatting; 1427 1428# this weird construct does like uniq, it avoids duplicates. 1429# it is required since math is not in the %style_commands as it is 1430# in context command. 1431my @all_style_commands = keys %{{ map { $_ => 1 } 1432 (keys(%style_commands), keys(%{$style_attribute_commands{'normal'}}), 1433 'dmn') }}; 1434 1435foreach my $command(@all_style_commands) { 1436 # default is no attribute. 1437 if ($style_attribute_commands{'normal'}->{$command}) { 1438 $style_commands_formatting{'normal'}->{$command}->{'attribute'} 1439 = $style_attribute_commands{'normal'}->{$command}; 1440 $style_commands_formatting{'preformatted'}->{$command}->{'attribute'} 1441 = $style_attribute_commands{'normal'}->{$command}; 1442 } 1443 if ($style_attribute_commands{'preformatted'}->{$command}) { 1444 $style_commands_formatting{'preformatted'}->{$command}->{'attribute'} = 1445 $style_attribute_commands{'preformatted'}->{$command}; 1446 } 1447 if ($quoted_style_commands{$command}) { 1448 foreach my $context ('normal', 'string', 'preformatted') { 1449 $style_commands_formatting{$context}->{$command}->{'quote'} = 1; 1450 } 1451 } 1452 $default_commands_conversion{$command} = \&_convert_style_command; 1453} 1454 1455delete $style_commands_formatting{'preformatted'}->{'sc'}->{'attribute'}; 1456delete $style_commands_formatting{'preformatted'}->{'sc'}; 1457 1458sub _parse_attribute($) 1459{ 1460 my $element = shift; 1461 return ('', '', '') if (!defined($element)); 1462 my ($class, $attributes) = ('', ''); 1463 if ($element =~ /^(\w+)(\s+.*)/) 1464 { 1465 $element = $1; 1466 $attributes = $2; 1467 if ($attributes =~ s/^\s+class=\"([^\"]+)\"//) { 1468 $class = $1; 1469 } 1470 } 1471 return ($element, $class, $attributes); 1472} 1473 1474sub _convert_style_command($$$$) 1475{ 1476 my $self = shift; 1477 my $cmdname = shift; 1478 my $command = shift; 1479 my $args = shift; 1480 1481 my $text = $args->[0]->{'normal'}; 1482 if (!defined($text)) { 1483 # happens with bogus @-commands without argument, like @strong something 1484 #cluck "text not defined in _convert_style_command"; 1485 return ''; 1486 } 1487 # handle the effect of kbdinputstyle 1488 if ($cmdname eq 'kbd' and $command->{'extra'} 1489 and $command->{'extra'}->{'code'}) { 1490 $cmdname = 'code'; 1491 } 1492 1493 my $attribute_hash = {}; 1494 if ($self->in_preformatted()) { 1495 $attribute_hash = $self->{'style_commands_formatting'}->{'preformatted'}; 1496 } elsif (!$self->in_string()) { 1497 $attribute_hash = $self->{'style_commands_formatting'}->{'normal'}; 1498 } 1499 if (defined($attribute_hash->{$cmdname})) { 1500 if (defined($attribute_hash->{$cmdname}->{'attribute'})) { 1501 my ($style, $class, $attribute_text) 1502 = _parse_attribute ($attribute_hash->{$cmdname}->{'attribute'}); 1503 my $open = $self->_attribute_class($style, $class); 1504 if ($open ne '') { 1505 $text = $open . "$attribute_text>" 1506 . $text . "</$style>"; 1507 } elsif ($attribute_text ne '') { 1508 $text = "<$style $attribute_text>". $text . "</$style>"; 1509 } 1510 } 1511 if (defined($attribute_hash->{$cmdname}->{'quote'})) { 1512 $text = $self->get_conf('OPEN_QUOTE_SYMBOL') . $text 1513 . $self->get_conf('CLOSE_QUOTE_SYMBOL'); 1514 } 1515 } 1516 return $text; 1517} 1518 1519sub _convert_w_command($$$$) 1520{ 1521 my $self = shift; 1522 my $cmdname = shift; 1523 my $command = shift; 1524 my $args = shift; 1525 my $text = $args->[0]->{'normal'}; 1526 if (!defined($text)) { 1527 $text = ''; 1528 } 1529 if ($self->in_string) { 1530 return $text; 1531 } else { 1532 return $text . '<!-- /@w -->'; 1533 } 1534} 1535$default_commands_conversion{'w'} = \&_convert_w_command; 1536 1537sub _convert_value_command($$$$) 1538{ 1539 my $self = shift; 1540 my $cmdname = shift; 1541 my $command = shift; 1542 my $args = shift; 1543 1544 return $self->convert_tree($self->gdt('@{No value for `{value}\'@}', 1545 {'value' => $command->{'type'}})); 1546} 1547 1548$default_commands_conversion{'value'} = \&_convert_value_command; 1549 1550sub _convert_email_command($$$$) 1551{ 1552 my $self = shift; 1553 my $cmdname = shift; 1554 my $command = shift; 1555 my $args = shift; 1556 1557 my $mail_arg = shift @$args; 1558 my $text_arg = shift @$args; 1559 my $mail = ''; 1560 my $mail_string = ''; 1561 if (defined($mail_arg)) { 1562 $mail = $mail_arg->{'monospace'}; 1563 $mail_string = $mail_arg->{'monospacestring'}; 1564 } 1565 my $text = ''; 1566 if (defined($text_arg)) { 1567 $text = $text_arg->{'normal'}; 1568 } 1569 $text = $mail unless ($text ne ''); 1570 return $text if ($mail eq ''); 1571 if ($self->in_string()) { 1572 return "$mail_string ($text)"; 1573 } else { 1574 return "<a href=\"mailto:$mail_string\">$text</a>"; 1575 } 1576} 1577 1578$default_commands_conversion{'email'} = \&_convert_email_command; 1579 1580sub _convert_explained_command($$$$) 1581{ 1582 my $self = shift; 1583 my $cmdname = shift; 1584 my $command = shift; 1585 my $args = shift; 1586 1587 my $with_explanation; 1588 my $explanation_result; 1589 my $explanation_string; 1590 my $normalized_type 1591 = Texinfo::Convert::NodeNameNormalization::normalize_node( 1592 {'contents' => $command->{'args'}->[0]->{'contents'}}); 1593 1594 if ($args->[1] and defined($args->[1]->{'string'}) 1595 and $args->[1]->{'string'} =~ /\S/) { 1596 $with_explanation = 1; 1597 $explanation_string = $args->[1]->{'string'}; 1598 1599 # Convert the expanation of the acronym. Must do this before we save 1600 # the explanation for the future, otherwise we get infinite recursion 1601 # for recursively-defined acronyms. 1602 $explanation_result = $self->convert_tree( $args->[1]->{'tree'} ); 1603 1604 $self->{'explained_commands'}->{$cmdname}->{$normalized_type} = 1605 $command->{'args'}->[1]->{'contents'}; 1606 } elsif ($command->{'extra'}->{'explanation_contents'}) { 1607 if (@{$command->{'extra'}->{'explanation_contents'}}) { 1608 $explanation_string = $self->convert_tree_new_formatting_context( 1609 {'type' => '_string', 1610 'contents' => $command->{'extra'}->{'explanation_contents'}}, 1611 $cmdname, $cmdname); 1612 } 1613 } elsif ($self->{'explained_commands'}->{$cmdname}->{$normalized_type}) { 1614 $explanation_string = $self->convert_tree_new_formatting_context( 1615 {'type' => '_string', 1616 'contents' => $self->{'explained_commands'} 1617 ->{$cmdname}->{$normalized_type}}, 1618 $cmdname, $cmdname); 1619 1620 $command->{'extra'}->{'explanation_contents'} 1621 = $self->{'explained_commands'}->{$cmdname}->{$normalized_type}; 1622 } else { 1623 # Avoid ever giving an explanation for this element. This prevents 1624 # infinite recursion for a recursively-defined acronym, when an 1625 # @acronym within the explanation could end up referring to the 1626 # containing @acronym. 1627 1628 $command->{'extra'}->{'explanation_contents'} = []; 1629 } 1630 my $result = $args->[0]->{'normal'}; 1631 if (!$self->in_string()) { 1632 if (defined($explanation_string)) { 1633 $result = "<$cmdname title=\"$explanation_string\">".$result; 1634 } else { 1635 $result = "<$cmdname>".$result; 1636 } 1637 $result .= "</$cmdname>"; 1638 } 1639 if ($with_explanation) { 1640 $result = $self->convert_tree($self->gdt('{explained_string} ({explanation})', 1641 {'explained_string' => {'type' => '_converted', 1642 'text' => $result}, 1643 'explanation' => {'type' => '_converted', 1644 'text' => $explanation_result}})); 1645 } 1646 1647 return $result; 1648} 1649 1650foreach my $explained_command (keys(%explained_commands)) { 1651 $default_commands_conversion{$explained_command} 1652 = \&_convert_explained_command; 1653} 1654 1655sub _convert_anchor_command($$$$) 1656{ 1657 my $self = shift; 1658 my $cmdname = shift; 1659 my $command = shift; 1660 my $args = shift; 1661 1662 my $id = $self->command_id($command); 1663 if (defined($id) and $id ne '' and !@{$self->{'multiple_pass'}} 1664 and !$self->in_string()) { 1665 return "<span id=\"$id\"></span>"; 1666 } 1667 return ''; 1668} 1669 1670$default_commands_conversion{'anchor'} = \&_convert_anchor_command; 1671 1672my $foot_num; 1673my $foot_lines; 1674my $NO_NUMBER_FOOTNOTE_SYMBOL = '*'; 1675 1676my $footid_base = 'FOOT'; 1677my $docid_base = 'DOCF'; 1678 1679# to avoid duplicate names, use a prefix that cannot happen in anchors 1680my $target_prefix = "t_h"; 1681my %footnote_id_numbers; 1682sub _convert_footnote_command($$$$) 1683{ 1684 my $self = shift; 1685 my $cmdname = shift; 1686 my $command = shift; 1687 my $args = shift; 1688 1689 my $number_in_doc; 1690 $foot_num++; 1691 if ($self->get_conf('NUMBER_FOOTNOTES')) { 1692 $number_in_doc = $foot_num; 1693 } else { 1694 $number_in_doc = $NO_NUMBER_FOOTNOTE_SYMBOL; 1695 } 1696 1697 return "($number_in_doc)" if ($self->in_string()); 1698 #print STDERR "FOOTNOTE $command\n"; 1699 my $footid = $self->command_target($command); 1700 1701 # happens for bogus footnotes 1702 if (!defined($footid)) { 1703 return ''; 1704 } 1705 1706 # ID for linking back to the main text from the footnote. 1707 my $docid = $footid; 1708 $docid =~ s/^$footid_base/$docid_base/; 1709 1710 my $document_filename; 1711 my $footnote_filename; 1712 if ($self->get_conf('footnotestyle') eq 'separate') { 1713 $footnote_filename = $self->command_filename($command); 1714 $document_filename = $self->{'current_filename'}; 1715 $footnote_filename = '' if (!defined($footnote_filename)); 1716 $document_filename = '' if (!defined($document_filename)); 1717 1718 if ($document_filename eq $footnote_filename) { 1719 $document_filename = $footnote_filename = ''; 1720 } 1721 } else { 1722 $document_filename = $footnote_filename = ''; 1723 } 1724 my $footnote_text; 1725 if ($args->[0]) { 1726 $footnote_text = $args->[0]->{'normal'}; 1727 } else { 1728 $footnote_text = ''; 1729 } 1730 chomp ($footnote_text); 1731 $footnote_text .= "\n"; 1732 1733 if (@{$self->{'multiple_pass'}}) { 1734 $footid = $target_prefix.$self->{'multiple_pass'}->[-1].'_'.$footid.'_'.$foot_num; 1735 $docid = $target_prefix.$self->{'multiple_pass'}->[-1].'_'.$docid.'_'.$foot_num; 1736 } else { 1737 if (!defined($footnote_id_numbers{$footid})) { 1738 $footnote_id_numbers{$footid} = $foot_num; 1739 } else { 1740 # This should rarely happen, except for @footnote is @copying and 1741 # multiple @insertcopying... 1742 # Here it is not checked that there is no clash with another anchor. 1743 # However, unless there are more than 1000 footnotes this should not 1744 # happen. 1745 $footid .= '_'.$foot_num; 1746 $docid .= '_'.$foot_num; 1747 } 1748 } 1749 1750 $foot_lines .= '<h5>' . 1751 "<a id=\"$footid\" href=\"$document_filename#$docid\">($number_in_doc)</a></h5>\n" 1752 . $footnote_text; 1753 1754 my $footnote_number_text; 1755 if ($self->in_preformatted()) { 1756 $footnote_number_text = "($number_in_doc)"; 1757 } else { 1758 $footnote_number_text = "<sup>$number_in_doc</sup>"; 1759 } 1760 return "<a id=\"$docid\" href=\"$footnote_filename#$footid\">$footnote_number_text</a>"; 1761} 1762$default_commands_conversion{'footnote'} = \&_convert_footnote_command; 1763 1764sub _convert_uref_command($$$$) 1765{ 1766 my $self = shift; 1767 my $cmdname = shift; 1768 my $command = shift; 1769 my $args = shift; 1770 1771 my @args = @$args; 1772 my $url_arg = shift @args; 1773 my $text_arg = shift @args; 1774 my $replacement_arg = shift @args; 1775 1776 my ($url, $text, $replacement); 1777 $url = $url_arg->{'monospacestring'} if defined($url_arg); 1778 $text = $text_arg->{'normal'} if defined($text_arg); 1779 $replacement = $replacement_arg->{'normal'} if defined($replacement_arg); 1780 1781 $text = $replacement if (defined($replacement) and $replacement ne ''); 1782 $text = $url if (!defined($text) or $text eq ''); 1783 return $text if (!defined($url) or $url eq ''); 1784 return "$text ($url)" if ($self->in_string()); 1785 return "<a href=\"$url\">$text</a>"; 1786} 1787 1788$default_commands_conversion{'uref'} = \&_convert_uref_command; 1789$default_commands_conversion{'url'} = \&_convert_uref_command; 1790 1791my @image_files_extensions = ('.png', '.jpg', '.jpeg', '.gif'); 1792sub _convert_image_command($$$$) 1793{ 1794 my $self = shift; 1795 my $cmdname = shift; 1796 my $command = shift; 1797 my $args = shift; 1798 1799 my @extensions = @image_files_extensions; 1800 1801 if (defined($args->[0]->{'monospacetext'}) and $args->[0]->{'monospacetext'} ne '') { 1802 my $basefile = $args->[0]->{'monospacetext'}; 1803 return $basefile if ($self->in_string()); 1804 my $extension; 1805 if (defined($args->[4]) and defined($args->[4]->{'monospacetext'})) { 1806 $extension = $args->[4]->{'monospacetext'}; 1807 unshift @extensions, ("$extension", ".$extension"); 1808 } 1809 my $image_file; 1810 foreach my $extension (@extensions) { 1811 if ($self->Texinfo::Common::locate_include_file ($basefile.$extension)) { 1812 # use the basename and not the file found. It is agreed that it is 1813 # better, since in any case the files are moved. 1814 $image_file = $basefile.$extension; 1815 last; 1816 } 1817 } 1818 if (!defined($image_file) or $image_file eq '') { 1819 if (defined($extension) and $extension ne '') { 1820 $image_file = $basefile.$extension; 1821 } else { 1822 $image_file = "$basefile.jpg"; 1823 } 1824 #cluck "err ($self->{'ignore_notice'})"; 1825 $self->line_warn(sprintf( 1826 __("\@image file `%s' (for HTML) not found, using `%s'"), 1827 $basefile, $image_file), $command->{'line_nr'}); 1828 } 1829 if (defined($self->get_conf('IMAGE_LINK_PREFIX'))) { 1830 $image_file = $self->get_conf('IMAGE_LINK_PREFIX') . $image_file; 1831 } 1832 my $alt_string; 1833 if (defined($args->[3]) and defined($args->[3]->{'string'})) { 1834 $alt_string = $args->[3]->{'string'}; 1835 } 1836 if (!defined($alt_string) or ($alt_string eq '')) { 1837 $alt_string = $self->protect_text($basefile); 1838 } 1839 return "<img src=\"".$self->protect_text($image_file)."\" alt=\"$alt_string\">"; 1840 } 1841 return ''; 1842} 1843 1844$default_commands_conversion{'image'} = \&_convert_image_command; 1845 1846sub _convert_math_command($$$$) 1847{ 1848 my $self = shift; 1849 my $cmdname = shift; 1850 my $command = shift; 1851 my $args = shift; 1852 1853 my $arg = $args->[0]->{'normal'}; 1854 1855 my $math_type = $self->get_conf('HTML_MATH'); 1856 if ($math_type and $math_type eq 'mathjax') { 1857 # MathJax won't handle tags in code 1858 # TODO: instead convert inside $command to LaTeX, when such a conversion 1859 # becomes possible 1860 if ($arg !~ /</) { 1861 $self->{'element_math'} = 1; 1862 return "<em class=\'tex2jax_process\'>\\($arg\\)</em>"; 1863 } 1864 } 1865 return "<em class=\'math\'>$arg</em>"; 1866} 1867 1868$default_commands_conversion{'math'} = \&_convert_math_command; 1869 1870sub _convert_accent_command($$$$) 1871{ 1872 my $self = shift; 1873 my $cmdname = shift; 1874 my $command = shift; 1875 my $args = shift; 1876 1877 return $self->xml_accents($command, $self->in_upper_case()); 1878} 1879 1880foreach my $command (keys(%accent_commands)) { 1881 $default_commands_conversion{$command} = \&_convert_accent_command; 1882} 1883 1884# key is formatted as code since it is in code_style_commands 1885sub _convert_key_command($$$$) 1886{ 1887 my $self = shift; 1888 my $cmdname = shift; 1889 my $command = shift; 1890 my $args = shift; 1891 1892 my $text = $args->[0]->{'normal'}; 1893 if (!defined($text)) { 1894 # happens with bogus @-commands without argument, like @strong something 1895 return ''; 1896 } 1897 if ($self->in_string()) { 1898 return $text; 1899 } 1900 #return $self->protect_text('<') .$text .$self->protect_text('>'); 1901 my $class = $cmdname; 1902 if (!$self->in_code()) { 1903 return $self->_attribute_class('tt', $class).'>'.$text .'</tt>';; 1904 } else { 1905 my $open = $self->_attribute_class('span', $class); 1906 if ($open ne '') { 1907 return $open.'>'.$text.'</span>'; 1908 } else { 1909 return $text; 1910 } 1911 } 1912} 1913 1914$default_commands_conversion{'key'} = \&_convert_key_command; 1915 1916# argument is formatted as code since indicateurl is in code_style_commands 1917sub _convert_indicateurl_command($$$$) 1918{ 1919 my $self = shift; 1920 my $cmdname = shift; 1921 my $command = shift; 1922 my $args = shift; 1923 1924 my $text = $args->[0]->{'normal'}; 1925 if (!defined($text)) { 1926 # happens with bogus @-commands without argument, like @strong something 1927 return ''; 1928 } 1929 if (!$self->in_string()) { 1930 return $self->get_conf('OPEN_QUOTE_SYMBOL').'<code>' .$text 1931 .'</code>'.$self->get_conf('CLOSE_QUOTE_SYMBOL'); 1932 } else { 1933 return $self->get_conf('OPEN_QUOTE_SYMBOL').$text. 1934 $self->get_conf('CLOSE_QUOTE_SYMBOL'); 1935 } 1936} 1937 1938$default_commands_conversion{'indicateurl'} = \&_convert_indicateurl_command; 1939 1940 1941sub _convert_titlefont_command($$$$) 1942{ 1943 my $self = shift; 1944 my $cmdname = shift; 1945 my $command = shift; 1946 my $args = shift; 1947 1948 my $text = $args->[0]->{'normal'}; 1949 if (!defined($text)) { 1950 # happens with bogus @-commands without argument, like @strong something 1951 return ''; 1952 } 1953 return &{$self->{'format_heading_text'}}($self, 'titlefont', $text, 0, $command); 1954} 1955$default_commands_conversion{'titlefont'} = \&_convert_titlefont_command; 1956 1957sub _convert_U_command($$$$) 1958{ 1959 my $self = shift; 1960 my $cmdname = shift; 1961 my $command = shift; 1962 my $args = shift; 1963 1964 my $arg = $args->[0]->{'normal'}; 1965 my $res; 1966 if (defined($arg) && $arg) { 1967 # checks on the value already done in Parser, just output it here. 1968 $res = "&#x$arg;"; 1969 } else { 1970 $res = ''; 1971 } 1972 return $res; 1973} 1974$default_commands_conversion{'U'} = \&_convert_U_command; 1975 1976sub _default_format_comment($$) { 1977 my $self = shift; 1978 my $text = shift; 1979 return $self->xml_comment(' '.$text); 1980} 1981 1982sub protect_text($$) { 1983 my $self = shift; 1984 my $text = shift; 1985 return &{$self->{'format_protect_text'}}($self, $text); 1986} 1987 1988sub _default_format_protect_text($$) { 1989 my $self = shift; 1990 my $text = shift; 1991 my $result = $self->xml_protect_text($text); 1992 $result =~ s/\f//g; 1993 return $result; 1994} 1995 1996sub _default_format_heading_text($$$$$) 1997{ 1998 my $self = shift; 1999 my $cmdname = shift; 2000 my $text = shift; 2001 my $level = shift; 2002 my $command = shift; 2003 2004 return '' if ($text !~ /\S/); 2005 2006 # This should seldom happen. 2007 if ($self->in_string()) { 2008 $text .= "\n" unless ($cmdname eq 'titlefont'); 2009 return $text; 2010 } 2011 2012 my $class; 2013 if ($cmdname eq 'node') { 2014 $class = 'node-heading'; 2015 } else { 2016 $class = $cmdname; 2017 } 2018 2019 my $align = ''; 2020 $align = ' align="center"' if ($cmdname eq 'centerchap' or $cmdname eq 'settitle'); 2021 if ($level < 1) { 2022 $level = 1; 2023 } elsif ($level > $self->get_conf('MAX_HEADER_LEVEL')) { 2024 $level = $self->get_conf('MAX_HEADER_LEVEL'); 2025 } 2026 my $result = $self->_attribute_class("h$level", $class) ."$align>$text</h$level>"; 2027 # titlefont appears inline in text, so no end of line is 2028 # added. The end of line should be added by the user if needed. 2029 $result .= "\n" unless ($cmdname eq 'titlefont'); 2030 $result .= $self->get_conf('DEFAULT_RULE') . "\n" 2031 if ($cmdname eq 'part' 2032 and defined($self->get_conf('DEFAULT_RULE')) 2033 and $self->get_conf('DEFAULT_RULE') ne ''); 2034 return $result; 2035} 2036 2037# Associated to a button. Return text to use for a link in button bar. 2038# Depending on USE_NODE_DIRECTIONS and xrefautomaticsectiontitle 2039# use section or node for link direction and string. 2040sub _default_panel_button_dynamic_direction($$;$$) 2041{ 2042 my $self = shift; 2043 my $direction = shift; 2044 my $omit_rel = shift; 2045 my $use_first_element_in_file_directions = shift; 2046 2047 my $result = undef; 2048 2049 if ((defined($self->get_conf('USE_NODE_DIRECTIONS')) 2050 and $self->get_conf('USE_NODE_DIRECTIONS')) 2051 or (not defined($self->get_conf('USE_NODE_DIRECTIONS')) 2052 and $self->get_conf('USE_NODES'))) { 2053 $direction = 'Node'.$direction; 2054 } 2055 2056 if ($use_first_element_in_file_directions) { 2057 $direction = 'FirstInFile'.$direction; 2058 } 2059 2060 my $href = $self->_element_direction($self->{'current_element'}, 2061 $direction, 'href'); 2062 my $node; 2063 2064 2065 if ($self->get_conf('xrefautomaticsectiontitle') eq 'on') { 2066 $node = $self->_element_direction($self->{'current_element'}, 2067 $direction, 'section'); 2068 } 2069 2070 if (!defined($node)) { 2071 $node = $self->_element_direction($self->{'current_element'}, 2072 $direction, 'node'); 2073 } 2074 2075 my $anchor; 2076 if (defined($href) and defined($node) and $node =~ /\S/) { 2077 my $anchor_attributes = $omit_rel ? '' 2078 : $self->_direction_href_attributes($direction); 2079 $anchor = "<a href=\"$href\"${anchor_attributes}>$node</a>"; 2080 } 2081 if (defined($anchor)) { 2082 # i18n 2083 $result = $self->get_conf('BUTTONS_TEXT')->{$direction}.": $anchor"; 2084 } 2085 # 1 to communicate that a delimiter is needed for that button 2086 return ($result, 1); 2087} 2088 2089# Used for button bar at the foot of a node, with "rel" and "accesskey" 2090# attributes omitted. 2091sub _default_panel_button_dynamic_direction_node_footer($$) 2092{ 2093 my $self = shift; 2094 my $direction = shift; 2095 2096 return _default_panel_button_dynamic_direction($self, $direction, 1); 2097} 2098 2099# used for button bar at the foot of a section or chapter with 2100# directions of first element in file used instead of the last 2101# element directions. 2102sub _default_panel_button_dynamic_direction_section_footer($$) { 2103 my $self = shift; 2104 my $direction = shift; 2105 2106 return _default_panel_button_dynamic_direction($self, $direction, undef, 1); 2107} 2108 2109# how to create IMG tag 2110# this is only used in html, and only if ICONS is set and the button 2111# is active. 2112sub _default_format_button_icon_img($$$;$) 2113{ 2114 my $self = shift; 2115 my $button = shift; 2116 my $icon = shift; 2117 my $name = shift; 2118 return '' if (!defined($icon)); 2119 $button = "" if (!defined ($button)); 2120 $name = '' if (!defined($name)); 2121 my $alt = ''; 2122 if ($name ne '') { 2123 if ($button ne '') { 2124 $alt = "$button: $name"; 2125 } else { 2126 $alt = $name; 2127 } 2128 } else { 2129 $alt = $button; 2130 } 2131 return qq{<img src="$icon" border="0" alt="$alt" align="middle">}; 2132} 2133 2134sub _direction_href_attributes($$) 2135{ 2136 my $self = shift; 2137 my $direction = shift; 2138 2139 my $href_attributes = ''; 2140 if ($self->get_conf('USE_ACCESSKEY') 2141 and $self->get_conf('BUTTONS_ACCESSKEY')) { 2142 my $accesskey = $self->get_conf('BUTTONS_ACCESSKEY')->{$direction}; 2143 if (defined($accesskey) and ($accesskey ne '')) { 2144 $href_attributes = " accesskey=\"$accesskey\""; 2145 } 2146 } 2147 if ($self->get_conf('USE_REL_REV') and $self->get_conf('BUTTONS_REL')) { 2148 my $button_rel = $self->get_conf('BUTTONS_REL')->{$direction}; 2149 if (defined($button_rel) and ($button_rel ne '')) { 2150 $href_attributes .= " rel=\"$button_rel\""; 2151 } 2152 } 2153 return $href_attributes; 2154} 2155 2156my %html_default_node_directions; 2157foreach my $node_directions ('NodeNext', 'NodePrev', 'NodeUp') { 2158 $html_default_node_directions{$node_directions} = 1; 2159} 2160 2161sub _default_format_button($$) 2162{ 2163 my $self = shift; 2164 my $button = shift; 2165 2166 my ($active, $passive, $need_delimiter); 2167 if (ref($button) eq 'CODE') { 2168 ($active, $need_delimiter) = &$button($self); 2169 } elsif (ref($button) eq 'SCALAR') { 2170 $active = "$$button" if defined($$button); 2171 $need_delimiter = 1; 2172 } elsif (ref($button) eq 'ARRAY' and scalar(@$button == 2)) { 2173 my $text = $button->[1]; 2174 my $button_href = $button->[0]; 2175 # $button_href is simple text and $text is a reference 2176 if (defined($button_href) and !ref($button_href) 2177 and defined($text) and (ref($text) eq 'SCALAR') and defined($$text)) { 2178 # use given text 2179 my $href = $self->_element_direction($self->{'current_element'}, 2180 $button_href, 'href'); 2181 if ($href) { 2182 my $anchor_attributes = $self->_direction_href_attributes($button_href); 2183 $active = "<a href=\"$href\"${anchor_attributes}>$$text</a>"; 2184 } else { 2185 $passive = $$text; 2186 } 2187 $need_delimiter = 1; 2188 # $button_href is simple text and $text is a reference on code 2189 } elsif (defined($button_href) and !ref($button_href) 2190 and defined($text) and (ref($text) eq 'CODE')) { 2191 ($active, $need_delimiter) = &$text($self, $button_href); 2192 # $button_href is simple text and $text is also a simple text 2193 } elsif (defined($button_href) and !ref($button_href) 2194 and defined($text) and !ref($text)) { 2195 if ($text =~ s/^->\s*//) { 2196 $active = $self->_element_direction($self->{'current_element'}, 2197 $button_href, $text); 2198 } else { 2199 my $href = $self->_element_direction($self->{'current_element'}, 2200 $button_href, 'href'); 2201 my $text_formatted = $self->_element_direction($self->{'current_element'}, 2202 $button_href, $text); 2203 if ($href) { 2204 my $anchor_attributes = $self->_direction_href_attributes($button_href); 2205 $active = "<a href=\"$href\"${anchor_attributes}>$text_formatted</a>"; 2206 } else { 2207 $passive = $text_formatted; 2208 } 2209 } 2210 $need_delimiter = 1; 2211 } 2212 } elsif ($button eq ' ') { 2213 # handle space button 2214 if ($self->get_conf('ICONS') and $self->get_conf('ACTIVE_ICONS') 2215 and defined($self->get_conf('ACTIVE_ICONS')->{$button}) 2216 and $self->get_conf('ACTIVE_ICONS')->{$button} ne '') { 2217 my $button_name = $self->get_conf('BUTTONS_NAME')->{$button}; 2218 $active = &{$self->{'format_button_icon_img'}}($self, $button_name, 2219 $self->get_conf('ACTIVE_ICONS')->{' '}); 2220 } else { 2221 $active = $self->get_conf('BUTTONS_TEXT')->{$button}; 2222 } 2223 $need_delimiter = 0; 2224 } else { 2225 my $href = $self->_element_direction($self->{'current_element'}, 2226 $button, 'href'); 2227 if ($href) { 2228 # button is active 2229 my $btitle = ''; 2230 if ($self->get_conf('BUTTONS_GOTO') 2231 and defined($self->get_conf('BUTTONS_GOTO')->{$button})) { 2232 $btitle = ' title="' . $self->get_conf('BUTTONS_GOTO')->{$button} . '"'; 2233 } 2234 if ($self->get_conf('USE_ACCESSKEY') and $self->get_conf('BUTTONS_ACCESSKEY')) { 2235 my $accesskey = $self->get_conf('BUTTONS_ACCESSKEY')->{$button}; 2236 if (defined($accesskey) and $accesskey ne '') { 2237 $btitle .= " accesskey=\"$accesskey\""; 2238 } 2239 } 2240 if ($self->get_conf('USE_REL_REV') and ($self->get_conf('BUTTONS_REL'))) { 2241 my $button_rel = $self->get_conf('BUTTONS_REL')->{$button}; 2242 if (defined($button_rel) and $button_rel ne '') { 2243 $btitle .= " rel=\"$button_rel\""; 2244 } 2245 } 2246 my $use_icon; 2247 if ($self->get_conf('ICONS') and $self->get_conf('ACTIVE_ICONS') 2248 and $self->get_conf('BUTTONS_NAME')) { 2249 my $active_icon = $self->get_conf('ACTIVE_ICONS')->{$button}; 2250 my $button_name = $self->get_conf('BUTTONS_NAME')->{$button}; 2251 if (defined($active_icon) and $active_icon ne '' 2252 and defined($button_name)) { 2253 # use icon 2254 $active = "<a href=\"$href\"${btitle}>". 2255 &{$self->{'format_button_icon_img'}}($self, $button_name, $active_icon, 2256 $self->_element_direction($self->{'current_element'}, 2257 $button, 'string')) ."</a>"; 2258 $use_icon = 1; 2259 } 2260 } 2261 if (!$use_icon) { 2262 # use text 2263 $active = '[' . "<a href=\"$href\"${btitle}>". 2264 $self->get_conf('BUTTONS_TEXT')->{$button}."</a>" . ']'; 2265 } 2266 } else { 2267 # button is passive 2268 my $use_icon; 2269 if ($self->get_conf('ICONS') and $self->get_conf('PASSIVE_ICONS') 2270 and $self->get_conf('BUTTONS_NAME')) { 2271 my $passive_icon = $self->get_conf('PASSIVE_ICONS')->{$button}; 2272 my $button_name = $self->get_conf('BUTTONS_NAME')->{$button}; 2273 if ($passive_icon and $passive_icon ne '') { 2274 $passive = &{$self->{'format_button_icon_img'}}($self, $button_name, 2275 $passive_icon, 2276 $self->_element_direction($self->{'current_element'}, 2277 $button, 'string')); 2278 $use_icon = 1; 2279 } 2280 } 2281 if (!$use_icon) { 2282 $passive = '[' . $self->get_conf('BUTTONS_TEXT')->{$button} . ']'; 2283 } 2284 } 2285 $need_delimiter = 0; 2286 } 2287 # FIXME chose another option among those proposed in comments below? 2288 if (not defined($need_delimiter)) { 2289 # option 1: be forgiving if $need_delimiter is not set 2290 # if ($html_default_node_directions{$button}) { 2291 # $need_delimiter = 1; 2292 # } else { 2293 # $need_delimiter = 0; 2294 # } 2295 # option 2: be somewhat forgiving but show a backtrace 2296 #cluck ("need_delimiter not defined"); 2297 # $need_delimiter = 0; 2298 # option3: no pity 2299 confess ("need_delimiter not defined"); 2300 } 2301 return ($active, $passive, $need_delimiter); 2302} 2303 2304sub _default_format_navigation_header_panel($$$$;$) 2305{ 2306 my $self = shift; 2307 my $buttons = shift; 2308 my $cmdname = shift; 2309 my $command = shift; 2310 my $vertical = shift; 2311 2312 # if VERTICAL_HEAD_NAVIGATION, the buttons are in a vertical table which 2313 # is itself in the first column of a table opened in header_navigation 2314 #my $vertical = $self->get_conf('VERTICAL_HEAD_NAVIGATION'); 2315 2316 my $first_button = 1; 2317 my $result = ''; 2318 if ($self->get_conf('HEADER_IN_TABLE')) { 2319 $result .= $self->_attribute_class('table', 'header') 2320 .' cellpadding="1" cellspacing="1" border="0">'."\n"; 2321 $result .= "<tr>" unless $vertical; 2322 } else { 2323 $result .= $self->_attribute_class('div', 'header').">\n<p>\n"; 2324 } 2325 foreach my $button (@$buttons) { 2326 if ($self->get_conf('HEADER_IN_TABLE')) { 2327 $result .= qq{<tr valign="top" align="left">\n} if $vertical; 2328 $result .= qq{<td valign="middle" align="left">}; 2329 } 2330 my $direction; 2331 if (ref($button) eq 'ARRAY' 2332 and defined($button->[0]) and !ref($button->[0])) { 2333 $direction = $button->[0]; 2334 } elsif (defined($button) and !ref($button)) { 2335 $direction = $button; 2336 } 2337 2338 my ($active, $passive, $need_delimiter) = &{$self->{'format_button'}}($self, $button); 2339 if ($self->get_conf('HEADER_IN_TABLE')) { 2340 if (defined($active)) { 2341 $first_button = 0 if ($first_button); 2342 $result .= $active; 2343 } elsif (defined($passive)) { 2344 $first_button = 0 if ($first_button); 2345 $result .= $passive; 2346 } 2347 $result .= "</td>\n"; 2348 $result .= "</tr>\n" if $vertical; 2349 } elsif (defined($active)) { 2350 # only active buttons are print out when not in table 2351 if ($need_delimiter and !$first_button) { 2352 $active = ', ' .$active; 2353 } 2354 $result .= $active; 2355 $first_button = 0 if ($first_button); 2356 } 2357 } 2358 if ($self->get_conf('HEADER_IN_TABLE')) { 2359 $result .= "</tr>" unless $vertical; 2360 $result .= "</table>\n"; 2361 } else { 2362 $result .= "</p>\n</div>\n"; 2363 } 2364 return $result; 2365} 2366 2367sub _default_format_navigation_header($$$$) 2368{ 2369 my $self = shift; 2370 my $buttons = shift; 2371 my $cmdname = shift; 2372 my $command = shift; 2373 2374 my $result = ''; 2375 if ($self->get_conf('VERTICAL_HEAD_NAVIGATION')) { 2376 $result .= '<table border="0" cellpadding="0" cellspacing="0"> 2377<tr valign="top"> 2378<td align="left"> 2379'; 2380 } 2381 $result .= &{$self->{'format_navigation_header_panel'}}($self, $buttons, 2382 $cmdname, $command, 2383 $self->get_conf('VERTICAL_HEAD_NAVIGATION')); 2384 if ($self->get_conf('VERTICAL_HEAD_NAVIGATION')) { 2385 $result .= '</td> 2386<td align="left"> 2387'; 2388 } elsif ($self->get_conf('SPLIT') eq 'node') { 2389 $result .= $self->get_conf('DEFAULT_RULE')."\n"; 2390 } 2391 return $result; 2392} 2393 2394sub _default_format_element_header($$$$) 2395{ 2396 my $self = shift; 2397 my $cmdname = shift; 2398 my $command = shift; 2399 my $element = shift; 2400 2401 my $result = ''; 2402 2403 print STDERR "Element $element (@{$element->{'contents'}}) ". 2404 Texinfo::Structuring::_print_element_command_texi($element) ."\n" 2405 if ($self->get_conf('DEBUG')); 2406 2407 # Do the heading if the command is the first command in the element 2408 if (($element->{'contents'}->[0] eq $command 2409 or (!$element->{'contents'}->[0]->{'cmdname'} 2410 and $element->{'contents'}->[1] eq $command)) 2411 # and there is more than one element 2412 and ($element->{'element_next'} or $element->{'element_prev'})) { 2413 my $is_top = $self->element_is_top($element); 2414 my $first_in_page = (defined($element->{'filename'}) 2415 and $self->{'counter_in_file'}->{$element->{'filename'}} == 1); 2416 my $previous_is_top = ($element->{'element_prev'} 2417 and $self->element_is_top($element->{'element_prev'})); 2418 2419 print STDERR "Header ($previous_is_top, $is_top, $first_in_page): " 2420 .Texinfo::Structuring::_print_root_command_texi($command)."\n" 2421 if ($self->get_conf('DEBUG')); 2422 2423 if ($is_top) { 2424 # use TOP_BUTTONS for top. 2425 $result .= &{$self->{'format_navigation_header'}}($self, 2426 $self->get_conf('TOP_BUTTONS'), $cmdname, $command) 2427 if ($self->get_conf('SPLIT') or $self->get_conf('HEADERS')); 2428 } else { 2429 if ($first_in_page and !$self->get_conf('HEADERS')) { 2430 if ($self->get_conf('SPLIT') eq 'chapter') { 2431 $result .= &{$self->{'format_navigation_header'}}($self, 2432 $self->get_conf('CHAPTER_BUTTONS'), $cmdname, $command); 2433 2434 $result .= $self->get_conf('DEFAULT_RULE') ."\n" 2435 if (defined($self->get_conf('DEFAULT_RULE')) 2436 and !$self->get_conf('VERTICAL_HEAD_NAVIGATION')); 2437 } elsif ($self->get_conf('SPLIT') eq 'section') { 2438 $result .= &{$self->{'format_navigation_header'}}($self, 2439 $self->get_conf('SECTION_BUTTONS'), $cmdname, $command); 2440 } 2441 } 2442 if (($first_in_page or $previous_is_top) 2443 and $self->get_conf('HEADERS')) { 2444 $result .= &{$self->{'format_navigation_header'}}($self, 2445 $self->get_conf('SECTION_BUTTONS'), $cmdname, $command); 2446 } elsif($self->get_conf('HEADERS') or $self->get_conf('SPLIT') eq 'node') { 2447 # got to do this here, as it isn't done otherwise since 2448 # navigation_header is not called 2449 $result .= &{$self->{'format_navigation_header_panel'}}($self, 2450 $self->get_conf('SECTION_BUTTONS'), $cmdname, $command); 2451 } 2452 } 2453 } 2454 return $result; 2455} 2456 2457sub register_opened_section_level($$$) 2458{ 2459 my $self = shift; 2460 my $level = shift; 2461 my $close = shift; 2462 while (@{$self->{'pending_closes'}} < $level) { 2463 push(@{$self->{'pending_closes'}}, ""); 2464 } 2465 push(@{$self->{'pending_closes'}}, $close); 2466} 2467 2468sub close_registered_sections_level($$) 2469{ 2470 my $self = shift; 2471 my $level = shift; 2472 if (not defined($level)) { 2473 cluck 'close_registered_sections_level $level not defined'; 2474 } 2475 my @closed_elements; 2476 my $result = ''; 2477 while (@{$self->{'pending_closes'}} > $level) { 2478 my $close = pop @{$self->{'pending_closes'}}; 2479 push(@closed_elements, $close) 2480 if ($close); 2481 } 2482 return @closed_elements; 2483} 2484 2485sub _convert_heading_command($$$$$) 2486{ 2487 my $self = shift; 2488 my $cmdname = shift; 2489 my $command = shift; 2490 my $args = shift; 2491 my $content = shift; 2492 2493 my $result = ''; 2494 2495 # not clear that it may really happen 2496 if ($self->in_string) { 2497 $result .= $self->command_string($command) ."\n" if ($cmdname ne 'node'); 2498 $result .= $content if (defined($content)); 2499 return $result; 2500 } 2501 2502 my $element_id = $self->command_id($command); 2503 my $section; 2504 if ($cmdname eq 'node' and $command->{'extra'}->{'associated_section'}) { 2505 $section = $command->{'extra'}->{'associated_section'}; 2506 } elsif ($cmdname ne 'node' 2507 and not $command->{'extra'}->{'associated_node'} 2508 # to avoid *heading* @-commands 2509 and $Texinfo::Common::root_commands{$cmdname}) { 2510 $section = $command; 2511 } 2512 2513 if ($section) { 2514 my $level = $section->{'level'}; 2515 $result .= join('', $self->close_registered_sections_level($level)); 2516 $self->register_opened_section_level($level, "</div>\n"); 2517 2518 $result .= '<div class="' . $section->{'cmdname'} . '"'; 2519 2520 $result .= " id=\"$element_id\"" 2521 if (defined($element_id) and $element_id ne ''); 2522 $result .= ">\n"; 2523 } else { 2524 $result .= "<span id=\"$element_id\"></span>" 2525 if (defined($element_id) and $element_id ne ''); 2526 } 2527 2528 print STDERR "Process $command " 2529 .Texinfo::Structuring::_print_root_command_texi($command)."\n" 2530 if ($self->get_conf('DEBUG')); 2531 my $element; 2532 if ($Texinfo::Common::root_commands{$command->{'cmdname'}} 2533 and $command->{'parent'} 2534 and $command->{'parent'}->{'type'} 2535 and $command->{'parent'}->{'type'} eq 'element') { 2536 $element = $command->{'parent'}; 2537 } 2538 if ($element) { 2539 $result .= &{$self->{'format_element_header'}}($self, $cmdname, 2540 $command, $element); 2541 } 2542 2543 my $heading_level; 2544 # node is used as heading if there is nothing else. 2545 if ($cmdname eq 'node') { 2546 if (!$element or (!$element->{'extra'}->{'section'} 2547 and $element->{'extra'}->{'node'} 2548 and $element->{'extra'}->{'node'} eq $command 2549 # bogus node may not have been normalized 2550 and defined($command->{'extra'}->{'normalized'}))) { 2551 if ($command->{'extra'}->{'normalized'} eq 'Top') { 2552 $heading_level = 0; 2553 } else { 2554 $heading_level = 3; 2555 } 2556 } 2557 } elsif (defined $command->{'level'}) { 2558 $heading_level = $command->{'level'}; 2559 } else { 2560 # for *heading* @-commands which do not have a level 2561 # in the document as they are not associated with the 2562 # sectioning tree, but still have a $heading_level 2563 $heading_level = Texinfo::Structuring::section_level($command); 2564 } 2565 2566 my $heading = $self->command_text($command); 2567 # $heading not defined may happen if the command is a @node, for example 2568 # if there is an error in the node. 2569 if (defined($heading) and $heading ne '' and defined($heading_level)) { 2570 2571 if ($self->get_conf('TOC_LINKS') 2572 and $Texinfo::Common::root_commands{$cmdname} 2573 and $Texinfo::Common::sectioning_commands{$cmdname}) { 2574 my $content_href = $self->command_contents_href($command, 'contents', 2575 $self->{'current_filename'}); 2576 if ($content_href) { 2577 $heading = "<a href=\"$content_href\">$heading</a>"; 2578 } 2579 } 2580 2581 if ($self->in_preformatted()) { 2582 $result .= '<strong>'.$heading.'</strong>'."\n"; 2583 } else { 2584 # if the level was changed, set the command name right 2585 if ($cmdname ne 'node' 2586 and $heading_level ne $Texinfo::Common::command_structuring_level{$cmdname}) { 2587 $cmdname 2588 = $Texinfo::Common::level_to_structuring_command{$cmdname}->[$heading_level]; 2589 } 2590 $result .= &{$self->{'format_heading_text'}}($self, $cmdname, $heading, 2591 $heading_level +$self->get_conf('CHAPTER_HEADER_LEVEL') -1, $command); 2592 } 2593 } 2594 $result .= $content if (defined($content)); 2595 2596 my $table_of_contents_was_output = 0.; 2597 if ($self->get_conf('CONTENTS_OUTPUT_LOCATION') eq 'after_top' 2598 and $cmdname eq 'top' 2599 and $self->{'structuring'} and $self->{'structuring'}->{'sectioning_root'} 2600 and scalar(@{$self->{'structuring'}->{'sections_list'}}) > 1) { 2601 foreach my $content_command_name ('contents', 'shortcontents') { 2602 if ($self->get_conf($content_command_name)) { 2603 my $contents_text 2604 = $self->_contents_inline_element($content_command_name, undef); 2605 if ($contents_text ne '') { 2606 $result .= $contents_text; 2607 #$result .= $contents_text . $self->get_conf('DEFAULT_RULE')."\n"; 2608 $table_of_contents_was_output = 1; 2609 } 2610 } 2611 } 2612 } 2613 if (not $table_of_contents_was_output 2614 and $self->get_conf('FORMAT_MENU') eq 'sectiontoc' 2615 and $sectioning_commands{$cmdname} 2616 and ($cmdname ne 'top' 2617 or (not ($self->_has_contents_or_shortcontents() 2618 and $self->get_conf('CONTENTS_OUTPUT_LOCATION') 2619 eq 'inline')))) { 2620 $result .= _mini_toc($self, $command); 2621 } 2622 return $result; 2623} 2624 2625foreach my $command (keys(%sectioning_commands), 'node') { 2626 $default_commands_conversion{$command} = \&_convert_heading_command; 2627} 2628 2629sub _convert_raw_command($$$$) 2630{ 2631 my $self = shift; 2632 my $cmdname = shift; 2633 my $command = shift; 2634 my $content = shift; 2635 2636 if ($cmdname eq $self->{'output_format'}) { 2637 return $content; 2638 } 2639 $self->line_warn(sprintf(__("raw format %s is not converted"), 2640 $cmdname), $command->{'line_nr'}); 2641 return $self->protect_text($content); 2642} 2643 2644foreach my $command (keys(%format_raw_commands)) { 2645 $default_commands_conversion{$command} = \&_convert_raw_command; 2646} 2647 2648sub _convert_inline_command($$$$) 2649{ 2650 my $self = shift; 2651 my $cmdname = shift; 2652 my $command = shift; 2653 my $args = shift; 2654 2655 my $format_arg = shift @$args; 2656 2657 my $format; 2658 if (defined($format_arg)) { 2659 $format = $format_arg->{'monospacetext'}; 2660 } 2661 return '' if (!defined($format) or $format eq ''); 2662 2663 my $arg_index = undef; 2664 if ($inline_format_commands{$cmdname}) { 2665 if ($cmdname eq 'inlinefmtifelse' 2666 and ! $self->{'expanded_formats_hash'}->{$format}) { 2667 $arg_index = 1; 2668 } elsif ($self->{'expanded_formats_hash'}->{$format}) { 2669 $arg_index = 0; 2670 } 2671 } elsif (defined($command->{'extra'}->{'expand_index'})) { 2672 $arg_index = 0; 2673 } 2674 if (defined($arg_index) and $arg_index < scalar(@$args)) { 2675 my $text_arg = $args->[$arg_index]; 2676 if ($text_arg) { 2677 if ($text_arg->{'normal'}) { 2678 return $text_arg->{'normal'}; 2679 } elsif ($text_arg->{'raw'}) { 2680 return $text_arg->{'raw'}; 2681 } 2682 } 2683 } 2684 return ''; 2685} 2686 2687foreach my $command (keys(%inline_commands)) { 2688 $default_commands_conversion{$command} = \&_convert_inline_command; 2689} 2690 2691sub _indent_with_table ($) 2692{ 2693 my $content = shift; 2694 2695 return '<table><tr><td> </td><td>'.$content."</td></tr></table>\n"; 2696} 2697 2698my $html_menu_entry_index = 0; 2699sub _convert_preformatted_command($$$$) 2700{ 2701 my $self = shift; 2702 my $cmdname = shift; 2703 my $command = shift; 2704 my $content = shift; 2705 my $extra_classes; 2706 2707 # this is mainly for classes as there are purprosely no classes 2708 # for small* 2709 my $main_cmdname; 2710 if ($small_alias{$cmdname}) { 2711 $main_cmdname = $small_alias{$cmdname}; 2712 } else { 2713 $main_cmdname = $cmdname; 2714 } 2715 2716 if ($cmdname eq 'menu') { 2717 $html_menu_entry_index = 0; 2718 } elsif ($cmdname eq 'example') { 2719 if ($command->{'args'}) { 2720 $extra_classes = []; 2721 for my $example_arg (@{$command->{'args'}}) { 2722 # convert or remove all @-commands, using simple ascii and unicode 2723 # characters 2724 my $converted_arg = Texinfo::Convert::NodeNameNormalization::convert($example_arg); 2725 if ($converted_arg ne '') { 2726 push @$extra_classes, $converted_arg; 2727 } 2728 } 2729 } 2730 } elsif ($main_cmdname eq 'lisp') { 2731 $main_cmdname = 'example'; 2732 $extra_classes = ['lisp']; 2733 } 2734 2735 if ($content ne '' and !$self->in_string()) { 2736 if ($self->get_conf('COMPLEX_FORMAT_IN_TABLE')) { 2737 if ($indented_preformatted_commands{$cmdname}) { 2738 return _indent_with_table ($content); 2739 } else { 2740 return $content."\n"; 2741 } 2742 } else { 2743 return $self->_attribute_class('div', $main_cmdname, $extra_classes).">\n".$content.'</div>'."\n"; 2744 } 2745 } else { 2746 return $content; 2747 } 2748} 2749 2750foreach my $preformatted_command (keys(%preformatted_commands)) { 2751 $default_commands_conversion{$preformatted_command} 2752 = \&_convert_preformatted_command; 2753} 2754 2755sub _convert_indented_command($$$$) 2756{ 2757 my $self = shift; 2758 my $cmdname = shift; 2759 my $command = shift; 2760 my $content = shift; 2761 2762 # no class for @small* variants 2763 $cmdname = $small_alias{$cmdname} 2764 if $small_alias{$cmdname}; 2765 2766 if ($content ne '' and !$self->in_string()) { 2767 if ($self->get_conf('COMPLEX_FORMAT_IN_TABLE')) { 2768 return _indent_with_table ($content); 2769 } else { 2770 return $self->_attribute_class('blockquote', $cmdname).">\n" 2771 .$content.'</blockquote>'."\n"; 2772 } 2773 } else { 2774 return $content; 2775 } 2776} 2777 2778$default_commands_conversion{'indentedblock'} = \&_convert_indented_command; 2779 2780sub _convert_verbatim_command($$$$) 2781{ 2782 my $self = shift; 2783 my $cmdname = shift; 2784 my $command = shift; 2785 my $content = shift; 2786 2787 if (!$self->in_string) { 2788 return $self->_attribute_class('pre', $cmdname).'>' 2789 .$content . '</pre>'; 2790 } else { 2791 return $content; 2792 } 2793} 2794 2795$default_commands_conversion{'verbatim'} = \&_convert_verbatim_command; 2796 2797sub _convert_displaymath_command($$$$) 2798{ 2799 my $self = shift; 2800 my $cmdname = shift; 2801 my $command = shift; 2802 my $content = shift; 2803 2804 if ($self->in_string) { 2805 return $content; 2806 } 2807 2808 my $result = ''; 2809 $result .= $self->_attribute_class('div', 'displaymath').'>'; 2810 if ($self->get_conf('HTML_MATH') 2811 and $self->get_conf('HTML_MATH') eq 'mathjax') { 2812 $self->{'element_math'} = 1; 2813 $result .= $self->_attribute_class('em', 'tex2jax_process').'>' 2814 ."\\[$content\\]".'</em>'; 2815 } else { 2816 $result .= $self->_attribute_class('em').'>'."$content".'</em>'; 2817 } 2818 $result .= '</div>'; 2819 return $result; 2820} 2821 2822$default_commands_conversion{'displaymath'} = \&_convert_displaymath_command; 2823 2824sub _convert_verbatiminclude_command($$$$) 2825{ 2826 my $self = shift; 2827 my $cmdname = shift; 2828 my $command = shift; 2829 my $args = shift; 2830 2831 my $verbatim_include_verbatim 2832 = $self->Texinfo::Common::expand_verbatiminclude($command); 2833 if (defined($verbatim_include_verbatim)) { 2834 return $self->convert_tree($verbatim_include_verbatim); 2835 } else { 2836 return ''; 2837 } 2838} 2839 2840$default_commands_conversion{'verbatiminclude'} 2841 = \&_convert_verbatiminclude_command; 2842 2843sub _convert_command_noop($$$$) 2844{ 2845 my $self = shift; 2846 my $cmdname = shift; 2847 my $command = shift; 2848 my $content = shift; 2849 2850 return $content; 2851} 2852 2853$default_commands_conversion{'raggedright'} = \&_convert_command_noop; 2854$default_commands_conversion{'flushleft'} = \&_convert_command_noop; 2855$default_commands_conversion{'flushright'} = \&_convert_command_noop; 2856$default_commands_conversion{'group'} = \&_convert_command_noop; 2857 2858 2859sub _convert_sp_command($$$$) 2860{ 2861 my $self = shift; 2862 my $cmdname = shift; 2863 my $command = shift; 2864 my $args = shift; 2865 2866 if (defined($command->{'extra'}->{'misc_args'}->[0])) { 2867 my $sp_nr = $command->{'extra'}->{'misc_args'}->[0]; 2868 if ($self->in_preformatted() or $self->in_string()) { 2869 return "\n" x $sp_nr; 2870 } else { 2871 return "<br>\n" x $sp_nr; 2872 } 2873 } 2874} 2875 2876$default_commands_conversion{'sp'} = \&_convert_sp_command; 2877 2878sub _convert_exdent_command($$$$) 2879{ 2880 my $self = shift; 2881 my $cmdname = shift; 2882 my $command = shift; 2883 my $args = shift; 2884 2885 # FIXME do something better with css and span? 2886 my $preformatted = $self->in_preformatted(); 2887 2888 if ($self->in_preformatted() or $self->in_string()) { 2889 return $self->_convert_preformatted_type($cmdname, $command, 2890 $args->[0]->{'normal'} ."\n"); 2891 } else { 2892 # ignore alignment information 2893 return "<p>".$args->[0]->{'normal'} ."\n</p>"; 2894 } 2895} 2896 2897$default_commands_conversion{'exdent'} = \&_convert_exdent_command; 2898 2899sub _convert_center_command($$$$) 2900{ 2901 my $self = shift; 2902 my $cmdname = shift; 2903 my $command = shift; 2904 my $args = shift; 2905 2906 if ($self->in_string()) { 2907 return $self->_convert_preformatted_type($cmdname, $command, 2908 $args->[0]->{'normal'}."\n"); 2909 } else { 2910 return "<div align=\"center\">".$args->[0]->{'normal'}."\n</div>"; 2911 } 2912} 2913 2914$default_commands_conversion{'center'} = \&_convert_center_command; 2915 2916sub _convert_author_command($$$$) 2917{ 2918 my $self = shift; 2919 my $cmdname = shift; 2920 my $command = shift; 2921 my $args = shift; 2922 2923 return '' if (!$args->[0] or !$command->{'extra'}->{'titlepage'}); 2924 if (!$self->in_string()) { 2925 return "<strong>$args->[0]->{'normal'}</strong><br>\n"; 2926 } else { 2927 return $args->[0]->{'normal'}."\n"; 2928 } 2929} 2930 2931$default_commands_conversion{'author'} = \&_convert_author_command; 2932 2933sub _convert_title_command($$$$) 2934{ 2935 my $self = shift; 2936 my $cmdname = shift; 2937 my $command = shift; 2938 my $args = shift; 2939 return '' if (!$args->[0]); 2940 if (!$self->in_string()) { 2941 return "<h1>$args->[0]->{'normal'}</h1>\n"; 2942 } else { 2943 return $args->[0]->{'normal'}; 2944 } 2945} 2946$default_commands_conversion{'title'} = \&_convert_title_command; 2947 2948sub _convert_subtitle_command($$$$) 2949{ 2950 my $self = shift; 2951 my $cmdname = shift; 2952 my $command = shift; 2953 my $args = shift; 2954 return '' if (!$args->[0]); 2955 if (!$self->in_string()) { 2956 return "<h3 align=\"right\">$args->[0]->{'normal'}</h3>\n"; 2957 } else { 2958 return $args->[0]->{'normal'}; 2959 } 2960} 2961$default_commands_conversion{'subtitle'} = \&_convert_subtitle_command; 2962 2963sub _convert_insertcopying_command($$$$) 2964{ 2965 my $self = shift; 2966 my $cmdname = shift; 2967 my $command = shift; 2968 2969 if ($self->{'extra'} and $self->{'extra'}->{'copying'}) { 2970 return $self->convert_tree({'contents' 2971 => $self->{'extra'}->{'copying'}->{'contents'}}); 2972 } 2973 return ''; 2974} 2975$default_commands_conversion{'insertcopying'} 2976 = \&_convert_insertcopying_command; 2977 2978sub _convert_listoffloats_command($$$$) 2979{ 2980 my $self = shift; 2981 my $cmdname = shift; 2982 my $command = shift; 2983 my $args = shift; 2984 2985 if (!$self->in_string() 2986 and $command->{'extra'} and $command->{'extra'}->{'type'} 2987 and defined($command->{'extra'}->{'type'}->{'normalized'}) 2988 and $self->{'floats'} 2989 and $self->{'floats'}->{$command->{'extra'}->{'type'}->{'normalized'}} 2990 and @{$self->{'floats'}->{$command->{'extra'}->{'type'}->{'normalized'}}}) { 2991 my $listoffloats_name = $command->{'extra'}->{'type'}->{'normalized'}; 2992 my $result = $self->_attribute_class('dl', 'listoffloats').">\n" ; 2993 foreach my $float (@{$self->{'floats'}->{$listoffloats_name}}) { 2994 my $float_href = $self->command_href($float); 2995 next if (!$float_href); 2996 $result .= '<dt>'; 2997 my $float_text = $self->command_text($float); 2998 if (defined($float_text) and $float_text ne '') { 2999 if ($float_href) { 3000 $result .= "<a href=\"$float_href\">$float_text</a>"; 3001 } else { 3002 $result .= $float_text; 3003 } 3004 } 3005 $result .= '</dt>'; 3006 my $caption; 3007 if ($float->{'extra'}->{'shortcaption'}) { 3008 $caption = $float->{'extra'}->{'shortcaption'}; 3009 } elsif ($float->{'extra'}->{'caption'}) { 3010 $caption = $float->{'extra'}->{'caption'}; 3011 } 3012 3013 my $caption_text; 3014 if ($caption) { 3015 $caption_text = $self->convert_tree_new_formatting_context( 3016 $caption->{'args'}->[0], $cmdname, 'listoffloats'); 3017 } else { 3018 $caption_text = ''; 3019 } 3020 $result .= '<dd>'.$caption_text.'</dd>'."\n"; 3021 } 3022 return $result . "</dl>\n"; 3023 } else { 3024 return ''; 3025 } 3026} 3027$default_commands_conversion{'listoffloats'} = \&_convert_listoffloats_command; 3028 3029sub _in_preformatted_in_menu($) 3030{ 3031 my $self = shift; 3032 return 1 if ($self->get_conf('SIMPLE_MENU')); 3033 my @pre_classes = $self->preformatted_classes_stack(); 3034 foreach my $pre_class (@pre_classes) { 3035 return 1 if ($preformatted_commands{$pre_class}); 3036 } 3037 return 0; 3038} 3039 3040sub _convert_menu_command($$$$) 3041{ 3042 my $self = shift; 3043 my $cmdname = shift; 3044 my $command = shift; 3045 my $content = shift; 3046 3047 return $content if ($cmdname eq 'detailmenu'); 3048 3049 $html_menu_entry_index = 0; 3050 if ($content !~ /\S/) { 3051 return ''; 3052 } 3053 # This can probably only happen with incorrect input, 3054 # for instance menu in copying 3055 # FIXME check? 3056 if ($self->in_string()) { 3057 return $content; 3058 } 3059 my $begin_row = ''; 3060 my $end_row = ''; 3061 if ($self->_in_preformatted_in_menu()) { 3062 $begin_row = '<tr><td>'; 3063 $end_row = '</td></tr>'; 3064 } 3065 return $self->_attribute_class('table', 'menu') 3066 ." border=\"0\" cellspacing=\"0\">${begin_row}\n" 3067 . $content . "${end_row}</table>\n"; 3068} 3069$default_commands_conversion{'menu'} = \&_convert_menu_command; 3070$default_commands_conversion{'detailmenu'} = \&_convert_menu_command; 3071 3072sub _convert_float_command($$$$$) 3073{ 3074 my $self = shift; 3075 my $cmdname = shift; 3076 my $command = shift; 3077 my $args = shift; 3078 my $content = shift; 3079 3080 my ($caption, $prepended) = Texinfo::Common::float_name_caption($self, 3081 $command); 3082 my $caption_text = ''; 3083 my $prepended_text; 3084 if ($self->in_string()) { 3085 if ($prepended) { 3086 $prepended_text = $self->convert_tree_new_formatting_context( 3087 $prepended, 'float prepended'); 3088 } else { 3089 $prepended_text = ''; 3090 } 3091 if ($caption) { 3092 $caption_text = $self->convert_tree_new_formatting_context( 3093 {'contents' => $caption->{'args'}->[0]->{'contents'}}, 3094 'float caption'); 3095 } 3096 return $prepended.$content.$caption_text; 3097 } 3098 3099 my $id = $self->command_id($command); 3100 my $label; 3101 if (defined($id) and $id ne '') { 3102 $label = "<span id=\"$id\"></span>"; 3103 } else { 3104 $label = ''; 3105 } 3106 3107 if ($prepended) { 3108 if ($caption) { 3109 # prepend the prepended tree to the first paragraph 3110 my @caption_original_contents = @{$caption->{'args'}->[0]->{'contents'}}; 3111 my @caption_contents; 3112 my $new_paragraph; 3113 while (@caption_original_contents) { 3114 my $content = shift @caption_original_contents; 3115 if ($content->{'type'} and $content->{'type'} eq 'paragraph') { 3116 %{$new_paragraph} = %{$content}; 3117 $new_paragraph->{'contents'} = [@{$content->{'contents'}}]; 3118 unshift (@{$new_paragraph->{'contents'}}, {'cmdname' => 'strong', 3119 'args' => [{'type' => 'brace_command_arg', 3120 'contents' => [$prepended]}]}); 3121 push @caption_contents, $new_paragraph; 3122 last; 3123 } else { 3124 push @caption_contents, $content; 3125 } 3126 } 3127 push @caption_contents, @caption_original_contents; 3128 if ($new_paragraph) { 3129 $caption_text = $self->convert_tree_new_formatting_context( 3130 {'contents' => \@caption_contents}, 'float caption'); 3131 $prepended_text = ''; 3132 } 3133 } 3134 if ($caption_text eq '') { 3135 $prepended_text = $self->convert_tree_new_formatting_context( 3136 $prepended, 'float prepended'); 3137 if ($prepended_text ne '') { 3138 $prepended_text = '<p><strong>'.$prepended_text.'</strong></p>'; 3139 } 3140 } 3141 } else { 3142 $prepended_text = ''; 3143 } 3144 3145 if ($caption and $caption_text eq '') { 3146 $caption_text = $self->convert_tree_new_formatting_context( 3147 $caption->{'args'}->[0], 'float caption'); 3148 } 3149 if ($prepended_text.$caption_text ne '') { 3150 $prepended_text = $self->_attribute_class('div','float-caption'). '>' 3151 . $prepended_text; 3152 $caption_text .= '</div>'; 3153 } 3154 return $self->_attribute_class('div','float'). '>' .$label."\n".$content. 3155 $prepended_text.$caption_text . '</div>'; 3156} 3157$default_commands_conversion{'float'} = \&_convert_float_command; 3158 3159sub _convert_quotation_command($$$$$) 3160{ 3161 my $self = shift; 3162 my $cmdname = shift; 3163 my $command = shift; 3164 my $args = shift; 3165 my $content = shift; 3166 3167 #$cmdname = $small_alias{$cmdname} 3168 # if $small_alias{$cmdname}; 3169 3170 my $attribution = ''; 3171 if ($command->{'extra'} and $command->{'extra'}->{'authors'}) { 3172 foreach my $author (@{$command->{'extra'}->{'authors'}}) { 3173 my $centered_author = $self->gdt("\@center --- \@emph{{author}}\n", 3174 {'author' => $author->{'args'}->[0]->{'contents'}}); 3175 $centered_author->{'parent'} = $command; 3176 $attribution .= $self->convert_tree($centered_author); 3177 } 3178 } 3179 if (!$self->in_string()) { 3180 return "<blockquote>\n" . $content . "</blockquote>\n" . $attribution; 3181 } else { 3182 return $content.$attribution; 3183 } 3184} 3185$default_commands_conversion{'quotation'} = \&_convert_quotation_command; 3186 3187sub _convert_cartouche_command($$$$) 3188{ 3189 my $self = shift; 3190 my $cmdname = shift; 3191 my $command = shift; 3192 my $content = shift; 3193 3194 if ($content =~ /\S/ and !$self->in_string()) { 3195 return $self->_attribute_class('table', 'cartouche') 3196 ." border=\"1\"><tr><td>\n". $content ."</td></tr></table>\n"; 3197 } 3198 return $content; 3199} 3200 3201$default_commands_conversion{'cartouche'} = \&_convert_cartouche_command; 3202 3203sub _convert_itemize_command($$$$) 3204{ 3205 my $self = shift; 3206 my $cmdname = shift; 3207 my $command = shift; 3208 my $content = shift; 3209 3210 if ($self->in_string()) { 3211 return $content; 3212 } 3213 if ($command->{'extra'}->{'command_as_argument'} 3214 and $command->{'extra'}->{'command_as_argument'}->{'cmdname'} eq 'bullet') { 3215 return "<ul>\n" . $content. "</ul>\n"; 3216 } else { 3217 return $self->_attribute_class('ul',$NO_BULLET_LIST_CLASS).">\n" 3218 . $content . "</ul>\n"; 3219 } 3220} 3221 3222$default_commands_conversion{'itemize'} = \&_convert_itemize_command; 3223 3224sub _convert_enumerate_command($$$$) 3225{ 3226 my $self = shift; 3227 my $cmdname = shift; 3228 my $command = shift; 3229 my $content = shift; 3230 3231 if ($self->in_string()) { 3232 return $content; 3233 } 3234 if ($content eq '') { 3235 return ''; 3236 } 3237 my $specification = $command->{'extra'}->{'enumerate_specification'}; 3238 if (defined $specification) { 3239 my ($start, $type); 3240 if ($specification =~ /^\d*$/ and $specification ne '1') { 3241 $start = $specification; 3242 } elsif ($specification =~ /^[A-Z]$/) { 3243 $start = 1 + ord($specification) - ord('A'); 3244 $type = 'A'; 3245 } elsif ($specification =~ /^[a-z]$/) { 3246 $start = 1 + ord($specification) - ord('a'); 3247 $type = 'a'; 3248 } 3249 if (defined $type and defined $start) { 3250 return "<ol type=\"$type\" start=\"$start\">\n" . $content . "</ol>\n"; 3251 } elsif (defined $start) { 3252 return "<ol start=\"$start\">\n" . $content . "</ol>\n"; 3253 } 3254 } 3255 return "<ol>\n" . $content . "</ol>\n"; 3256} 3257 3258$default_commands_conversion{'enumerate'} = \&_convert_enumerate_command; 3259 3260sub _convert_multitable_command($$$$) 3261{ 3262 my $self = shift; 3263 my $cmdname = shift; 3264 my $command = shift; 3265 my $content = shift; 3266 3267 if ($self->in_string()) { 3268 return $content; 3269 } 3270 if ($content =~ /\S/) { 3271 return "<table>\n" . $content . "</table>\n"; 3272 } else { 3273 return ''; 3274 } 3275} 3276 3277$default_commands_conversion{'multitable'} = \&_convert_multitable_command; 3278 3279sub _convert_xtable_command($$$$) 3280{ 3281 my $self = shift; 3282 my $cmdname = shift; 3283 my $command = shift; 3284 my $content = shift; 3285 3286 if ($self->in_string()) { 3287 return $content; 3288 } 3289 if ($content ne '') { 3290 return "<dl compact=\"compact\">\n" . $content . "</dl>\n"; 3291 } else { 3292 return ''; 3293 } 3294} 3295$default_commands_conversion{'table'} = \&_convert_xtable_command; 3296$default_commands_conversion{'ftable'} = \&_convert_xtable_command; 3297$default_commands_conversion{'vtable'} = \&_convert_xtable_command; 3298 3299sub _convert_item_command($$$$) 3300{ 3301 my $self = shift; 3302 my $cmdname = shift; 3303 my $command = shift; 3304 my $content = shift; 3305 3306 if ($self->in_string()) { 3307 return $content; 3308 } 3309 if ($command->{'parent'}->{'cmdname'} 3310 and $command->{'parent'}->{'cmdname'} eq 'itemize') { 3311 my $prepend ; 3312 my $itemize = $command->{'parent'}; 3313 if ($itemize->{'extra'}->{'command_as_argument'} 3314 and $itemize->{'extra'}->{'command_as_argument'}->{'cmdname'} eq 'bullet') { 3315 $prepend = ''; 3316 } else { 3317 # Setting multiple expansion should not be needed, except in 3318 # case of invalid constructs 3319 $prepend = $self->convert_tree_new_formatting_context( 3320 $itemize->{'args'}->[0], 3321 $command->{'cmdname'}, 'item_prepended'); 3322 } 3323 if ($content =~ /\S/) { 3324 return '<li>' . $prepend .' '. $content . '</li>'; 3325 } else { 3326 return ''; 3327 } 3328 } elsif ($command->{'parent'}->{'cmdname'} 3329 and $command->{'parent'}->{'cmdname'} eq 'enumerate') { 3330 if ($content =~ /\S/) { 3331 return '<li>' . ' ' . $content . '</li>'; 3332 } else { 3333 return ''; 3334 } 3335 } elsif ($command->{'parent'}->{'type'} 3336 and $command->{'parent'}->{'type'} eq 'table_term') { 3337 # FIXME instead use the code of Plaintext or DocBook. 3338 my $args = $content; 3339 if ($args->[0]) { 3340 my $tree = $self->_table_item_content_tree($command, 3341 [$args->[0]->{'tree'}]); 3342 my $result = $self->convert_tree ($tree); 3343 foreach my $command_name (reverse($self->commands_stack())) { 3344 if ($preformatted_code_commands{$command_name}) { 3345 $result = '<tt>' .$result. '</tt>'; 3346 last; 3347 } 3348 } 3349 my $index_id = $self->command_id ($command); 3350 my $anchor; 3351 if (defined($index_id)) { 3352 $anchor = $self->_get_copiable_anchor($index_id); 3353 $index_id = " id='$index_id'"; 3354 } else { 3355 $anchor = ''; 3356 $index_id = ''; 3357 } 3358 3359 return "<dt${index_id}><span>$result$anchor</span></dt>\n"; 3360 } else { 3361 return ''; 3362 } 3363 } elsif ($command->{'parent'}->{'type'} 3364 and $command->{'parent'}->{'type'} eq 'row') { 3365 return $self->_convert_tab_command ($cmdname, $command, $content); 3366 } 3367 return ''; 3368} 3369$default_commands_conversion{'item'} = \&_convert_item_command; 3370$default_commands_conversion{'headitem'} = \&_convert_item_command; 3371$default_commands_conversion{'itemx'} = \&_convert_item_command; 3372 3373sub _convert_tab_command ($$$$) 3374{ 3375 my $self = shift; 3376 my $cmdname = shift; 3377 my $command = shift; 3378 my $content = shift; 3379 3380 my $cell_nr = $command->{'extra'}->{'cell_number'}; 3381 my $row = $command->{'parent'}; 3382 my $row_cmdname = $row->{'contents'}->[0]->{'cmdname'}; 3383 my $multitable = $row->{'parent'}->{'parent'}; 3384 3385 my $fractions = ''; 3386 my $cf = $multitable->{'extra'}->{'columnfractions'}; 3387 if ($cf) { 3388 if (exists($cf->{'extra'}->{'misc_args'}->[$cell_nr-1])) { 3389 my $fraction = sprintf('%d', 3390 100*$cf->{'extra'}->{'misc_args'}->[$cell_nr-1]); 3391 $fractions = " width=\"$fraction%\""; 3392 } 3393 } 3394 3395 $content =~ s/^\s*//; 3396 $content =~ s/\s*$//; 3397 3398 if ($self->in_string()) { 3399 return $content; 3400 } 3401 if ($row_cmdname eq 'headitem') { 3402 return "<th${fractions}>" . $content . '</th>'; 3403 } else { 3404 return "<td${fractions}>" . $content . '</td>'; 3405 } 3406} 3407$default_commands_conversion{'tab'} = \&_convert_tab_command; 3408 3409sub _convert_xref_commands($$$$) 3410{ 3411 my $self = shift; 3412 my $cmdname = shift; 3413 my $root = shift; 3414 my $args = shift; 3415 3416 my $tree; 3417 my $name; 3418 if ($cmdname ne 'inforef' 3419 and defined($args->[2]->{'normal'}) and $args->[2]->{'normal'} ne '') { 3420 $name = $args->[2]->{'normal'}; 3421 } elsif (defined($args->[1]->{'normal'}) and $args->[1]->{'normal'} ne '') { 3422 $name = $args->[1]->{'normal'} 3423 } 3424 3425 if ($cmdname eq 'inforef') { 3426 $args->[3] = $args->[2]; 3427 $args->[2] = undef; 3428 } 3429 3430 my $file_arg_tree; 3431 my $file = ''; 3432 if (defined($args->[3]->{'monospacetext'}) 3433 and $args->[3]->{'monospacetext'} ne '') { 3434 $file_arg_tree = $args->[3]->{'tree'}; 3435 $file = $args->[3]->{'monospacetext'}; 3436 } 3437 3438 my $book = ''; 3439 $book = $args->[4]->{'normal'} if (defined($args->[4]->{'normal'})); 3440 3441 # internal reference 3442 if ($cmdname ne 'inforef' and $book eq '' and $file eq '' 3443 and $root->{'extra'}->{'node_argument'} 3444 and defined($root->{'extra'}->{'node_argument'}->{'normalized'}) 3445 and !$root->{'extra'}->{'node_argument'}->{'manual_content'} 3446 and $self->{'labels'} 3447 and $self->{'labels'}->{$root->{'extra'}->{'node_argument'}->{'normalized'}}) { 3448 my $node 3449 = $self->label_command($root->{'extra'}->{'node_argument'}->{'normalized'}); 3450 # This is the node if USE_NODES, otherwise this may be the sectioning 3451 # command (if the sectioning command is really associated to the node) 3452 my $command = $self->command_element_command($node); 3453 $command = $node if (!$node->{'extra'}->{'associated_section'} 3454 or $node->{'extra'}->{'associated_section'} ne $command); 3455 3456 my $href = $self->command_href($command, undef, $root); 3457 3458 if (!defined($name)) { 3459 if ($self->get_conf('xrefautomaticsectiontitle') eq 'on' 3460 and $node->{'extra'}->{'associated_section'}) { 3461 $command = $node->{'extra'}->{'associated_section'}; 3462 $name = $self->command_text($command, 'text_nonumber'); 3463 } elsif ($node->{'cmdname'} eq 'float') { 3464 if (!$self->get_conf('XREF_USE_FLOAT_LABEL')) { 3465 $name = $self->command_text($command); 3466 } 3467 if (!defined($name) or $name eq '') { 3468 if (defined($args->[0]->{'monospace'})) { 3469 $name = $args->[0]->{'monospace'}; 3470 } else { 3471 $name = ''; 3472 } 3473 } 3474 } elsif (!$self->get_conf('XREF_USE_NODE_NAME_ARG') 3475 and (defined($self->get_conf('XREF_USE_NODE_NAME_ARG')) 3476 or !$self->in_preformatted())) { 3477 $name = $self->command_text($command, 'text_nonumber'); 3478 #die "$command $command->{'normalized'}" if (!defined($name)); 3479 } elsif (defined($args->[0]->{'monospace'})) { 3480 $name = $args->[0]->{'monospace'}; 3481 } else { 3482 $name = ''; 3483 } 3484 } 3485 my $reference = $name; 3486 $reference = "<a href=\"$href\">$name</a>" if ($href ne '' 3487 and !$self->in_string()); 3488 3489 # maybe use {'extra'}->{'node_argument'}? 3490 my $is_section = ($command->{'cmdname'} ne 'node' 3491 and $command->{'cmdname'} ne 'anchor' 3492 and $command->{'cmdname'} ne 'float'); 3493 if ($cmdname eq 'pxref') { 3494 $tree = $self->gdt('see {reference_name}', 3495 { 'reference_name' => {'type' => '_converted', 'text' => $reference} }); 3496 } elsif ($cmdname eq 'xref' or $cmdname eq 'inforef') { 3497 $tree = $self->gdt('See {reference_name}', 3498 { 'reference_name' => {'type' => '_converted', 'text' => $reference} }); 3499 } elsif ($cmdname eq 'ref') { 3500 $tree = $self->gdt('{reference_name}', 3501 { 'reference_name' => {'type' => '_converted', 'text' => $reference} }); 3502 } 3503 } else { 3504 # external reference 3505 my $node_entry = {}; 3506 $node_entry->{'node_content'} = $root->{'extra'}->{'node_argument'}->{'node_content'} 3507 if ($root->{'extra'}->{'node_argument'} 3508 and $root->{'extra'}->{'node_argument'}->{'node_content'}); 3509 $node_entry->{'normalized'} = $root->{'extra'}->{'node_argument'}->{'normalized'} 3510 if ($root->{'extra'}->{'node_argument'} 3511 and exists($root->{'extra'}->{'node_argument'}->{'normalized'})); 3512 3513 # file argument takes precedence over the file in the node (file)node entry 3514 if (defined($file_arg_tree) and $file ne '') { 3515 $node_entry->{'manual_content'} = $file_arg_tree->{'contents'}; 3516 } elsif ($root->{'extra'}->{'node_argument'} 3517 and $root->{'extra'}->{'node_argument'}->{'manual_content'}) { 3518 $node_entry->{'manual_content'} 3519 = $root->{'extra'}->{'node_argument'}->{'manual_content'}; 3520 my $file_with_node_tree = {'type' => '_code', 3521 'contents' => [@{$node_entry->{'manual_content'}}]}; 3522 $file = $self->convert_tree($file_with_node_tree, 'node file in ref'); 3523 } 3524 my $href = $self->command_href($node_entry, undef, $root); 3525 3526 if ($book eq '') { 3527 if (!defined($name)) { 3528 my $node_name = $self->command_text($node_entry); 3529 $name = $node_name; 3530 } elsif ($file ne '') { 3531 $name = "($file)$name"; 3532 } 3533 } elsif (!defined($name) and $node_entry->{'node_content'}) { 3534 my $node_no_file_tree = {'type' => '_code', 3535 'contents' => [@{$node_entry->{'node_content'}}]}; 3536 my $node_name = $self->convert_tree($node_no_file_tree, 'node in ref'); 3537 if (defined($node_name) and ($self->get_conf('KEEP_TOP_EXTERNAL_REF') 3538 or $node_name ne 'Top')) { 3539 $name = $node_name; 3540 } 3541 } 3542 3543 # not exactly sure when it happens. Something like @ref{(file),,,Manual}? 3544 $name = $args->[0]->{'monospace'} 3545 if (!defined($name) 3546 # FIXME could it really be Top? 3547 and ($self->get_conf('KEEP_TOP_EXTERNAL_REF') 3548 or $args->[0]->{'monospace'} ne 'Top')); 3549 3550 $name = '' if (!defined($name)); 3551 my $reference = $name; 3552 my $book_reference = ''; 3553 if (!$self->in_string() and $href ne '') { 3554 # attribute to distiguish links to Texinfo manuals from other links 3555 # and to provide manual name of target 3556 my $attribute = ''; 3557 if ($file) { 3558 $attribute = "data-manual=\"".$self->protect_text($file)."\" "; 3559 } 3560 if ($name ne '') { 3561 $reference = "<a ${attribute}href=\"$href\">$name</a>"; 3562 } elsif ($book ne '') { 3563 $book_reference = "<a ${attribute}href=\"$href\">$book</a>"; 3564 } 3565 } 3566 if ($cmdname eq 'pxref') { 3567 if (($book ne '') and ($href ne '') and ($reference ne '')) { 3568 $tree = $self->gdt('see {reference} in @cite{{book}}', 3569 { 'reference' => {'type' => '_converted', 'text' => $reference}, 3570 'book' => {'type' => '_converted', 'text' => $book }}); 3571 } elsif ($book_reference ne '') { 3572 $tree = $self->gdt('see @cite{{book_reference}}', 3573 { 'book_reference' => {'type' => '_converted', 3574 'text' => $book_reference }}); 3575 } elsif (($book ne '') and ($reference ne '')) { 3576 $tree = $self->gdt('see `{section}\' in @cite{{book}}', 3577 { 'section' => {'type' => '_converted', 'text' => $reference}, 3578 'book' => {'type' => '_converted', 'text' => $book }}); 3579 } elsif ($book ne '') { # should seldom or even never happen 3580 $tree = $self->gdt('see @cite{{book}}', 3581 {'book' => {'type' => '_converted', 'text' => $book }}); 3582 } elsif ($href ne '') { 3583 $tree = $self->gdt('see {reference}', 3584 { 'reference' => {'type' => '_converted', 'text' => $reference} }); 3585 } elsif ($reference ne '') { 3586 $tree = $self->gdt('see `{section}\'', { 3587 'section' => {'type' => '_converted', 'text' => $reference} }); 3588 } 3589 } elsif ($cmdname eq 'xref' or $cmdname eq 'inforef') { 3590 if (($book ne '') and ($href ne '') and ($reference ne '')) { 3591 $tree = $self->gdt('See {reference} in @cite{{book}}', 3592 { 'reference' => {'type' => '_converted', 'text' => $reference}, 3593 'book' => {'type' => '_converted', 'text' => $book }}); 3594 } elsif ($book_reference ne '') { 3595 $tree = $self->gdt('See @cite{{book_reference}}', 3596 { 'book_reference' => {'type' => '_converted', 3597 'text' => $book_reference }}); 3598 } elsif (($book ne '') and ($reference ne '')) { 3599 $tree = $self->gdt('See `{section}\' in @cite{{book}}', 3600 { 'section' => {'type' => '_converted', 'text' => $reference}, 3601 'book' => {'type' => '_converted', 'text' => $book }}); 3602 } elsif ($book ne '') { # should seldom or even never happen 3603 $tree = $self->gdt('See @cite{{book}}', 3604 {'book' => {'type' => '_converted', 'text' => $book }}); 3605 } elsif ($href ne '') { 3606 $tree = $self->gdt('See {reference}', 3607 { 'reference' => {'type' => '_converted', 'text' => $reference} }); 3608 } elsif ($reference ne '') { 3609 $tree = $self->gdt('See `{section}\'', { 3610 'section' => {'type' => '_converted', 'text' => $reference} }); 3611 } 3612 } else { 3613 if (($book ne '') and ($href ne '') and ($reference ne '')) { 3614 $tree = $self->gdt('{reference} in @cite{{book}}', 3615 { 'reference' => {'type' => '_converted', 'text' => $reference}, 3616 'book' => {'type' => '_converted', 'text' => $book }}); 3617 } elsif ($book_reference ne '') { 3618 $tree = $self->gdt('@cite{{book_reference}}', 3619 { 'book_reference' => {'type' => '_converted', 3620 'text' => $book_reference }}); 3621 } elsif (($book ne '') and ($reference ne '')) { 3622 $tree = $self->gdt('`{section}\' in @cite{{book}}', 3623 { 'section' => {'type' => '_converted', 'text' => $reference}, 3624 'book' => {'type' => '_converted', 'text' => $book }}); 3625 } elsif ($book ne '') { # should seldom or even never happen 3626 $tree = $self->gdt('@cite{{book}}', 3627 {'book' => {'type' => '_converted', 'text' => $book }}); 3628 } elsif ($href ne '') { 3629 $tree = $self->gdt('{reference}', 3630 { 'reference' => {'type' => '_converted', 'text' => $reference} }); 3631 } elsif ($reference ne '') { 3632 $tree = $self->gdt('`{section}\'', { 3633 'section' => {'type' => '_converted', 'text' => $reference} }); 3634 } 3635 } 3636 if (!defined($tree)) { 3637 # May happen if there is no argument 3638 #die "external: $cmdname, ($args), '$name' '$file' '$book' '$href' '$reference'. tree undef"; 3639 return ''; 3640 } 3641 } 3642 return $self->convert_tree($tree); 3643} 3644foreach my $command(keys(%ref_commands)) { 3645 $default_commands_conversion{$command} = \&_convert_xref_commands; 3646} 3647 3648sub _convert_index_command($$$$) 3649{ 3650 my $self = shift; 3651 my $cmdname = shift; 3652 my $command = shift; 3653 my $args = shift; 3654 3655 my $index_id = $self->command_id($command); 3656 if (defined($index_id) and $index_id ne '' 3657 and !@{$self->{'multiple_pass'}} 3658 and !$self->in_string()) { 3659 my $result = "<span id=\"$index_id\"></span>"; 3660 $result .= "\n" unless ($self->in_preformatted()); 3661 return $result; 3662 } 3663 return ''; 3664} 3665$default_commands_conversion{'cindex'} = \&_convert_index_command; 3666 3667my %formatted_index_entries; 3668 3669sub _convert_printindex_command($$$$) 3670{ 3671 my $self = shift; 3672 my $cmdname = shift; 3673 my $command = shift; 3674 my $args = shift; 3675 3676 my $index_name; 3677 if ($command->{'extra'} and $command->{'extra'}->{'misc_args'} 3678 and defined($command->{'extra'}->{'misc_args'}->[0])) { 3679 $index_name = $command->{'extra'}->{'misc_args'}->[0]; 3680 } else { 3681 return ''; 3682 } 3683 if (!$self->{'index_entries_by_letter'} 3684 or !$self->{'index_entries_by_letter'}->{$index_name} 3685 or !@{$self->{'index_entries_by_letter'}->{$index_name}}) { 3686 return ''; 3687 } 3688 3689 #foreach my $letter_entry (@{$self->{'index_entries_by_letter'}->{$index_name}}) { 3690 # print STDERR "IIIIIII $letter_entry->{'letter'}\n"; 3691 # foreach my $index_entry (@{$letter_entry->{'entries'}}) { 3692 # print STDERR " ".join('|', keys(%$index_entry))."||| $index_entry->{'key'}\n"; 3693 # } 3694 #} 3695 return '' if ($self->in_string()); 3696 3697 $self->_new_document_context($cmdname); 3698 3699 my $result = ''; 3700 3701 # First do the summary letters linking to the letters done below 3702 my %letter_id; 3703 my @non_alpha = (); 3704 my @alpha = (); 3705 # collect the links 3706 my $symbol_idx = 0; 3707 foreach my $letter_entry (@{$self->{'index_entries_by_letter'}->{$index_name}}) { 3708 my $letter = $letter_entry->{'letter'}; 3709 my $index_element_id = $self->_element_direction($self->{'current_element'}, 3710 'This', 'target'); 3711 if (!defined($index_element_id)) { 3712 $index_element_id = $target_prefix; 3713 } 3714 my $is_symbol = $letter !~ /^[[:alpha:]]/; 3715 my $identifier; 3716 if ($is_symbol) { 3717 $symbol_idx++; 3718 $identifier = $index_element_id . "_${index_name}_symbol-$symbol_idx"; 3719 } else { 3720 $identifier = $index_element_id . "_${index_name}_letter-${letter}"; 3721 } 3722 $letter_id{$letter} = $identifier; 3723 3724 my $summary_letter_link = $self->_attribute_class('a', 'summary-letter') 3725 ." href=\"#$identifier\"><b>".$self->protect_text($letter).'</b></a>'; 3726 if ($is_symbol) { 3727 push @non_alpha, $summary_letter_link; 3728 } else { 3729 push @alpha, $summary_letter_link; 3730 } 3731 } 3732 # Format the summary letters 3733 my $join = ''; 3734 my $non_alpha_text = ''; 3735 my $alpha_text = ''; 3736 $join = " \n<br>\n" if (@non_alpha and @alpha); 3737 if (@non_alpha) { 3738 $non_alpha_text = join("\n \n", @non_alpha) . "\n"; 3739 } 3740 if (@alpha) { 3741 $alpha_text = join("\n \n", @alpha) . "\n \n"; 3742 } 3743 # format the summary 3744 my $summary = "<table><tr><th valign=\"top\">" 3745 . $self->convert_tree($self->gdt('Jump to')) .": </th><td>" . 3746 $non_alpha_text . $join . $alpha_text . "</td></tr></table>\n"; 3747 3748 $result .= $summary; 3749 3750 # now format the index entries 3751 $result .= $self->_attribute_class('table', "index-$index_name") 3752 ." border=\"0\">\n" . "<tr><td></td><th align=\"left\">" 3753 . $self->convert_tree($self->gdt('Index Entry')) 3754 . "</th><td> </td><th align=\"left\"> " 3755 . $self->convert_tree($self->gdt('Section')) 3756 ."</th></tr>\n" . "<tr><td colspan=\"4\"> ".$self->get_conf('DEFAULT_RULE') 3757 ."</td></tr>\n"; 3758 foreach my $letter_entry (@{$self->{'index_entries_by_letter'}->{$index_name}}) { 3759 my $letter = $letter_entry->{'letter'}; 3760 my $entries_text = ''; 3761 foreach my $index_entry_ref (@{$letter_entry->{'entries'}}) { 3762 # to avoid double error messages set ignore_notice if an entry was 3763 # already formatted once, for example if there are multiple printindex. 3764 my $already_formatted; 3765 if (!$formatted_index_entries{$index_entry_ref}) { 3766 $formatted_index_entries{$index_entry_ref} = 1; 3767 } else { 3768 $already_formatted = 1; 3769 $self->{'ignore_notice'}++; 3770 } 3771 3772 my $entry; 3773 if ($index_entry_ref->{'in_code'}) { 3774 $entry = $self->convert_tree({'type' => '_code', 3775 'contents' => $index_entry_ref->{'content'}}); 3776 } else { 3777 $entry = $self->convert_tree({'contents' => $index_entry_ref->{'content'}}); 3778 } 3779 $entry .= $self->convert_index_subentries($index_entry_ref); 3780 if ($already_formatted) { 3781 $self->{'ignore_notice'}--; 3782 } 3783 3784 next if ($entry !~ /\S/); 3785 $entry = '<code>' .$entry .'</code>' if ($index_entry_ref->{'in_code'}); 3786 my $entry_href = $self->command_href($index_entry_ref->{'command'}); 3787 my $associated_command; 3788 if ($self->get_conf('NODE_NAME_IN_INDEX')) { 3789 $associated_command = $index_entry_ref->{'node'}; 3790 if (!defined($associated_command)) { 3791 $associated_command 3792 = $self->command_node($index_entry_ref->{'command'}); 3793 } 3794 } 3795 if (!$associated_command) { 3796 $associated_command 3797 = $self->command_element_command($index_entry_ref->{'command'}); 3798 if (!$associated_command) { 3799 # Use Top if not associated command found 3800 $associated_command 3801 = $self->element_command($self->global_element('Top')); 3802 } 3803 } 3804 my ($associated_command_href, $associated_command_text); 3805 if ($associated_command) { 3806 $associated_command_href = $self->command_href($associated_command); 3807 $associated_command_text = $self->command_text($associated_command); 3808 } 3809 3810 $entries_text .= '<tr><td></td><td valign="top">' 3811 . "<a href=\"$entry_href\">$entry</a>" . 3812 $self->get_conf('INDEX_ENTRY_COLON') . 3813 '</td><td> </td><td valign="top">'; 3814 $entries_text .= "<a href=\"$associated_command_href\">$associated_command_text</a>" 3815 if ($associated_command_href); 3816 $entries_text .= "</td></tr>\n"; 3817 } 3818 # a letter and associated indice entries 3819 $result .= '<tr>' . 3820 "<th id=\"$letter_id{$letter}\">".$self->protect_text($letter) 3821 . "</th><td></td><td></td></tr>\n" . $entries_text . 3822 "<tr><td colspan=\"4\"> ".$self->get_conf('DEFAULT_RULE')."</td></tr>\n"; 3823 3824 } 3825 $result .= "</table>\n"; 3826 3827 pop @{$self->{'document_context'}}; 3828 3829 return $result .$summary; 3830} 3831$default_commands_conversion{'printindex'} = \&_convert_printindex_command; 3832 3833sub _contents_inline_element($$$) 3834{ 3835 my $self = shift; 3836 my $cmdname = shift; 3837 my $command = shift; 3838 3839 my $content = &{$self->{'format_contents'}}($self, $cmdname, $command); 3840 if ($content) { 3841 my $element_name = $contents_command_element_name{$cmdname}; 3842 my $special_element 3843 = $self->special_element($element_name); 3844 my $heading; 3845 my $result = "<div class=\"${element_name}_element\""; 3846 if ($special_element) { 3847 my $id = $self->command_id($special_element); 3848 if ($id ne '') { 3849 $result .= " id=\"$id\""; 3850 } 3851 $heading = $self->command_text($special_element); 3852 } else { 3853 # happens when called as convert() and not output() 3854 #cluck "$cmdname special element not defined"; 3855 $heading 3856 = $self->convert_tree ($self->get_conf('SPECIAL_ELEMENTS_NAME')->{$element_name}); 3857 } 3858 $result .= ">\n"; 3859 my $class = $self->get_conf('SPECIAL_ELEMENTS_CLASS')->{$element_name}; 3860 $result .= &{$self->{'format_heading_text'}}($self, $class.'-heading', 3861 $heading, $self->get_conf('CHAPTER_HEADER_LEVEL'))."\n"; 3862 $result .= $content . "</div>\n"; 3863 return $result; 3864 } 3865 return ''; 3866} 3867 3868sub _convert_informative_command($$$$) 3869{ 3870 my $self = shift; 3871 my $cmdname = shift; 3872 my $command = shift; 3873 3874 return '' if ($self->in_string()); 3875 $cmdname = 'shortcontents' if ($cmdname eq 'summarycontents'); 3876 3877 $self->_informative_command($command); 3878 if ($self->get_conf('CONTENTS_OUTPUT_LOCATION') eq 'inline' 3879 and ($cmdname eq 'contents' or $cmdname eq 'shortcontents') 3880 and $self->get_conf($cmdname) 3881 and $self->{'structuring'} and $self->{'structuring'}->{'sectioning_root'} 3882 and scalar(@{$self->{'structuring'}->{'sections_list'}}) > 1) { 3883 return $self->_contents_inline_element($cmdname, $command); 3884 } 3885 if ($cmdname eq 'documentlanguage') { 3886 $self->_translate_names(); 3887 } 3888 return ''; 3889} 3890 3891foreach my $informative_command (@informative_global_commands) { 3892 $default_commands_conversion{$informative_command} 3893 = \&_convert_informative_command; 3894} 3895 3896# associate same formatting function for @small* command 3897# as for the associated @-command 3898foreach my $small_command (keys(%small_alias)) { 3899 $default_commands_conversion{$small_command} 3900 = $default_commands_conversion{$small_alias{$small_command}}; 3901} 3902 3903# Keys are tree element types, values are function references to convert 3904# elements of that type. Can be overridden with 3905# Texinfo::Config::texinfo_types_conversion, setup by 3906# Texinfo::Config::texinfo_register_type_formatting() 3907my %default_types_conversion; 3908 3909sub default_types_conversion($$) 3910{ 3911 my $self = shift; 3912 my $type = shift; 3913 return $default_types_conversion{$type}; 3914} 3915 3916# Ignored commands 3917foreach my $type ('empty_line_after_command', 'preamble', 3918 'preamble_before_setfilename', 3919 'empty_spaces_after_command', 'spaces_at_end', 3920 'empty_spaces_before_argument', 'empty_spaces_before_paragraph', 3921 'empty_spaces_after_close_brace') { 3922 $default_types_conversion{$type} = undef; 3923} 3924 3925my %paragraph_style = ( 3926 'center' => 'center', 3927 'flushleft' => 'left', 3928 'flushright' => 'right', 3929 ); 3930 3931sub _quotation_arg_to_prepend($$) 3932{ 3933 my $self = shift; 3934 my $command = shift; 3935 if ($command->{'parent'} and $command->{'parent'}->{'cmdname'} 3936 and ($command->{'parent'}->{'cmdname'} eq 'quotation' 3937 or $command->{'parent'}->{'cmdname'} eq 'smallquotation') 3938 and $command->{'parent'}->{'args'} 3939 and $command->{'parent'}->{'args'}->[0] 3940 and $command->{'parent'}->{'args'}->[0]->{'contents'} 3941 and @{$command->{'parent'}->{'args'}->[0]->{'contents'}}) { 3942 return $self->convert_tree($self->gdt('@b{{quotation_arg}:} ', 3943 {'quotation_arg' => 3944 $command->{'parent'}->{'args'}->[0]->{'contents'}})); 3945 3946 } 3947 return undef; 3948} 3949 3950sub _convert_paragraph_type($$$$) 3951{ 3952 my $self = shift; 3953 my $type = shift; 3954 my $command = shift; 3955 my $content = shift; 3956 3957 if ($self->paragraph_number() == 1) { 3958 my $in_format = $self->top_format(); 3959 if ($in_format) { 3960 # no first paragraph in those environment to avoid extra spacing 3961 if ($in_format eq 'itemize' 3962 or $in_format eq 'enumerate' 3963 or $in_format eq 'multitable') { 3964 return $content; 3965 } else { 3966 my $prepended = $self->_quotation_arg_to_prepend($command); 3967 $content = $prepended.$content if (defined($prepended)); 3968 } 3969 } 3970 } 3971 return $content if ($self->in_string()); 3972 3973 if ($content =~ /\S/) { 3974 my $align = $self->in_align(); 3975 if ($align and $paragraph_style{$align}) { 3976 return "<p align=\"$paragraph_style{$align}\">".$content."</p>"; 3977 } else { 3978 return "<p>".$content."</p>"; 3979 } 3980 } else { 3981 return ''; 3982 } 3983} 3984 3985$default_types_conversion{'paragraph'} = \&_convert_paragraph_type; 3986 3987sub _preformatted_class() 3988{ 3989 my $self = shift; 3990 my $pre_class; 3991 my @pre_classes = $self->preformatted_classes_stack(); 3992 foreach my $class (@pre_classes) { 3993 # FIXME maybe add or $pre_class eq 'menu-preformatted' to override 3994 # 'menu-preformatted' with 'menu-comment'? 3995 $pre_class = $class unless ($pre_class 3996 and $preformatted_code_commands{$pre_class} 3997 and !($preformatted_code_commands{$class} 3998 or $class eq 'menu-preformatted')); 3999 } 4000 return $pre_class; 4001} 4002 4003sub _convert_preformatted_type($$$$) 4004{ 4005 my $self = shift; 4006 my $type = shift; 4007 my $command = shift; 4008 my $content = shift; 4009 4010 if (!defined($content)) { 4011 cluck "content undef in _convert_preformatted_type " 4012 .Texinfo::Common::_print_current($command); 4013 } 4014 4015 my $current = $command; 4016 4017 # !defined preformatted_number may happen if there is something before the 4018 # first preformatted. For example an @exdent. 4019 if ($self->preformatted_number() and $self->preformatted_number() == 1) { 4020 my $prepended = $self->_quotation_arg_to_prepend($command); 4021 $content = $prepended.$content if (defined($prepended)); 4022 } 4023 4024 return '' if ($content eq ''); 4025 return $content if ($type eq 'rawpreformatted'); 4026 4027 my $pre_class = $self->_preformatted_class(); 4028 4029 if ($self->top_format() eq 'multitable') { 4030 $content =~ s/^\s*//; 4031 $content =~ s/\s*$//; 4032 } 4033 4034 # menu_entry_description is always in a preformatted container 4035 # in the tree, as the whole menu is meant to be an 4036 # environment where spaces and newlines are preserved. 4037 # 4038 # However, if not in preformatted block command (nor in SIMPLE_MENU), 4039 # we don't preserve spaces and newlines in menu_entry_description, 4040 # instead the whole menu_entry is in a table, so here, not <pre> 4041 if ($command->{'parent'}->{'type'} 4042 and $command->{'parent'}->{'type'} eq 'menu_entry_description' 4043 and !$self->_in_preformatted_in_menu()) { 4044 return $content; 4045 } 4046 4047 if ($self->in_string()) { 4048 return $content; 4049 } 4050 $content =~ s/^\n/\n\n/; # a newline immediately after a <pre> is ignored. 4051 my $result = $self->_attribute_class('pre', $pre_class).">".$content."</pre>"; 4052 4053 # this may happen with lines without textual content 4054 # between a def* and def*x. 4055 if ($command->{'parent'}->{'cmdname'} 4056 and $command->{'parent'}->{'cmdname'} =~ /^def/) { 4057 $result = '<dd>'.$result.'</dd>'; 4058 } 4059 return $result; 4060} 4061 4062$default_types_conversion{'preformatted'} = \&_convert_preformatted_type; 4063$default_types_conversion{'rawpreformatted'} = \&_convert_preformatted_type; 4064 4065sub _convert_bracketed_type($$$$) { 4066 my $self = shift; 4067 my $type = shift; 4068 my $command = shift; 4069 my $content = shift; 4070#print STDERR "$self $type $command $content\n"; 4071 4072 return '{'.$content.'}'; 4073} 4074 4075$default_types_conversion{'bracketed'} = \&_convert_bracketed_type; 4076 4077sub _convert_definfoenclose_type($$$$) { 4078 my $self = shift; 4079 my $type = shift; 4080 my $command = shift; 4081 my $content = shift; 4082 4083 return $self->protect_text($command->{'extra'}->{'begin'}) . $content 4084 .$self->protect_text($command->{'extra'}->{'end'}); 4085} 4086 4087$default_types_conversion{'definfoenclose_command'} 4088 = \&_convert_definfoenclose_type; 4089 4090sub _convert_text($$$) 4091{ 4092 my $self = shift; 4093 my $type = shift; 4094 my $command = shift; 4095 my $text = shift; 4096 4097 if ($self->in_verbatim()) { 4098 return $self->protect_text($text); 4099 } 4100 return $text if ($self->in_raw()); 4101 $text = uc($text) if ($self->in_upper_case()); 4102 $text = $self->protect_text($text); 4103 if ($self->get_conf('ENABLE_ENCODING') and 4104 !$self->get_conf('ENABLE_ENCODING_USE_ENTITY') 4105 and $self->get_conf('OUTPUT_ENCODING_NAME') 4106 and $self->get_conf('OUTPUT_ENCODING_NAME') eq 'utf-8') { 4107 $text = Texinfo::Convert::Unicode::unicode_text($text, 4108 ($self->in_code() or $self->in_math())); 4109 } elsif (!$self->in_code() and !$self->in_math()) { 4110 if ($self->get_conf('USE_ISO')) { 4111 $text =~ s/---/\&mdash\;/g; 4112 $text =~ s/--/\&ndash\;/g; 4113 $text =~ s/``/\&ldquo\;/g; 4114 $text =~ s/''/\&rdquo\;/g; 4115 $text =~ s/'/\&rsquo\;/g; 4116 $text =~ s/`/\&lsquo\;/g; 4117 } else { 4118 $text =~ s/``/"/g; 4119 $text =~ s/''/"/g; 4120 $text =~ s/---/\x{1F}/g; 4121 $text =~ s/--/-/g; 4122 $text =~ s/\x{1F}/--/g; 4123 } 4124 } 4125 $text = $self->_protect_space($text); 4126 return $text; 4127} 4128 4129$default_types_conversion{'text'} = \&_convert_text; 4130 4131sub _simplify_text_for_comparison($) 4132{ 4133 my $text = shift; 4134 $text =~ s/[^\w]//g; 4135 return $text; 4136} 4137 4138sub _convert_row_type($$$$) { 4139 my $self = shift; 4140 my $type = shift; 4141 my $command = shift; 4142 my $content = shift; 4143 4144 return $content if ($self->in_string()); 4145 if ($content =~ /\S/) { 4146 my $row_cmdname = $command->{'contents'}->[0]->{'cmdname'}; 4147 if ($row_cmdname eq 'headitem') { 4148 return '<thead><tr>' . $content . '</tr></thead>' . "\n"; 4149 } else { 4150 return '<tr>' . $content . '</tr>' . "\n"; 4151 } 4152 } else { 4153 return ''; 4154 } 4155} 4156$default_types_conversion{'row'} = \&_convert_row_type; 4157 4158sub _convert_menu_entry_type($$$) 4159{ 4160 my $self = shift; 4161 my $type = shift; 4162 my $command = shift; 4163 4164 my $href; 4165 my $rel = ''; 4166 my $section; 4167 my $node_entry = $command->{'extra'}->{'menu_entry_node'}; 4168 # external node 4169 my $external_node; 4170 if ($node_entry->{'manual_content'}) { 4171 $href = $self->command_href($node_entry, undef, $command); 4172 $external_node = 1; 4173 } else { 4174 my $node = $self->label_command($node_entry->{'normalized'}); 4175 # if !NODE_NAME_IN_MENU, we pick the associated section, except if 4176 # the node is the element command 4177 if ($node->{'extra'}->{'associated_section'} 4178 and !$self->get_conf('NODE_NAME_IN_MENU') 4179 and !($self->command_element_command($node) eq $node)) { 4180 $section = $node->{'extra'}->{'associated_section'}; 4181 $href = $self->command_href($section, undef, $command); 4182 } else { 4183 $href = $self->command_href($node, undef, $command); 4184 } 4185 if ($node->{'extra'}->{'isindex'}) { 4186 # Mark the target as an index. See 4187 # http://microformats.org/wiki/existing-rel-values#HTML5_link_type_extensions 4188 $rel = ' rel="index"'; 4189 } 4190 } 4191 4192 $html_menu_entry_index++; 4193 my $accesskey = ''; 4194 $accesskey = " accesskey=\"$html_menu_entry_index\"" 4195 if ($self->get_conf('USE_ACCESSKEY') and $html_menu_entry_index < 10); 4196 4197 my $MENU_SYMBOL = $self->get_conf('MENU_SYMBOL'); 4198 my $MENU_ENTRY_COLON = $self->get_conf('MENU_ENTRY_COLON'); 4199 4200 if ($self->_in_preformatted_in_menu() or $self->in_string()) { 4201 my $result = ''; 4202 my $i = 0; 4203 my @args = @{$command->{'args'}}; 4204 while (@args) { 4205 last if ($args[0]->{'type'} 4206 and $args[0]->{'type'} eq 'menu_entry_description'); 4207 my $arg = shift @args; 4208 if ($arg->{'type'} and $arg->{'type'} eq 'menu_entry_node') { 4209 my $name = $self->convert_tree( 4210 {'type' => '_code', 'contents' => $arg->{'contents'}}); 4211 if ($href ne '' and !$self->in_string()) { 4212 $result .= "<a href=\"$href\"$rel$accesskey>".$name."</a>"; 4213 } else { 4214 $result .= $name; 4215 } 4216 } elsif ($arg->{'type'} and $arg->{'type'} eq 'menu_entry_leading_text') { 4217 my $text = $arg->{'text'}; 4218 4219 $text =~ s/\*/$MENU_SYMBOL/; 4220 $result .= $text; 4221 } else { 4222 $result .= $self->convert_tree($arg, "menu_arg preformatted [$i]"); 4223 } 4224 $i++; 4225 } 4226 my $description = ''; 4227 foreach my $arg (@args) { 4228 $description .= $self->convert_tree($arg, "menu_arg preformatted [$i]"); 4229 $i++; 4230 } 4231 4232 if (!$self->get_conf('SIMPLE_MENU')) { 4233 $description =~ s/^<pre[^>]*>//; 4234 $description =~ s/<\/pre>$//; 4235 } 4236 4237 $result = $result . $description; 4238 4239 if (!$self->get_conf('SIMPLE_MENU')) { 4240 my $pre_class = $self->_preformatted_class(); 4241 $result = $self->_attribute_class('pre', $pre_class).">".$result."</pre>"; 4242 } 4243 return $result; 4244 } 4245 4246 my $name; 4247 my $name_no_number; 4248 if ($section) { 4249 $name = $self->command_text($section); 4250 $name_no_number = $self->command_text($section, 'text_nonumber'); 4251 if ($href ne '' and $name ne '') { 4252 $name = "<a href=\"$href\"$rel$accesskey>".$name."</a>"; 4253 } 4254 } 4255 if (!defined($name) or $name eq '') { 4256 if ($command->{'extra'}->{'menu_entry_name'}) { 4257 $name = $self->convert_tree($command->{'extra'}->{'menu_entry_name'}); 4258 } 4259 if (!defined($name) or $name eq '') { 4260 if ($node_entry->{'manual_content'}) { 4261 $name = $self->command_text($node_entry); 4262 } else { 4263 $name = $self->convert_tree({'type' => '_code', 4264 'contents' => $node_entry->{'node_content'}}, 4265 "menu_arg name"); 4266 } 4267 } 4268 $name =~ s/^\s*//; 4269 $name_no_number = $name; 4270 if ($href ne '') { 4271 $name = "<a href=\"$href\"$rel$accesskey>".$name."</a>"; 4272 } 4273 $name = "$MENU_SYMBOL ".$name; 4274 } 4275 my $description = ''; 4276 if ($command->{'extra'}->{'menu_entry_description'}) { 4277 $description = $self->convert_tree ($command->{'extra'}->{'menu_entry_description'}, 4278 "menu_arg description"); 4279 if ($self->get_conf('AVOID_MENU_REDUNDANCY')) { 4280 $description = '' if (_simplify_text_for_comparison($name_no_number) 4281 eq _simplify_text_for_comparison($description)); 4282 } 4283 } 4284 return "<tr><td align=\"left\" valign=\"top\">$name$MENU_ENTRY_COLON</td><td> </td><td align=\"left\" valign=\"top\">$description</td></tr>\n"; 4285} 4286 4287$default_types_conversion{'menu_entry'} = \&_convert_menu_entry_type; 4288 4289sub _convert_menu_comment_type($$$$) 4290{ 4291 my $self = shift; 4292 my $type = shift; 4293 my $command = shift; 4294 my $content = shift; 4295 4296 if ($self->_in_preformatted_in_menu() or $self->in_string()) { 4297 return $content; 4298 } else { 4299 return "<tr><th colspan=\"3\" align=\"left\" valign=\"top\">".$content 4300 ."</th></tr>"; 4301 } 4302} 4303 4304$default_types_conversion{'menu_comment'} = \&_convert_menu_comment_type; 4305 4306sub _convert_before_item_type($$$$) 4307{ 4308 my $self = shift; 4309 my $type = shift; 4310 my $command = shift; 4311 my $content = shift; 4312 4313 return '' if ($content !~ /\S/); 4314 return $content if ($self->in_string()); 4315 my $top_format = $self->top_format(); 4316 if ($top_format eq 'itemize' or $top_format eq 'enumerate') { 4317 return '<li>'. $content .'</li>'; 4318 } elsif ($top_format eq 'table' or $top_format eq 'vtable' 4319 or $top_format eq 'ftable') { 4320 return '<dd>'. $content .'</dd>'."\n"; 4321 } elsif ($top_format eq 'multitable') { 4322 $content =~ s/^\s*//; 4323 $content =~ s/\s*$//; 4324 4325 return '<tr><td>'.$content.'</td></tr>'."\n"; 4326 } 4327} 4328 4329$default_types_conversion{'before_item'} = \&_convert_before_item_type; 4330 4331sub _convert_def_line_type($$$$) 4332{ 4333 my $self = shift; 4334 my $type = shift; 4335 my $command = shift; 4336 my $content = shift; 4337 4338 if ($self->in_string()) { 4339 return $self->protect_text(Texinfo::Convert::Text::convert( 4340 $command, Texinfo::Common::_convert_text_options($self))); 4341 } 4342 4343 my $index_label = ''; 4344 my $index_id = $self->command_id($command); 4345 if (defined($index_id) and $index_id ne '' and !@{$self->{'multiple_pass'}}) { 4346 $index_label = " id=\"$index_id\""; 4347 } 4348 my $arguments 4349 = Texinfo::Common::definition_arguments_content($command); 4350 4351 if (!$self->get_conf('DEF_TABLE')) { 4352 my $tree; 4353 my $command_name; 4354 if ($Texinfo::Common::def_aliases{$command->{'extra'}->{'def_command'}}) { 4355 $command_name = $Texinfo::Common::def_aliases{$command->{'extra'}->{'def_command'}}; 4356 } else { 4357 $command_name = $command->{'extra'}->{'def_command'}; 4358 } 4359 my $name; 4360 if ($command->{'extra'}->{'def_parsed_hash'}->{'name'}) { 4361 $name = $command->{'extra'}->{'def_parsed_hash'}->{'name'}; 4362 } else { 4363 $name = ''; 4364 } 4365 my $category = $command->{'extra'}->{'def_parsed_hash'}->{'category'}; 4366 my $category_result = ''; 4367 my $category_tree; 4368 if ($category) { 4369 $category_tree 4370 = {'type' => '_code', 4371 'contents'=>[$self->gdt("{category}: ", {'category' => $category})] 4372 }; 4373 # NB perhaps the category shouldn't be in_code. 4374 } else { 4375 $category = ''; 4376 } 4377 # no type 4378 if ($command_name eq 'deffn' 4379 or $command_name eq 'defvr' 4380 or $command_name eq 'deftp' 4381 or (($command_name eq 'deftypefn' 4382 or $command_name eq 'deftypevr') 4383 and !$command->{'extra'}->{'def_parsed_hash'}->{'type'}) 4384 or (($command_name eq 'defop' 4385 or ($command_name eq 'deftypeop' 4386 and !$command->{'extra'}->{'def_parsed_hash'}->{'type'}) 4387 or $command_name eq 'defcv' 4388 or ($command_name eq 'deftypecv' 4389 and !$command->{'extra'}->{'def_parsed_hash'}->{'type'})) 4390 and !$command->{'extra'}->{'def_parsed_hash'}->{'class'})) { 4391 $category_result = $self->convert_tree($category_tree); 4392 if ($arguments) { 4393 $tree = $self->gdt("\@strong{{name}} \@emph{{arguments}}", { 4394 'name' => $name, 4395 'arguments' => $arguments}); 4396 } else { 4397 $tree = $self->gdt("\@strong{{name}}", {'name' => $name}); 4398 } 4399 # with a type 4400 } elsif ($command_name eq 'deftypefn' 4401 or $command_name eq 'deftypevr' 4402 or (($command_name eq 'deftypeop' 4403 or $command_name eq 'deftypecv') 4404 and !$command->{'extra'}->{'def_parsed_hash'}->{'class'})) { 4405 if ($arguments) { 4406 my $strings = { 4407 'name' => $name, 4408 'type' => $command->{'extra'}->{'def_parsed_hash'}->{'type'}, 4409 'arguments' => $arguments}; 4410 if ($self->get_conf('deftypefnnewline') eq 'on') { 4411 $category_tree 4412 = {'type' => '_code', 4413 'contents' 4414 => [$self->gdt("{category}:\@* ", {'category' => $category})] 4415 }; 4416 $tree 4417 = $self->gdt("\@emph{{type}}\@* \@strong{{name}} \@emph{{arguments}}", 4418 $strings); 4419 } else { 4420 $tree 4421 = $self->gdt("\@emph{{type}} \@strong{{name}} \@emph{{arguments}}", 4422 $strings); 4423 } 4424 } else { 4425 my $strings = { 4426 'type' => $command->{'extra'}->{'def_parsed_hash'}->{'type'}, 4427 'name' => $name}; 4428 if ($self->get_conf('deftypefnnewline') eq 'on') { 4429 $category_tree 4430 = {'type' => '_code', 4431 'contents' 4432 => [$self->gdt("{category}:\@* ", {'category' => $category})] 4433 }; 4434 } else { 4435 $tree = $self->gdt("\@emph{{type}} \@strong{{name}}", 4436 $strings); 4437 } 4438 } 4439 $category_result = $self->convert_tree($category_tree); 4440 # with a class, no type 4441 } elsif ($command_name eq 'defcv' 4442 or ($command_name eq 'deftypecv' 4443 and !$command->{'extra'}->{'def_parsed_hash'}->{'type'})) { 4444 if ($arguments) { 4445 $tree = $self->gdt("{category} of {class}: \@strong{{name}} \@emph{{arguments}}", { 4446 'category' => $category, 4447 'name' => $name, 4448 'class' => $command->{'extra'}->{'def_parsed_hash'}->{'class'}, 4449 'arguments' => $arguments}); 4450 } else { 4451 $tree = $self->gdt("{category} of {class}: \@strong{{name}}", { 4452 'category' => $category, 4453 'class' => $command->{'extra'}->{'def_parsed_hash'}->{'class'}, 4454 'name' => $name}); 4455 } 4456 } elsif ($command_name eq 'defop' 4457 or ($command_name eq 'deftypeop' 4458 and !$command->{'extra'}->{'def_parsed_hash'}->{'type'})) { 4459 if ($arguments) { 4460 $tree = $self->gdt("{category} on {class}: \@strong{{name}} \@emph{{arguments}}", { 4461 'category' => $category, 4462 'name' => $name, 4463 'class' => $command->{'extra'}->{'def_parsed_hash'}->{'class'}, 4464 'arguments' => $arguments}); 4465 } else { 4466 $tree = $self->gdt("{category} on {class}: \@strong{{name}}", { 4467 'category' => $category, 4468 'class' => $command->{'extra'}->{'def_parsed_hash'}->{'class'}, 4469 'name' => $name}); 4470 } 4471 # with a class and a type 4472 } elsif ($command_name eq 'deftypeop') { 4473 if ($arguments) { 4474 my $strings = { 4475 'category' => $category, 4476 'name' => $name, 4477 'class' => $command->{'extra'}->{'def_parsed_hash'}->{'class'}, 4478 'type' => $command->{'extra'}->{'def_parsed_hash'}->{'type'}, 4479 'arguments' => $arguments}; 4480 if ($self->get_conf('deftypefnnewline') eq 'on') { 4481 $tree 4482 = $self->gdt("{category} on {class}:\@* \@emph{{type}}\@* \@strong{{name}} \@emph{{arguments}}", 4483 $strings); 4484 } else { 4485 $tree 4486 = $self->gdt("{category} on {class}: \@emph{{type}} \@strong{{name}} \@emph{{arguments}}", 4487 $strings); 4488 } 4489 } else { 4490 my $strings = { 4491 'category' => $category, 4492 'type' => $command->{'extra'}->{'def_parsed_hash'}->{'type'}, 4493 'class' => $command->{'extra'}->{'def_parsed_hash'}->{'class'}, 4494 'name' => $name}; 4495 if ($self->get_conf('deftypefnnewline') eq 'on') { 4496 $tree 4497 = $self->gdt("{category} on {class}:\@* \@emph{{type}}\@* \@strong{{name}}", 4498 $strings); 4499 } else { 4500 $tree 4501 = $self->gdt("{category} on {class}: \@emph{{type}} \@strong{{name}}", 4502 $strings); 4503 } 4504 } 4505 } elsif ($command_name eq 'deftypecv') { 4506 if ($arguments) { 4507 my $strings = { 4508 'category' => $category, 4509 'name' => $name, 4510 'class' => $command->{'extra'}->{'def_parsed_hash'}->{'class'}, 4511 'type' => $command->{'extra'}->{'def_parsed_hash'}->{'type'}, 4512 'arguments' => $arguments}; 4513 if ($self->get_conf('deftypefnnewline') eq 'on') { 4514 $tree 4515 = $self->gdt("{category} of {class}:\@* \@emph{{type}}\@* \@strong{{name}} \@emph{{arguments}}", 4516 $strings); 4517 } else { 4518 $tree 4519 = $self->gdt("{category} of {class}: \@emph{{type}} \@strong{{name}} \@emph{{arguments}}", 4520 $strings); 4521 } 4522 } else { 4523 my $strings = { 4524 'category' => $category, 4525 'type' => $command->{'extra'}->{'def_parsed_hash'}->{'type'}, 4526 'class' => $command->{'extra'}->{'def_parsed_hash'}->{'class'}, 4527 'name' => $name}; 4528 if ($self->get_conf('deftypefnnewline') eq 'on') { 4529 $tree 4530 = $self->gdt("{category} of {class}:\@* \@emph{{type}}\@* \@strong{{name}}", 4531 $strings); 4532 } else { 4533 $tree 4534 = $self->gdt("{category} of {class}: \@emph{{type}} \@strong{{name}}", 4535 $strings); 4536 } 4537 } 4538 } 4539 4540 if ($category_result ne '') { 4541 $category_result = $self->_attribute_class('span', 'category') 4542 .">$category_result</span>"; 4543 } 4544 my $anchor = $self->_get_copiable_anchor($index_id); 4545 return "<dt$index_label>".$category_result 4546 ."<span>".$self->convert_tree({'type' => '_code', 4547 'contents' => [$tree]}) . "$anchor</span></dt>\n"; 4548 } else { 4549 my $category_prepared = ''; 4550 if ($command->{'extra'} and $command->{'extra'}->{'def_parsed_hash'} 4551 and %{$command->{'extra'}->{'def_parsed_hash'}}) { 4552 my $parsed_definition_category 4553 = Texinfo::Common::definition_category ($self, $command); 4554 if ($parsed_definition_category) { 4555 $category_prepared = $self->convert_tree({'type' => '_code', 4556 'contents' => [$parsed_definition_category]}); 4557 } 4558 } 4559 4560 my $arguments_text = ''; 4561 if ($arguments) { 4562 $arguments_text = $self->convert_tree({'type' => '_code', 4563 'contents' => $arguments}); 4564 $arguments_text = '<em> ' . $arguments_text . '</em>' 4565 if ($arguments_text =~ /\S/); 4566 } 4567 4568 4569 my $def_type = ''; 4570 my $type_name = ''; 4571 if ($command->{'extra'}->{'def_parsed_hash'}->{'type'}) { 4572 $def_type = $self->convert_tree({'type' => '_code', 4573 'contents' => [$command->{'extra'}->{'def_parsed_hash'}->{'type'}]}); 4574 } 4575 $type_name = " <em>$def_type</em>" if ($def_type ne ''); 4576 my $name = ''; 4577 if ($command->{'extra'}->{'def_parsed_hash'}->{'name'}) { 4578 $name = $self->convert_tree({'type' => '_code', 4579 'contents' => [$command->{'extra'}->{'def_parsed_hash'}->{'name'}]}); 4580 } 4581 $type_name .= ' <strong>' . $name . '</strong>' if ($name ne ''); 4582 $type_name .= $arguments_text; 4583 4584 return "<tr$index_label><td align=\"left\">" . $type_name . 4585 "</td><td align=\"right\">" . $category_prepared . "</td></tr>\n"; 4586 } 4587} 4588 4589sub _get_copiable_anchor { 4590 my ($self, $id) = @_; 4591 my $result = ''; 4592 if ($id and $self->get_conf('COPIABLE_ANCHORS')) { 4593 $result = "<a href='#$id' class='copiable-anchor'> ¶</a>"; 4594 } 4595 return $result; 4596} 4597 4598$default_types_conversion{'def_line'} = \&_convert_def_line_type; 4599 4600sub _convert_def_item_type($$$$) 4601{ 4602 my $self = shift; 4603 my $type = shift; 4604 my $command = shift; 4605 my $content = shift; 4606 4607 return $content if ($self->in_string()); 4608 if ($content =~ /\S/) { 4609 if (! $self->get_conf('DEF_TABLE')) { 4610 return '<dd>' . $content . '</dd>'; 4611 } else { 4612 return '<tr><td colspan="2">' . $content . '</td></tr>'; 4613 } 4614 } 4615} 4616 4617$default_types_conversion{'def_item'} = \&_convert_def_item_type; 4618$default_types_conversion{'inter_def_item'} = \&_convert_def_item_type; 4619 4620sub _convert_def_command($$$$) { 4621 my $self = shift; 4622 my $cmdname = shift; 4623 my $command = shift; 4624 my $content = shift; 4625 4626 return $content if ($self->in_string()); 4627 if (!$self->get_conf('DEF_TABLE')) { 4628 return $self->_attribute_class('dl', 'def').">\n". $content ."</dl>\n"; 4629 } else { 4630 return "<table width=\"100%\">\n" . $content . "</table>\n"; 4631 } 4632} 4633 4634foreach my $command (keys(%def_commands)) { 4635 $default_commands_conversion{$command} = \&_convert_def_command; 4636} 4637 4638sub _convert_table_item_type($$$$) 4639{ 4640 my $self = shift; 4641 my $type = shift; 4642 my $command = shift; 4643 my $content = shift; 4644 4645 return $content if ($self->in_string()); 4646 if ($content =~ /\S/) { 4647 return '<dd>' . $content . '</dd>'."\n"; 4648 } 4649} 4650 4651$default_types_conversion{'table_item'} = \&_convert_table_item_type; 4652$default_types_conversion{'inter_item'} = \&_convert_table_item_type; 4653 4654# This type is the only one present if there are no elements. It is 4655# therefore used to do the formatting of the element in case there are no 4656# element. 4657sub _convert_root_text_type($$$$) 4658{ 4659 my $self = shift; 4660 my $type = shift; 4661 my $command = shift; 4662 my $content = shift; 4663 4664 my $result = $content; 4665 #$result =~ s/^\s*//; 4666 # if there is no element, the parent should not be an element 4667 if (!$command->{'parent'} 4668 or !$command->{'parent'}->{'type'} 4669 or $command->{'parent'}->{'type'} ne 'element') { 4670 $result .= &{$self->{'format_footnotes_text'}}($self); 4671 $result .= $self->get_conf('DEFAULT_RULE') ."\n", 4672 if ($self->get_conf('PROGRAM_NAME_IN_FOOTER') 4673 and defined($self->get_conf('DEFAULT_RULE')) 4674 and !$self->in_string()); 4675 } 4676 return $result; 4677} 4678 4679$default_types_conversion{'text_root'} = \&_convert_root_text_type; 4680 4681sub _contents_shortcontents_in_title($) 4682{ 4683 my $self = shift; 4684 4685 my $result = ''; 4686 4687 if ($self->{'structuring'} and $self->{'structuring'}->{'sectioning_root'} 4688 and scalar(@{$self->{'structuring'}->{'sections_list'}}) > 1 4689 and $self->get_conf('CONTENTS_OUTPUT_LOCATION') eq 'after_title') { 4690 foreach my $command ('contents', 'shortcontents') { 4691 if ($self->get_conf($command)) { 4692 my $contents_text = $self->_contents_inline_element($command, undef); 4693 if ($contents_text ne '') { 4694 $result .= $contents_text . $self->get_conf('DEFAULT_RULE')."\n"; 4695 } 4696 } 4697 } 4698 } 4699 return $result; 4700} 4701 4702# Convert @titlepage. Falls back to simpletitle. 4703sub _default_format_titlepage($) 4704{ 4705 my $self = shift; 4706 4707 my $titlepage_text; 4708 if ($self->{'extra'}->{'titlepage'}) { 4709 $titlepage_text = $self->convert_tree({'contents' 4710 => $self->{'extra'}->{'titlepage'}->{'contents'}}); 4711 } elsif ($self->{'simpletitle_tree'}) { 4712 my $title_text = $self->convert_tree_new_formatting_context( 4713 $self->{'simpletitle_tree'}, 'simpletitle_string'); 4714 $titlepage_text = &{$self->{'format_heading_text'}}($self, 'settitle', $title_text, 4715 0, {'cmdname' => 'settitle', 4716 'contents' => $self->{'simpletitle_tree'}->{'contents'}}); 4717 } 4718 my $result = ''; 4719 $result .= $titlepage_text.$self->get_conf('DEFAULT_RULE')."\n" 4720 if (defined($titlepage_text)); 4721 $result .= $self->_contents_shortcontents_in_title(); 4722 return $result; 4723} 4724 4725sub _print_title($) 4726{ 4727 my $self = shift; 4728 4729 my $result = ''; 4730 if ($self->get_conf('SHOW_TITLE')) { 4731 if ($self->get_conf('USE_TITLEPAGE_FOR_TITLE')) { 4732 $result .= &{$self->{'format_titlepage'}}($self); 4733 } else { 4734 if ($self->{'simpletitle_tree'}) { 4735 my $title_text = $self->convert_tree_new_formatting_context( 4736 $self->{'simpletitle_tree'}, 'simpletitle_string'); 4737 $result .= &{$self->{'format_heading_text'}}($self, 'settitle', $title_text, 4738 0, {'cmdname' => 'settitle', 4739 'contents' => $self->{'simpletitle_tree'}->{'contents'}}); 4740 } 4741 $result .= $self->_contents_shortcontents_in_title(); 4742 } 4743 } 4744 return $result; 4745} 4746 4747# Function for converting the top-level elements in the conversion: a section 4748# or a node. $ELEMENT was created in this module (in _prepare_elements), with 4749# type 'element' (it's not a tree element created by the parser). $CONTENT 4750# is the contents of the node/section, already converted. 4751sub _convert_element_type($$$$) 4752{ 4753 my $self = shift; 4754 my $type = shift; 4755 my $element = shift; 4756 my $content = shift; 4757 4758 if ($self->in_string()) { 4759 if (defined($content)) { 4760 return $content; 4761 } else { 4762 return ''; 4763 } 4764 } 4765 4766 my $result = ''; 4767 my $special_element; 4768 4769 if ($element->{'extra'}->{'special_element'}) { 4770 $special_element = $element->{'extra'}->{'special_element'}; 4771 $result .= join('', $self->close_registered_sections_level(0)); 4772 my $id = $self->command_id($element); 4773 $result .= "<div class=\"${special_element}_element\""; 4774 if ($id ne '') { 4775 $result .= " id=\"$id\""; 4776 } 4777 $result .= ">\n"; 4778 if ($self->get_conf('HEADERS') 4779 # first in page 4780 or $self->{'counter_in_file'}->{$element->{'filename'}} == 1) { 4781 $result .= &{$self->{'format_navigation_header'}}($self, 4782 $self->get_conf('MISC_BUTTONS'), undef, $element); 4783 } 4784 my $heading = $self->command_text($element); 4785 my $element_name = $element->{'extra'}->{'special_element'}; 4786 my $class = $self->get_conf('SPECIAL_ELEMENTS_CLASS')->{$element_name}; 4787 my $level = $self->get_conf('CHAPTER_HEADER_LEVEL'); 4788 if ($element_name eq 'Footnotes') { 4789 $level = $self->get_conf('FOOTNOTE_SEPARATE_HEADER_LEVEL'); 4790 } 4791 $result .= &{$self->{'format_heading_text'}}($self, $class.'-heading', 4792 $heading, $level)."\n"; 4793 4794 my $special_element_body .= &{$self->{'format_special_element_body'}} 4795 ($self, $special_element, $element); 4796 4797 # This may happen with footnotes in regions that are not expanded, 4798 # like @copying or @titlepage 4799 if ($special_element_body eq '') { 4800 return ''; 4801 } 4802 $result .= $special_element_body . '</div>'; 4803 } elsif (!$element->{'element_prev'}) { 4804 $result .= $self->_print_title(); 4805 if (!$element->{'element_next'}) { 4806 # only one element 4807 $result .= $content; 4808 $result .= &{$self->{'format_footnotes_text'}}($self); 4809 $result .= $self->get_conf('DEFAULT_RULE'); 4810 $result .= join('', $self->close_registered_sections_level(0)); 4811 return $result; 4812 } 4813 } 4814 $result .= $content unless ($special_element); 4815 $result .= &{$self->{'format_element_footer'}}($self, $type, 4816 $element, $content); 4817 return $result; 4818} 4819 4820sub _default_format_element_footer($$$$) 4821{ 4822 my $self = shift; 4823 my $type = shift; 4824 my $element = shift; 4825 my $content = shift; 4826 4827 my $result = ''; 4828 my $is_top = $self->element_is_top($element); 4829 my $next_is_top = ($element->{'element_next'} 4830 and $self->element_is_top($element->{'element_next'})); 4831 my $next_is_special = (defined($element->{'element_next'}) 4832 and $element->{'element_next'}->{'extra'}->{'special_element'}); 4833 4834 my $end_page = (!$element->{'element_next'} 4835 or (defined($element->{'filename'}) 4836 and $element->{'filename'} ne $element->{'element_next'}->{'filename'} 4837 and $self->{'file_counters'}->{$element->{'filename'}} == 1)); 4838 4839 my $is_special = $element->{'extra'}->{'special_element'}; 4840 4841 if (($end_page or $next_is_top or $next_is_special or $is_top) 4842 and $self->get_conf('VERTICAL_HEAD_NAVIGATION') 4843 and ($self->get_conf('SPLIT') ne 'node' 4844 or $self->get_conf('HEADERS') or $is_special or $is_top)) { 4845 $result .= "</td> 4846</tr> 4847</table>"."\n"; 4848 } 4849 4850 my $rule = ''; 4851 my $buttons; 4852 4853 if ($end_page) { 4854 $result .= join('', $self->close_registered_sections_level(0)); 4855 4856 # setup buttons for navigation footer 4857 if (($is_top or $is_special) 4858 and ($self->get_conf('SPLIT') or !$self->get_conf('MONOLITHIC')) 4859 and (($self->get_conf('HEADERS') 4860 or ($self->get_conf('SPLIT') and $self->get_conf('SPLIT') ne 'node')))) { 4861 if ($is_top) { 4862 $buttons = $self->get_conf('TOP_BUTTONS'); 4863 } else { 4864 $buttons = $self->get_conf('MISC_BUTTONS'); 4865 } 4866 } elsif ($self->get_conf('SPLIT') eq 'section') { 4867 $buttons = $self->get_conf('SECTION_FOOTER_BUTTONS'); 4868 } elsif ($self->get_conf('SPLIT') eq 'chapter') { 4869 $buttons = $self->get_conf('CHAPTER_FOOTER_BUTTONS'); 4870 } elsif ($self->get_conf('SPLIT') eq 'node') { 4871 if ($self->get_conf('HEADERS')) { 4872 my $no_footer_word_count; 4873 if ($self->get_conf('WORDS_IN_PAGE')) { 4874 my @cnt = split(/\W*\s+\W*/, $content); 4875 if (scalar(@cnt) < $self->get_conf('WORDS_IN_PAGE')) { 4876 $no_footer_word_count = 1; 4877 } 4878 } 4879 $buttons = $self->get_conf('NODE_FOOTER_BUTTONS') 4880 unless ($no_footer_word_count); 4881 } 4882 } 4883 } 4884 # FIXME the following condition is almost a duplication of end_page 4885 # except that the file counter needs not be 1 4886 if ((!$element->{'element_next'} 4887 or (defined($element->{'filename'}) 4888 and $element->{'filename'} ne $element->{'element_next'}->{'filename'})) 4889 and $self->get_conf('footnotestyle') eq 'end') { 4890 $result .= &{$self->{'format_footnotes_text'}}($self); 4891 } 4892 4893 if (!$buttons or $is_top or $is_special 4894 or ($end_page and ($self->get_conf('SPLIT') eq 'chapter' 4895 or $self->get_conf('SPLIT') eq 'section')) 4896 or ($self->get_conf('SPLIT') eq 'node' and $self->get_conf('HEADERS'))) { 4897 $rule = $self->get_conf('DEFAULT_RULE'); 4898 } 4899 4900 if (!$end_page and ($is_top or $next_is_top or ($next_is_special 4901 and !$is_special))) { 4902 $rule = $self->get_conf('BIG_RULE'); 4903 } 4904 4905 if ($buttons or !$end_page or $self->get_conf('PROGRAM_NAME_IN_FOOTER')) { 4906 $result .= "$rule\n" if ($rule); 4907 } 4908 if ($buttons) { 4909 $result .= &{$self->{'format_navigation_header_panel'}}($self, $buttons, 4910 undef, $element); 4911 } 4912 4913 return $result; 4914} 4915 4916$default_types_conversion{'element'} = \&_convert_element_type; 4917 4918sub _new_document_context($$) 4919{ 4920 my $self = shift; 4921 my $cmdname = shift; 4922 4923 push @{$self->{'document_context'}}, 4924 {'cmdname' => $cmdname, 4925 'formatting_context' => [{'cmdname' => $cmdname}], 4926 'composition_context' => ['raggedright'], 4927 'formats' => [], 4928 'monospace' => [0], 4929 }; 4930} 4931 4932# Functions accessed with e.g. 'format_heading_text'. 4933my %default_formatting_references = ( 4934 'format_heading_text' => \&_default_format_heading_text, 4935 'format_comment' => \&_default_format_comment, 4936 'format_protect_text' => \&_default_format_protect_text, 4937 'format_css_lines' => \&_default_format_css_lines, 4938 'format_begin_file' => \&_default_format_begin_file, 4939 'format_node_redirection_page' => \&_default_format_node_redirection_page, 4940 'format_end_file' => \&_default_format_end_file, 4941 'format_special_element_body' => \&_default_format_special_element_body, 4942 'format_footnotes_text' => \&_default_format_footnotes_text, 4943 'format_program_string' => \&_default_format_program_string, 4944 'format_titlepage' => \&_default_format_titlepage, 4945 'format_navigation_header' => \&_default_format_navigation_header, 4946 'format_navigation_header_panel' => \&_default_format_navigation_header_panel, 4947 'format_element_header' => \&_default_format_element_header, 4948 'format_element_footer' => \&_default_format_element_footer, 4949 'format_button' => \&_default_format_button, 4950 'format_button_icon_img' => \&_default_format_button_icon_img, 4951 'format_contents' => \&_default_format_contents, 4952 'format_frame_files' => \&_default_format_frame_files, 4953); 4954 4955sub _use_entity_is_entity($$) 4956{ 4957 my $self = shift; 4958 my $text = shift; 4959 return 0 if (!$self->get_conf('ENABLE_ENCODING_USE_ENTITY')); 4960 return 1 if ($text =~ /^&/ and $text =~ /;$/); 4961} 4962 4963sub _complete_commands_formatting($$) 4964{ 4965 my $self = shift; 4966 my $command = shift; 4967 if (!defined ($self->{'commands_formatting'}->{'normal'}->{$command})) { 4968 $self->{'commands_formatting'}->{'normal'}->{$command} = ''; 4969 } 4970 if (!defined ($self->{'commands_formatting'}->{'preformatted'}->{$command})) { 4971 $self->{'commands_formatting'}->{'preformatted'}->{$command} = 4972 $self->{'commands_formatting'}->{'normal'}->{$command}; 4973 } 4974 if (!defined ($self->{'commands_formatting'}->{'string'}->{$command})) { 4975 $self->{'commands_formatting'}->{'string'}->{$command} = 4976 $self->{'commands_formatting'}->{'preformatted'}->{$command}; 4977 } 4978} 4979 4980my %htmlxref_entries = ( 4981 'node' => [ 'node', 'section', 'chapter', 'mono' ], 4982 'section' => [ 'section', 'chapter','node', 'mono' ], 4983 'chapter' => [ 'chapter', 'section', 'node', 'mono' ], 4984 'mono' => [ 'mono', 'chapter', 'section', 'node' ], 4985); 4986 4987sub _parse_htmlxref_files($$) 4988{ 4989 my $self = shift; 4990 my $files = shift; 4991 my $htmlxref; 4992 4993 foreach my $file (@$files) { 4994 my ($fname) = $file; 4995 if ($self->get_conf('TEST')) { 4996 $fname =~ s/([^\/]+\/)*//; # strip directories for out-of-source builds 4997 } 4998 print STDERR "html refs config file: $file\n" if ($self->get_conf('DEBUG')); 4999 unless (open (HTMLXREF, $file)) { 5000 $self->document_warn( 5001 sprintf(__("could not open html refs config file %s: %s"), 5002 $file, $!)); 5003 next; 5004 } 5005 my $line_nr = 0; 5006 my %variables; 5007 while (my $hline = <HTMLXREF>) { 5008 my $line = $hline; 5009 $line_nr++; 5010 next if $hline =~ /^\s*#/; 5011 #$hline =~ s/[#]\s.*//; 5012 $hline =~ s/^\s*//; 5013 next if $hline =~ /^\s*$/; 5014 chomp ($hline); 5015 if ($hline =~ s/^\s*(\w+)\s*=\s*//) { 5016 # handle variables 5017 my $var = $1; 5018 my $re = join '|', map { quotemeta $_ } keys %variables; 5019 $hline =~ s/\$\{($re)\}/defined $variables{$1} ? $variables{$1} 5020 : "\${$1}"/ge; 5021 $variables{$var} = $hline; 5022 next; 5023 } 5024 my @htmlxref = split /\s+/, $hline; 5025 my $manual = shift @htmlxref; 5026 my $split_or_mono = shift @htmlxref; 5027 #print STDERR "$split_or_mono $Texi2HTML::Config::htmlxref_entries{$split_or_mono} $line_nr\n"; 5028 if (!defined($split_or_mono)) { 5029 $self->file_line_warn(__("missing type"), $fname, $line_nr); 5030 next; 5031 } elsif (!defined($htmlxref_entries{$split_or_mono})) { 5032 $self->file_line_warn(sprintf(__("unrecognized type: %s"), 5033 $split_or_mono), $fname, $line_nr); 5034 next; 5035 } 5036 my $href = shift @htmlxref; 5037 next if (exists($htmlxref->{$manual}->{$split_or_mono})); 5038 5039 if (defined($href)) { # substitute 'variables' 5040 my $re = join '|', map { quotemeta $_ } keys %variables; 5041 $href =~ s/\$\{($re)\}/defined $variables{$1} ? $variables{$1} 5042 : "\${$1}"/ge; 5043 $href =~ s/\/*$// if ($split_or_mono ne 'mono'); 5044 } 5045 $htmlxref->{$manual}->{$split_or_mono} = $href; 5046 } 5047 if (!close (HTMLXREF)) { 5048 $self->document_warn(sprintf(__( 5049 "error on closing html refs config file %s: %s"), 5050 $file, $!)); 5051 } 5052 } 5053 return $htmlxref; 5054} 5055 5056sub _load_htmlxref_files { 5057 my ($self) = @_; 5058 5059 my @htmlxref_dirs = (); 5060 if ($self->get_conf('TEST')) { 5061 my $curdir = File::Spec->curdir(); 5062 # to have reproducible tests, do not use system or user 5063 # directories if TEST is set. 5064 @htmlxref_dirs = File::Spec->catdir($curdir, '.texinfo'); 5065 5066 my $input_directory = $self->{'info'}->{'input_directory'}; 5067 if (defined($input_directory) 5068 and $input_directory ne '.' and $input_directory ne '') { 5069 unshift @htmlxref_dirs, $input_directory; 5070 } 5071 } elsif ($self->{'language_config_dirs'} 5072 and @{$self->{'language_config_dirs'}}) { 5073 @htmlxref_dirs = @{$self->{'language_config_dirs'}}; 5074 } 5075 unshift @htmlxref_dirs, '.'; 5076 5077 my @texinfo_htmlxref_files; 5078 my $init_file_from_conf = $self->get_conf('HTMLXREF'); 5079 if ($init_file_from_conf) { 5080 if (!$self->get_conf('TEST')) { 5081 @texinfo_htmlxref_files = ( $init_file_from_conf ); 5082 } else { 5083 @texinfo_htmlxref_files 5084 = Texinfo::Common::locate_init_file ($init_file_from_conf, 5085 \@htmlxref_dirs, 1); 5086 } 5087 } elsif (!$self->get_conf('TEST')) { 5088 @texinfo_htmlxref_files 5089 = Texinfo::Common::locate_init_file ('htmlxref.cnf', 5090 \@htmlxref_dirs, 1); 5091 } 5092 $self->{'htmlxref_files'} = \@texinfo_htmlxref_files; 5093 5094 $self->{'htmlxref'} = {}; 5095 if ($self->{'htmlxref_files'}) { 5096 $self->{'htmlxref'} = _parse_htmlxref_files($self, 5097 $self->{'htmlxref_files'}); 5098 } 5099} 5100 5101 5102sub converter_initialize($) 5103{ 5104 my $self = shift; 5105 5106 $foot_num = 0; 5107 $foot_lines = ''; 5108 %formatted_index_entries = (); 5109 %footnote_id_numbers = (); 5110 5111 %{$self->{'css_map'}} = %css_map; 5112 5113 _load_htmlxref_files($self); 5114 5115 foreach my $type (keys(%default_types_conversion)) { 5116 if (exists($Texinfo::Config::texinfo_types_conversion{$type})) { 5117 $self->{'types_conversion'}->{$type} 5118 = $Texinfo::Config::texinfo_types_conversion{$type}; 5119 } else { 5120 $self->{'types_conversion'}->{$type} 5121 = $default_types_conversion{$type}; 5122 } 5123 } 5124 # FIXME API with a function call? Used in cvs.init. 5125 foreach my $type (keys(%default_code_types)) { 5126 $self->{'code_types'}->{$type} = $default_code_types{$type}; 5127 } 5128 if ($Texinfo::Config::texinfo_code_types) { 5129 foreach my $type (keys(%$Texinfo::Config::texinfo_code_types)) { 5130 $self->{'code_types'}->{$type} 5131 = $Texinfo::Config::texinfo_code_types->{$type}; 5132 } 5133 } 5134 5135 # FIXME put value in a category in Texinfo::Common? 5136 foreach my $command (keys(%misc_commands), keys(%brace_commands), 5137 keys (%block_commands), keys(%no_brace_commands), 'value') { 5138 if (exists($Texinfo::Config::texinfo_commands_conversion{$command})) { 5139 $self->{'commands_conversion'}->{$command} 5140 = $Texinfo::Config::texinfo_commands_conversion{$command}; 5141 } else { 5142 if ($self->get_conf('FORMAT_MENU') ne 'menu' 5143 and ($command eq 'menu' or $command eq 'detailmenu')) { 5144 $self->{'commands_conversion'}->{$command} = undef; 5145 } elsif ($format_raw_commands{$command} 5146 and !$self->{'expanded_formats_hash'}->{$command}) { 5147 } elsif (exists($default_commands_conversion{$command})) { 5148 $self->{'commands_conversion'}->{$command} 5149 = $default_commands_conversion{$command}; 5150 if ($command eq 'menu' and $self->get_conf('SIMPLE_MENU')) { 5151 $self->{'commands_conversion'}->{$command} 5152 = $default_commands_conversion{'example'}; 5153 } 5154 } 5155 } 5156 } 5157 5158 foreach my $context ('normal', 'preformatted', 'string') { 5159 foreach my $command (keys(%{$default_commands_formatting{'normal'}})) { 5160 if (exists ($Texinfo::Config::commands_formatting{$context}->{$command})) { 5161 $self->{'commands_formatting'}->{$context}->{$command} 5162 = $Texinfo::Config::commands_formatting{$context}->{$command}; 5163 } else { 5164 if (defined($default_commands_formatting{$context}->{$command})) { 5165 if ($self->get_conf('ENABLE_ENCODING') 5166 and Texinfo::Convert::Unicode::unicode_for_brace_no_arg_command( 5167 $command, $self->get_conf('OUTPUT_ENCODING_NAME')) 5168 and !$self->_use_entity_is_entity($default_commands_formatting{$context}->{$command})) { 5169 $self->{'commands_formatting'}->{$context}->{$command} 5170 = Texinfo::Convert::Unicode::unicode_for_brace_no_arg_command( 5171 $command, $self->get_conf('OUTPUT_ENCODING_NAME')); 5172 } else { 5173 $self->{'commands_formatting'}->{$context}->{$command} 5174 = $default_commands_formatting{$context}->{$command}; 5175 } 5176 } 5177 } 5178 if (exists ($Texinfo::Config::commands_translation{$context}->{$command})) { 5179 $self->{'commands_translation'}->{$context}->{$command} 5180 = $Texinfo::Config::commands_translation{$context}->{$command}; 5181 delete $self->{'translated_commands'}->{$command}; 5182 } elsif (defined($default_commands_translation{$context}->{$command})) { 5183 $self->{'commands_translation'}->{$context}->{$command} 5184 = $default_commands_translation{$context}->{$command}; 5185 delete $self->{'translated_commands'}->{$command}; 5186 } 5187 } 5188 } 5189 5190 # set sane defaults in case there is none and the default formatting 5191 # function is used 5192 foreach my $command (keys(%{$default_commands_formatting{'normal'}})) { 5193 if ($self->{'commands_conversion'}->{$command} 5194 and $self->{'commands_conversion'}->{$command} 5195 eq $default_commands_conversion{$command}) { 5196 $self->_complete_commands_formatting($command); 5197 } 5198 } 5199 5200 foreach my $context (keys(%style_commands_formatting)) { 5201 foreach my $command (keys(%{$style_commands_formatting{$context}})) { 5202 if (exists ($Texinfo::Config::style_commands_formatting{$context}->{$command})) { 5203 $self->{'style_commands_formatting'}->{$context}->{$command} 5204 = $Texinfo::Config::style_commands_formatting{$context}->{$command}; 5205 } elsif (exists($style_commands_formatting{$context}->{$command})) { 5206 $self->{'style_commands_formatting'}->{$context}->{$command} 5207 = $style_commands_formatting{$context}->{$command}; 5208 } 5209 } 5210 } 5211 5212 foreach my $command (keys %{$self->{'commands_conversion'}}) { 5213 if (exists($Texinfo::Config::commands_args{$command})) { 5214 $self->{'commands_args'}->{$command} 5215 = $Texinfo::Config::commands_args{$command}; 5216 } elsif (exists($default_commands_args{$command})) { 5217 $self->{'commands_args'}->{$command} = $default_commands_args{$command}; 5218 } 5219 } 5220 5221 foreach my $formatting_reference (keys(%default_formatting_references)) { 5222 $self->{'default_formatting_functions'}->{$formatting_reference} 5223 = $default_formatting_references{$formatting_reference}; 5224 if (defined($Texinfo::Config::texinfo_formatting_references{$formatting_reference})) { 5225 $self->{$formatting_reference} 5226 = $Texinfo::Config::texinfo_formatting_references{$formatting_reference}; 5227 } else { 5228 $self->{$formatting_reference} 5229 = $default_formatting_references{$formatting_reference}; 5230 } 5231 } 5232 5233 $self->{'document_context'} = []; 5234 $self->{'multiple_pass'} = []; 5235 $self->{'pending_closes'} = []; 5236 $self->_new_document_context('_toplevel_context'); 5237 5238 if ($self->get_conf('SPLIT') and $self->get_conf('SPLIT') ne 'chapter' 5239 and $self->get_conf('SPLIT') ne 'section' 5240 and $self->get_conf('SPLIT') ne 'node') { 5241 $self->force_conf('SPLIT', 'node'); 5242 } 5243 5244 return $self; 5245} 5246 5247# the entry point for _convert 5248sub convert_tree($$;$) 5249{ 5250 my $self = shift; 5251 my $element = shift; 5252 my $explanation = shift; 5253 5254 return $self->_convert($element, $explanation); 5255} 5256 5257sub _normalized_to_id($) 5258{ 5259 my $id = shift; 5260 if (!defined($id)) { 5261 cluck "_normalized_to_id id not defined"; 5262 return ''; 5263 } 5264 $id =~ s/^([0-9_])/g_t$1/; 5265 return $id; 5266} 5267 5268sub _default_format_css_lines($) 5269{ 5270 my $self = shift; 5271 5272 return if ($self->get_conf('NO_CSS')); 5273 5274 my $css_refs = $self->get_conf('CSS_REFS'); 5275 5276 return if (!@{$self->{'css_import_lines'}} and !@{$self->{'css_rule_lines'}} 5277 and !keys(%{$self->{'css_map'}}) and !@$css_refs); 5278 5279 my $css_text = "<style type=\"text/css\">\n<!--\n"; 5280 $css_text .= join('',@{$self->{'css_import_lines'}}) . "\n" 5281 if (@{$self->{'css_import_lines'}}); 5282 foreach my $css_rule (sort(keys(%{$self->{'css_map'}}))) { 5283 next unless ($self->{'css_map'}->{$css_rule}); 5284 $css_text .= "$css_rule {$self->{'css_map'}->{$css_rule}}\n"; 5285 } 5286 $css_text .= join('',@{$self->{'css_rule_lines'}}) . "\n" 5287 if (@{$self->{'css_rule_lines'}}); 5288 $css_text .= "-->\n</style>\n"; 5289 foreach my $ref (@$css_refs) { 5290 $css_text .= "<link rel=\"stylesheet\" type=\"text/css\" href=\"$ref\">\n"; 5291 } 5292 $self->set_conf('CSS_LINES', $css_text); 5293} 5294 5295sub _process_css_file($$$) 5296{ 5297 my $self = shift; 5298 my $fh =shift; 5299 my $file = shift; 5300 my $in_rules = 0; 5301 my $in_comment = 0; 5302 my $in_import = 0; 5303 my $in_string = 0; 5304 my $rules = []; 5305 my $imports = []; 5306 my $line_nr = 0; 5307 while (my $line = <$fh>) { 5308 $line_nr++; 5309 #print STDERR "Line: $line"; 5310 if ($in_rules) { 5311 push @$rules, $line; 5312 next; 5313 } 5314 my $text = ''; 5315 while (1) { 5316 #sleep 1; 5317 #print STDERR "${text}!in_comment $in_comment in_rules $in_rules in_import $in_import in_string $in_string: $line"; 5318 if ($in_comment) { 5319 if ($line =~ s/^(.*?\*\/)//) { 5320 $text .= $1; 5321 $in_comment = 0; 5322 } else { 5323 push @$imports, $text . $line; 5324 last; 5325 } 5326 } elsif (!$in_string and $line =~ s/^\///) { 5327 if ($line =~ s/^\*//) { 5328 $text .= '/*'; 5329 $in_comment = 1; 5330 } else { 5331 push (@$imports, $text. "\n") if ($text ne ''); 5332 push (@$rules, '/' . $line); 5333 $in_rules = 1; 5334 last; 5335 } 5336 } elsif (!$in_string and $in_import and $line =~ s/^([\"\'])//) { 5337 # strings outside of import start rules 5338 $text .= "$1"; 5339 $in_string = quotemeta("$1"); 5340 } elsif ($in_string and $line =~ s/^(\\$in_string)//) { 5341 $text .= $1; 5342 } elsif ($in_string and $line =~ s/^($in_string)//) { 5343 $text .= $1; 5344 $in_string = 0; 5345 } elsif ((! $in_string and !$in_import) 5346 and ($line =~ s/^([\\]?\@import)$// 5347 or $line =~ s/^([\\]?\@import\s+)//)) { 5348 $text .= $1; 5349 $in_import = 1; 5350 } elsif (!$in_string and $in_import and $line =~ s/^\;//) { 5351 $text .= ';'; 5352 $in_import = 0; 5353 } elsif (($in_import or $in_string) and $line =~ s/^(.)//) { 5354 $text .= $1; 5355 } elsif (!$in_import and $line =~ s/^([^\s])//) { 5356 push (@$imports, $text. "\n") if ($text ne ''); 5357 push (@$rules, $1 . $line); 5358 $in_rules = 1; 5359 last; 5360 } elsif ($line =~ s/^(\s)//) { 5361 $text .= $1; 5362 } elsif ($line eq '') { 5363 push (@$imports, $text); 5364 last; 5365 } 5366 } 5367 } 5368 #file_line_warn (__("string not closed in css file"), $file) if ($in_string); 5369 #file_line_warn (__("--css-file ended in comment"), $file) if ($in_comment); 5370 #file_line_warn (__("\@import not finished in css file"), $file) if ($in_import and !$in_comment and !$in_string); 5371 $self->file_line_warn(sprintf(__("string not closed in css file"), 5372 $file, $line_nr)) if ($in_string); 5373 $self->file_line_warn(sprintf(__("--css-include ended in comment"), 5374 $file, $line_nr)) if ($in_comment); 5375 $self->file_line_warn(sprintf(__("\@import not finished in css file"), 5376 $file, $line_nr)) 5377 if ($in_import and !$in_comment and !$in_string); 5378 return ($imports, $rules); 5379} 5380 5381sub _prepare_css($) 5382{ 5383 my $self = shift; 5384 5385 return if ($self->get_conf('NO_CSS')); 5386 5387 my @css_import_lines; 5388 my @css_rule_lines; 5389 5390 my $css_files = $self->get_conf('CSS_FILES'); 5391 foreach my $file (@$css_files) { 5392 my $css_file_fh; 5393 my $css_file; 5394 if ($file eq '-') { 5395 $css_file_fh = \*STDIN; 5396 $css_file = '-'; 5397 } else { 5398 $css_file = $self->Texinfo::Common::locate_include_file($file); 5399 unless (defined($css_file)) { 5400 $self->document_warn(sprintf( 5401 __("CSS file %s not found"), $file)); 5402 next; 5403 } 5404 # FIXME use open_out? 5405 unless (open (CSSFILE, $css_file)) { 5406 $self->document_warn(sprintf(__( 5407 "could not open --include-file %s: %s"), 5408 $css_file, $!)); 5409 next; 5410 } 5411 $css_file_fh = \*CSSFILE; 5412 } 5413 my ($import_lines, $rules_lines); 5414 ($import_lines, $rules_lines) 5415 = $self->_process_css_file ($css_file_fh, $css_file); 5416 if (!close($css_file_fh)) { 5417 $self->document_warn(sprintf(__("error on closing CSS file %s: %s"), 5418 $css_file, $!)); 5419 } 5420 push @css_import_lines, @$import_lines; 5421 push @css_rule_lines, @$rules_lines; 5422 5423 } 5424 if ($self->get_conf('DEBUG')) { 5425 if (@css_import_lines) { 5426 print STDERR "# css import lines\n"; 5427 foreach my $line (@css_import_lines) { 5428 print STDERR "$line"; 5429 } 5430 } 5431 if (@css_rule_lines) { 5432 print STDERR "# css rule lines\n"; 5433 foreach my $line (@css_rule_lines) { 5434 print STDERR "$line"; 5435 } 5436 } 5437 } 5438 $self->{'css_import_lines'} = \@css_import_lines; 5439 $self->{'css_rule_lines'} = \@css_rule_lines; 5440} 5441 5442# Get the name of a file containing a node, as well as the anchor within 5443# that file to link to that node. Argument is the 'extra' value on 5444# an element hash, or something that looks like it. 5445sub _node_id_file($$) 5446{ 5447 my $self = shift; 5448 my $node_info = shift; 5449 5450 my $target; 5451 my $normalized; 5452 if ($node_info->{'normalized'}) { 5453 $normalized = $node_info->{'normalized'}; 5454 } elsif ($node_info->{'node_content'}) { 5455 $normalized = Texinfo::Convert::NodeNameNormalization::normalize_node ( 5456 { 'contents' => $node_info->{'node_content'} }); 5457 } 5458 5459 if (defined($normalized)) { 5460 $target = _normalized_to_id($normalized); 5461 } else { 5462 $target = ''; 5463 } 5464 # to find out the Top node, one could check $node_info->{'normalized'} 5465 if (defined($Texinfo::Config::node_target_name)) { 5466 $target = &$Texinfo::Config::node_target_name($node_info, $target); 5467 } 5468 5469 my $filename = $self->_node_filename($node_info); 5470 5471 return ($filename, $target); 5472} 5473 5474sub _new_sectioning_command_target($$) 5475{ 5476 my $self = shift; 5477 my $command = shift; 5478 5479 my ($normalized_name, $filename) 5480 = $self->_sectioning_command_normalized_filename($command); 5481 5482 my $target_base = _normalized_to_id($normalized_name); 5483 if ($target_base !~ /\S/ and $command->{'cmdname'} eq 'top' 5484 and defined($self->{'misc_elements_targets'}->{'Top'})) { 5485 $target_base = $self->{'misc_elements_targets'}->{'Top'}; 5486 } 5487 my $nr=1; 5488 my $target = $target_base; 5489 if ($target ne '') { 5490 while ($self->{'seen_ids'}->{$target}) { 5491 $target = $target_base.'-'.$nr; 5492 $nr++; 5493 # Avoid integer overflow 5494 die if ($nr == 0); 5495 } 5496 } 5497 5498 # These are undefined if the $target is set to ''. 5499 my $target_contents; 5500 my $target_shortcontents; 5501 if ($Texinfo::Common::sectioning_commands{$command->{'cmdname'}}) { 5502 if ($target ne '') { 5503 my $target_base_contents = $target; 5504 $target_base_contents =~ s/^g_t//; 5505 $target_contents = 'toc-'.$target_base_contents; 5506 my $toc_nr = $nr -1; 5507 while ($self->{'seen_ids'}->{$target_contents}) { 5508 $target_contents = 'toc-'.$target_base_contents.'-'.$toc_nr; 5509 $toc_nr++; 5510 # Avoid integer overflow 5511 die if ($toc_nr == 0); 5512 } 5513 5514 $target_shortcontents = 'stoc-'.$target_base_contents; 5515 my $target_base_shortcontents = $target_base; 5516 $target_base_shortcontents =~ s/^g_t//; 5517 my $stoc_nr = $nr -1; 5518 while ($self->{'seen_ids'}->{$target_shortcontents}) { 5519 $target_shortcontents = 'stoc-'.$target_base_shortcontents 5520 .'-'.$stoc_nr; 5521 $stoc_nr++; 5522 # Avoid integer overflow 5523 die if ($stoc_nr == 0); 5524 } 5525 } 5526 } 5527 5528 if (defined($Texinfo::Config::sectioning_command_target_name)) { 5529 ($target, $target_contents, 5530 $target_shortcontents, $filename) 5531 = &$Texinfo::Config::sectioning_command_target_name($self, 5532 $command, $target, 5533 $target_contents, 5534 $target_shortcontents, 5535 $filename); 5536 } 5537 if ($self->get_conf('DEBUG')) { 5538 print STDERR "Register $command->{'cmdname'} $target\n"; 5539 } 5540 $self->{'targets'}->{$command} = { 5541 'target' => $target, 5542 'section_filename' => $filename, 5543 }; 5544 $self->{'seen_ids'}->{$target} = 1; 5545 if (defined($target_contents)) { 5546 $self->{'targets'}->{$command}->{'contents_target'} = $target_contents; 5547 } else { 5548 $self->{'targets'}->{$command}->{'contents_target'} = ''; 5549 } 5550 if (defined($target_shortcontents)) { 5551 $self->{'targets'}->{$command}->{'shortcontents_target'} 5552 = $target_shortcontents; 5553 } else { 5554 $self->{'targets'}->{$command}->{'shortcontents_target'} = ''; 5555 } 5556 return $self->{'targets'}->{$command}; 5557} 5558 5559# This set 2 unrelated things. 5560# * The targets and id of sectioning elements 5561# * the target, id and normalized filename of 'labels', ie everything that 5562# may be the target of a ref, like @node, @float, @anchor... 5563# conversion to HTML is done on-demand, upon call to command_text. 5564# Note that 'node_filename', which is set here for Top too, is not 5565# used later for Top, see the NOTE below. 5566sub _set_root_commands_targets_node_files($$) 5567{ 5568 my $self = shift; 5569 my $elements = shift; 5570 5571 my $no_unidecode; 5572 $no_unidecode = 1 if (defined($self->get_conf('USE_UNIDECODE')) 5573 and !$self->get_conf('USE_UNIDECODE')); 5574 5575 my $extension = ''; 5576 $extension = '.'.$self->get_conf('EXTENSION') 5577 if (defined($self->get_conf('EXTENSION')) 5578 and $self->get_conf('EXTENSION') ne ''); 5579 if ($self->{'labels'}) { 5580 foreach my $root_command (values(%{$self->{'labels'}})) { 5581 my ($filename, $target) = $self->_node_id_file($root_command->{'extra'}); 5582 $filename .= $extension; 5583 if (defined($Texinfo::Config::node_file_name)) { 5584 $filename = &$Texinfo::Config::node_file_name($self, $root_command, 5585 $filename); 5586 } 5587 if ($self->get_conf('DEBUG')) { 5588 print STDERR "Register label($root_command) $target, $filename\n"; 5589 } 5590 $self->{'targets'}->{$root_command} = {'target' => $target, 5591 'node_filename' => $filename}; 5592 $self->{'seen_ids'}->{$target} = 1; 5593 } 5594 } 5595 5596 if ($elements) { 5597 foreach my $element (@$elements) { 5598 foreach my $root_command(@{$element->{'contents'}}) { 5599 # this happens for type 'text_root' which precedes the 5600 # root commands. The target may also already be set for top node. 5601 next if (!defined($root_command->{'cmdname'}) 5602 or $self->{'targets'}->{$root_command}); 5603 if ($Texinfo::Common::sectioning_commands{$root_command->{'cmdname'}}) { 5604 $self->_new_sectioning_command_target($root_command); 5605 } 5606 } 5607 } 5608 } 5609} 5610 5611sub _get_element($$;$); 5612 5613# If $find_container is set, the element that holds the command is found, 5614# otherwise the element that holds the command content is found. This is 5615# mostly relevant for footnote only. 5616sub _get_element($$;$) 5617{ 5618 my $self = shift; 5619 my $command = shift; 5620 my $find_container = shift; 5621 5622 my $current = $command; 5623 5624 my ($element, $root_command); 5625 while (1) { 5626 if ($current->{'type'}) { 5627 if ($current->{'type'} eq 'element') { 5628 return ($current, $root_command); 5629 } 5630 } 5631 if ($current->{'cmdname'}) { 5632 if ($root_commands{$current->{'cmdname'}}) { 5633 $root_command = $current; 5634 return ($element, $root_command) if defined($element); 5635 } elsif ($region_commands{$current->{'cmdname'}}) { 5636 if ($current->{'cmdname'} eq 'copying' 5637 and $self->{'extra'} and $self->{'extra'}->{'insertcopying'}) { 5638 foreach my $insertcopying(@{$self->{'extra'}->{'insertcopying'}}) { 5639 my ($element, $root_command) 5640 = $self->_get_element($insertcopying, $find_container); 5641 return ($element, $root_command) 5642 if (defined($element) or defined($root_command)); 5643 } 5644 } elsif ($current->{'cmdname'} eq 'titlepage' 5645 and $self->get_conf('USE_TITLEPAGE_FOR_TITLE') 5646 and $self->get_conf('SHOW_TITLE') 5647 and $self->{'elements'}->[0]) { 5648 return ($self->{'elements'}->[0], 5649 $self->{'elements'}->[0]->{'extra'}->{'element_command'}); 5650 } 5651 die "Problem $element, $root_command" if (defined($element) 5652 or defined($root_command)); 5653 return (undef, undef); 5654 } elsif ($current->{'cmdname'} eq 'footnote' 5655 and $self->{'special_elements_types'}->{'Footnotes'} 5656 and $find_container) { 5657 # in that case there is no root_command 5658 $element = $self->{'special_elements_types'}->{'Footnotes'}; 5659 return ($element); 5660 } 5661 } 5662 if ($current->{'parent'}) { 5663 $current = $current->{'parent'}; 5664 } else { 5665 return ($element, $root_command); 5666 } 5667 } 5668} 5669 5670sub _set_pages_files($$) 5671{ 5672 my $self = shift; 5673 my $elements = shift; 5674 my $special_elements = shift; 5675 5676 # Ensure that the document has pages 5677 return undef if (!defined($elements) or !@$elements); 5678 5679 my $extension = ''; 5680 $extension = '.'.$self->get_conf('EXTENSION') 5681 if (defined($self->get_conf('EXTENSION')) 5682 and $self->get_conf('EXTENSION') ne ''); 5683 5684 if (!$self->get_conf('SPLIT')) { 5685 foreach my $element (@$elements) { 5686 if (!defined($element->{'filename'})) { 5687 $element->{'filename'} = $self->{'output_filename'}; 5688 $element->{'out_filename'} = $self->{'output_file'}; 5689 } 5690 } 5691 } else { 5692 my $node_top; 5693 #my $section_top; 5694 $node_top = $self->{'labels'}->{'Top'} if ($self->{'labels'}); 5695 #$section_top = $self->{'extra'}->{'top'} if ($self->{'extra'}); 5696 5697 my $top_node_filename = $self->_top_node_filename(); 5698 # first determine the top node file name. 5699 if ($node_top and defined($top_node_filename)) { 5700 my ($node_top_element) = $self->_get_element($node_top); 5701 die "BUG: No element for top node" if (!defined($node_top)); 5702 $self->_set_element_file($node_top_element, $top_node_filename); 5703 } 5704 my $file_nr = 0; 5705 my $previous_page; 5706 foreach my $element(@$elements) { 5707 # For Top node. 5708 next if (defined($element->{'filename'})); 5709 if (!$element->{'extra'}->{'first_in_page'}) { 5710 cluck ("No first_in_page for $element\n"); 5711 } 5712 if (!defined($element->{'extra'}->{'first_in_page'}->{'filename'})) { 5713 my $file_element = $element->{'extra'}->{'first_in_page'}; 5714 foreach my $root_command (@{$file_element->{'contents'}}) { 5715 if ($root_command->{'cmdname'} 5716 and $root_command->{'cmdname'} eq 'node') { 5717 my $node_filename; 5718 # double node are not normalized, they are handled here 5719 if (!defined($root_command->{'extra'}->{'normalized'}) 5720 or !defined($self->{'labels'}->{$root_command->{'extra'}->{'normalized'}})) { 5721 $node_filename = 'unknown_node'; 5722 $node_filename .= $extension; 5723 } else { 5724 if (!defined($self->{'targets'}->{$root_command}) 5725 or !defined($self->{'targets'}->{$root_command}->{'node_filename'})) { 5726 # Could have been a double node, thus use equivalent node. 5727 # However since double nodes are not normalized, in fact it 5728 # never happens. 5729 $root_command 5730 = $self->{'labels'}->{$root_command->{'extra'}->{'normalized'}}; 5731 } 5732 $node_filename 5733 = $self->{'targets'}->{$root_command}->{'node_filename'}; 5734 } 5735 $self->_set_element_file($file_element, $node_filename); 5736 last; 5737 } 5738 } 5739 if (!defined($file_element->{'filename'})) { 5740 # use section to do the file name if there is no node 5741 my $command = $self->element_command($file_element); 5742 if ($command) { 5743 if ($command->{'cmdname'} eq 'top' and !$node_top 5744 and defined($top_node_filename)) { 5745 $self->_set_element_file($file_element, $top_node_filename); 5746 } else { 5747 $self->_set_element_file($file_element, 5748 $self->{'targets'}->{$command}->{'section_filename'}); 5749 } 5750 } else { 5751 # when everything else has failed 5752 if ($file_nr == 0 and !$node_top 5753 and defined($top_node_filename)) { 5754 $self->_set_element_file($file_element, $top_node_filename); 5755 } else { 5756 my $filename = $self->{'document_name'} . "_$file_nr"; 5757 $filename .= $extension; 5758 $self->_set_element_file($element, $filename); 5759 } 5760 $file_nr++; 5761 } 5762 } 5763 } 5764 $element->{'filename'} 5765 = $element->{'extra'}->{'first_in_page'}->{'filename'}; 5766 $element->{'out_filename'} 5767 = $element->{'extra'}->{'first_in_page'}->{'out_filename'}; 5768 } 5769 } 5770 5771 foreach my $element (@$elements) { 5772 if (defined($Texinfo::Config::element_file_name)) { 5773 # NOTE the information that it is associated with @top or @node Top 5774 # may be determined with $self->element_is_top($element); 5775 my $filename = &$Texinfo::Config::element_file_name($self, $element, 5776 $element->{'filename'}); 5777 $self->_set_element_file($element, $filename) if (defined($filename)); 5778 } 5779 $self->{'file_counters'}->{$element->{'filename'}}++; 5780 print STDERR "Page $element ".Texinfo::Structuring::_print_element_command_texi($element).": $element->{'filename'}($self->{'file_counters'}->{$element->{'filename'}})\n" 5781 if ($self->get_conf('DEBUG')); 5782 } 5783 if ($special_elements) { 5784 my $previous_element = $elements->[-1]; 5785 foreach my $element (@$special_elements) { 5786 my $filename 5787 = $self->{'targets'}->{$element}->{'misc_filename'}; 5788 if (defined($filename)) { 5789 $self->_set_element_file($element, $filename); 5790 $self->{'file_counters'}->{$element->{'filename'}}++; 5791 print STDERR "Special page $element: $element->{'filename'}($self->{'file_counters'}->{$element->{'filename'}})\n" 5792 if ($self->get_conf('DEBUG')); 5793 } 5794 $element->{'element_prev'} = $previous_element; 5795 $previous_element->{'element_next'} = $element; 5796 $previous_element = $element; 5797 } 5798 } 5799} 5800 5801# $ROOT is a parsed Texinfo tree. Return a list of the "elements" we need to 5802# output in the HTML file(s). Each "element" is what can go in one HTML file, 5803# such as the content between @node lines in the Texinfo source. 5804sub _prepare_elements($$) 5805{ 5806 my $self = shift; 5807 my $root = shift; 5808 5809 my $elements; 5810 5811 # do that now to have it available for formatting 5812 # NOTE this calls Convert::Converter::_informative_command on all the 5813 # @informative_global commands. 5814 # Thus sets among others language and encodings. 5815 $self->_set_global_multiple_commands(-1); 5816 $self->_translate_names(); 5817 5818 if ($self->get_conf('USE_NODES')) { 5819 $elements = Texinfo::Structuring::split_by_node($root); 5820 } else { 5821 $elements = Texinfo::Structuring::split_by_section($root); 5822 } 5823 5824 $self->{'elements'} = $elements 5825 if (defined($elements)); 5826 5827 # This may be done as soon as elements are available. 5828 $self->_prepare_global_targets($elements); 5829 5830 # Do that before the other elements, to be sure that special page ids 5831 # are registered before elements id are. 5832 my $special_elements 5833 = $self->_prepare_special_elements($elements); 5834 5835 $self->{'special_elements'} = $special_elements 5836 if (defined($special_elements)); 5837 5838 #if ($elements) { 5839 # foreach my $element(@{$elements}) { 5840 # print STDERR "ELEMENT $element->{'type'}: $element\n"; 5841 # } 5842 #} 5843 5844 $self->_set_root_commands_targets_node_files($elements); 5845 5846 return ($elements, $special_elements); 5847} 5848 5849sub _prepare_special_elements($$) 5850{ 5851 my $self = shift; 5852 my $elements = shift; 5853 5854 my %do_special; 5855 # FIXME let the user decide how @*contents are treated? 5856 if ($self->{'structuring'} and $self->{'structuring'}->{'sectioning_root'} 5857 and scalar(@{$self->{'structuring'}->{'sections_list'}}) > 1) { 5858 foreach my $cmdname ('contents', 'shortcontents') { 5859 my $type = $contents_command_element_name{$cmdname}; 5860 if ($self->get_conf($cmdname)) { 5861 if ($self->get_conf('CONTENTS_OUTPUT_LOCATION') 5862 eq 'separate_element') { 5863 $do_special{$type} = 1; 5864 } 5865 } 5866 } 5867 } 5868 if ($self->{'extra'}->{'footnote'} 5869 and $self->get_conf('footnotestyle') eq 'separate' 5870 and $elements and scalar(@$elements) > 1) { 5871 $do_special{'Footnotes'} = 1; 5872 } 5873 5874 if ((!defined($self->get_conf('DO_ABOUT')) 5875 and $elements and scalar(@$elements) > 1 5876 and ($self->get_conf('SPLIT') or $self->get_conf('HEADERS'))) 5877 or ($self->get_conf('DO_ABOUT'))) { 5878 $do_special{'About'} = 1; 5879 } 5880 5881 my $extension = ''; 5882 $extension = $self->get_conf('EXTENSION') 5883 if (defined($self->get_conf('EXTENSION'))); 5884 5885 my $special_elements = []; 5886 foreach my $type (@{$self->{'misc_elements_order'}}) { 5887 next unless ($do_special{$type}); 5888 5889 my $element = {'type' => 'element', 5890 'extra' => {'special_element' => $type, 5891 }}; 5892 $element->{'extra'}->{'directions'}->{'This'} = $element; 5893 $self->{'special_elements_types'}->{$type} = $element; 5894 push @$special_elements, $element; 5895 5896 my $target = $self->{'misc_elements_targets'}->{$type}; 5897 my $default_filename; 5898 if ($self->get_conf('SPLIT') or !$self->get_conf('MONOLITHIC')) { 5899 $default_filename = $self->{'document_name'}. 5900 $self->{'misc_pages_file_string'}->{$type}; 5901 $default_filename .= '.'.$extension if (defined($extension)); 5902 } else { 5903 $default_filename = undef; 5904 } 5905 5906 my $filename; 5907 if (defined($Texinfo::Config::special_element_target_file_name)) { 5908 ($target, $filename) 5909 = &$Texinfo::Config::special_element_target_file_name( 5910 $self, 5911 $element, 5912 $target, 5913 $default_filename); 5914 } 5915 $filename = $default_filename if (!defined($filename)); 5916 5917 if ($self->get_conf('DEBUG')) { 5918 my $fileout = $filename; 5919 $fileout = 'UNDEF' if (!defined($fileout)); 5920 print STDERR "Add special $element $type: target $target,\n". 5921 " filename $fileout\n" 5922 } 5923 if ($self->get_conf('SPLIT') or !$self->get_conf('MONOLITHIC') 5924 or (defined($filename) ne defined($default_filename)) 5925 or (defined($filename) and $filename ne $default_filename)) { 5926 $self->_set_element_file($element, $filename); 5927 print STDERR "NEW page for $type ($filename)\n" if ($self->get_conf('DEBUG')); 5928 } 5929 $self->{'targets'}->{$element} = {'target' => $target, 5930 'misc_filename' => $filename, 5931 }; 5932 $self->{'seen_ids'}->{$target} = 1; 5933 } 5934 if ($self->get_conf('FRAMES')) { 5935 foreach my $type (keys(%{$self->{'frame_pages_file_string'}})) { 5936 my $default_filename; 5937 $default_filename = $self->{'document_name'}. 5938 $self->{'frame_pages_file_string'}->{$type}; 5939 $default_filename .= '.'.$extension if (defined($extension)); 5940 5941 my $element = {'type' => 'element', 5942 'extra' => {'special_element' => $type, 5943 }}; 5944 5945 # only the filename is used 5946 my ($target, $filename); 5947 if (defined($Texinfo::Config::special_element_target_file_name)) { 5948 ($target, $filename) 5949 = &$Texinfo::Config::special_element_target_file_name( 5950 $self, 5951 $element, 5952 $target, 5953 $default_filename); 5954 } 5955 $filename = $default_filename if (!defined($filename)); 5956 $self->{'frame_pages_filenames'}->{$type} = $filename; 5957 } 5958 } 5959 return $special_elements; 5960} 5961 5962sub _prepare_contents_elements($) 5963{ 5964 my $self = shift; 5965 5966 if ($self->{'structuring'} and $self->{'structuring'}->{'sectioning_root'} 5967 and scalar(@{$self->{'structuring'}->{'sections_list'}}) > 1) { 5968 foreach my $cmdname ('contents', 'shortcontents') { 5969 my $type = $contents_command_element_name{$cmdname}; 5970 if ($self->get_conf($cmdname)) { 5971 my $default_filename; 5972 if ($self->get_conf('CONTENTS_OUTPUT_LOCATION') eq 'after_title') { 5973 if ($self->{'elements'}) { 5974 $default_filename = $self->{'elements'}->[0]->{'filename'}; 5975 } 5976 } elsif ($self->get_conf('CONTENTS_OUTPUT_LOCATION') eq 'after_top') { 5977 my $section_top = undef; 5978 if ($self->{'extra'} and $self->{'extra'}->{'top'}) { 5979 $section_top = $self->{'extra'}->{'top'}; 5980 $default_filename = $self->command_filename($section_top) 5981 } 5982 } elsif ($self->get_conf('CONTENTS_OUTPUT_LOCATION') eq 'inline') { 5983 if ($self->{'extra'} and $self->{'extra'}->{$cmdname}) { 5984 foreach my $command(@{$self->{'extra'}->{$cmdname}}) { 5985 my ($element, $root_command) 5986 = $self->_get_element($command); 5987 if (defined($element)) { 5988 $default_filename = $element->{'filename'}; 5989 last; 5990 } 5991 } 5992 } else { 5993 next; 5994 } 5995 } else { # in this case, there should already be a special element 5996 # if needed, done together with the other special elements. 5997 next; 5998 } 5999 6000 my $element = {'type' => 'element', 6001 'extra' => {'special_element' => $type}}; 6002 $self->{'special_elements_types'}->{$type} = $element; 6003 my $target = $self->{'misc_elements_targets'}->{$type}; 6004 my $filename; 6005 if (defined($Texinfo::Config::special_element_target_file_name)) { 6006 ($target, $filename) 6007 = &$Texinfo::Config::special_element_target_file_name( 6008 $self, 6009 $element, 6010 $target, 6011 $default_filename); 6012 } 6013 $filename = $default_filename if (!defined($filename)); 6014 print STDERR "Add content $element $type: target $target,\n". 6015 " filename $filename\n" if ($self->get_conf('DEBUG')); 6016 $self->{'targets'}->{$element} = {'target' => $target, 6017 'misc_filename' => $filename, 6018 'filename' => $filename, 6019 }; 6020 } 6021 } 6022 } 6023} 6024 6025# Associate elements with the global targets, First, Last, Top, Index. 6026sub _prepare_global_targets($$) 6027{ 6028 my $self = shift; 6029 my $elements = shift; 6030 6031 $self->{'global_target_elements'}->{'First'} = $elements->[0]; 6032 $self->{'global_target_elements'}->{'Last'} = $elements->[-1]; 6033 # It is always the first printindex, even if it is not output (for example 6034 # it is in @copying and @titlepage, which are certainly wrong constructs). 6035 if ($self->{'extra'} and $self->{'extra'}->{'printindex'}) { 6036 my ($element, $root_command) 6037 = $self->_get_element($self->{'extra'}->{'printindex'}->[0]); 6038 if (defined($element)) { 6039 if ($root_command and $root_command->{'cmdname'} eq 'node' 6040 and $element->{'extra'}->{'section'}) { 6041 $root_command = $element->{'extra'}->{'section'}; 6042 } 6043 if ($root_command and $root_command->{'cmdname'} ne 'node') { 6044 while ($root_command->{'level'} > 1 6045 and $root_command->{'section_up'} 6046 and $root_command->{'section_up'}->{'parent'}) { 6047 $root_command = $root_command->{'section_up'}; 6048 $element = $root_command->{'parent'}; 6049 } 6050 } 6051 $self->{'global_target_elements'}->{'Index'} = $element; 6052 } 6053 } 6054 6055 my $node_top; 6056 $node_top = $self->{'labels'}->{'Top'} if ($self->{'labels'}); 6057 my $section_top; 6058 $section_top = $self->{'extra'}->{'top'} if ($self->{'extra'}); 6059 if ($section_top) { 6060 $self->{'global_target_elements'}->{'Top'} = $section_top->{'parent'}; 6061 } elsif ($node_top) { 6062 my $element_top = $node_top->{'parent'}; 6063 if (!$element_top) { 6064 die "No parent for node_top: ".Texinfo::Common::_print_current($node_top); 6065 } 6066 $self->{'global_target_elements'}->{'Top'} = $element_top; 6067 } else { 6068 $self->{'global_target_elements'}->{'Top'} = $elements->[0]; 6069 } 6070 6071 if ($self->get_conf('DEBUG')) { 6072 print STDERR "GLOBAL DIRECTIONS:\n"; 6073 foreach my $global_direction (@global_directions) { 6074 if (defined($self->{'global_target_elements'}->{$global_direction})) { 6075 print STDERR "$global_direction($self->{'global_target_elements'}->{$global_direction}): ". 6076 Texinfo::Structuring::_print_element_command_texi( 6077 $self->{'global_target_elements'}->{$global_direction})."\n"; 6078 } 6079 } 6080 } 6081} 6082 6083sub _prepare_index_entries($) 6084{ 6085 my $self = shift; 6086 6087 if ($self->{'parser'}) { 6088 my $no_unidecode; 6089 $no_unidecode = 1 if (defined($self->get_conf('USE_UNIDECODE')) 6090 and !$self->get_conf('USE_UNIDECODE')); 6091 6092 my $index_names = $self->{'parser'}->indices_information(); 6093 $self->{'index_names'} = $index_names; 6094 my $merged_index_entries 6095 = Texinfo::Structuring::merge_indices($index_names); 6096 $self->{'index_entries_by_letter'} 6097 = Texinfo::Structuring::sort_indices_by_letter ($self->{'parser'}, 6098 $merged_index_entries, $index_names); 6099 $self->{'index_entries'} = $merged_index_entries; 6100 6101 foreach my $index_name (sort(keys(%$index_names))) { 6102 foreach my $index_entry (@{$index_names->{$index_name}->{'index_entries'}}) { 6103 my $region = ''; 6104 $region = "$index_entry->{'region'}->{'cmdname'}-" 6105 if (defined($index_entry->{'region'})); 6106 my @contents = @{$index_entry->{'content_normalized'}}; 6107 my $trimmed_contents 6108 = Texinfo::Common::trim_spaces_comment_from_content(\@contents); 6109 my $normalized_index = 6110 Texinfo::Convert::NodeNameNormalization::transliterate_texinfo( 6111 {'contents' => \@contents}, $no_unidecode); 6112 my $target_base = "index-" . $region .$normalized_index; 6113 my $nr=1; 6114 my $target = $target_base; 6115 while ($self->{'seen_ids'}->{$target}) { 6116 $target = $target_base.'-'.$nr; 6117 $nr++; 6118 # Avoid integer overflow 6119 die if ($nr == 0); 6120 } 6121 $self->{'seen_ids'}->{$target} = 1; 6122 $self->{'targets'}->{$index_entry->{'command'}} = {'target' => $target, 6123 }; 6124 } 6125 } 6126 } 6127} 6128 6129sub _prepare_footnotes($) 6130{ 6131 my $self = shift; 6132 6133 if ($self->{'extra'}->{'footnote'}) { 6134 my $footnote_nr = 0; 6135 foreach my $footnote (@{$self->{'extra'}->{'footnote'}}) { 6136 $footnote_nr++; 6137 my $nr = $footnote_nr; 6138 my $footid = $footid_base.$nr; 6139 my $docid = $docid_base.$nr; 6140 while ($self->{'seen_ids'}->{$docid} or $self->{'seen_ids'}->{$footid}) { 6141 $nr++; 6142 $footid = $footid_base.$nr; 6143 $docid = $docid_base.$nr; 6144 # Avoid integer overflow 6145 die if ($nr == 0); 6146 } 6147 $self->{'seen_ids'}->{$footid} = 1; 6148 $self->{'seen_ids'}->{$docid} = 1; 6149 $self->{'targets'}->{$footnote} = { 'target' => $footid }; 6150 print STDERR "Enter footnote $footnote: target $footid, nr $footnote_nr\n" 6151 .Texinfo::Convert::Texinfo::convert($footnote)."\n" 6152 if ($self->get_conf('DEBUG')); 6153 } 6154 } 6155} 6156 6157# TODO this encapsulates some information. 6158# The encapsulation and API should be more consistent for 6159# the overall module. 6160sub _htmlxref($$) 6161{ 6162 my $self = shift; 6163 my $file = shift; 6164 6165 return $self->{'htmlxref'}->{$file}; 6166} 6167 6168sub _external_node_href($$$$) 6169{ 6170 my $self = shift; 6171 my $external_node = shift; 6172 my $filename = shift; 6173 my $link_command = shift; 6174 6175 #print STDERR "external_node: ".join('|', keys(%$external_node))."\n"; 6176 my ($target_filebase, $target) = $self->_node_id_file($external_node); 6177 6178 my $xml_target = _normalized_to_id($target); 6179 6180 my $default_target_split = $self->get_conf('EXTERNAL_CROSSREF_SPLIT'); 6181 6182 my $external_file_extension = '.html'; 6183 6184 my $target_split; 6185 my $file; 6186 if ($external_node->{'manual_content'}) { 6187 my $manual_name = Texinfo::Convert::Text::convert( 6188 {'contents' => $external_node->{'manual_content'}}, 6189 { 'code' => 1, 6190 Texinfo::Common::_convert_text_options($self)}); 6191 my $manual_base = $manual_name; 6192 $manual_base =~ s/\.info*$//; 6193 $manual_base =~ s/^.*\///; 6194 my $document_split = $self->get_conf('SPLIT'); 6195 $document_split = 'mono' if (!$document_split); 6196 my $split_found; 6197 my $href; 6198 my $htmlxref_info = $self->_htmlxref($manual_base); 6199 if ($htmlxref_info) { 6200 foreach my $split_ordered (@{$htmlxref_entries{$document_split}}) { 6201 if (defined($htmlxref_info->{$split_ordered})) { 6202 $split_found = $split_ordered; 6203 $href = $htmlxref_info->{$split_ordered}; 6204 last; 6205 } 6206 } 6207 } 6208 if (defined($split_found)) { 6209 $target_split = 1 unless ($split_found eq 'mono'); 6210 } else { # nothing specified for that manual, use default 6211 $target_split = $default_target_split; 6212 if ($self->get_conf('CHECK_HTMLXREF')) { 6213 if (defined($link_command) and $link_command->{'line_nr'}) { 6214 $self->line_warn(sprintf(__( 6215 "no htmlxref.cnf entry found for `%s'"), $manual_name), 6216 $link_command->{'line_nr'}); 6217 } elsif (!$self->{'check_htmlxref_already_warned'}->{$manual_name}) { 6218 $self->document_warn(sprintf(__( 6219 "no htmlxref.cnf entry found for `%s'"), $manual_name), 6220 ); 6221 } 6222 $self->{'check_htmlxref_already_warned'}->{$manual_name} = 1; 6223 } 6224 } 6225 6226 if ($target_split) { 6227 if (defined($href)) { 6228 $file = $href; 6229 } elsif (defined($self->get_conf('EXTERNAL_DIR'))) { 6230 $file = $self->get_conf('EXTERNAL_DIR')."/$manual_base"; 6231 } elsif ($self->get_conf('SPLIT')) { 6232 $file = "../$manual_base"; 6233 } 6234 $file .= "/"; 6235 } else {# target not split 6236 if (defined($href)) { 6237 $file = $href; 6238 } else { 6239 if (defined($self->get_conf('EXTERNAL_DIR'))) { 6240 $file = $self->get_conf('EXTERNAL_DIR')."/$manual_base"; 6241 } elsif ($self->get_conf('SPLIT')) { 6242 $file = "../$manual_base"; 6243 } else { 6244 $file = $manual_base; 6245 } 6246 $file .= $external_file_extension; 6247 } 6248 } 6249 } else { 6250 $file = ''; 6251 $target_split = $default_target_split; 6252 } 6253 6254 if ($target eq '') { 6255 if ($target_split) { 6256 if (defined($self->get_conf('TOP_NODE_FILE_TARGET'))) { 6257 return $file . $self->get_conf('TOP_NODE_FILE_TARGET'); 6258 } else { 6259 return $file;# . '#Top'; 6260 } 6261 } else { 6262 return $file . '#Top'; 6263 } 6264 } 6265 6266 if (! $target_split) { 6267 return $file . '#' . $xml_target; 6268 } else { 6269 my $file_name; 6270 if ($target eq 'Top' and defined($self->get_conf('TOP_NODE_FILE_TARGET'))) { 6271 $file_name = $self->get_conf('TOP_NODE_FILE_TARGET'); 6272 } else { 6273 $file_name = $target_filebase . $external_file_extension; 6274 } 6275 return $file . $file_name . '#' . $xml_target; 6276 } 6277} 6278 6279my %valid_types = ( 6280 'href' => 1, 6281 'string' => 1, 6282 'text' => 1, 6283 'tree' => 1, 6284 'target' => 1, 6285 'node' => 1, 6286 'section' => 1 6287); 6288 6289foreach my $no_number_type ('text', 'tree', 'string') { 6290 $valid_types{$no_number_type .'_nonumber'} = 1; 6291} 6292 6293# sub _element_direction($SELF, $ELEMENT, $DIRECTION, $TYPE, $FILENAME) 6294# 6295# Return text used for linking from $ELEMENT in direction $DIRECTION. The 6296# text returned depends on $TYPE. 6297# 6298# $element can be undef. 6299# $element undef happens at least when there is no output file, or for 6300# the table of content when frames are used. That call would result 6301# for instance from _element_direction being called from _get_links, 6302# itself called from 'format_begin_file' which, in the default case 6303# points to _default_format_begin_file. 6304# TODO are there other cases? 6305sub _element_direction($$$$;$) 6306{ 6307 my $self = shift; 6308 my $element = shift; 6309 my $direction = shift; 6310 my $type = shift; 6311 my $filename = shift; 6312 6313 my $element_target; 6314 my $command; 6315 my $target; 6316 6317 $filename = $self->{'current_filename'} if (!defined($filename)); 6318 6319 if (!$valid_types{$type}) { 6320 print STDERR "Incorrect type $type in _element_direction call\n"; 6321 return undef; 6322 } 6323 if ($self->{'global_target_elements'}->{$direction}) { 6324 $element_target = $self->{'global_target_elements'}->{$direction}; 6325 } elsif ($element and $element->{'extra'} 6326 and $element->{'extra'}->{'directions'} 6327 and $element->{'extra'}->{'directions'}->{$direction}) { 6328 $element_target 6329 = $element->{'extra'}->{'directions'}->{$direction}; 6330 # output TOP_NODE_UP related infos even if element is not 6331 # defined which should mostly correspond to cases when there is no 6332 # output file, for example in the tests. 6333 } elsif ((not defined($element) 6334 or ($element and $self->element_is_top($element))) 6335 and defined($self->get_conf('TOP_NODE_UP_URL')) 6336 and ($direction eq 'Up' or $direction eq 'NodeUp')) { 6337 if ($type eq 'href') { 6338 return $self->get_conf('TOP_NODE_UP_URL'); 6339 } elsif ($type eq 'text' or $type eq 'node' or $type eq 'string' or $type eq 'section') { 6340 return $self->get_conf('TOP_NODE_UP'); 6341 } else { 6342 cluck("type $type not available for TOP_NODE_UP\n"); 6343 return ''; 6344 } 6345 } 6346 6347 if ($element_target) { 6348 ######## debug 6349 if (!$element_target->{'type'}) { 6350 die "No type for element_target $direction $element_target: " 6351 . Texinfo::Common::_print_current_keys($element_target) 6352 . "directions :". Texinfo::Structuring::_print_directions($element); 6353 } 6354 ######## 6355 if ($element_target->{'type'} eq 'external_node') { 6356 my $external_node = $element_target->{'extra'}; 6357 if ($type eq 'href') { 6358 return $self->command_href($external_node, $filename); 6359 } elsif ($type eq 'text' or $type eq 'node') { 6360 return $self->command_text($external_node); 6361 } elsif ($type eq 'string') { 6362 return $self->command_text($external_node, $type); 6363 } 6364 } elsif ($type eq 'node') { 6365 $command = $element_target->{'extra'}->{'node'}; 6366 $target = $self->{'targets'}->{$command} if ($command); 6367 $type = 'text'; 6368 } elsif ($type eq 'section') { 6369 $command = $element_target->{'extra'}->{'section'}; 6370 $target = $self->{'targets'}->{$command} if ($command); 6371 $type = 'text_nonumber'; 6372 } else { 6373 if ($element_target->{'extra'}->{'special_element'}) { 6374 $command = $element_target; 6375 } else { 6376 $command = $element_target->{'extra'}->{'element_command'}; 6377 } 6378 if ($type eq 'href') { 6379 if (defined($command)) { 6380 return $self->command_href($command, $filename); 6381 } else { 6382 return ''; 6383 } 6384 } 6385 $target = $self->{'targets'}->{$command} if ($command); 6386 } 6387 } elsif ($self->special_element($direction)) { 6388 $element_target = $self->special_element($direction); 6389 $command = $element_target; 6390 if ($type eq 'href') { 6391 return $self->command_href($element_target, $filename); 6392 } 6393 $target = $self->{'targets'}->{$element_target}; 6394 } else { 6395 return undef; 6396 } 6397 6398 if (exists($target->{$type})) { 6399 return $target->{$type}; 6400 } elsif ($type eq 'target') { 6401 return undef; 6402 } elsif ($command) { 6403 return $self->command_text($command, $type); 6404 } 6405} 6406 6407# Output a list of the nodes immediately below this one 6408sub _mini_toc 6409{ 6410 my ($self, $command) = @_; 6411 6412 my $filename = $self->{'current_filename'}; 6413 my $result = ''; 6414 my $entry_index = 0; 6415 my $accesskey; 6416 6417 if ($command->{'section_childs'} and @{$command->{'section_childs'}}) { 6418 $result .= $self->_attribute_class('ul', 'section-toc').">\n"; 6419 6420 foreach my $section (@{$command->{'section_childs'}}) { 6421 my $tree = $self->command_text($section, 'tree_nonumber'); 6422 my $text = $self->_convert($tree); 6423 6424 $entry_index++; 6425 $accesskey = ''; 6426 $accesskey = " accesskey=\"$entry_index\"" 6427 if ($self->get_conf('USE_ACCESSKEY') and $entry_index < 10); 6428 6429 my $href = $self->command_href($section, $filename); 6430 if ($text ne '') { 6431 if ($href ne '') { 6432 my $href_attribute = ''; 6433 if ($href ne '') { 6434 $href_attribute = " href=\"$href\""; 6435 } 6436 $result .= "<li><a${href_attribute}$accesskey>$text</a>"; 6437 } else { 6438 $result .= "<li>$text"; 6439 } 6440 $result .= "</li>\n"; 6441 } 6442 } 6443 $result .= "</ul>\n"; 6444 } 6445 return $result; 6446} 6447 6448sub _default_format_contents($$;$$) 6449{ 6450 my $self = shift; 6451 my $cmdname = shift; 6452 my $command = shift; 6453 my $filename = shift; 6454 $filename = $self->{'current_filename'} if (!defined($filename)); 6455 6456 return '' 6457 if (!$self->{'structuring'} or !$self->{'structuring'}->{'sectioning_root'}); 6458 6459 my $section_root = $self->{'structuring'}->{'sectioning_root'}; 6460 my $contents; 6461 $contents = 1 if ($cmdname eq 'contents'); 6462 6463 my $min_root_level = $section_root->{'section_childs'}->[0]->{'level'}; 6464 my $max_root_level = $section_root->{'section_childs'}->[0]->{'level'}; 6465 foreach my $top_section(@{$section_root->{'section_childs'}}) { 6466 $min_root_level = $top_section->{'level'} 6467 if ($top_section->{'level'} < $min_root_level); 6468 $max_root_level = $top_section->{'level'} 6469 if ($top_section->{'level'} > $max_root_level); 6470 } 6471 # chapter level elements are considered top-level here. 6472 $max_root_level = 1 if ($max_root_level < 1); 6473 #print STDERR "ROOT_LEVEL Max: $max_root_level, Min: $min_root_level\n"; 6474 my $ul_class = ''; 6475 $ul_class = $NO_BULLET_LIST_CLASS if ($self->get_conf('NUMBER_SECTIONS')); 6476 6477 my $result = ''; 6478 if ($contents and !defined($self->get_conf('BEFORE_TOC_LINES')) 6479 or (!$contents and !defined($self->get_conf('BEFORE_OVERVIEW')))) { 6480 $result .= $self->_attribute_class('div', $cmdname).">\n"; 6481 } elsif($contents) { 6482 $result .= $self->get_conf('BEFORE_TOC_LINES'); 6483 } else { 6484 $result .= $self->get_conf('BEFORE_OVERVIEW'); 6485 } 6486 6487 my $toplevel_contents; 6488 if (@{$section_root->{'section_childs'}} > 1) { 6489 # or $section_root->{'section_childs'}->[0]->{'cmdname'} ne 'top') { 6490 $result .= $self->_attribute_class('ul', $ul_class) .">\n"; 6491 $toplevel_contents = 1; 6492 } 6493 foreach my $top_section (@{$section_root->{'section_childs'}}) { 6494 my $section = $top_section; 6495 SECTION: 6496 while ($section) { 6497 if ($section->{'cmdname'} ne 'top') { 6498 my $text = $self->command_text($section); 6499 my $href; 6500 if (!$contents and $self->get_conf('OVERVIEW_LINK_TO_TOC')) { 6501 $href = $self->command_contents_href($section, 'contents', $filename); 6502 } else { 6503 $href = $self->command_href($section, $filename); 6504 } 6505 my $toc_id = $self->command_contents_target($section, $cmdname); 6506 if ($text ne '') { 6507 # no indenting for shortcontents 6508 $result .= (' ' x (2*($section->{'level'} - $min_root_level))) 6509 if ($contents); 6510 if ($toc_id ne '' or $href ne '') { 6511 my $toc_name_attribute = ''; 6512 if ($toc_id ne '') { 6513 $toc_name_attribute = " id=\"$toc_id\""; 6514 } 6515 my $href_attribute = ''; 6516 if ($href ne '') { 6517 $href_attribute = " href=\"$href\""; 6518 } 6519 my $rel = ''; 6520 if ($section->{'extra'} 6521 and $section->{'extra'}->{'associated_node'} 6522 and $section->{'extra'}->{'associated_node'}->{'extra'}->{'isindex'}) { 6523 $rel = ' rel="index"'; 6524 } 6525 $result .= "<li><a${toc_name_attribute}${href_attribute}$rel>$text</a>"; 6526 } else { 6527 $result .= "<li>$text"; 6528 } 6529 } 6530 } elsif ($section->{'section_childs'} and @{$section->{'section_childs'}} 6531 and $toplevel_contents) { 6532 $result .= "<li>"; 6533 } 6534 # for shortcontents don't do child if child is not toplevel 6535 if ($section->{'section_childs'} 6536 and ($contents or $section->{'level'} < $max_root_level)) { 6537 # no indenting for shortcontents 6538 $result .= "\n". ' ' x (2*($section->{'level'} - $min_root_level)) 6539 if ($contents); 6540 $result .= $self->_attribute_class('ul', $ul_class) .">\n"; 6541 $section = $section->{'section_childs'}->[0]; 6542 } elsif ($section->{'section_next'} and $section->{'cmdname'} ne 'top') { 6543 $result .= "</li>\n"; 6544 last if ($section eq $top_section); 6545 $section = $section->{'section_next'}; 6546 } else { 6547 #last if ($section eq $top_section); 6548 if ($section eq $top_section) { 6549 $result .= "</li>\n" unless ($section->{'cmdname'} eq 'top'); 6550 last; 6551 } 6552 while ($section->{'section_up'}) { 6553 $section = $section->{'section_up'}; 6554 $result .= "</li>\n". ' ' x (2*($section->{'level'} - $min_root_level)) 6555 . "</ul>"; 6556 if ($section eq $top_section) { 6557 $result .= "</li>\n" if ($toplevel_contents); 6558 last SECTION; 6559 } 6560 if ($section->{'section_next'}) { 6561 $result .= "</li>\n"; 6562 $section = $section->{'section_next'}; 6563 last; 6564 } 6565 } 6566 } 6567 } 6568 } 6569 if (@{$section_root->{'section_childs'}} > 1) { 6570 # or $section_root->{'section_childs'}->[0]->{'cmdname'} ne 'top') { 6571 $result .= "\n</ul>"; 6572 } 6573 if ($contents and !defined($self->get_conf('AFTER_TOC_LINES')) 6574 or (!$contents and !defined($self->get_conf('AFTER_OVERVIEW')))) { 6575 $result .= "\n</div>\n"; 6576 } elsif($contents) { 6577 $result .= $self->get_conf('AFTER_TOC_LINES'); 6578 } else { 6579 $result .= $self->get_conf('AFTER_OVERVIEW'); 6580 } 6581 return $result; 6582} 6583 6584sub _default_format_program_string($) 6585{ 6586 my $self = shift; 6587 if (defined($self->get_conf('PROGRAM')) 6588 and $self->get_conf('PROGRAM') ne '' 6589 and defined($self->get_conf('PACKAGE_URL'))) { 6590 return $self->convert_tree( 6591 $self->gdt('This document was generated on @emph{@today{}} using @uref{{program_homepage}, @emph{{program}}}.', 6592 { 'program_homepage' => $self->get_conf('PACKAGE_URL'), 6593 'program' => $self->get_conf('PROGRAM') })); 6594 } else { 6595 return $self->convert_tree( 6596 $self->gdt('This document was generated on @emph{@today{}}.')); 6597 } 6598} 6599 6600sub _default_format_end_file($) 6601{ 6602 my $self = shift; 6603 my $program_text = ''; 6604 if ($self->get_conf('PROGRAM_NAME_IN_FOOTER')) { 6605 my $program_string = &{$self->{'format_program_string'}}($self); 6606 $program_text .= "<p><font size=\"-1\"> 6607 $program_string 6608</font></p>"; 6609 } 6610 my $pre_body_close = $self->get_conf('PRE_BODY_CLOSE'); 6611 $pre_body_close = '' if (!defined($pre_body_close)); 6612 6613 my $setting = $self->get_conf('JS_WEBLABELS'); 6614 my $path = $self->get_conf('JS_WEBLABELS_FILE'); 6615 if ($setting and $path 6616 and ($setting eq 'generate' or $setting eq 'reference') 6617 and %{$self->{'jslicenses_element'}}) { 6618 $pre_body_close .= 6619"<a href='$path' rel='jslicense'><small>" 6620.$self->convert_tree($self->gdt('JavaScript license information')) 6621.'</small></a>'; 6622 } 6623 6624 return "${program_text} 6625 6626$pre_body_close 6627</body> 6628</html> 6629"; 6630} 6631 6632# This is used for normal output files and other files, like 6633# redirection file headers. $COMMAND is the tree element for 6634# a @node that is being output in the file. 6635sub _file_header_informations($$) 6636{ 6637 my $self = shift; 6638 my $command = shift; 6639 6640 my $title; 6641 if ($command) { 6642 my $command_string = 6643 $self->command_text($command, 'string'); 6644 if (defined($command_string) 6645 and $command_string ne $self->{'title_string'}) { 6646 my $element_tree; 6647 if ($self->get_conf('SECTION_NAME_IN_TITLE') 6648 and $command->{'extra'} 6649 and $command->{'extra'}->{'associated_section'} 6650 and $command->{'extra'}->{'associated_section'}->{'args'} 6651 and $command->{'extra'}->{'associated_section'}->{'args'}->[0]) { 6652 $element_tree = $command->{'extra'}->{'associated_section'}->{'args'}->[0]; 6653 } else { 6654 $element_tree = $self->command_text($command, 'tree'); 6655 } 6656 my $title_tree = $self->gdt('{element_text} ({title})', 6657 { 'title' => $self->{'title_tree'}, 6658 'element_text' => $element_tree }); 6659 $title = $self->convert_tree_new_formatting_context( 6660 {'type' => '_string', 'contents' => [$title_tree]}, 6661 $command->{'cmdname'}, 'element_title'); 6662 } 6663 } 6664 $title = $self->{'title_string'} if (!defined($title)); 6665 6666 my $description; 6667 if ($self->{'documentdescription_string'}) { 6668 $description = $self->{'documentdescription_string'}; 6669 } else { 6670 $description = $title; 6671 } 6672 $description = "<meta name=\"description\" content=\"$description\">" 6673 if ($description ne ''); 6674 my $encoding = ''; 6675 $encoding 6676 = "<meta http-equiv=\"Content-Type\" content=\"text/html; charset=". 6677 $self->get_conf('OUTPUT_ENCODING_NAME')."\">" 6678 if (defined($self->get_conf('OUTPUT_ENCODING_NAME')) 6679 and ($self->get_conf('OUTPUT_ENCODING_NAME') ne '')); 6680 6681 my $date = ''; 6682 if ($self->get_conf('DATE_IN_HEADER')) { 6683 my $today = $self->convert_tree_new_formatting_context( 6684 {'cmdname' => 'today'}, 'DATE_IN_HEADER'); 6685 $date = "\n<meta name=\"date\" content=\"$today\">"; 6686 } 6687 6688 my $css_lines; 6689 if (defined($self->get_conf('CSS_LINES'))) { 6690 $css_lines = $self->get_conf('CSS_LINES'); 6691 } else { 6692 $css_lines = ''; 6693 } 6694 my $doctype = $self->get_conf('DOCTYPE'); 6695 my $bodytext = $self->get_conf('BODYTEXT'); 6696 if ($self->{'element_math'} and $self->get_conf('HTML_MATH')) { 6697 if ($self->get_conf('HTML_MATH') eq 'mathjax') { 6698 $bodytext .= ' class="tex2jax_ignore"'; 6699 } 6700 } 6701 my $copying_comment = ''; 6702 $copying_comment = $self->{'copying_comment'} 6703 if (defined($self->{'copying_comment'})); 6704 my $after_body_open = ''; 6705 $after_body_open = $self->get_conf('AFTER_BODY_OPEN') 6706 if (defined($self->get_conf('AFTER_BODY_OPEN'))); 6707 my $extra_head = ''; 6708 $extra_head = $self->get_conf('EXTRA_HEAD') 6709 if (defined($self->get_conf('EXTRA_HEAD'))); 6710 my $program_and_version = $self->get_conf('PACKAGE_AND_VERSION'); 6711 my $program_homepage = $self->get_conf('PACKAGE_URL'); 6712 my $program = $self->get_conf('PROGRAM'); 6713 my $generator = ''; 6714 if (defined($program) and $program ne '') { 6715 $generator = "\n<meta name=\"Generator\" content=\"$program\">"; 6716 } 6717 6718 if (defined($self->get_conf('INFO_JS_DIR'))) { 6719 if (!$self->get_conf('SPLIT')) { 6720 $self->document_error( 6721 sprintf(__("%s not meaningful for non-split output"), 6722 'INFO_JS_DIR')); 6723 } else { 6724 my $jsdir = $self->get_conf('INFO_JS_DIR'); 6725 if ($jsdir eq '.') { 6726 $jsdir = ''; 6727 } else { 6728 $jsdir =~ s,/*$,/,; # append a single slash 6729 } 6730 6731 $extra_head .= 6732'<link rel="stylesheet" type="text/css" href="'.$jsdir.'info.css"/> 6733<script src="'.$jsdir.'modernizr.js" type="text/javascript"></script> 6734<script src="'.$jsdir.'info.js" type="text/javascript"></script>'; 6735 } 6736 for my $key (keys %{$self->{'jslicenses_infojs'}}) { 6737 $self->{'jslicenses_element'}->{$key} = $self->{'jslicenses_infojs'}->{$key}; 6738 } 6739 } 6740 if (($self->{'element_math'} or !$self->get_conf('SPLIT')) 6741 and defined($self->get_conf('HTML_MATH')) 6742 and $self->get_conf('HTML_MATH') eq 'mathjax') { 6743 my $mathjax_script = $self->get_conf('MATHJAX_SCRIPT'); 6744 6745 $extra_head .= 6746"<script type='text/javascript'> 6747MathJax = { 6748 options: { 6749 skipHtmlTags: {'[-]': ['pre']}, 6750 ignoreHtmlClass: 'tex2jax_ignore', 6751 processHtmlClass: 'tex2jax_process' 6752 }, 6753}; 6754</script>" 6755.'<script type="text/javascript" id="MathJax-script" async 6756 src="'.$mathjax_script.'"> 6757</script>'; 6758 6759 for my $key (keys %{$self->{'jslicenses_math'}}) { 6760 $self->{'jslicenses_element'}->{$key} = $self->{'jslicenses_math'}->{$key}; 6761 } 6762 } 6763 6764 return ($title, $description, $encoding, $date, $css_lines, 6765 $doctype, $bodytext, $copying_comment, $after_body_open, 6766 $extra_head, $program_and_version, $program_homepage, 6767 $program, $generator); 6768} 6769 6770sub _get_links ($$$) 6771{ 6772 my $self = shift; 6773 my $filename = shift; 6774 my $element = shift; 6775 6776 my $links = ''; 6777 if ($self->get_conf('USE_LINKS')) { 6778 my $link_buttons = $self->get_conf('LINKS_BUTTONS'); 6779 foreach my $link (@$link_buttons) { 6780 my $link_href = $self->_element_direction($element, 6781 $link, 'href', $filename); 6782 #print STDERR "$link -> $link_href \n"; 6783 if ($link_href and $link_href ne '') { 6784 my $link_string = $self->_element_direction($element, 6785 $link, 'string'); 6786 my $link_title = ''; 6787 $link_title = " title=\"$link_string\"" if (defined($link_string)); 6788 my $rel = ''; 6789 $rel = " rel=\"".$self->get_conf('BUTTONS_REL')->{$link}.'"' 6790 if (defined($self->get_conf('BUTTONS_REL')->{$link})); 6791 $links .= "<link href=\"$link_href\"${rel}${link_title}>\n"; 6792 } 6793 } 6794 } 6795 return $links; 6796} 6797 6798sub _default_format_begin_file($$$) 6799{ 6800 my $self = shift; 6801 my $filename = shift; 6802 my $element = shift; 6803 6804 my $command; 6805 if ($element and $self->get_conf('SPLIT')) { 6806 $command = $self->element_command($element); 6807 } 6808 6809 my ($title, $description, $encoding, $date, $css_lines, 6810 $doctype, $bodytext, $copying_comment, $after_body_open, 6811 $extra_head, $program_and_version, $program_homepage, 6812 $program, $generator) = $self->_file_header_informations($command); 6813 6814 my $links = $self->_get_links ($filename, $element); 6815 6816 my $result = "$doctype 6817<html> 6818<!-- Created by $program_and_version, $program_homepage --> 6819<head> 6820$encoding 6821$copying_comment<title>$title</title> 6822 6823$description 6824<meta name=\"keywords\" content=\"$title\"> 6825<meta name=\"resource-type\" content=\"document\"> 6826<meta name=\"distribution\" content=\"global\">${generator}$date 6827<meta name=\"viewport\" content=\"width=device-width,initial-scale=1\"> 6828 6829${links}$css_lines 6830$extra_head 6831</head> 6832 6833<body $bodytext> 6834$after_body_open"; 6835 6836 return $result; 6837} 6838 6839sub _default_format_node_redirection_page($$) 6840{ 6841 my $self = shift; 6842 my $command = shift; 6843 6844 my ($title, $description, $encoding, $date, $css_lines, 6845 $doctype, $bodytext, $copying_comment, $after_body_open, 6846 $extra_head, $program_and_version, $program_homepage, 6847 $program, $generator) = $self->_file_header_informations($command); 6848 6849 my $name = $self->command_text($command); 6850 my $href = $self->command_href($command); 6851 my $direction = "<a href=\"$href\">$name</a>"; 6852 my $string = $self->convert_tree ( 6853 $self->gdt('The node you are looking for is at {href}.', 6854 { 'href' => {'type' => '_converted', 'text' => $direction }})); 6855 my $result = "$doctype 6856<html> 6857<!-- Created by $program_and_version, $program_homepage --> 6858<!-- This file redirects to the location of a node or anchor --> 6859<head> 6860$encoding 6861$copying_comment<title>$title</title> 6862 6863$description 6864<meta name=\"keywords\" content=\"$title\"> 6865<meta name=\"resource-type\" content=\"document\"> 6866<meta name=\"distribution\" content=\"global\">${generator}$date 6867$css_lines 6868<meta http-equiv=\"Refresh\" content=\"0; url=$href\"> 6869<meta name=\"viewport\" content=\"width=device-width,initial-scale=1\"> 6870$extra_head 6871</head> 6872 6873<body $bodytext> 6874$after_body_open 6875<p>$string</p> 6876</body> 6877"; 6878 return $result; 6879} 6880 6881sub _default_format_footnotes_text($) 6882{ 6883 my $self = shift; 6884 return '' if (!$foot_lines); 6885 my $result = $self->_attribute_class('div', 'footnote').">\n"; 6886 $result .= $self->get_conf('DEFAULT_RULE') . "\n" 6887 if (defined($self->get_conf('DEFAULT_RULE')) 6888 and $self->get_conf('DEFAULT_RULE') ne ''); 6889 my $footnote_heading 6890 = $self->convert_tree ($self->get_conf('SPECIAL_ELEMENTS_NAME')->{'Footnotes'}); 6891 my $class = $self->get_conf('SPECIAL_ELEMENTS_CLASS')->{'Footnotes'}; 6892 my $level = $self->get_conf('FOOTNOTE_END_HEADER_LEVEL'); 6893 $result .= &{$self->{'format_heading_text'}}($self, $class.'-heading', 6894 $footnote_heading, $level)."\n"; 6895 $result .= &{$self->{'format_special_element_body'}}($self, 'Footnotes', 6896 $self->{'current_element'}); 6897 $result .= "</div>\n"; 6898 return $result; 6899} 6900 6901sub _default_format_special_element_body($$$) 6902{ 6903 my $self = shift; 6904 my $special_type = shift; 6905 my $element = shift; 6906 6907 if ($special_type eq 'About') { 6908 my $about = "<p>\n"; 6909 my $PRE_ABOUT = $self->get_conf('PRE_ABOUT'); 6910 if (defined($PRE_ABOUT)) { 6911 if (ref($PRE_ABOUT) eq 'CODE') { 6912 $about .= &$PRE_ABOUT($self, $element); 6913 } else { 6914 $about .= $PRE_ABOUT; 6915 } 6916 } else { 6917 $about .= ' '.&{$self->{'format_program_string'}}($self) ."\n"; 6918 } 6919 $about .= <<EOT; 6920</p> 6921<p> 6922EOT 6923 $about .= $self->convert_tree($self->gdt(' The buttons in the navigation panels have the following meaning:')) . "\n"; 6924 $about .= <<EOT; 6925</p> 6926<table border="1"> 6927 <tr> 6928EOT 6929 $about .= ' <th> ' . $self->convert_tree($self->gdt('Button')) . " </th>\n" . 6930 ' <th> ' . $self->convert_tree($self->gdt('Name')) . " </th>\n" . 6931 ' <th> ' . $self->convert_tree($self->gdt('Go to')) . " </th>\n" . 6932 ' <th> ' . $self->convert_tree($self->gdt('From 1.2.3 go to')) . "</th>\n" . " </tr>\n"; 6933 6934 foreach my $button (@{$self->get_conf('SECTION_BUTTONS')}) { 6935 next if ($button eq ' ' or ref($button) eq 'CODE' or ref($button) eq 'SCALAR' 6936 or ref($button) eq 'ARRAY'); 6937 my $button_name = $self->get_conf('BUTTONS_NAME')->{$button}; 6938 $about .= " <tr>\n <td align=\"center\">"; 6939 $about .= 6940 ($self->get_conf('ICONS') && $self->get_conf('ACTIVE_ICONS')->{$button} ? 6941 &{$self->{'format_button_icon_img'}}($self, $button_name, 6942 $self->get_conf('ACTIVE_ICONS')->{$button}) : 6943 ' [' . $self->get_conf('BUTTONS_TEXT')->{$button} . '] '); 6944 $about .= "</td>\n"; 6945 $about .= 6946" <td align=\"center\">".$button_name."</td> 6947 <td>".$self->get_conf('BUTTONS_GOTO')->{$button}."</td> 6948 <td>".$self->get_conf('BUTTONS_EXAMPLE')->{$button}."</td> 6949 </tr> 6950"; 6951 } 6952 6953 $about .= <<EOT; 6954</table> 6955 6956<p> 6957EOT 6958 $about .= $self->convert_tree($self->gdt(' where the @strong{ Example } assumes that the current position is at @strong{ Subsubsection One-Two-Three } of a document of the following structure:')) . "\n"; 6959 6960# where the <strong> Example </strong> assumes that the current position 6961# is at <strong> Subsubsection One-Two-Three </strong> of a document of 6962# the following structure: 6963 $about .= <<EOT; 6964</p> 6965 6966<ul> 6967EOT 6968 $about .= ' <li> 1. ' . $self->convert_tree($self->gdt('Section One')) . "\n" . 6969" <ul>\n" . 6970' <li>1.1 ' . $self->convert_tree($self->gdt('Subsection One-One')) . "\n"; 6971 $about .= <<EOT; 6972 <ul> 6973 <li>...</li> 6974 </ul> 6975 </li> 6976EOT 6977 $about .= ' <li>1.2 ' . $self->convert_tree($self->gdt('Subsection One-Two')) . "\n" . 6978" <ul>\n" . 6979' <li>1.2.1 ' . $self->convert_tree($self->gdt('Subsubsection One-Two-One')) . "</li>\n" . 6980' <li>1.2.2 ' . $self->convert_tree($self->gdt('Subsubsection One-Two-Two')) . "</li>\n" . 6981' <li>1.2.3 ' . $self->convert_tree($self->gdt('Subsubsection One-Two-Three')) . " \n" 6982. 6983' <strong><== ' . $self->convert_tree($self->gdt('Current Position')) . " </strong></li>\n" . 6984' <li>1.2.4 ' . $self->convert_tree($self->gdt('Subsubsection One-Two-Four')) . "</li>\n" . 6985" </ul>\n" . 6986" </li>\n" . 6987' <li>1.3 ' . $self->convert_tree($self->gdt('Subsection One-Three')) . "\n"; 6988 $about .= <<EOT; 6989 <ul> 6990 <li>...</li> 6991 </ul> 6992 </li> 6993EOT 6994 $about .= ' <li>1.4 ' . $self->convert_tree($self->gdt('Subsection One-Four')) . "</li>\n"; 6995 6996 my $AFTER_ABOUT = ''; 6997 if (defined($self->get_conf('AFTER_ABOUT'))) { 6998 $AFTER_ABOUT = $self->get_conf('AFTER_ABOUT'); 6999 } 7000 $about .= <<EOT; 7001 </ul> 7002 </li> 7003</ul> 7004$AFTER_ABOUT 7005EOT 7006 return $about; 7007 } elsif ($special_type eq 'Contents') { 7008 return &{$self->{'format_contents'}}($self, 'contents', undef); 7009 } elsif ($special_type eq 'Overview') { 7010 return &{$self->{'format_contents'}}($self, 'shortcontents', undef); 7011 } elsif ($special_type eq 'Footnotes') { 7012 my $result = $foot_lines; 7013 $foot_lines = ''; 7014 return $result; 7015 } 7016} 7017 7018sub _do_jslicenses_file { 7019 my $self = shift; 7020 7021 my $setting = $self->get_conf('JS_WEBLABELS'); 7022 my $path = $self->get_conf('JS_WEBLABELS_FILE'); 7023 7024 # Possible settings: 7025 # 'generate' - create file at JS_WEBLABELS_FILE 7026 # 'reference' - reference file at JS_WEBLABELS_FILE but do not create it 7027 # 'omit' - do nothing 7028 return if (!$setting or $setting ne 'generate'); 7029 7030 my $a = ''; 7031 $a .= '<!DOCTYPE html> 7032<html><head><title>jslicense labels</title></head> 7033<body> 7034<table id="jslicense-labels1"> 7035'; 7036 my $h = $self->{'jslicenses'}; 7037 7038 for my $file (keys %{$self->{'jslicenses'}}) { 7039 $a .= "<tr>\n"; 7040 $a .= "<td><a href=\"$file\">$file</a></td>\n"; 7041 $a .= "<td><a href=\"$h->{$file}->[1]\">$h->{$file}->[0]</a></td>\n"; 7042 $a .= "<td><a href=\"$h->{$file}->[2]\">$h->{$file}->[2]</a></td>\n"; 7043 $a .= "</tr>\n"; 7044 } 7045 7046 $a .= "</body></html>\n"; 7047 7048 if (File::Spec->file_name_is_absolute($path) or $path =~ /^[A-Za-z]*:/) { 7049 $self->document_warn(sprintf( 7050__("cannot use absolute path or URL `%s' for JS_WEBLABELS_FILE when generating web labels file"), $path)); 7051 return; 7052 } 7053 my $license_file = File::Spec->catdir($self->{'destination_directory'}, 7054 $path); 7055 my $fh = $self->Texinfo::Common::open_out($license_file); 7056 if (defined($fh)) { 7057 print $fh $a; 7058 $self->register_close_file($license_file); 7059 if (!close ($fh)) { 7060 $self->document_error(sprintf(__("error on closing %s: %s"), 7061 $license_file, $!)); 7062 } 7063 } else { 7064 $self->document_error(sprintf(__("could not open %s for writing: %s"), 7065 $license_file, $!)); 7066 } 7067} 7068 7069sub _default_format_frame_files($) 7070{ 7071 my $self = shift; 7072 7073 my $frame_file = $self->{'frame_pages_filenames'}->{'Frame'}; 7074 my $frame_outfile; 7075 if (defined($self->{'destination_directory'}) 7076 and $self->{'destination_directory'} ne '') { 7077 $frame_outfile = File::Spec->catfile($self->{'destination_directory'}, 7078 $frame_file); 7079 } else { 7080 $frame_outfile = $frame_file; 7081 } 7082 7083 my $toc_frame_file = $self->{'frame_pages_filenames'}->{'Toc_Frame'}; 7084 my $toc_frame_outfile; 7085 if (defined($self->{'destination_directory'}) 7086 and $self->{'destination_directory'} ne '') { 7087 $toc_frame_outfile = File::Spec->catfile($self->{'destination_directory'}, 7088 $toc_frame_file); 7089 } else { 7090 $toc_frame_outfile = $toc_frame_file; 7091 } 7092 7093 my $frame_fh = $self->Texinfo::Common::open_out($frame_outfile); 7094 if (defined($frame_fh)) { 7095 my $doctype = $self->get_conf('FRAMESET_DOCTYPE'); 7096 my $top_file = ''; 7097 if ($self->global_element('Top')) { 7098 my $top_element = $self->global_element('Top'); 7099 $top_file = $top_element->{'filename'}; 7100 } 7101 my $title = $self->{'title_string'}; 7102 print $frame_fh <<EOT; 7103$doctype 7104<html> 7105<head><title>$title</title></head> 7106<frameset cols="140,*"> 7107 <frame name="toc" src="$toc_frame_file"> 7108 <frame name="main" src="$top_file"> 7109</frameset> 7110</html> 7111EOT 7112 7113 $self->register_close_file($frame_outfile); 7114 if (!close ($frame_fh)) { 7115 $self->document_error(sprintf(__("error on closing frame file %s: %s"), 7116 $frame_outfile, $!)); 7117 return 0; 7118 } 7119 } else { 7120 $self->document_error(sprintf(__("could not open %s for writing: %s"), 7121 $frame_outfile, $!)); 7122 return 0; 7123 } 7124 7125 my $toc_frame_fh = $self->Texinfo::Common::open_out($toc_frame_outfile); 7126 if (defined($toc_frame_fh)) { 7127 7128 my $header = &{$self->{'format_begin_file'}}($self, $toc_frame_file, undef); 7129 print $toc_frame_fh $header; 7130 print $toc_frame_fh '<h2>Content</h2>'."\n"; 7131 my $shortcontents = 7132 &{$self->{'format_contents'}}($self, 'shortcontents', undef); 7133 $shortcontents =~ s/\bhref=/target="main" href=/g; 7134 print $toc_frame_fh $shortcontents; 7135 print $toc_frame_fh "</body></html>\n"; 7136 7137 $self->register_close_file($toc_frame_outfile); 7138 if (!close ($toc_frame_fh)) { 7139 $self->document_error(sprintf(__("error on closing TOC frame file %s: %s"), 7140 $toc_frame_outfile, $!)); 7141 return 0; 7142 } 7143 } else { 7144 $self->document_error(sprintf(__("could not open %s for writing: %s"), 7145 $toc_frame_outfile, $!)); 7146 return 0; 7147 } 7148 return 1; 7149} 7150 7151sub _has_contents_or_shortcontents($) 7152{ 7153 my $self = shift; 7154 foreach my $cmdname ('contents', 'shortcontents') { 7155 if ($self->{'extra'} and $self->{'extra'}->{$cmdname}) { 7156 return 1; 7157 } 7158 } 7159 return 0; 7160} 7161 7162sub convert($$) 7163{ 7164 my $self = shift; 7165 my $root = shift; 7166 7167 my $result = ''; 7168 7169 # This should return undef if called on a tree without node or sections. 7170 my ($elements, $special_elements) 7171 = $self->_prepare_elements($root); 7172 $self->_prepare_index_entries(); 7173 $self->_prepare_footnotes(); 7174 7175 if (!defined($elements)) { 7176 $result = $self->_convert($root); 7177 } else { 7178 foreach my $element (@$elements) { 7179 my $element_text = $self->_convert($element); 7180 $result .= $element_text; 7181 } 7182 } 7183 7184 return $result; 7185} 7186 7187# This is called from the main program on the converter. 7188sub output_internal_links($) 7189{ 7190 my $self = shift; 7191 my $out_string = ''; 7192 if ($self->{'elements'}) { 7193 foreach my $element (@{$self->{'elements'}}) { 7194 my $text; 7195 my $href; 7196 my $command = $self->element_command($element); 7197 if (defined($command)) { 7198 # Use '' for filename, to force a filename in href. 7199 $href = $self->command_href($command, ''); 7200 my $tree = $self->command_text($command, 'tree'); 7201 if ($tree) { 7202 $text = Texinfo::Convert::Text::convert($tree, 7203 {Texinfo::Common::_convert_text_options($self)}); 7204 } 7205 } 7206 if (defined($href) or defined($text)) { 7207 $out_string .= $href if (defined($href)); 7208 $out_string .= "\ttoc\t"; 7209 $out_string .= $text if (defined($text)); 7210 $out_string .= "\n"; 7211 } 7212 } 7213 } 7214 if ($self->{'parser'}) { 7215 foreach my $index_name (sort(keys (%{$self->{'index_entries_by_letter'}}))) { 7216 foreach my $letter_entry (@{$self->{'index_entries_by_letter'}->{$index_name}}) { 7217 foreach my $index_entry (@{$letter_entry->{'entries'}}) { 7218 my $href; 7219 my $key; 7220 $href = $self->command_href($index_entry->{'command'}, ''); 7221 $key = $index_entry->{'key'}; 7222 if (defined($key) and $key =~ /\S/) { 7223 $out_string .= $href if (defined($href)); 7224 $out_string .= "\t$index_name\t"; 7225 $out_string .= $key; 7226 $out_string .= "\n"; 7227 } 7228 } 7229 } 7230 } 7231 } 7232 if ($out_string ne '') { 7233 return $out_string; 7234 } else { 7235 return undef; 7236 } 7237} 7238 7239my @possible_stages = ('setup', 'structure', 'init', 'finish'); 7240my %possible_stages; 7241foreach my $stage (@possible_stages) { 7242 $possible_stages{$stage} = 1; 7243} 7244 7245sub run_stage_handlers($$$) 7246{ 7247 my $converter = shift; 7248 my $root = shift; 7249 my $stage = shift; 7250 die if (!$possible_stages{$stage}); 7251 7252 return 1 if (!defined($Texinfo::Config::texinfo_default_stage_handlers{$stage})); 7253 7254 my @sorted_priorities = sort keys(%{$Texinfo::Config::texinfo_default_stage_handlers{$stage}}); 7255 foreach my $priority (@sorted_priorities) { 7256 foreach my $handler (@{$Texinfo::Config::texinfo_default_stage_handlers{$stage}->{$priority}}) { 7257 if ($converter->get_conf('DEBUG')) { 7258 print STDERR "HANDLER($stage) , priority $priority: $handler\n"; 7259 } 7260 my $status = &{$handler}($converter, $root, $stage); 7261 if (!$status) { 7262 #if ($converter->get_conf('VERBOSE')) { 7263 # print STDERR "Handler $handler of $stage($priority) failed\n"; 7264 #} 7265 $converter->document_error(sprintf(__( 7266 "handler %s of stage %s priority %s failed"), 7267 $handler, $stage, $priority)); 7268 return $status; 7269 } 7270 } 7271 } 7272 return 1; 7273} 7274 7275my $default_priority = 'default'; 7276 7277{ 7278package Texinfo::Config; 7279 7280# Note that these variables are available for the Texinfo modules 7281# but, in general should not be accessed directly by the users who 7282# customize formatting and should use the associated functions, 7283# such as texinfo_register_handler(), texinfo_register_formatting_function(), 7284# texinfo_register_command_formatting() or texinfo_register_type_formatting(). 7285use vars qw(%texinfo_default_stage_handlers %texinfo_formatting_references 7286 %texinfo_commands_conversion %texinfo_types_conversion); 7287 7288sub texinfo_register_handler($$;$) 7289{ 7290 my $stage = shift; 7291 my $handler = shift; 7292 my $priority = shift; 7293 7294 if (!$possible_stages{$stage}) { 7295 carp ("Unknown stage $stage\n"); 7296 return 0; 7297 } 7298 $priority = $default_priority if (!defined($priority)); 7299 push @{$texinfo_default_stage_handlers{$stage}->{$priority}}, $handler; 7300 return 1; 7301} 7302 7303sub texinfo_register_formatting_function($$) 7304{ 7305 my $thing = shift; 7306 my $handler = shift; 7307 if (!$default_formatting_references{$thing}) { 7308 carp ("Unknown formatting type $thing\n"); 7309 return 0; 7310 } 7311 $texinfo_formatting_references{$thing} = $handler; 7312} 7313 7314sub texinfo_register_command_formatting($$) 7315{ 7316 my $command = shift; 7317 my $reference = shift; 7318 $texinfo_commands_conversion{$command} = $reference; 7319} 7320 7321sub texinfo_register_type_formatting($$) 7322{ 7323 my $command = shift; 7324 my $reference = shift; 7325 $texinfo_types_conversion{$command} = $reference; 7326} 7327 7328 7329} 7330 7331# Main function for outputting a manual in HTML. 7332# $SELF is the output converter object of class Texinfo::Convert::HTML (this 7333# module), and $ROOT is the Texinfo tree from the parser. 7334sub output($$) 7335{ 7336 my $self = shift; 7337 my $root = shift; 7338 7339 # no splitting when writing to the null device or to stdout or returning 7340 # a string 7341 if (defined($self->get_conf('OUTFILE')) 7342 and ($Texinfo::Common::null_device_file{$self->get_conf('OUTFILE')} 7343 or $self->get_conf('OUTFILE') eq '-' 7344 or $self->get_conf('OUTFILE') eq '')) { 7345 $self->force_conf('SPLIT', 0); 7346 $self->force_conf('MONOLITHIC', 1); 7347 $self->force_conf('FRAMES', 0); 7348 } 7349 if ($self->get_conf('SPLIT')) { 7350 $self->set_conf('NODE_FILES', 1); 7351 } 7352 if ($self->get_conf('FRAMES')) { 7353 $self->set_conf('shortcontents', 1); 7354 } 7355 $self->set_conf('EXTERNAL_CROSSREF_SPLIT', $self->get_conf('SPLIT')); 7356 7357 my $setup_status = $self->run_stage_handlers($root, 'setup'); 7358 return undef unless($setup_status); 7359 7360 $self->_prepare_css(); 7361 7362 # this sets OUTFILE, to be used if not split, but also 7363 # 'destination_directory' and 'output_filename' that are useful when split. 7364 $self->_set_outfile(); 7365 return undef unless $self->_create_destination_directory(); 7366 7367 # Get the list of "elements" to be processed, i.e. nodes or sections. 7368 # This should return undef if called on a tree without node or sections. 7369 my ($elements, $special_elements) = $self->_prepare_elements($root); 7370 7371 Texinfo::Structuring::split_pages($elements, $self->get_conf('SPLIT')); 7372 7373 # determine file names associated with the different pages, and setup 7374 # the counters for special element pages. 7375 if ($self->{'output_file'} ne '') { 7376 $self->_set_pages_files($elements, $special_elements); 7377 } 7378 7379 $self->_prepare_contents_elements(); 7380 7381 # do element directions. 7382 Texinfo::Structuring::elements_directions($self, $elements); 7383 7384 # do element directions related to files. 7385 # FIXME do it here or before? Here it means that 7386 # PrevFile and NextFile can be set. 7387 Texinfo::Structuring::elements_file_directions($self, $elements); 7388 7389 # Associate the special elements that have no page with the main page. 7390 # This may only happen if not split. 7391 if ($special_elements 7392 and $elements and $elements->[0] 7393 and defined($elements->[0]->{'filename'})) { 7394 foreach my $special_element (@$special_elements) { 7395 if (!defined($special_element->{'filename'})) { 7396 $special_element->{'filename'} = $elements->[0]->{'filename'}; 7397 $special_element->{'out_filename'} = $elements->[0]->{'out_filename'}; 7398 $self->{'file_counters'}->{$special_element->{'filename'}}++; 7399 } 7400 } 7401 } 7402 7403 $self->_prepare_index_entries(); 7404 $self->_prepare_footnotes(); 7405 7406 # 'file_counters' is dynamic, decreased when the element is encountered 7407 # 'elements_in_file_count' is not modified afterwards 7408 foreach my $filename (keys(%{$self->{'file_counters'}})) { 7409 $self->{'elements_in_file_count'}->{$filename} = $self->{'file_counters'}->{$filename}; 7410 } 7411 7412 my $structure_status = $self->run_stage_handlers($root, 'structure'); 7413 return undef unless($structure_status); 7414 7415 &{$self->{'format_css_lines'}}($self); 7416 7417 $self->set_conf('BODYTEXT', 7418 'lang="' . $self->get_conf('documentlanguage') . '"'); 7419 7420 # prepare title. fulltitle uses more possibility than simpletitle for 7421 # title, including @-commands found in @titlepage only. Therefore 7422 # simpletitle is more in line with what makeinfo in C does. 7423 my $fulltitle; 7424 foreach my $fulltitle_command('settitle', 'title', 7425 'shorttitlepage', 'top') { 7426 if ($self->{'extra'}->{$fulltitle_command}) { 7427 my $command = $self->{'extra'}->{$fulltitle_command}; 7428 next if (!$command->{'args'} 7429 or (!$command->{'args'}->[0]->{'contents'} 7430 or $command->{'extra'}->{'missing_argument'})); 7431 print STDERR "Using $fulltitle_command as title\n" 7432 if ($self->get_conf('DEBUG')); 7433 $fulltitle = {'contents' => $command->{'args'}->[0]->{'contents'}}; 7434 last; 7435 } 7436 } 7437 if (!$fulltitle and $self->{'extra'}->{'titlefont'} 7438 and $self->{'extra'}->{'titlefont'}->[0]->{'args'} 7439 and defined($self->{'extra'}->{'titlefont'}->[0]->{'args'}->[0]) 7440 and @{$self->{'extra'}->{'titlefont'}->[0]->{'args'}->[0]->{'contents'}}) { 7441 $fulltitle = $self->{'extra'}->{'titlefont'}->[0]; 7442 } 7443 # prepare simpletitle 7444 foreach my $simpletitle_command('settitle', 'shorttitlepage') { 7445 if ($self->{'extra'}->{$simpletitle_command}) { 7446 my $command = $self->{'extra'}->{$simpletitle_command}; 7447 next if ($command->{'extra'} 7448 and $command->{'extra'}->{'missing_argument'}); 7449 $self->{'simpletitle_tree'} = 7450 {'contents' => $command->{'args'}->[0]->{'contents'}}; 7451 last; 7452 } 7453 } 7454 7455 my $html_title_string; 7456 if ($fulltitle) { 7457 $self->{'title_tree'} = $fulltitle; 7458 $html_title_string = $self->convert_tree_new_formatting_context( 7459 {'type' => '_string', 'contents' => [$self->{'title_tree'}]}, 7460 'title_string'); 7461 } 7462 if (!defined($html_title_string) or $html_title_string !~ /\S/) { 7463 my $default_title = $self->gdt('Untitled Document'); 7464 $self->{'title_tree'} = $default_title; 7465 $self->{'title_string'} = $self->convert_tree_new_formatting_context( 7466 {'type' => '_string', 'contents' => [$self->{'title_tree'}]}, 7467 'title_string'); 7468 $self->file_line_warn(__( 7469 "must specify a title with a title command or \@top"), 7470 $self->{'info'}->{'input_file_name'}); 7471 } else { 7472 $self->{'title_string'} = $html_title_string; 7473 } 7474 7475 # copying comment 7476 if ($self->{'extra'}->{'copying'}) { 7477 my $copying_comment = Texinfo::Convert::Text::convert( 7478 {'contents' => $self->{'extra'}->{'copying'}->{'contents'}}, 7479 {Texinfo::Common::_convert_text_options($self)}); 7480 if ($copying_comment ne '') { 7481 $self->{'copying_comment'} = &{$self->{'format_comment'}}($self, $copying_comment); 7482 } 7483 } 7484 7485 # documentdescription 7486 if (defined($self->get_conf('documentdescription'))) { 7487 $self->{'documentdescription_string'} 7488 = $self->get_conf('documentdescription'); 7489 } elsif ($self->{'extra'}->{'documentdescription'}) { 7490 $self->{'documentdescription_string'} 7491 = $self->convert_tree_new_formatting_context( 7492 {'type' => '_string', 7493 'contents' => $self->{'extra'}->{'documentdescription'}->{'contents'}}, 7494 'documentdescription'); 7495 chomp($self->{'documentdescription_string'}); 7496 } 7497 7498 my $init_status = $self->run_stage_handlers($root, 'init'); 7499 return undef unless($init_status); 7500 7501 if ($self->get_conf('FRAMES')) { 7502 my $status = &{$self->{'format_frame_files'}}($self); 7503 return undef if (!$status); 7504 } 7505 7506 if ($self->get_conf('HTML_MATH') 7507 and $self->get_conf('HTML_MATH') eq 'mathjax') { 7508 # See https://www.gnu.org/licenses/javascript-labels.html 7509 # 7510 # The link to the source for mathjax does not strictly follow the advice 7511 # there: instead we link to instructions for obtaining the full source in 7512 # its preferred form of modification. 7513 7514 my ($mathjax_script, $mathjax_source); 7515 7516 $mathjax_script = $self->get_conf('MATHJAX_SCRIPT'); 7517 if (!$mathjax_script) { 7518 $mathjax_script = 'https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-svg.js'; 7519 $self->set_conf('MATHJAX_SCRIPT', $mathjax_script); 7520 } 7521 7522 $mathjax_source = $self->get_conf('MATHJAX_SOURCE'); 7523 if (!$mathjax_source) { 7524 $mathjax_source = 'http://docs.mathjax.org/en/latest/web/hosting.html#getting-mathjax-via-git'; 7525 $self->set_conf('MATHJAX_SOURCE', $mathjax_source); 7526 } 7527 7528 $self->{'jslicenses_math'}->{$mathjax_script} = 7529 [ 'Apache License, Version 2.0.', 7530 'https://www.apache.org/licenses/LICENSE-2.0', 7531 $mathjax_source ]; 7532 # append to hash 7533 %{$self->{'jslicenses'}} = ( %{$self->{'jslicenses'}}, 7534 %{$self->{'jslicenses_math'}} ); 7535 7536 } 7537 if ($self->get_conf('INFO_JS_DIR')) { 7538 $self->{'jslicenses_infojs'}->{'js/info.js'} = 7539 [ 'GNU General Public License 3.0 or later', 7540 'http://www.gnu.org/licenses/gpl-3.0.html', 7541 'js/info.js' ]; 7542 7543 $self->{'jslicenses_infojs'}->{'js/modernizr.js'} = 7544 [ 'Expat', 7545 'http://www.jclark.com/xml/copying.txt', 7546 'js/modernizr.js' ]; 7547 %{$self->{'jslicenses'}} = ( %{$self->{'jslicenses'}}, 7548 %{$self->{'jslicenses_infojs'}} ); 7549 } 7550 7551 # FIXME here call _unset_global_multiple_commands? Problem is 7552 # that some conversion, for instance for page header requires 7553 # that the correct language is set, for instance. The @-command 7554 # will necessarily appear later on -- even if it appears a the 7555 # beginning of the file. 7556 7557 my $fh; 7558 my $output = ''; 7559 7560 if (!$elements or !defined($elements->[0]->{'filename'})) { 7561 # no page 7562 my $outfile; 7563 if ($self->{'output_file'} ne '') { 7564 if ($self->get_conf('SPLIT')) { 7565 $outfile = $self->_top_node_filename(); 7566 if (defined($self->{'destination_directory'}) 7567 and $self->{'destination_directory'} ne '') { 7568 $outfile = File::Spec->catfile($self->{'destination_directory'}, 7569 $outfile); 7570 } 7571 } else { 7572 $outfile = $self->{'output_file'}; 7573 } 7574 $fh = $self->Texinfo::Common::open_out($outfile); 7575 if (!$fh) { 7576 $self->document_error(sprintf(__("could not open %s for writing: %s"), 7577 $outfile, $!)); 7578 return undef; 7579 } 7580 } 7581 $self->{'current_filename'} = $self->{'output_filename'}; 7582 7583 my $body = ''; 7584 if ($elements and @$elements) { 7585 foreach my $element (@$elements) { 7586 my $element_text = $self->_convert($element); 7587 $body .= $element_text; 7588 } 7589 } else { 7590 $body .= $self->_print_title(); 7591 $body .= $self->_convert($root); 7592 } 7593 7594 my $header = &{$self->{'format_begin_file'}}($self, 7595 $self->{'output_filename'}, undef); 7596 $output .= $self->_output_text($header, $fh); 7597 $output .= $self->_output_text($body, $fh); 7598 $output .= $self->_output_text(&{$self->{'format_end_file'}}($self), $fh); 7599 7600 # NOTE do not close STDOUT now to avoid a perl warning. 7601 if ($fh and $outfile ne '-') { 7602 $self->register_close_file($outfile); 7603 if (!close($fh)) { 7604 $self->document_error(sprintf(__("error on closing %s: %s"), 7605 $outfile, $!)); 7606 } 7607 } 7608 return $output if ($self->{'output_file'} eq ''); 7609 } else { 7610 # output with pages 7611 print STDERR "DO Elements with filenames\n" 7612 if ($self->get_conf('DEBUG')); 7613 my %files; 7614 7615 # Now do the output, converting each member in @$elements in turn. 7616 $special_elements = [] if (!defined($special_elements)); 7617 foreach my $element (@$elements, @$special_elements) { 7618 my $file_fh; 7619 $self->{'current_filename'} = $element->{'filename'}; 7620 $self->{'counter_in_file'}->{$element->{'filename'}}++; 7621 if ($self->{'counter_in_file'}->{$element->{'filename'}} == 1) { 7622 $self->{'jslicenses_element'} = {}; 7623 $self->{'element_math'} = 0; 7624 } 7625 7626 # First do the special pages, to avoid outputting these if they are 7627 # empty. 7628 my $special_element_content; 7629 if ($element->{'extra'} and $element->{'extra'}->{'special_element'}) { 7630 $special_element_content .= $self->_convert($element); 7631 if ($special_element_content eq '') { 7632 $self->{'file_counters'}->{$element->{'filename'}}--; 7633 next ; 7634 } 7635 } 7636 7637 # convert body before header in case this affects the header 7638 my $body = ''; 7639 if (defined($special_element_content)) { 7640 $body = $special_element_content; 7641 } else { 7642 $body = $self->_convert($element); 7643 } 7644 7645 if (!$files{$element->{'filename'}}->{'fh'}) { 7646 $file_fh = $self->Texinfo::Common::open_out($element->{'out_filename'}); 7647 if (!$file_fh) { 7648 $self->document_error(sprintf(__("could not open %s for writing: %s"), 7649 $element->{'out_filename'}, $!)); 7650 return undef; 7651 } 7652 print $file_fh "".&{$self->{'format_begin_file'}}($self, 7653 $element->{'filename'}, 7654 $element); 7655 $files{$element->{'filename'}}->{'fh'} = $file_fh; 7656 } else { 7657 $file_fh = $files{$element->{'filename'}}->{'fh'}; 7658 } 7659 print $file_fh $body; 7660 $self->{'file_counters'}->{$element->{'filename'}}--; 7661 if ($self->{'file_counters'}->{$element->{'filename'}} == 0) { 7662 # end file 7663 print $file_fh "". &{$self->{'format_end_file'}}($self); 7664 7665 # NOTE do not close STDOUT here to avoid a perl warning 7666 if ($element->{'out_filename'} ne '-') { 7667 $self->register_close_file($element->{'out_filename'}); 7668 if (!close($file_fh)) { 7669 $self->document_error(sprintf(__("error on closing %s: %s"), 7670 $element->{'out_filename'}, $!)); 7671 return undef; 7672 } 7673 } 7674 } 7675 } 7676 if ($self->get_conf('INFO_JS_DIR')) { 7677 my $jsdir = File::Spec->catdir($self->{'destination_directory'}, 7678 $self->get_conf('INFO_JS_DIR')); 7679 if (!-d $jsdir) { 7680 if (-f $jsdir) { 7681 $self->document_error( 7682 sprintf(__("%s already exists but is not a directory"), $jsdir)); 7683 } else { 7684 mkdir $jsdir; 7685 } 7686 } 7687 if (-d $jsdir) { 7688 my $jssrcdir; 7689 if (!$Texinfo::ModulePath::texinfo_uninstalled) { 7690 $jssrcdir = File::Spec->catdir( 7691 $Texinfo::ModulePath::lib_dir, 'js'); 7692 } else { 7693 $jssrcdir = File::Spec->catdir( 7694 $Texinfo::ModulePath::top_srcdir, 'js'); 7695 } 7696 for my $f ('info.js', 'modernizr.js', 'info.css') { 7697 my $from = File::Spec->catfile($jssrcdir, $f); 7698 7699 if (!copy($from, $jsdir)) { 7700 $self->document_error( 7701 sprintf(__("error on copying %s into %s"), $from, $jsdir)); 7702 } 7703 } 7704 } 7705 } 7706 } 7707 7708 if (%{$self->{'jslicenses'}}) { 7709 $self->_do_jslicenses_file(); 7710 } 7711 7712 my $finish_status = $self->run_stage_handlers($root, 'finish'); 7713 return undef unless($finish_status); 7714 7715 my $extension = ''; 7716 $extension = '.'.$self->get_conf('EXTENSION') 7717 if (defined($self->get_conf('EXTENSION')) 7718 and $self->get_conf('EXTENSION') ne ''); 7719 # do node redirection pages 7720 $self->{'current_filename'} = undef; 7721 if ($self->get_conf('NODE_FILES') 7722 and $self->{'labels'} and $self->{'output_file'} ne '') { 7723 foreach my $label (sort(keys (%{$self->{'labels'}}))) { 7724 my $node = $self->{'labels'}->{$label}; 7725 my $target = $self->_get_target($node); 7726 # filename may not be defined in case of an @anchor or similar in 7727 # @titlepage, and @titlepage is not used. 7728 my $filename = $self->command_filename($node); 7729 my $node_filename; 7730 # NOTE 'node_filename' is not used for Top, so the other manual 7731 # must use the same convention to get it right. We avoid doing 7732 # also 'node_filename' to avoid unneeded redirection files. 7733 if ($node->{'extra'} and $node->{'extra'}->{'normalized'} 7734 and $node->{'extra'}->{'normalized'} eq 'Top' 7735 and defined($self->get_conf('TOP_NODE_FILE_TARGET'))) { 7736 $node_filename = $self->get_conf('TOP_NODE_FILE_TARGET'); 7737 } else { 7738 $node_filename = $target->{'node_filename'}; 7739 } 7740 7741 if (defined($filename) and $node_filename ne $filename) { 7742 my $redirection_page 7743 = &{$self->{'format_node_redirection_page'}}($self, $node); 7744 my $out_filename; 7745 if (defined($self->{'destination_directory'}) 7746 and $self->{'destination_directory'} ne '') { 7747 $out_filename = File::Spec->catfile($self->{'destination_directory'}, 7748 $node_filename); 7749 } else { 7750 $out_filename = $node_filename; 7751 } 7752 my $file_fh = $self->Texinfo::Common::open_out($out_filename); 7753 if (!$file_fh) { 7754 $self->document_error(sprintf(__( 7755 "could not open %s for writing: %s"), 7756 $out_filename, $!)); 7757 } else { 7758 print $file_fh $redirection_page; 7759 $self->register_close_file($out_filename); 7760 if (!close ($file_fh)) { 7761 $self->document_error(sprintf(__( 7762 "error on closing redirection node file %s: %s"), 7763 $out_filename, $!)); 7764 return undef; 7765 } 7766 } 7767 } 7768 } 7769 } 7770 return undef; 7771} 7772 7773# Convert the 'contents' of a tree element. 7774sub _convert_contents($$$) 7775{ 7776 my $self = shift; 7777 my $root = shift; 7778 my $command_type = shift; 7779 my $content_formatted = ''; 7780 if (ref($root->{'contents'}) ne 'ARRAY') { 7781 cluck "for $root contents not an array: $root->{'contents'}"; 7782 print STDERR Texinfo::Common::_print_current($root); 7783 } 7784 7785 my $content_idx = 0; 7786 foreach my $content (@{$root->{'contents'}}) { 7787 my $new_content = $self->_convert($content, "$command_type [$content_idx]"); 7788 if (!defined($new_content)) { 7789 cluck "content not defined for $command_type [$content_idx]\n"; 7790 print STDERR "root is: ".Texinfo::Common::_print_current ($root); 7791 print STDERR "content is: ".Texinfo::Common::_print_current ($content); 7792 } else { 7793 $content_formatted .= $new_content; 7794 } 7795 $content_idx++; 7796 } 7797 return $content_formatted; 7798} 7799 7800#my $characters_replaced_from_class_names = quotemeta('[](),~#:/\\@+=!;.,?* '); 7801# FIXME not clear what character should be allowed and which ones replaced besides space 7802my $characters_replaced_from_class_names = quotemeta(' '); 7803sub _protect_class_name($$) 7804{ 7805 my $self = shift; 7806 my $class_name = shift; 7807 $class_name =~ s/[$characters_replaced_from_class_names]/-/g; 7808 return $self->protect_text($class_name); 7809} 7810 7811# $extra_classes should be an array reference or undef 7812sub _attribute_class($$$;$) 7813{ 7814 my $self = shift; 7815 my $element = shift; 7816 my $class = shift; 7817 my $extra_classes = shift; 7818 7819 if (!defined($class) or $class eq '' or $self->get_conf('NO_CSS')) { 7820 if ($element eq 'span') { 7821 return ''; 7822 } else { 7823 return "<$element"; 7824 } 7825 } 7826 7827 my $style = ''; 7828 7829 if ($self->get_conf('INLINE_CSS_STYLE') 7830 and defined($self->{'css_map'}->{"$element.$class"})) { 7831 $style = ' style="'.$self->{'css_map'}->{"$element.$class"}.'"'; 7832 } 7833 my $extra_class_str = ''; 7834 if (defined($extra_classes)) { 7835 my $extra_class_conversion = join(' ', map {$self->_protect_class_name($_)} @$extra_classes); 7836 if ($extra_class_conversion ne '') { 7837 $extra_class_str = ' '.$extra_class_conversion; 7838 } 7839 } 7840 return "<$element class=\"$class$extra_class_str\"$style"; 7841} 7842 7843sub _protect_space($$) 7844{ 7845 my $self = shift; 7846 my $text = shift; 7847 7848 return $text if ($self->in_preformatted()); 7849 7850 if ($self->in_space_protected()) { 7851 my $open = $self->_attribute_class('span', 'nolinebreak'); 7852 if ($open ne '') { 7853 $open .= '>'; 7854 # Protect spaces in the html leading attribute in case we are in 'w' 7855 $open =~ s/ /\x{1F}/g; 7856 # Special span to avoid breaking at _- 7857 $text =~ s/(\S*[_-]\S*)/${open}$1<\/span>/g; 7858 } 7859 $text .= ' ' if (chomp($text)); 7860 # Protect spaces within text 7861 $text =~ s/ / /g; 7862 # Revert protected spaces in leading html attribute 7863 $text =~ s/\x{1F}/ /g; 7864 } 7865 return $text; 7866} 7867 7868# Convert tree element $ROOT, and return HTML text for the output files. 7869sub _convert($$;$); 7870 7871sub _convert($$;$) 7872{ 7873 my $self = shift; 7874 my $root = shift; 7875 # only used for debug 7876 my $explanation = shift; 7877 7878 # to help debug and trace 7879 my $command_type = ''; 7880 if ($root->{'cmdname'}) { 7881 $command_type = "\@$root->{'cmdname'} "; 7882 } 7883 if (defined($root->{'type'})) { 7884 $command_type .= $root->{'type'}; 7885 } 7886 7887 if ($self->get_conf('DEBUG')) { 7888 $explanation = 'NO EXPLANATION' if (!defined($explanation)); 7889 print STDERR "ROOT($explanation):$root (".join('|',@{$self->{'document_context'}->[-1]->{'formatting_context'}})."), ->"; 7890 print STDERR " cmd: $root->{'cmdname'}," if ($root->{'cmdname'}); 7891 print STDERR " type: $root->{'type'}" if ($root->{'type'}); 7892 my $text = $root->{'text'}; 7893 if (defined($text)) { 7894 $text =~ s/\n/\\n/; 7895 print STDERR " text: $text"; 7896 } 7897 print STDERR "\n"; 7898 } 7899 7900 if (ref($root) ne 'HASH') { 7901 cluck "_convert: root not a HASH\n"; 7902 return ''; 7903 } 7904 7905 if (($root->{'type'} 7906 and exists ($self->{'types_conversion'}->{$root->{'type'}}) 7907 and !defined($self->{'types_conversion'}->{$root->{'type'}})) 7908 or ($root->{'cmdname'} 7909 and exists($self->{'commands_conversion'}->{$root->{'cmdname'}}) 7910 and !defined($self->{'commands_conversion'}->{$root->{'cmdname'}}))) { 7911 if ($self->get_conf('DEBUG')) { 7912 my $string = 'IGNORED'; 7913 $string .= " \@$root->{'cmdname'}" if ($root->{'cmdname'}); 7914 $string .= " $root->{'type'}" if ($root->{'type'}); 7915 print STDERR "$string\n"; 7916 } 7917 return ''; 7918 } 7919 7920 # Process text 7921 if (defined($root->{'text'})) { 7922 # already converted to html, keep it as is 7923 if ($root->{'type'} and $root->{'type'} eq '_converted') { 7924 return $root->{'text'}; 7925 } 7926 if ($root->{'type'} and $root->{'type'} eq 'untranslated') { 7927 my $translated = $self->gdt($root->{'text'}); 7928 my $result = $self->_convert($translated); 7929 return $result; 7930 } 7931 my $result = &{$self->{'types_conversion'}->{'text'}} ($self, 7932 $root->{'type'}, 7933 $root, 7934 $root->{'text'}); 7935 print STDERR "DO TEXT => `$result'\n" if ($self->get_conf('DEBUG')); 7936 return $result; 7937 } 7938 7939 if ($root->{'extra'} and $root->{'extra'}->{'missing_argument'} 7940 and (!$root->{'contents'} or !@{$root->{'contents'}})) { 7941 print STDERR "MISSING_ARGUMENT\n" if ($self->get_conf('DEBUG')); 7942 return ''; 7943 } 7944 7945 # commands like @deffnx have both a cmdname and a def_line type. It is 7946 # better to consider them as a def_line type, as the whole point of the 7947 # def_line type is to handle the same the def*x and def* line formatting. 7948 if ($root->{'cmdname'} 7949 and !($root->{'type'} and $root->{'type'} eq 'def_line' 7950 or $root->{'type'} and $root->{'type'} eq 'definfoenclose_command')) { 7951 my $command_name = $root->{'cmdname'}; 7952 # use the same command name for all the index entry commands 7953 if ($root->{'extra'} and $root->{'extra'}->{'index_entry'} 7954 and $root->{'cmdname'} and $root->{'cmdname'} =~ /index$/) { 7955 $command_name = 'cindex'; 7956 } 7957 if ($root_commands{$command_name}) { 7958 $self->{'current_root_command'} = $root; 7959 } 7960 if (exists($self->{'commands_conversion'}->{$command_name})) { 7961 if (exists($context_brace_commands{$command_name})) { 7962 $self->_new_document_context($command_name); 7963 } 7964 push @{$self->{'document_context'}->[-1]->{'commands'}}, 7965 $root->{'cmdname'}; 7966 if (exists($format_context_commands{$command_name})) { 7967 push @{$self->{'document_context'}->[-1]->{'formatting_context'}}, 7968 {'cmdname' => $command_name}; 7969 } 7970 if (exists($block_commands{$command_name})) { 7971 push @{$self->{'document_context'}->[-1]->{'formats'}}, $command_name; 7972 } 7973 if (exists ($composition_context_commands{$command_name})) { 7974 push @{$self->{'document_context'}->[-1]->{'composition_context'}}, $command_name; 7975 } 7976 if ($pre_class_commands{$command_name}) { 7977 push @{$self->{'document_context'}->[-1]->{'preformatted_classes'}}, 7978 $pre_class_commands{$command_name}; 7979 } 7980 if ($format_raw_commands{$command_name}) { 7981 $self->{'document_context'}->[-1]->{'raw'}++; 7982 } elsif ($command_name eq 'verb' or $command_name eq 'verbatim') { 7983 $self->{'document_context'}->[-1]->{'verbatim'}++; 7984 } 7985 if ($code_style_commands{$command_name} or 7986 $preformatted_code_commands{$command_name}) { 7987 push @{$self->{'document_context'}->[-1]->{'monospace'}}, 1; 7988 } elsif ($regular_font_style_commands{$command_name}) { 7989 push @{$self->{'document_context'}->[-1]->{'monospace'}}, 0; 7990 } elsif ($upper_case_commands{$command_name}) { 7991 $self->{'document_context'}->[-1]->{'formatting_context'}->[-1]->{'upper_case'}++; 7992 } elsif ($math_commands{$command_name}) { 7993 $self->{'document_context'}->[-1]->{'math'}++; 7994 } elsif ($command_name eq 'w') { 7995 $self->{'document_context'}->[-1]->{'formatting_context'}->[-1]->{'space_protected'}++; 7996 } 7997 my $content_formatted; 7998 if ($root->{'contents'}) { 7999 $content_formatted = $self->_convert_contents($root, $command_type); 8000 } 8001 my $args_formatted; 8002 if ($brace_commands{$command_name} 8003 or ($misc_commands{$command_name} 8004 and $misc_commands{$command_name} eq 'line') 8005 or (($command_name eq 'item' or $command_name eq 'itemx') 8006 and ($root->{'parent'}->{'type'} 8007 and $root->{'parent'}->{'type'} eq 'table_term')) 8008 or ($command_name eq 'quotation' 8009 or $command_name eq 'smallquotation') 8010 or ($command_name eq 'float')) { 8011 $args_formatted = []; 8012 if ($root->{'args'}) { 8013 my @args_specification; 8014 @args_specification = @{$self->{'commands_args'}->{$command_name}} 8015 if (defined($self->{'commands_args'}->{$command_name})); 8016 my $arg_idx = 0; 8017 foreach my $arg (@{$root->{'args'}}) { 8018 my $arg_spec = shift @args_specification; 8019 $arg_spec = ['normal'] if (!defined($arg_spec)); 8020 my $arg_formatted = {'tree' => $arg}; 8021 foreach my $arg_type (@$arg_spec) { 8022 my $explanation = "$command_type \[$arg_idx\]$arg_type"; 8023 if ($arg_type eq 'normal') { 8024 $arg_formatted->{'normal'} = $self->_convert($arg, $explanation); 8025 } elsif ($arg_type eq 'monospace') { 8026 push @{$self->{'document_context'}->[-1]->{'monospace'}}, 1; 8027 #$self->{'document_context'}->[-1]->{'code'}++; 8028 $arg_formatted->{$arg_type} = $self->_convert($arg, $explanation); 8029 #$self->{'document_context'}->[-1]->{'code'}--; 8030 pop @{$self->{'document_context'}->[-1]->{'monospace'}}; 8031 } elsif ($arg_type eq 'string') { 8032 $self->_new_document_context($command_type); 8033 $self->{'document_context'}->[-1]->{'string'}++; 8034 $arg_formatted->{$arg_type} = $self->_convert($arg, $explanation); 8035 pop @{$self->{'document_context'}}; 8036 } elsif ($arg_type eq 'monospacestring') { 8037 $self->_new_document_context($command_type); 8038 $self->{'document_context'}->[-1]->{'monospace'}->[-1] = 1; 8039 $self->{'document_context'}->[-1]->{'string'}++; 8040 $arg_formatted->{$arg_type} = $self->_convert($arg, $explanation); 8041 pop @{$self->{'document_context'}}; 8042 } elsif ($arg_type eq 'monospacetext') { 8043 $arg_formatted->{$arg_type} 8044 = Texinfo::Convert::Text::convert($arg, {'code' => 1, 8045 Texinfo::Common::_convert_text_options($self)}); 8046 } elsif ($arg_type eq 'raw') { 8047 $self->{'document_context'}->[-1]->{'raw'}++; 8048 $arg_formatted->{$arg_type} = $self->_convert($arg, $explanation); 8049 $self->{'document_context'}->[-1]->{'raw'}--; 8050 } 8051 } 8052 8053 push @$args_formatted, $arg_formatted; 8054 $arg_idx++; 8055 } 8056 } 8057 } 8058 if (exists ($composition_context_commands{$command_name})) { 8059 pop @{$self->{'document_context'}->[-1]->{'composition_context'}}; 8060 } 8061 if ($pre_class_commands{$command_name}) { 8062 pop @{$self->{'document_context'}->[-1]->{'preformatted_classes'}}; 8063 } 8064 if ($code_style_commands{$command_name} 8065 or $preformatted_code_commands{$command_name} 8066 or $regular_font_style_commands{$command_name}) { 8067 #$self->{'document_context'}->[-1]->{'code'}--; 8068 pop @{$self->{'document_context'}->[-1]->{'monospace'}}; 8069 } elsif ($upper_case_commands{$command_name}) { 8070 $self->{'document_context'}->[-1]->{'formatting_context'}->[-1]->{'upper_case'}--; 8071 } elsif ($math_commands{$command_name}) { 8072 $self->{'document_context'}->[-1]->{'math'}--; 8073 } elsif ($command_name eq 'w') { 8074 $self->{'document_context'}->[-1]->{'formatting_context'}->[-1]->{'space_protected'}--; 8075 } 8076 if ($format_raw_commands{$command_name}) { 8077 $self->{'document_context'}->[-1]->{'raw'}--; 8078 } elsif ($command_name eq 'verb' or $command_name eq 'verbatim') { 8079 $self->{'document_context'}->[-1]->{'verbatim'}--; 8080 } 8081 if (exists($block_commands{$command_name})) { 8082 pop @{$self->{'document_context'}->[-1]->{'formats'}}; 8083 } 8084 if (exists($format_context_commands{$command_name})) { 8085 pop @{$self->{'document_context'}->[-1]->{'formatting_context'}}; 8086 } 8087 pop @{$self->{'document_context'}->[-1]->{'commands'}}; 8088 if (exists($context_brace_commands{$command_name})) { 8089 pop @{$self->{'document_context'}}; 8090 } 8091 8092 if ($root->{'cmdname'} eq 'node') { 8093 $self->{'current_node'} = $root; 8094 } 8095 elsif ($root->{'cmdname'} eq 'menu' and $self->{'current_node'}) { 8096 $self->{'seenmenus'}->{$self->{'current_node'}} = 1; 8097 } 8098 # args are formatted, now format the command itself 8099 my $result; 8100 if ($args_formatted) { 8101 if (!defined($self->{'commands_conversion'}->{$command_name})) { 8102 print STDERR "No command_conversion for $command_name\n"; 8103 $result = ''; 8104 } else { 8105 $result = &{$self->{'commands_conversion'}->{$command_name}}($self, 8106 $command_name, $root, $args_formatted, $content_formatted); 8107 } 8108 } else { 8109 $result = &{$self->{'commands_conversion'}->{$command_name}}($self, 8110 $command_name, $root, $content_formatted); 8111 } 8112 return $result; 8113 } else { 8114 print STDERR "Unknown command `$command_name'\n" 8115 if ($self->get_conf('VERBOSE') or $self->get_conf('DEBUG')); 8116 return ''; 8117 } 8118 if ($root_commands{$command_name}) { 8119 delete $self->{'current_root_command'}; 8120 } 8121 } elsif ($root->{'type'}) { 8122 push @{$self->{'document_context'}->[-1]->{'commands'}}, 8123 $root->{'cmdname'} 8124 if ($root->{'cmdname'}); 8125 8126 if ($root->{'type'} eq 'paragraph') { 8127 $self->{'document_context'}->[-1]->{'formatting_context'}->[-1]->{'paragraph_number'}++; 8128 } elsif ($root->{'type'} eq 'preformatted' 8129 or $root->{'type'} eq 'rawpreformatted') { 8130 $self->{'document_context'}->[-1]->{'formatting_context'}->[-1]->{'preformatted_number'}++; 8131 } elsif ($root->{'type'} eq 'element') { 8132 $self->{'current_element'} = $root; 8133 $self->{'current_filename'} = $root->{'filename'}; 8134 } elsif ($pre_class_types{$root->{'type'}}) { 8135 push @{$self->{'document_context'}->[-1]->{'preformatted_classes'}}, 8136 $pre_class_types{$root->{'type'}}; 8137 push @{$self->{'document_context'}->[-1]->{'composition_context'}}, 8138 $root->{'type'}; 8139 } 8140 8141 if ($self->{'code_types'}->{$root->{'type'}}) { 8142 #$self->{'document_context'}->[-1]->{'code'}++; 8143 push @{$self->{'document_context'}->[-1]->{'monospace'}}, 1; 8144 } 8145 if ($root->{'type'} eq '_string') { 8146 $self->{'document_context'}->[-1]->{'string'}++; 8147 } 8148 8149 my $content_formatted; 8150 if ($root->{'type'} eq 'definfoenclose_command') { 8151 if ($root->{'args'}) { 8152 $content_formatted = $self->_convert($root->{'args'}->[0]); 8153 } 8154 } elsif ($root->{'contents'}) { 8155 $content_formatted = $self->_convert_contents($root, $command_type); 8156 } 8157 8158 my $result = ''; 8159 if (exists($self->{'types_conversion'}->{$root->{'type'}})) { 8160 $result = &{$self->{'types_conversion'}->{$root->{'type'}}} ($self, 8161 $root->{'type'}, 8162 $root, 8163 $content_formatted); 8164 #print STDERR "Converting type $root->{'type'} -> $result\n"; 8165 } elsif (defined($content_formatted)) { 8166 $result = $content_formatted; 8167 } 8168 if ($self->{'code_types'}->{$root->{'type'}}) { 8169 #$self->{'document_context'}->[-1]->{'code'}--; 8170 pop @{$self->{'document_context'}->[-1]->{'monospace'}}; 8171 } 8172 if ($root->{'type'} eq '_string') { 8173 $self->{'document_context'}->[-1]->{'string'}--; 8174 } 8175 if ($root->{'type'} eq 'element') { 8176 delete $self->{'current_element'}; 8177 delete $self->{'current_filename'}; 8178 } elsif ($pre_class_types{$root->{'type'}}) { 8179 pop @{$self->{'document_context'}->[-1]->{'preformatted_classes'}}; 8180 pop @{$self->{'document_context'}->[-1]->{'composition_context'}}; 8181 } 8182 print STDERR "DO type ($root->{'type'}) => `$result'\n" 8183 if ($self->get_conf('DEBUG')); 8184 pop @{$self->{'document_context'}->[-1]->{'commands'}} 8185 if ($root->{'cmdname'}); 8186 return $result; 8187 # no type, no cmdname, but contents. 8188 } elsif ($root->{'contents'}) { 8189 # this happens inside accents, for section/node names, for @images. 8190 my $content_formatted = ''; 8191 my $i = 0; 8192 foreach my $content (@{$root->{'contents'}}) { 8193 $content_formatted .= $self->_convert($content, "$command_type [$i]"); 8194 $i++; 8195 } 8196 print STDERR "UNNAMED HOLDER => `$content_formatted'\n" 8197 if ($self->get_conf('DEBUG')); 8198 return $content_formatted; 8199 } else { 8200 print STDERR "UNNAMED empty\n" if ($self->get_conf('DEBUG')); 8201 if ($self->{'types_conversion'}->{''}) { 8202 return &{$self->{'types_conversion'}->{''}} ($self, $root); 8203 } else { 8204 return ''; 8205 } 8206 } 8207 print STDERR "DEBUG: HERE!($root)\n"; 8208} 8209 8210sub _set_variables_texi2html() 8211{ 8212 my @texi2html_options = ( 8213 ['FORMAT_MENU', 'menu'], 8214 ['NO_USE_SETFILENAME', 1], 8215 ['USE_SETFILENAME_EXTENSION', 0], 8216 ['footnotestyle', 'separate'], 8217 ['CONTENTS_OUTPUT_LOCATION', 'separate_element'], 8218 ['FORCE', 1], 8219 ['AVOID_MENU_REDUNDANCY', 1], 8220 ['USE_ACCESSKEY', 0], 8221 ['NODE_NAME_IN_MENU', 0], 8222 ['OVERVIEW_LINK_TO_TOC', 0], 8223 ['USE_UP_NODE_FOR_ELEMENT_UP', 1], 8224 ['USE_REL_REV', 0], 8225 ['USE_LINKS', 0], 8226 ['USE_NODES', 0], 8227 ['USE_NUMERIC_ENTITY', 1], 8228 ['SPLIT', ''], 8229 ['PROGRAM_NAME_IN_FOOTER', 1], 8230 ['HEADER_IN_TABLE', 1], 8231 ['USE_TITLEPAGE_FOR_TITLE', 1], 8232 ['MENU_ENTRY_COLON', ''], 8233 ['INDEX_ENTRY_COLON', ''], 8234 ['ENABLE_ENCODING_USE_ENTITY', 1], 8235 ['DO_ABOUT', undef], 8236 ['NODE_NAME_IN_INDEX', 0], 8237 ['CHAPTER_HEADER_LEVEL', 1], 8238 ['BIG_RULE', '<hr size="6">'], 8239 ['FOOTNOTE_END_HEADER_LEVEL', 3], 8240 ['FOOTNOTE_SEPARATE_HEADER_LEVEL', 1], 8241 ['KEEP_TOP_EXTERNAL_REF', 1], 8242 ['SECTION_BUTTONS', ['FastBack', 'Back', 'Up', 'Forward', 'FastForward', 8243 ' ', ' ', ' ', ' ', 8244 'Top', 'Contents', 'Index', 'About' ]], 8245 ['TOP_BUTTONS', ['Back', 'Forward', ' ', 8246 'Contents', 'Index', 'About']], 8247 8248 ['MISC_BUTTONS', [ 'Top', 'Contents', 'Index', 'About' ]], 8249 ['CHAPTER_BUTTONS', [ 'FastBack', 'FastForward', ' ', 8250 ' ', ' ', ' ', ' ', 8251 'Top', 'Contents', 'Index', 'About', ]], 8252 ['SECTION_FOOTER_BUTTONS', [ 'FastBack', 'FirstInFileBack', 'FirstInFileUp', 8253 'Forward', 'FastForward' ]], 8254 ['CHAPTER_FOOTER_BUTTONS', [ 'FastBack', 'FastForward', ' ', 8255 ' ', ' ', ' ', ' ', 8256 'Top', 'Contents', 'Index', 'About', ]], 8257 ['NODE_FOOTER_BUTTONS', [ 'FastBack', 'Back', 8258 'Up', 'Forward', 'FastForward', 8259 ' ', ' ', ' ', ' ', 8260 'Top', 'Contents', 'Index', 'About' ]], 8261 ); 8262 foreach my $option (@texi2html_options) { 8263 #no warnings 'once'; 8264 $defaults{$option->[0]} = $option->[1]; 8265 } 8266} 8267 82681; 8269 8270__END__ 8271# $Id$ 8272# Automatically generated from maintain/template.pod 8273 8274=head1 NAME 8275 8276Texinfo::Convert::HTML - Convert Texinfo tree to HTML 8277 8278=head1 SYNOPSIS 8279 8280 my $converter 8281 = Texinfo::Convert::HTML->converter({'parser' => $parser}); 8282 8283 $converter->output($tree); 8284 $converter->convert($tree); 8285 $converter->convert_tree($tree); 8286 $converter->output_internal_links(); # HTML only 8287 8288=head1 DESCRIPTION 8289 8290Texinfo::Convert::HTML converts a Texinfo tree to HTML. 8291 8292=head1 METHODS 8293 8294=over 8295 8296=item $converter = Texinfo::Convert::HTML->converter($options) 8297 8298Initialize converter from Texinfo to HTML. 8299 8300The I<$options> hash reference holds options for the converter. In 8301this option hash reference a parser object may be associated with the 8302I<parser> key. The other options should be configuration options 8303described in the Texinfo manual. Those options, when appropriate, 8304override the document content. 8305 8306See L<Texinfo::Convert::Converter> for more informations. 8307 8308=item $converter->output($tree) 8309 8310Convert a Texinfo tree I<$tree> and output the result in files as 8311described in the Texinfo manual. 8312 8313=item $result = $converter->convert($tree) 8314 8315Convert a Texinfo tree I<$tree> or tree portion and return 8316the resulting output. 8317 8318=item $result = $converter->convert_tree($tree) 8319 8320Convert a Texinfo tree portion I<$tree> and return the resulting 8321output. This function does not try to output a full document but only 8322portions. For a full document use C<convert>. 8323 8324=item $result = $converter->output_internal_links() 8325 8326Returns text representing the links in the document. The format should 8327follow the C<--internal-links> option of the texi2any/makeinfo 8328specification. This is only supported in (and relevant for) HTML. 8329 8330=back 8331 8332=head1 AUTHOR 8333 8334Patrice Dumas, E<lt>pertusus@free.frE<gt> 8335 8336=cut 8337