| # $Id: DocBook.pm 6991 2016-02-06 12:16:13Z gavin $ |
| # DocBook.pm: output tree as DocBook. |
| # |
| # Copyright 2011, 2012, 2013, 2014, 2015 Free Software Foundation, Inc. |
| # |
| # This program is free software; you can redistribute it and/or modify |
| # it under the terms of the GNU General Public License as published by |
| # the Free Software Foundation; either version 3 of the License, |
| # or (at your option) any later version. |
| # |
| # This program is distributed in the hope that it will be useful, |
| # but WITHOUT ANY WARRANTY; without even the implied warranty of |
| # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| # GNU General Public License for more details. |
| # |
| # You should have received a copy of the GNU General Public License |
| # along with this program. If not, see <http://www.gnu.org/licenses/>. |
| # |
| # Original author: Patrice Dumas <pertusus@free.fr> |
| |
| package Texinfo::Convert::DocBook; |
| |
| use 5.00405; |
| use strict; |
| |
| use Texinfo::Convert::Converter; |
| use Texinfo::Common; |
| use Texinfo::Convert::Unicode; |
| use Texinfo::Convert::Text; |
| use Texinfo::Convert::Plaintext; |
| # for debugging |
| use Texinfo::Convert::Texinfo; |
| use Data::Dumper; |
| use Carp qw(cluck); |
| |
| require Exporter; |
| use vars qw($VERSION @ISA @EXPORT @EXPORT_OK %EXPORT_TAGS); |
| @ISA = qw(Exporter Texinfo::Convert::Converter); |
| |
| # Items to export into callers namespace by default. Note: do not export |
| # names by default without a very good reason. Use EXPORT_OK instead. |
| # Do not simply export all your public functions/methods/constants. |
| |
| # This allows declaration use Texinfo::Convert::DocBook ':all'; |
| # If you do not need this, moving things directly into @EXPORT or @EXPORT_OK |
| # will save memory. |
| %EXPORT_TAGS = ( 'all' => [ qw( |
| convert |
| convert_tree |
| output |
| ) ] ); |
| |
| @EXPORT_OK = ( @{ $EXPORT_TAGS{'all'} } ); |
| |
| @EXPORT = qw( |
| ); |
| |
| $VERSION = '6.1'; |
| |
| my $nbsp = '&#'.hex('00A0').';'; |
| my $mdash = '&#'.hex('2014').';'; |
| my $ndash = '&#'.hex('2013').';'; |
| my $ldquo = '&#'.hex('201C').';'; |
| my $rdquo = '&#'.hex('201D').';'; |
| my $lsquo = '&#'.hex('2018').';'; |
| my $rsquo = '&#'.hex('2019').';'; |
| |
| my %defaults = ( |
| #'ENABLE_ENCODING' => 0, |
| 'SHOW_MENU' => 0, |
| 'EXTENSION' => 'xml', # dbk? |
| 'OUTPUT_ENCODING_NAME' => 'utf-8', |
| 'OUTFILE' => undef, |
| 'SUBDIR' => undef, |
| 'output_format' => 'docbook', |
| 'SPLIT' => 0, |
| 'documentlanguage' => 'en', |
| 'OPEN_QUOTE_SYMBOL' => $lsquo, |
| 'CLOSE_QUOTE_SYMBOL' => $rsquo, |
| ); |
| |
| my @docbook_image_extensions |
| = ('eps', 'gif', 'jpg', 'jpeg', 'pdf', 'png', 'svg'); |
| |
| my %docbook_special_quotations; |
| foreach my $special_quotation ( |
| 'caution', 'important', 'note', 'tip', 'warning') { |
| $docbook_special_quotations{$special_quotation} = 1; |
| } |
| |
| # For '*', there is no line break in DocBook, except in cmdsynopsis (in this |
| # case it is <sbr>). But currently we don't use cmdsynopsis, and it is |
| # unlikely we ever will. |
| my %docbook_specific_formatting = ( |
| 'TeX' => '&tex;', |
| 'LaTeX' => '&latex;', |
| "\t" => $nbsp, |
| "\n" => $nbsp, |
| " " => $nbsp, |
| 'tie' => $nbsp, |
| ); |
| my %docbook_commands_formatting |
| = %{$Texinfo::Convert::Converter::default_xml_commands_formatting{'normal'}}; |
| |
| foreach my $command (keys(%Texinfo::Convert::Unicode::unicode_entities)) { |
| $docbook_commands_formatting{$command} |
| = $Texinfo::Convert::Unicode::unicode_entities{$command}; |
| } |
| |
| foreach my $command (keys(%docbook_specific_formatting)) { |
| $docbook_commands_formatting{$command} |
| = $docbook_specific_formatting{$command}; |
| } |
| |
| my %quoted_style_commands = ( |
| 'samp' => 1, |
| ); |
| |
| # FIXME allow customization? (as in HTML) |
| my %upper_case_style_commands = ( |
| 'sc' => 1, |
| ); |
| |
| my @inline_elements = ('emphasis', 'abbrev', 'acronym', 'link', |
| 'inlinemediaobject', 'firstterm', 'footnote', 'replaceable', |
| 'subscript', 'superscript', 'wordasword'); |
| my %inline_elements; |
| foreach my $inline_element (@inline_elements) { |
| $inline_elements{$inline_element} = 1; |
| }; |
| |
| my %style_attribute_commands; |
| %style_attribute_commands = ( |
| 'b' => 'emphasis role="bold"', |
| 'cite' => 'citetitle', |
| 'code' => 'literal', |
| 'command' => 'command', |
| 'dfn' => 'firstterm', |
| 'emph' => 'emphasis', |
| 'env' => 'envar', |
| 'file' => 'filename', |
| 'footnote' => 'footnote', |
| 'headitemfont' => 'emphasis role="bold"', # actually <th> instead of <td> |
| 'i' => 'emphasis', |
| 'indicateurl' => 'literal', |
| 'kbd' => 'userinput', |
| 'key' => 'keycap', |
| 'math' => 'mathphrase', |
| 'option' => 'option', |
| 'r' => '', |
| 'samp' => 'literal', |
| 'sansserif' => '', |
| 'strong' => 'emphasis role="bold"', |
| 'sub' => 'subscript', |
| 'sup' => 'superscript', |
| 't' => 'literal', |
| 'var' => 'replaceable', |
| 'verb' => 'literal', |
| ); |
| |
| # this weird construct does like uniq, it avoids duplicates. |
| # it may be required since math is not in the %style_commands as it is |
| # in context command. |
| my @all_style_commands = keys %{{ map { $_ => 1 } |
| (keys(%Texinfo::Common::style_commands), keys(%style_attribute_commands), |
| 'w', 'dmn', 'titlefont') }}; |
| # special string for 'w'. |
| my $w_command_mark = '<!-- /@w -->'; |
| |
| my %style_commands_formatting; |
| foreach my $command(@all_style_commands) { |
| $style_commands_formatting{$command} = {}; |
| if ($style_attribute_commands{$command}) { |
| $style_commands_formatting{$command}->{'attribute'} |
| = $style_attribute_commands{$command}; |
| } |
| if ($quoted_style_commands{$command}) { |
| $style_commands_formatting{$command}->{'quote'} = 1; |
| } |
| if ($upper_case_style_commands{$command}) { |
| $style_commands_formatting{$command}->{'upper_case'} = 1; |
| } |
| } |
| |
| my %docbook_misc_elements_with_arg_map = ( |
| 'settitle' => 'title', |
| 'exdent' => 'simpara', |
| 'center' => '', |
| ); |
| |
| my %docbook_misc_commands = %Texinfo::Common::misc_commands; |
| |
| foreach my $command ('item', 'headitem', 'itemx', 'tab', |
| keys %Texinfo::Common::def_commands) { |
| delete $docbook_misc_commands{$command}; |
| } |
| |
| my %docbook_global_commands = ( |
| 'documentencoding' => 1, |
| 'documentlanguage' => 1, |
| ); |
| |
| sub converter_global_commands($) |
| { |
| return keys(%docbook_global_commands); |
| } |
| |
| my %default_args_code_style |
| = %Texinfo::Convert::Converter::default_args_code_style; |
| my %regular_font_style_commands = %Texinfo::Common::regular_font_style_commands; |
| |
| my %defcommand_name_type = ( |
| 'defcv' => 'property', |
| 'deffn' => 'function', |
| 'defop' => 'methodname', |
| 'deftp' => 'structname', |
| 'deftypecv' => 'property', |
| 'deftypefn' => 'function', |
| 'deftypeop' => 'methodname', |
| 'deftypevr' => 'varname', |
| 'defvr' => 'varname', |
| ); |
| |
| my %def_argument_types_docbook = ( |
| 'type' => ['returnvalue'], |
| 'class' => ['ooclass', 'classname'], |
| 'arg' => ['replaceable'], |
| 'typearg' => ['type'], |
| ); |
| |
| |
| my %ignored_types; |
| foreach my $type ( |
| 'empty_line_after_command', |
| 'empty_space_at_end_def_bracketed', |
| 'empty_spaces_after_close_brace', |
| 'empty_spaces_after_command', |
| 'empty_spaces_before_argument', |
| 'empty_spaces_before_paragraph', |
| 'menu_entry_leading_text', |
| 'menu_entry_separator', |
| 'preamble', |
| 'preamble_before_setfilename', |
| 'spaces_at_end', |
| ) { |
| $ignored_types{$type} = 1; |
| } |
| |
| my %type_elements = ( |
| 'paragraph' => 'para', |
| 'table_item' => 'listitem', |
| 'table_entry' => 'varlistentry', |
| 'row' => 'row', |
| 'multitable_head' => 'thead', |
| 'multitable_body' => 'tbody', |
| # Unfortunatly there does not seem to be anything better in DocBook. |
| 'def_item' => 'blockquote', |
| ); |
| |
| my %default_context_block_commands = ( |
| 'float' => 1, |
| ); |
| |
| my %docbook_preformatted_formats = ( |
| # command |
| 'example' => 'screen', |
| 'smallexample' => 'screen', |
| 'display' => 'literallayout', |
| 'smalldisplay' => 'literallayout', |
| 'lisp' => 'programlisting', |
| 'smalllisp' => 'programlisting', |
| 'format' => 'abstract', |
| 'smallformat' => 'screen', |
| # type |
| 'menu_comment' => 'literallayout', |
| 'menu_description' => 'literallayout', |
| ); |
| |
| sub converter_defaults($$) |
| { |
| return %defaults; |
| } |
| |
| |
| sub converter_initialize($) |
| { |
| my $self = shift; |
| |
| $self->{'document_context'} = [{'monospace' => [0], 'upper_case' => [0]}]; |
| $self->{'context_block_commands'} = {%default_context_block_commands}; |
| foreach my $raw (keys (%Texinfo::Common::format_raw_commands)) { |
| $self->{'context_block_commands'}->{$raw} = 1 |
| if $self->{'expanded_formats_hash'}->{$raw}; |
| } |
| } |
| |
| sub convert($$;$) |
| { |
| my $self = shift; |
| my $root = shift; |
| my $fh = shift; |
| |
| $self->_set_global_multiple_commands(-1); |
| return $self->convert_document_sections($root, $fh); |
| } |
| |
| sub convert_tree($$) |
| { |
| my $self = shift; |
| my $root = shift; |
| |
| return $self->_convert($root); |
| } |
| |
| sub output($$) |
| { |
| my $self = shift; |
| my $root = shift; |
| |
| $self->_set_outfile(); |
| return undef unless $self->_create_destination_directory(); |
| |
| my $fh; |
| if (! $self->{'output_file'} eq '') { |
| $fh = $self->Texinfo::Common::open_out ($self->{'output_file'}); |
| if (!$fh) { |
| $self->document_error(sprintf($self->__("could not open %s for writing: %s"), |
| $self->{'output_file'}, $!)); |
| return undef; |
| } |
| } |
| |
| $self->_set_global_multiple_commands(-1); |
| |
| my $encoding = ''; |
| if ($self->get_conf('OUTPUT_ENCODING_NAME') |
| and $self->get_conf('OUTPUT_ENCODING_NAME') ne 'utf-8') { |
| $encoding = " encoding=\"".$self->get_conf('OUTPUT_ENCODING_NAME')."\" "; |
| } |
| |
| my $id; |
| if ($self->{'output_file'} ne '') { |
| my $output_filename = $self->{'output_filename'}; |
| $id = " id=\"".$self->xml_protect_text($output_filename)."\""; |
| } else { |
| $id = ''; |
| } |
| |
| my $header = "<?xml version=\"1.0\"${encoding}?>".' |
| <!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN" "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd" [ |
| <!ENTITY tex "TeX"> |
| <!ENTITY latex "LaTeX"> |
| ]> |
| '. "<book${id} lang=\"".$self->get_conf('documentlanguage') ."\">\n"; |
| |
| my $result = ''; |
| $result .= $self->_output_text($header, $fh); |
| $result .= $self->convert_document_sections($root, $fh); |
| $result .= $self->_output_text("</book>\n", $fh); |
| if ($fh and $self->{'output_file'} ne '-') { |
| $self->register_close_file($self->{'output_file'}); |
| if (!close ($fh)) { |
| $self->document_error(sprintf($self->__("error on closing %s: %s"), |
| $self->{'output_file'}, $!)); |
| } |
| } |
| return $result; |
| } |
| |
| my %docbook_sections = ( |
| 'top' => 'chapter', |
| 'part' => 'part', |
| 'chapter' => 'chapter', |
| 'unnumbered' => 'chapter', |
| 'centerchap' => 'chapter', |
| 'appendix' => 'appendix', |
| 'majorheading' => 'other', |
| 'chapheading' => 'other', |
| 'heading' => 'sect1', |
| 'subheading' => 'sect2', |
| 'subsubheading' => 'sect3', |
| 2 => 'sect1', |
| 3 => 'sect2', |
| 4 => 'sect3' |
| ); |
| |
| my %docbook_special_unnumbered; |
| foreach my $special_unnumbered ('acknowledgements', 'colophon', |
| 'dedication', 'preface') { |
| $docbook_special_unnumbered{$special_unnumbered} = 1; |
| } |
| |
| sub _docbook_section_element($$) |
| { |
| my $self = shift; |
| my $root = shift; |
| my $heading_level = $root->{'level'}; |
| if (exists $docbook_sections{$heading_level}) { |
| return $docbook_sections{$heading_level}; |
| } |
| my $command = $self->_level_corrected_section($root); |
| if ($command eq 'unnumbered' |
| and $root->{'extra'}->{'associated_node'} |
| and $root->{'extra'}->{'associated_node'}->{'extra'}->{'normalized'} |
| and $docbook_special_unnumbered{lc($root->{'extra'}->{'associated_node'}->{'extra'}->{'normalized'})}) { |
| return lc($root->{'extra'}->{'associated_node'}->{'extra'}->{'normalized'}); |
| } |
| |
| return $docbook_sections{$command}; |
| } |
| |
| sub _index_entry($$) |
| { |
| my $self = shift; |
| my $root = shift; |
| if ($root->{'extra'} and $root->{'extra'}->{'index_entry'}) { |
| my $index_entry = $root->{'extra'}->{'index_entry'}; |
| # FIXME DocBook 5 role->type |
| my $result = "<indexterm role=\"$index_entry->{'index_name'}\"><primary>"; |
| push @{$self->{'document_context'}}, {'monospace' => [0], 'upper_case' => [0]}; |
| $self->{'document_context'}->[-1]->{'monospace'}->[-1] = 1 |
| if ($index_entry->{'in_code'}); |
| $result .= $self->_convert({'contents' => $index_entry->{'content'}}); |
| pop @{$self->{'document_context'}}; |
| return $result ."</primary></indexterm>" |
| } |
| return ''; |
| } |
| |
| sub docbook_accent($$$;$) |
| { |
| my $self = shift; |
| my $text = shift; |
| my $command = shift; |
| my $in_upper_case = shift; |
| my $accent = $command->{'cmdname'}; |
| |
| if ($in_upper_case and $text =~ /^\w$/) { |
| $text = uc ($text); |
| } |
| if (exists($Texinfo::Convert::Unicode::unicode_accented_letters{$accent}) |
| and exists($Texinfo::Convert::Unicode::unicode_accented_letters{$accent}->{$text})) { |
| return '&#' . |
| hex($Texinfo::Convert::Unicode::unicode_accented_letters{$accent}->{$text}). ';'; |
| } |
| # FIXME it is not possible to call xml_protect_text since what is in $text |
| # may already be xml. But this means that each time ascii_accent changes |
| # it should be changed here too. |
| return $text . '<' if ($accent eq 'v'); |
| return Texinfo::Convert::Text::ascii_accent($text, $command); |
| } |
| |
| sub _parse_attribute($) |
| { |
| my $element = shift; |
| return ('', '') if (!defined($element)); |
| my $attributes = ''; |
| if ($element =~ /^(\w+)(\s+.*)/) { |
| $element = $1; |
| $attributes = $2; |
| } |
| return ($element, $attributes); |
| } |
| |
| sub _protect_text($$) |
| { |
| my $self = shift; |
| my $text = shift; |
| my $result = $self->xml_protect_text($text); |
| # form feed not allowed in XML |
| $result =~ s/\f/ /g; |
| return $result; |
| } |
| |
| |
| sub _convert($$;$); |
| |
| sub _convert($$;$) |
| { |
| my $self = shift; |
| my $root = shift; |
| |
| #if (1) { |
| if (0) { # too verbose even for debugging, but for the bottom line ... |
| warn "root\n"; |
| warn " Command: $root->{'cmdname'}\n" if ($root->{'cmdname'}); |
| warn " Type: $root->{'type'}\n" if ($root->{'type'}); |
| warn " Text: $root->{'text'}\n" if (defined($root->{'text'})); |
| #warn " Special def_command: $root->{'extra'}->{'def_command'}\n" |
| # if (defined($root->{'extra'}) and $root->{'extra'}->{'def_command'}); |
| } |
| |
| return '' if ($root->{'type'} and $ignored_types{$root->{'type'}}); |
| my $result = ''; |
| if (defined($root->{'text'})) { |
| if (defined($root->{'type'}) and $root->{'type'} eq '_converted') { |
| return $root->{'text'}; |
| } elsif ($self->{'document_context'}->[-1]->{'raw'}) { |
| return $root->{'text'}; |
| } |
| $result = $root->{'text'}; |
| if ($self->{'document_context'}->[-1]->{'upper_case'}->[-1]) { |
| $result = uc($result); |
| } |
| $result = $self->_protect_text($result); |
| if (! defined($root->{'type'}) or $root->{'type'} ne 'raw') { |
| if (!$self->{'document_context'}->[-1]->{'monospace'}->[-1]) { |
| $result =~ s/``/$ldquo/g; |
| $result =~ s/\'\'/$rdquo/g; |
| $result =~ s/`/$lsquo/g; |
| $result =~ s/\'/$rsquo/g; |
| $result =~ s/---/$mdash/g; |
| $result =~ s/--/$ndash/g; |
| } |
| } |
| #warn "had text `$root->{'text'}', returning $result\n"; |
| return $result; |
| } |
| |
| #warn " onto main conditional\n"; |
| my @close_elements; |
| if ($root->{'cmdname'}) { |
| #warn " got cmdname $root->{'cmdname'}\n"; |
| if (defined($docbook_commands_formatting{$root->{'cmdname'}})) { |
| #warn " has commands_formatting \n"; |
| my $command; |
| if ($root->{'cmdname'} eq 'click' |
| and $root->{'extra'} |
| and defined($root->{'extra'}->{'clickstyle'})) { |
| $command = $root->{'extra'}->{'clickstyle'}; |
| } elsif ($self->{'document_context'}->[-1]->{'upper_case'}->[-1] |
| and $Texinfo::Common::letter_no_arg_commands{$root->{'cmdname'}} |
| and $Texinfo::Common::letter_no_arg_commands{uc($root->{'cmdname'})}) { |
| $command = uc($root->{'cmdname'}) |
| } else { |
| $command = $root->{'cmdname'}; |
| } |
| if ($self->{'translated_commands'}->{$command}) { |
| return $self->_convert(Texinfo::Common::translated_command_tree($self, |
| $command)); |
| } else { |
| return $docbook_commands_formatting{$command}; |
| } |
| } elsif ($root->{'cmdname'} eq 'today') { |
| return $self->_convert(Texinfo::Common::expand_today($self)); |
| } elsif ($Texinfo::Common::accent_commands{$root->{'cmdname'}}) { |
| return $self->convert_accents($root, \&docbook_accent, |
| $self->{'document_context'}->[-1]->{'upper_case'}->[-1]); |
| } elsif ($root->{'cmdname'} eq 'item' or $root->{'cmdname'} eq 'itemx' |
| or $root->{'cmdname'} eq 'headitem' or $root->{'cmdname'} eq 'tab') { |
| if ($root->{'cmdname'} eq 'item' |
| and $root->{'parent'}->{'cmdname'} |
| and ($root->{'parent'}->{'cmdname'} eq 'itemize' |
| or $root->{'parent'}->{'cmdname'} eq 'enumerate')) { |
| $result .= "<listitem>"; |
| if ($root->{'parent'}->{'cmdname'} eq 'itemize' |
| and $root->{'parent'}->{'extra'} |
| and !($root->{'parent'}->{'extra'}->{'command_as_argument'} |
| and $root->{'parent'}->{'extra'}->{'command_as_argument'}->{'cmdname'} eq 'bullet') |
| and $root->{'parent'}->{'extra'}->{'block_command_line_contents'} |
| and $root->{'parent'}->{'extra'}->{'block_command_line_contents'}->[0]) { |
| # $result .= $self->_convert({'contents' |
| # => $root->{'parent'}->{'extra'}->{'block_command_line_contents'}->[0]}) |
| # ." "; |
| $self->{'pending_prepend'} = $self->_convert( |
| {'contents' => $root->{'parent'}->{'extra'} |
| ->{'block_command_line_contents'}->[0]}) . " "; |
| } |
| push @close_elements, 'listitem'; |
| } elsif (($root->{'cmdname'} eq 'item' or $root->{'cmdname'} eq 'itemx') |
| and $root->{'parent'}->{'type'} |
| and $root->{'parent'}->{'type'} eq 'table_term') { |
| |
| my $converted_tree = $self->_table_item_content_tree($root, |
| $root->{'extra'}->{'misc_content'}); |
| |
| $result .= "<term>"; |
| $result .= $self->_index_entry($root); |
| $result .= $self->_convert($converted_tree); |
| chomp ($result); |
| $result .= "\n"; |
| $result .= "</term>"; |
| } else { |
| unless (($root->{'cmdname'} eq 'item' |
| or $root->{'cmdname'} eq 'headitem' |
| or $root->{'cmdname'} eq 'tab') |
| and $root->{'parent'}->{'type'} |
| and $root->{'parent'}->{'type'} eq 'row') { |
| warn "BUG: multitable cell command not in a row " |
| .Texinfo::Parser::_print_current($root); |
| } |
| |
| $result .= "<entry>"; |
| push @close_elements, 'entry'; |
| } |
| } elsif ($root->{'type'} and $root->{'type'} eq 'index_entry_command') { |
| my $end_line; |
| if ($root->{'args'}->[0]) { |
| $end_line = $self->_end_line_or_comment($root->{'args'}->[0]->{'contents'}); |
| if ($self->{'document_context'}->[-1]->{'in_preformatted'}) { |
| chomp($end_line); |
| } |
| } else { |
| # May that happen? |
| $end_line = ''; |
| } |
| return $self->_index_entry($root).${end_line}; |
| |
| } elsif (exists($docbook_misc_commands{$root->{'cmdname'}})) { |
| #warn " is dbk misc command\n"; |
| if ($docbook_global_commands{$root->{'cmdname'}}) { |
| $self->_informative_command($root); |
| return ''; |
| } |
| my $command; |
| if (exists ($docbook_misc_elements_with_arg_map{$root->{'cmdname'}})) { |
| $command = $docbook_misc_elements_with_arg_map{$root->{'cmdname'}}; |
| } |
| my $type = $docbook_misc_commands{$root->{'cmdname'}}; |
| if ($type eq 'text') { |
| if ($root->{'cmdname'} eq 'verbatiminclude') { |
| my $verbatim_include_verbatim |
| = Texinfo::Common::expand_verbatiminclude($self, $root); |
| if (defined($verbatim_include_verbatim)) { |
| $result .= $self->_convert($verbatim_include_verbatim); |
| } else { |
| return ''; |
| } |
| } else { |
| return ''; |
| } |
| } elsif ($type eq 'line') { |
| if ($root->{'cmdname'} eq 'node') { |
| if ($root->{'extra'} and !$root->{'extra'}->{'associated_section'} |
| and defined($root->{'extra'}->{'normalized'})) { |
| $result .= "<anchor id=\"$root->{'extra'}->{'normalized'}\"/>\n"; |
| } |
| } elsif ($Texinfo::Common::root_commands{$root->{'cmdname'}}) { |
| my $attribute = ''; |
| # FIXME it is not clear that a label should be set for |
| # @appendix* or @chapter/@*section as the formatter should be |
| # able to figure it out. For @unnumbered or if ! NUMBER_SECTIONS |
| # having a label (empty) is important. |
| my $label = ''; |
| if (defined($root->{'number'}) |
| and ($self->get_conf('NUMBER_SECTIONS') |
| or !defined($self->get_conf('NUMBER_SECTIONS')))) { |
| # Looking at docbook2html output, Appendix is appended in the |
| # section title, so only the letter is used. |
| $label = $root->{'number'}; |
| } |
| $command = $self->_docbook_section_element($root); |
| if (! $docbook_special_unnumbered{$command}) { |
| $attribute = " label=\"$label\""; |
| } |
| if ($root->{'extra'} and $root->{'extra'}->{'associated_node'}) { |
| $attribute .= " id=\"$root->{'extra'}->{'associated_node'}->{'extra'}->{'normalized'}\""; |
| } |
| $result .= "<$command${attribute}>\n"; |
| if ($root->{'args'} and $root->{'args'}->[0]) { |
| my ($arg, $end_line) |
| = $self->_convert_argument_and_end_line($root->{'args'}->[0]); |
| $result .= "<title>$arg</title>$end_line"; |
| chomp ($result); |
| $result .= "\n"; |
| } |
| if ($command eq 'part' and !Texinfo::Common::is_content_empty($root)) { |
| $result .= "<partintro>\n"; |
| } |
| } elsif ($Texinfo::Common::sectioning_commands{$root->{'cmdname'}}) { |
| if ($root->{'args'} and $root->{'args'}->[0]) { |
| my ($arg, $end_line) |
| = $self->_convert_argument_and_end_line($root->{'args'}->[0]); |
| $result .= |
| "<bridgehead renderas=\"$docbook_sections{$root->{'cmdname'}}\">$arg</bridgehead>$end_line"; |
| chomp ($result); |
| $result .= "\n"; |
| return $result; |
| } |
| return ''; |
| } else { |
| my $attribute = ''; |
| if (defined($command)) { |
| my ($arg, $end_line) |
| = $self->_convert_argument_and_end_line($root->{'args'}->[0]); |
| if ($command eq '') { |
| $result .= "$arg$end_line"; |
| } else { |
| $result .= "<$command${attribute}>$arg</$command>$end_line"; |
| } |
| chomp ($result); |
| $result .= "\n"; |
| return $result; |
| } |
| # misc commands not handled especially are ignored here. |
| return ''; |
| } |
| } elsif ($type eq 'skipline' or $type eq 'noarg') { |
| if ($root->{'cmdname'} eq 'insertcopying') { |
| if ($self->{'extra'} and $self->{'extra'}->{'copying'}) { |
| return $self->_convert({'contents' |
| => $self->{'extra'}->{'copying'}->{'contents'}}); |
| } else { |
| return ''; |
| } |
| } else { |
| return ''; |
| } |
| } elsif ($type eq 'special' or $type eq 'skipspace') { |
| return ''; |
| } elsif ($type eq 'lineraw') { |
| if ($root->{'cmdname'} eq 'c' or $root->{'cmdname'} eq 'comment') { |
| return $self->xml_comment($root->{'args'}->[0]->{'text'}) |
| } else { |
| return ""; |
| } |
| } else { |
| $self->_bug_message("unknown misc_command style $type", $root) |
| if ($type !~ /^\d$/); |
| if ($root->{'cmdname'} eq 'printindex') { |
| if (defined($root->{'extra'}) |
| and defined($root->{'extra'}->{'misc_args'})) { |
| # FIXME DocBook 5 |
| #return "<index type=\"$root->{'extra'}->{'misc_args'}->[0]\"></index>\n"; |
| return "<index role=\"$root->{'extra'}->{'misc_args'}->[0]\"></index>\n"; |
| } else { |
| return "<index></index>\n"; |
| } |
| } else { |
| return ''; |
| } |
| } |
| |
| } elsif ($root->{'type'} |
| and $root->{'type'} eq 'definfoenclose_command') { |
| my $in_monospace_not_normal; |
| if (defined($default_args_code_style{$root->{'cmdname'}}) |
| and $default_args_code_style{$root->{'cmdname'}}->[0]) { |
| $in_monospace_not_normal = 1; |
| } elsif ($regular_font_style_commands{$root->{'cmdname'}}) { |
| $in_monospace_not_normal = 0; |
| } |
| push @{$self->{'document_context'}->[-1]->{'monospace'}}, |
| $in_monospace_not_normal |
| if (defined($in_monospace_not_normal)); |
| my $arg = $self->_convert($root->{'args'}->[0]); |
| $result .= $self->xml_protect_text($root->{'extra'}->{'begin'}).$arg |
| .$self->xml_protect_text($root->{'extra'}->{'end'}); |
| pop @{$self->{'document_context'}->[-1]->{'monospace'}} |
| if (defined($in_monospace_not_normal)); |
| |
| |
| } elsif ($root->{'args'} |
| and exists($Texinfo::Common::brace_commands{$root->{'cmdname'}})) { |
| #debug_list (" brace command with args", $root->{'args'}); |
| if ($style_commands_formatting{$root->{'cmdname'}}) { |
| if ($Texinfo::Common::context_brace_commands{$root->{'cmdname'}}) { |
| push (@{$self->{'document_context'}}, |
| {'monospace' => [0], 'upper_case' => [0]}); |
| } |
| my $formatting = $style_commands_formatting{$root->{'cmdname'}}; |
| |
| my $in_monospace_not_normal; |
| if (defined($default_args_code_style{$root->{'cmdname'}}) |
| and $default_args_code_style{$root->{'cmdname'}}->[0]) { |
| $in_monospace_not_normal = 1; |
| } elsif ($regular_font_style_commands{$root->{'cmdname'}}) { |
| $in_monospace_not_normal = 0; |
| } |
| if ($formatting->{'upper_case'}) { |
| push @{$self->{'document_context'}->[-1]->{'upper_case'}}, 1; |
| } |
| push @{$self->{'document_context'}->[-1]->{'monospace'}}, |
| $in_monospace_not_normal |
| if (defined($in_monospace_not_normal)); |
| |
| my ($style, $attribute_text) = _parse_attribute($formatting->{'attribute'}); |
| my $result = $self->_convert($root->{'args'}->[0]); |
| if ($style ne '' and (!$self->{'document_context'}->[-1]->{'inline'} |
| or $inline_elements{$style})) { |
| $result = "<$style${attribute_text}>$result</$style>"; |
| if ($root->{'cmdname'} eq 'math') { |
| $result = "<inlineequation>$result</inlineequation>"; |
| } |
| } |
| if (defined($formatting->{'quote'})) { |
| $result = $self->get_conf('OPEN_QUOTE_SYMBOL') . $result |
| . $self->get_conf('CLOSE_QUOTE_SYMBOL'); |
| } |
| if (defined($formatting->{'upper_case'})) { |
| pop @{$self->{'document_context'}->[-1]->{'upper_case'}}; |
| } |
| pop @{$self->{'document_context'}->[-1]->{'monospace'}} |
| if (defined($in_monospace_not_normal)); |
| if ($Texinfo::Common::context_brace_commands{$root->{'cmdname'}}) { |
| pop @{$self->{'document_context'}}; |
| } |
| if ($root->{'cmdname'} eq 'w') { |
| $result .= $w_command_mark; |
| } |
| return $result; |
| } elsif ($root->{'cmdname'} eq 'anchor') { |
| if ($root->{'extra'} and defined($root->{'extra'}->{'normalized'})) { |
| return "<anchor id=\"$root->{'extra'}->{'normalized'}\"/>"; |
| } else { |
| return ''; |
| } |
| } elsif ($Texinfo::Common::ref_commands{$root->{'cmdname'}}) { |
| if ($root->{'extra'} and $root->{'extra'}->{'brace_command_contents'}) { |
| if ($root->{'cmdname'} eq 'inforef') { |
| my $filename; |
| if (scalar (@{$root->{'extra'}->{'brace_command_contents'}}) == 3 |
| and defined($root->{'extra'}->{'brace_command_contents'}->[-1])) { |
| $filename |
| = $self->xml_protect_text(Texinfo::Convert::Text::convert( |
| {'contents' => $root->{'extra'}->{'brace_command_contents'}->[-1]}, |
| {'code' => 1, Texinfo::Common::_convert_text_options($self)})); |
| } |
| my $node; |
| if (defined($root->{'extra'}->{'brace_command_contents'}->[0])) { |
| $node = {'contents' |
| => $root->{'extra'}->{'brace_command_contents'}->[0]}; |
| } |
| if ($node and defined($filename)) { |
| return $self->_convert($self->gdt( |
| "See Info file \@file{{myfile}}, node \@samp{{mynode}}", |
| { 'myfile' => {'type' => '_converted', 'text' => $filename}, |
| 'mynode' => $node})); |
| } elsif ($node) { |
| return $self->_convert($self->gdt( |
| "See node \@samp{{mynode}}", |
| {'mynode' => $node})); |
| } elsif (defined($filename)) { |
| return $self->_convert($self->gdt( |
| "See Info file \@file{{myfile}}", |
| { 'myfile' => {'type' => '_converted', 'text' => $filename}})); |
| } |
| #my $name; |
| #if (scalar (@{$root->{'extra'}->{'brace_command_contents'}}) >= 2 |
| # and defined($root->{'extra'}->{'brace_command_contents'}->[1])) { |
| # $name = $self->_convert({'contents' |
| # => $root->{'extra'}->{'brace_command_contents'}->[0]}); |
| #} |
| } else { |
| my $book_contents; |
| if (scalar (@{$root->{'extra'}->{'brace_command_contents'}}) == 5 |
| and defined($root->{'extra'}->{'brace_command_contents'}->[-1])) { |
| $book_contents = $root->{'extra'}->{'brace_command_contents'}->[-1]; |
| } |
| my $manual_file_contents; |
| if (scalar (@{$root->{'extra'}->{'brace_command_contents'}}) >= 4 |
| and defined($root->{'extra'}->{'brace_command_contents'}->[3])) { |
| $manual_file_contents = $root->{'extra'}->{'brace_command_contents'}->[3]; |
| } |
| my $section_name_contents; |
| if (defined($root->{'extra'}->{'brace_command_contents'}->[2])) { |
| $section_name_contents |
| = $root->{'extra'}->{'brace_command_contents'}->[2]; |
| } elsif (defined($root->{'extra'}->{'brace_command_contents'}->[1])) { |
| $section_name_contents |
| = $root->{'extra'}->{'brace_command_contents'}->[1]; |
| } elsif (defined($root->{'extra'}->{'brace_command_contents'}->[0]) |
| and (!$book_contents |
| or $root->{'extra'}->{'node_argument'}->{'manual_content'} |
| or $root->{'extra'}->{'node_argument'}->{'normalized'} ne 'Top')) { |
| $section_name_contents |
| = $root->{'extra'}->{'brace_command_contents'}->[0]; |
| } |
| # external ref |
| if ($book_contents or $manual_file_contents) { |
| return '' if (!$book_contents); |
| if ($section_name_contents) { |
| if ($root->{'cmdname'} eq 'ref') { |
| return $self->_convert( |
| $self->gdt('section ``{section_name}\'\' in @cite{{book}}', |
| { 'section_name' => {'contents' => $section_name_contents}, |
| 'book' => $book_contents })); |
| } elsif ($root->{'cmdname'} eq 'xref') { |
| return $self->_convert( |
| $self->gdt('See section ``{section_name}\'\' in @cite{{book}}', |
| { 'section_name' => {'contents' => $section_name_contents}, |
| 'book' => $book_contents })); |
| } elsif ($root->{'cmdname'} eq 'pxref') { |
| return $self->_convert( |
| $self->gdt('see section ``{section_name}\'\' in @cite{{book}}', |
| { 'section_name' => {'contents' => $section_name_contents}, |
| 'book' => $book_contents })); |
| } |
| } else { |
| if ($root->{'cmdname'} eq 'ref') { |
| return $self->_convert( |
| $self->gdt('@cite{{book}}', |
| {'book' => $book_contents })); |
| } elsif ($root->{'cmdname'} eq 'xref') { |
| return $self->_convert( |
| $self->gdt('See @cite{{book}}', |
| {'book' => $book_contents })); |
| } elsif ($root->{'cmdname'} eq 'pxref') { |
| return $self->_convert( |
| $self->gdt('see @cite{{book}}', |
| {'book' => $book_contents })); |
| } |
| } |
| } else { |
| my $linkend = ''; |
| if ($root->{'extra'}->{'node_argument'} |
| and defined($root->{'extra'}->{'node_argument'}->{'normalized'}) |
| and !$root->{'extra'}->{'node_argument'}->{'manual_content'}) { |
| $linkend = " linkend=\"$root->{'extra'}->{'node_argument'}->{'normalized'}\""; |
| } |
| my $argument = "<link${linkend}>".$self->_convert({'contents' => |
| $section_name_contents}) ."</link>"; |
| if ($root->{'cmdname'} eq 'ref') { |
| return $self->_convert( |
| $self->gdt('{title_ref}', {'title_ref' => |
| {'type' => '_converted', |
| 'text' => $argument}})); |
| } elsif ($root->{'cmdname'} eq 'xref') { |
| return $self->_convert( |
| $self->gdt('See {title_ref}', {'title_ref' => |
| {'type' => '_converted', |
| 'text' => $argument}})); |
| } elsif ($root->{'cmdname'} eq 'pxref') { |
| return $self->_convert( |
| $self->gdt('see {title_ref}', {'title_ref' => |
| {'type' => '_converted', |
| 'text' => $argument}})); |
| } |
| } |
| } |
| } else { |
| return ''; |
| } |
| } elsif ($root->{'cmdname'} eq 'image') { |
| if (defined($root->{'extra'}->{'brace_command_contents'}->[0])) { |
| my $basefile = Texinfo::Convert::Text::convert( |
| {'contents' => $root->{'extra'}->{'brace_command_contents'}->[0]}, |
| {'code' => 1, Texinfo::Common::_convert_text_options($self)}); |
| my $element; |
| my $is_inline = $self->_is_inline($root); |
| if ($is_inline) { |
| $result .= "<inlinemediaobject>"; |
| } else { |
| $result .= "<informalfigure><mediaobject>"; |
| } |
| my @files; |
| foreach my $extension (@docbook_image_extensions) { |
| if ($self->Texinfo::Common::locate_include_file ("$basefile.$extension")) { |
| push @files, ["$basefile.$extension", uc($extension)]; |
| } |
| } |
| my $image_file_found = scalar(@files);; |
| if (!$image_file_found) { |
| push @files, ["$basefile.jpg", 'JPG']; |
| } |
| foreach my $file (@files) { |
| $result .= "<imageobject><imagedata fileref=\"" |
| .$self->xml_protect_text($file->[0]) |
| ."\" format=\"$file->[1]\"></imagedata></imageobject>"; |
| } |
| my ($image_text, $image_width) |
| = $self->Texinfo::Convert::Plaintext::_image_text($root, $basefile); |
| if (defined($image_text)) { |
| $result .= "<textobject><literallayout>" |
| .$self->_protect_text($image_text) |
| .'</literallayout></textobject>'; |
| } |
| if (!defined($image_text) and !$image_file_found) { |
| $self->line_warn(sprintf( |
| $self->__("\@image file `%s' not found, using `%s'"), |
| $basefile, "$basefile.jpg"), $root->{'line_nr'}); |
| } |
| |
| if ($is_inline) { |
| $result .= "</inlinemediaobject>"; |
| } else { |
| $result .= "</mediaobject></informalfigure>"; |
| } |
| } |
| } elsif ($root->{'cmdname'} eq 'email') { |
| if ($root->{'extra'} and $root->{'extra'}->{'brace_command_contents'}) { |
| my $name; |
| my $email; |
| my $email_text; |
| if (scalar (@{$root->{'extra'}->{'brace_command_contents'}}) == 2 |
| and defined($root->{'extra'}->{'brace_command_contents'}->[-1])) { |
| $name = $root->{'extra'}->{'brace_command_contents'}->[1]; |
| } |
| if (defined($root->{'extra'}->{'brace_command_contents'}->[0])) { |
| $email = $root->{'extra'}->{'brace_command_contents'}->[0]; |
| $email_text |
| = $self->_protect_text(Texinfo::Convert::Text::convert( |
| {'contents' => $email}, |
| {'code' => 1, |
| Texinfo::Common::_convert_text_options($self)})); |
| } |
| if ($name and $email) { |
| return "<ulink url=\"mailto:$email_text\">" |
| .$self->_convert({'contents' => $name}).'</ulink>'; |
| } elsif ($email) { |
| return "<email>$email_text</email>"; |
| } elsif ($name) { |
| return $self->_convert({'contents' => $name}); |
| } |
| } else { |
| return ''; |
| } |
| |
| } elsif ($root->{'cmdname'} eq 'uref' or $root->{'cmdname'} eq 'url') { |
| if ($root->{'extra'} and $root->{'extra'}->{'brace_command_contents'}) { |
| my ($url_text, $url_content); |
| if (defined($root->{'extra'}->{'brace_command_contents'}->[0])) { |
| $url_content = $root->{'extra'}->{'brace_command_contents'}->[0]; |
| $url_text = $self->_protect_text(Texinfo::Convert::Text::convert( |
| {'contents' => $url_content}, |
| {'code' => 1, |
| Texinfo::Common::_convert_text_options($self)})); |
| } else { |
| $url_text = ''; |
| } |
| my $replacement; |
| if (scalar(@{$root->{'extra'}->{'brace_command_contents'}}) >= 2 |
| and defined($root->{'extra'}->{'brace_command_contents'}->[1])) { |
| $replacement = $self->_convert({'contents' |
| => $root->{'extra'}->{'brace_command_contents'}->[1]}); |
| } |
| if (!defined($replacement) or $replacement eq '') { |
| if (scalar(@{$root->{'extra'}->{'brace_command_contents'}}) == 3 |
| and defined($root->{'extra'}->{'brace_command_contents'}->[2])) { |
| $replacement = $self->_convert({'contents' |
| => $root->{'extra'}->{'brace_command_contents'}->[2]}); |
| } |
| } |
| if (!defined($replacement) or $replacement eq '') { |
| $replacement = $url_text; |
| } |
| return "<ulink url=\"$url_text\">$replacement</ulink>"; |
| # DocBook 5 |
| # return "<link xl:href=\"$url_text\">$replacement</link>"; |
| } |
| |
| } elsif ($root->{'cmdname'} eq 'abbr' or $root->{'cmdname'} eq 'acronym') { |
| my $argument; |
| if (scalar (@{$root->{'extra'}->{'brace_command_contents'}}) >= 1 |
| and defined($root->{'extra'}->{'brace_command_contents'}->[0])) { |
| my $arg = $self->_convert({'contents' |
| => $root->{'extra'}->{'brace_command_contents'}->[0]}); |
| if ($arg ne '') { |
| my $element; |
| if ($root->{'cmdname'} eq 'abbr') { |
| $element = 'abbrev'; |
| } else { |
| $element = $root->{'cmdname'}; |
| } |
| $argument = "<$element>$arg</$element>"; |
| } |
| } |
| # |
| if (scalar (@{$root->{'extra'}->{'brace_command_contents'}}) == 2 |
| and defined($root->{'extra'}->{'brace_command_contents'}->[-1])) { |
| if (defined($argument)) { |
| my $tree = $self->gdt('{abbr_or_acronym} ({explanation})', |
| {'abbr_or_acronym' => {'type' => '_converted', |
| 'text' => $argument}, |
| 'explanation' => |
| $root->{'extra'}->{'brace_command_contents'}->[-1]}); |
| return $self->_convert($tree); |
| } else { |
| return $self->_convert({'contents' |
| => $root->{'extra'}->{'brace_command_contents'}->[-1]}); |
| } |
| } elsif (defined($argument)) { |
| return $argument; |
| } else { |
| return ''; |
| } |
| |
| } elsif ($root->{'cmdname'} eq 'U') { |
| my $argument = $root->{'extra'}->{'brace_command_contents'}->[0] |
| ->[0]->{'text'}; |
| if (defined($argument) && $argument) { |
| $result = "&#x$argument;"; |
| } else { |
| $self->line_warn($self->__("no argument specified for \@U"), |
| $root->{'line_nr'}); |
| $result = ''; |
| } |
| return $result; |
| |
| } elsif ($Texinfo::Common::inline_commands{$root->{'cmdname'}}) { |
| my $expand = 0; |
| if ($Texinfo::Common::inline_format_commands{$root->{'cmdname'}}) { |
| if ($root->{'cmdname'} eq 'inlinefmtifelse' |
| or ($root->{'extra'} and $root->{'extra'}->{'format'} |
| and $self->{'expanded_formats_hash'}->{$root->{'extra'}->{'format'}})) { |
| $expand = 1; |
| } |
| } elsif (defined($root->{'extra'}->{'expand_index'})) { |
| $expand = 1; |
| } |
| return '' if (! $expand); |
| my $arg_index = 1; |
| if ($root->{'cmdname'} eq 'inlineraw') { |
| push @{$self->{'document_context'}}, {'monospace' => [0], 'upper_case' => [0]}; |
| $self->{'document_context'}->[-1]->{'raw'} = 1; |
| } elsif ($root->{'cmdname'} eq 'inlinefmtifelse' |
| and ! $self->{'expanded_formats_hash'}->{$root->{'extra'}->{'format'}}) { |
| $arg_index = 2; |
| } |
| if (scalar (@{$root->{'extra'}->{'brace_command_contents'}}) > $arg_index |
| and defined($root->{'extra'}->{'brace_command_contents'}->[$arg_index])) { |
| $result .= $self->_convert({'contents' |
| => $root->{'extra'}->{'brace_command_contents'}->[$arg_index]}); |
| } |
| if ($root->{'cmdname'} eq 'inlineraw') { |
| pop @{$self->{'document_context'}}; |
| } |
| #warn " returning braced cmd result $result\n"; |
| return $result; |
| } else { |
| # ignored brace command |
| #warn " returning empty string for ignored braced cmd\n"; |
| return ''; |
| } |
| |
| |
| # special case to ensure that @w leads to something even if empty |
| } elsif ($root->{'cmdname'} eq 'w') { |
| return $w_command_mark; |
| |
| } elsif (exists($Texinfo::Common::block_commands{$root->{'cmdname'}})) { |
| if ($self->{'context_block_commands'}->{$root->{'cmdname'}}) { |
| push (@{$self->{'document_context'}}, |
| {'monospace' => [0], 'upper_case' => [0]}); |
| } |
| my @attributes; |
| my $appended = ''; |
| my @elements; |
| if (exists($docbook_preformatted_formats{$root->{'cmdname'}})) { |
| push @{$self->{'document_context'}->[-1]->{'preformatted_stack'}}, |
| $docbook_preformatted_formats{$root->{'cmdname'}}; |
| } elsif ($root->{'cmdname'} eq 'enumerate') { |
| push @elements, 'orderedlist'; |
| my $numeration; |
| if ($root->{'extra'} |
| and $root->{'extra'}->{'enumerate_specification'}) { |
| if ($root->{'extra'}->{'enumerate_specification'} =~ /^[A-Z]/) { |
| $numeration = 'upperalpha'; |
| } elsif ($root->{'extra'}->{'enumerate_specification'} =~ /^[a-z]/) { |
| $numeration = 'loweralpha'; |
| } else { |
| $numeration = 'arabic'; |
| } |
| } else { |
| $numeration = 'arabic'; |
| } |
| push @attributes, " numeration=\"$numeration\""; |
| } elsif ($Texinfo::Common::item_line_commands{$root->{'cmdname'}}) { |
| push @elements, 'variablelist'; |
| } elsif ($root->{'cmdname'} eq 'itemize') { |
| push @elements, 'itemizedlist'; |
| #push @attributes, " mark=\"\""; |
| } elsif ($root->{'cmdname'} eq 'multitable') { |
| push @elements, "informaltable"; |
| push @attributes, ''; |
| my $columns_count; |
| if ($root->{'extra'} and defined($root->{'extra'}->{'max_columns'})) { |
| $columns_count = $root->{'extra'}->{'max_columns'}; |
| } else { |
| $columns_count = 0; |
| } |
| push @elements, 'tgroup'; |
| push @attributes, " cols=\"$columns_count\""; |
| if ($root->{'extra'}) { |
| my @fractions; |
| my $multiply; |
| if ($root->{'extra'}->{'prototypes'}) { |
| $multiply = 1; |
| foreach my $prototype (@{$root->{'extra'}->{'prototypes'}}) { |
| my $prototype_text |
| = Texinfo::Convert::Text::convert($prototype, |
| {Texinfo::Common::_convert_text_options($self)}); |
| push @fractions, |
| Texinfo::Convert::Unicode::string_width($prototype_text); |
| } |
| } elsif ($root->{'extra'}->{'columnfractions'}) { |
| @fractions = @{$root->{'extra'}->{'columnfractions'}}; |
| $multiply = 100; |
| } |
| foreach my $fraction (@fractions) { |
| $appended .= '<colspec colwidth="'.($fraction*$multiply) |
| .'*"></colspec>'; |
| } |
| } |
| } elsif ($root->{'cmdname'} eq 'float') { |
| if ($root->{'extra'} and defined($root->{'extra'}->{'normalized'})) { |
| $result .= "<anchor id=\"$root->{'extra'}->{'normalized'}\"/>\n"; |
| } |
| } elsif ($root->{'cmdname'} eq 'verbatim') { |
| push @elements, 'screen'; |
| } elsif ($root->{'cmdname'} eq 'quotation' |
| or $root->{'cmdname'} eq 'smallquotation') { |
| my $element; |
| if ($root->{'extra'}) { |
| if ($root->{'extra'}->{'authors'}) { |
| foreach my $author (@{$root->{'extra'}->{'authors'}}) { |
| if ($author->{'extra'} and $author->{'extra'}->{'misc_content'}) { |
| $appended .= '<attribution>'.$self->_convert( |
| {'contents' => $author->{'extra'}->{'misc_content'}}) |
| ."</attribution>\n"; |
| } |
| } |
| } |
| if ($root->{'extra'}->{'block_command_line_contents'} |
| and defined($root->{'extra'}->{'block_command_line_contents'}->[0])) { |
| my $quotation_arg_text = Texinfo::Convert::Text::convert( |
| {'contents' => $root->{'extra'}->{'block_command_line_contents'}->[0]}, |
| {Texinfo::Common::_convert_text_options($self)}); |
| if ($docbook_special_quotations{lc($quotation_arg_text)}) { |
| $element = lc($quotation_arg_text); |
| } else { |
| $self->{'pending_prepend'} |
| = $self->_convert($self->gdt('@b{{quotation_arg}:} ', |
| {'quotation_arg' => |
| $root->{'extra'}->{'block_command_line_contents'}->[0]})); |
| } |
| } |
| } |
| $element = 'blockquote' if (!defined($element)); |
| push @elements, $element; |
| } elsif ($root->{'cmdname'} eq 'copying') { |
| push @elements, ('bookinfo', 'legalnotice'); |
| } elsif ($Texinfo::Common::format_raw_commands{$root->{'cmdname'}}) { |
| return '' if (!$self->{'expanded_formats_hash'}->{$root->{'cmdname'}}); |
| # the context is here only for the command, so this is forgotten |
| # once all the raw internal text has been formatted |
| $self->{'document_context'}->[-1]->{'raw'} = 1; |
| } elsif ($Texinfo::Common::block_commands{$root->{'cmdname'}} eq 'raw') { |
| return ''; |
| } elsif ($Texinfo::Common::menu_commands{$root->{'cmdname'}}) { |
| return ''; |
| } |
| foreach my $element (@elements) { |
| my $attribute = shift @attributes; |
| $attribute = '' if (!defined($attribute)); |
| $result .= "<$element${attribute}>"; |
| unshift @close_elements, $element; |
| } |
| $result .= $appended if (defined($appended)); |
| } |
| } |
| #warn " end of cmdname\n"; |
| |
| |
| if ($root->{'type'}) { |
| #warn " have type $root->{'type'}\n"; |
| if (exists($docbook_preformatted_formats{$root->{'type'}})) { |
| push @{$self->{'document_context'}->[-1]->{'preformatted_stack'}}, |
| $docbook_preformatted_formats{$root->{'type'}}; |
| } |
| if (defined($type_elements{$root->{'type'}})) { |
| $result .= "<$type_elements{$root->{'type'}}>"; |
| } elsif ($root->{'type'} eq 'preformatted') { |
| $result .= "<$self->{'document_context'}->[-1]->{'preformatted_stack'}->[-1]>"; |
| $self->{'document_context'}->[-1]->{'in_preformatted'} = 1; |
| } elsif ($root->{'type'} eq 'def_line') { |
| $result .= "<synopsis>"; |
| $result .= $self->_index_entry($root); |
| push @{$self->{'document_context'}}, {'monospace' => [1], 'upper_case' => [0]}; |
| $self->{'document_context'}->[-1]->{'inline'}++; |
| if ($root->{'extra'} and $root->{'extra'}->{'def_args'}) { |
| my $main_command; |
| if ($Texinfo::Common::def_aliases{$root->{'extra'}->{'def_command'}}) { |
| $main_command = $Texinfo::Common::def_aliases{$root->{'extra'}->{'def_command'}}; |
| } else { |
| $main_command = $root->{'extra'}->{'def_command'}; |
| } |
| foreach my $arg (@{$root->{'extra'}->{'def_args'}}) { |
| my $type = $arg->[0]; |
| my $content = $self->_convert($arg->[1]); |
| if ($type eq 'spaces' or $type eq 'delimiter') { |
| $result .= $content; |
| } elsif ($type eq 'category') { |
| $result .= "<phrase role=\"category\"><emphasis role=\"bold\">$content</emphasis>:</phrase>"; |
| } elsif ($type eq 'name') { |
| $result .= "<$defcommand_name_type{$main_command}>$content</$defcommand_name_type{$main_command}>"; |
| } else { |
| if (!defined($def_argument_types_docbook{$type})) { |
| warn "BUG: no def_argument_types_docbook for $type"; |
| next; |
| } |
| foreach my $element (reverse ( |
| @{$def_argument_types_docbook{$type}})) { |
| $content = "<$element>$content</$element>"; |
| } |
| $result .= $content; |
| } |
| } |
| } |
| pop @{$self->{'document_context'}}; |
| $result .= "</synopsis>"; |
| $result .= "\n"; |
| } |
| } |
| |
| if ($root->{'contents'}) { |
| #warn " have contents $root->{'contents'}\n"; |
| my $in_code; |
| if ($root->{'cmdname'} |
| and $Texinfo::Common::preformatted_code_commands{$root->{'cmdname'}}) { |
| $in_code = 1; |
| } |
| push @{$self->{'document_context'}->[-1]->{'monospace'}}, 1 |
| if ($in_code); |
| if (ref($root->{'contents'}) ne 'ARRAY') { |
| cluck "contents not an array($root->{'contents'})."; |
| } |
| if (defined($self->{'pending_prepend'}) and $self->_in_inline($root)) { |
| $result .= $self->{'pending_prepend'}; |
| delete $self->{'pending_prepend'}; |
| } |
| foreach my $content (@{$root->{'contents'}}) { |
| $result .= $self->_convert($content); |
| } |
| pop @{$self->{'document_context'}->[-1]->{'monospace'}} |
| if ($in_code); |
| } |
| |
| if ($root->{'type'}) { |
| if (defined($type_elements{$root->{'type'}})) { |
| $result .= "</$type_elements{$root->{'type'}}>"; |
| } elsif ($root->{'type'} eq 'preformatted') { |
| $result .= "</$self->{'document_context'}->[-1]->{'preformatted_stack'}->[-1]>"; |
| delete $self->{'document_context'}->[-1]->{'in_preformatted'}; |
| } |
| } |
| $result = '{'.$result.'}' |
| if ($root->{'type'} and $root->{'type'} eq 'bracketed' |
| and (!$root->{'parent'}->{'type'} or |
| ($root->{'parent'}->{'type'} ne 'block_line_arg' |
| and $root->{'parent'}->{'type'} ne 'misc_line_arg'))); |
| foreach my $element (@close_elements) { |
| $result .= "</$element>"; |
| } |
| |
| if ($root->{'cmdname'} |
| and exists($Texinfo::Common::block_commands{$root->{'cmdname'}})) { |
| # a pending_prepend still there may happen if a quotation is empty. |
| delete $self->{'pending_prepend'}; |
| #$result .= "</$root->{'cmdname'}>\n"; |
| if (!$self->{'document_context'}->[-1]->{'raw'} |
| and exists($docbook_preformatted_formats{$root->{'cmdname'}})) { |
| my $format = pop @{$self->{'document_context'}->[-1]->{'preformatted_stack'}}; |
| die "BUG $format ne $docbook_preformatted_formats{$root->{'cmdname'}}" |
| if ($format ne $docbook_preformatted_formats{$root->{'cmdname'}}); |
| } |
| if ($self->{'context_block_commands'}->{$root->{'cmdname'}}) { |
| pop @{$self->{'document_context'}}; |
| } |
| |
| } elsif ($root->{'type'} and exists($docbook_preformatted_formats{$root->{'type'}})) { |
| my $format = pop @{$self->{'document_context'}->[-1]->{'preformatted_stack'}}; |
| die "BUG $format ne $docbook_preformatted_formats{$root->{'type'}}" |
| if ($format ne $docbook_preformatted_formats{$root->{'type'}}); |
| |
| # The command is closed either when the corresponding tree element |
| # is done, and the command is not associated to an element, or when |
| # the element is closed. |
| } elsif (($root->{'type'} and $root->{'type'} eq 'element' |
| and $root->{'extra'} and $root->{'extra'}->{'element_command'}) |
| or ($root->{'cmdname'} |
| and $Texinfo::Common::root_commands{$root->{'cmdname'}} |
| and $root->{'cmdname'} ne 'node' |
| and !($root->{'parent'} and $root->{'parent'}->{'type'} |
| and $root->{'parent'}->{'type'} eq 'element' |
| and $root->{'parent'}->{'extra'} |
| and $root->{'parent'}->{'extra'}->{'element_command'} eq $root))) { |
| if ($root->{'type'} and $root->{'type'} eq 'element') { |
| $root = $root->{'extra'}->{'element_command'}; |
| } |
| my $command = $self->_docbook_section_element($root); |
| if ($command eq 'part' and !Texinfo::Common::is_content_empty($root)) { |
| $result .= "</partintro>\n"; |
| } |
| my $command_texi = $self->_level_corrected_section($root); |
| if (!($root->{'section_childs'} and scalar(@{$root->{'section_childs'}})) |
| or $command_texi eq 'top') { |
| $result .= "</$command>\n"; |
| my $current = $root; |
| while ($current->{'section_up'} |
| # the most up element is a virtual sectioning root element, this |
| # condition avoids getting into it |
| and $current->{'section_up'}->{'cmdname'} |
| and !$current->{'section_next'} |
| and $self->_level_corrected_section($current->{'section_up'}) ne 'top') { |
| $current = $current->{'section_up'}; |
| $result .= '</'.$self->_docbook_section_element($current) .">\n"; |
| } |
| } |
| } |
| |
| #warn " returning $result\n"; |
| return $result; |
| } |
| |
| # figure: mandatory title->use it with shortcaption?. Has a caption. |
| |
| 1; |
| |
| __END__ |
| # $Id: DocBook.pm 6991 2016-02-06 12:16:13Z gavin $ |
| # Automatically generated from maintain/template.pod |
| |
| =head1 NAME |
| |
| Texinfo::Convert::DocBook - Convert Texinfo tree to DocBook |
| |
| =head1 SYNOPSIS |
| |
| my $converter |
| = Texinfo::Convert::DocBook->converter({'parser' => $parser}); |
| |
| $converter->output($tree); |
| $converter->convert($tree); |
| $converter->convert_tree($tree); |
| |
| =head1 DESCRIPTION |
| |
| Texinfo::Convert::DocBook converts a Texinfo tree to DocBook. |
| |
| =head1 METHODS |
| |
| =over |
| |
| =item $converter = Texinfo::Convert::DocBook->converter($options) |
| |
| Initialize converter from Texinfo to DocBook. |
| |
| The I<$options> hash reference holds options for the converter. In |
| this option hash reference a parser object may be associated with the |
| I<parser> key. The other options should be configuration options |
| described in the Texinfo manual. Those options, when appropriate, |
| override the document content. |
| |
| See L<Texinfo::Convert::Converter> for more informations. |
| |
| =item $converter->output($tree) |
| |
| Convert a Texinfo tree I<$tree> and output the result in files as |
| described in the Texinfo manual. |
| |
| =item $result = $converter->convert($tree) |
| |
| Convert a Texinfo tree I<$tree> or tree portion and return |
| the resulting output. |
| |
| =item $result = $converter->convert_tree($tree) |
| |
| Convert a Texinfo tree portion I<$tree> and return the resulting |
| output. This function does not try to output a full document but only |
| portions. For a full document use C<convert>. |
| |
| =back |
| |
| =head1 AUTHOR |
| |
| Patrice Dumas, E<lt>pertusus@free.frE<gt> |
| |
| =head1 COPYRIGHT AND LICENSE |
| |
| Copyright 2015 Free Software Foundation, Inc. |
| |
| This library is free software; you can redistribute it and/or modify |
| it under the terms of the GNU General Public License as published by |
| the Free Software Foundation; either version 3 of the License, or (at |
| your option) any later version. |
| |
| =cut |