TexinfoXML.pm 66 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767
  1. # TexinfoXML.pm: output tree as Texinfo XML.
  2. #
  3. # Copyright 2011, 2012, 2013, 2016 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. # Original author: Patrice Dumas <pertusus@free.fr>
  19. package Texinfo::Convert::TexinfoXML;
  20. use 5.00405;
  21. use strict;
  22. use Texinfo::Convert::Converter;
  23. use Texinfo::Common;
  24. use Texinfo::Convert::Unicode;
  25. # for debugging and adding the original line for some commands
  26. use Texinfo::Convert::Texinfo;
  27. use Data::Dumper;
  28. use Carp qw(cluck);
  29. require Exporter;
  30. use vars qw($VERSION @ISA @EXPORT @EXPORT_OK %EXPORT_TAGS);
  31. @ISA = qw(Exporter Texinfo::Convert::Converter);
  32. # Items to export into callers namespace by default. Note: do not export
  33. # names by default without a very good reason. Use EXPORT_OK instead.
  34. # Do not simply export all your public functions/methods/constants.
  35. # This allows declaration use Texinfo::Convert::TexinfoXML ':all';
  36. # If you do not need this, moving things directly into @EXPORT or @EXPORT_OK
  37. # will save memory.
  38. %EXPORT_TAGS = ( 'all' => [ qw(
  39. convert
  40. convert_tree
  41. output
  42. ) ] );
  43. @EXPORT_OK = ( @{ $EXPORT_TAGS{'all'} } );
  44. @EXPORT = qw(
  45. );
  46. $VERSION = '6.3.90';
  47. # XML specific
  48. my %defaults = (
  49. 'ENABLE_ENCODING' => 0,
  50. 'SHOW_MENU' => 1,
  51. 'EXTENSION' => 'xml',
  52. #'output_perl_encoding' => 'utf8',
  53. 'OUTPUT_ENCODING_NAME' => 'utf-8',
  54. 'TEXINFO_DTD_VERSION' => '5.0',
  55. 'OUTFILE' => undef,
  56. 'SUBDIR' => undef,
  57. 'output_format' => 'xml',
  58. 'SPLIT' => 0,
  59. 'documentlanguage' => 'en',
  60. );
  61. # our because it is used in the xml to texi translator
  62. our %commands_formatting = (
  63. '*' => 'linebreak',
  64. ' ' => ['spacecmd', 'type', 'spc'],
  65. "\t" => ['spacecmd', 'type', 'tab'],
  66. "\n" => ['spacecmd', 'type', 'nl'],
  67. '-' => 'hyphenbreak', # hyphenation hint
  68. '|' => '', # used in formatting commands @evenfooting and friends
  69. '/' => 'slashbreak',
  70. ':' => 'noeos',
  71. '!' => 'eosexcl',
  72. '?' => 'eosquest',
  73. '.' => 'eosperiod',
  74. '@' => 'arobase',
  75. '{' => 'lbrace',
  76. '}' => 'rbrace',
  77. '\\' => 'backslash', # should only appear in math
  78. 'TeX' => 'tex',
  79. 'LaTeX' => 'latex',
  80. 'bullet' => 'bullet',
  81. 'copyright' => 'copyright',
  82. 'registeredsymbol' => 'registered',
  83. 'dots' => 'dots',
  84. 'enddots' => 'enddots',
  85. 'error' => 'errorglyph',
  86. 'expansion' => 'expansion',
  87. 'arrow' => 'rarr',
  88. 'click' => ['click', 'command', 'arrow'],
  89. 'minus' => 'minus',
  90. 'point' => 'point',
  91. 'print' => 'printglyph',
  92. 'result' => 'result',
  93. 'l' => 'lslash',
  94. 'L' => 'Lslash',
  95. 'today' => ['today'],
  96. 'comma' => 'comma',
  97. 'atchar' => 'atchar',
  98. 'lbracechar' => 'lbracechar',
  99. 'rbracechar' => 'rbracechar',
  100. 'backslashchar' => 'backslashchar',
  101. 'hashchar' => 'hashchar',
  102. );
  103. # use default XML formatting to complete the hash, removing XML
  104. # specific formatting. This avoids some code duplication.
  105. my %default_xml_commands_formatting =
  106. %{$Texinfo::Convert::Converter::default_xml_commands_formatting{'normal'}};
  107. foreach my $command (keys(%default_xml_commands_formatting)) {
  108. if (!exists($commands_formatting{$command})) {
  109. if ($default_xml_commands_formatting{$command} ne '') {
  110. if ($default_xml_commands_formatting{$command} =~ /^&(.*);$/) {
  111. $commands_formatting{$command} = $1;
  112. } else {
  113. die "BUG: Strange xml_commands_formatting: $default_xml_commands_formatting{$command}\n";
  114. }
  115. } else {
  116. $commands_formatting{$command} = '';
  117. }
  118. }
  119. }
  120. # Following are XML specific formatting functions.
  121. # format specific. Used in few places where plain text is used outside
  122. # of attributes.
  123. sub protect_text($$)
  124. {
  125. my $self = shift;
  126. my $string = shift;
  127. return $self->_protect_text($string);
  128. }
  129. sub _xml_attributes($$)
  130. {
  131. my $self = shift;
  132. my $attributes = shift;
  133. if (ref($attributes) ne 'ARRAY') {
  134. cluck "attributes not an array($attributes).";
  135. }
  136. my $result = '';
  137. for (my $i = 0; $i < scalar(@$attributes); $i += 2) {
  138. # this cannot be used, because of formfeed, as in
  139. # attribute < which is substituted from &formfeed; is not allowed
  140. #my $text = $self->_protect_text($attributes->[$i+1]);
  141. my $text = $self->xml_protect_text($attributes->[$i+1]);
  142. # in fact form feed is not allowed at all in XML, even protected
  143. # and even in xml 1.1 in contrast to what is said on internet.
  144. # maybe this is a limitation of libxml?
  145. #$text =~ s/\f/&#12;/g;
  146. if ($attributes->[$i] ne 'spaces'
  147. and $attributes->[$i] ne 'trailingspaces') {
  148. $text =~ s/\f/&attrformfeed;/g;
  149. # &attrformfeed; resolves to \f so \ are doubled
  150. $text =~ s/\\/\\\\/g;
  151. }
  152. $result .= " $attributes->[$i]=\"".$text."\"";
  153. }
  154. return $result;
  155. }
  156. # format specific
  157. sub element($$$)
  158. {
  159. my $self = shift;
  160. my $element_name = shift;
  161. my $attributes = shift;
  162. my $result= '<'.$element_name;
  163. $result .= $self->_xml_attributes($attributes) if ($attributes);
  164. $result .= '/>';
  165. return $result;
  166. }
  167. # format specific
  168. sub open_element($$$)
  169. {
  170. my $self = shift;
  171. my $element_name = shift;
  172. my $attributes = shift;
  173. my $result= '<'."$element_name";
  174. $result .= $self->_xml_attributes($attributes) if ($attributes);
  175. $result .= '>';
  176. return $result;
  177. }
  178. # format specific
  179. sub close_element($$)
  180. {
  181. my $self = shift;
  182. my $element_name = shift;
  183. my $result= "</$element_name>";
  184. return $result;
  185. }
  186. # format specific
  187. sub format_atom($$)
  188. {
  189. my $self = shift;
  190. my $atom = shift;
  191. if ($commands_formatting{$atom} ne '') {
  192. return '&'.$commands_formatting{$atom}.';';
  193. } else {
  194. return '';
  195. }
  196. }
  197. # format specific
  198. sub format_comment($$)
  199. {
  200. my $self = shift;
  201. my $string = shift;
  202. return $self->xml_comment($string);
  203. }
  204. # form feed is not accepted in xml, replace it.
  205. sub _protect_text($$)
  206. {
  207. my $self = shift;
  208. my $text = shift;
  209. my $result = $self->xml_protect_text($text);
  210. $result =~ s/\f/&formfeed;/g;
  211. return $result;
  212. }
  213. # format specific
  214. sub format_text($$)
  215. {
  216. my $self = shift;
  217. my $root = shift;
  218. my $result = $self->_protect_text($root->{'text'});
  219. if (! defined($root->{'type'}) or $root->{'type'} ne 'raw') {
  220. if (!$self->{'document_context'}->[-1]->{'monospace'}->[-1]) {
  221. $result =~ s/``/&textldquo;/g;
  222. $result =~ s/\'\'/&textrdquo;/g;
  223. $result =~ s/---/&textmdash;/g;
  224. $result =~ s/--/&textndash;/g;
  225. $result =~ s/'/&textrsquo;/g;
  226. $result =~ s/`/&textlsquo;/g;
  227. }
  228. }
  229. return $result;
  230. }
  231. # output format specific
  232. sub format_header($)
  233. {
  234. my $self = shift;
  235. my $encoding = '';
  236. if ($self->get_conf('OUTPUT_ENCODING_NAME')
  237. and $self->get_conf('OUTPUT_ENCODING_NAME') ne 'utf-8') {
  238. $encoding = " encoding=\"".$self->get_conf('OUTPUT_ENCODING_NAME')."\" ";
  239. }
  240. my $texinfo_dtd_version = $self->get_conf('TEXINFO_DTD_VERSION');
  241. if (!defined($texinfo_dtd_version)) {
  242. $texinfo_dtd_version = '1.00';
  243. }
  244. my $header = "<?xml version=\"1.0\"${encoding}?>".'
  245. <!DOCTYPE texinfo PUBLIC "-//GNU//DTD TexinfoML V'.$texinfo_dtd_version.'//EN" "http://www.gnu.org/software/texinfo/dtd/'.$texinfo_dtd_version.'/texinfo.dtd">
  246. '. $self->open_element('texinfo', ['xml:lang', $self->get_conf('documentlanguage')])."\n";
  247. if ($self->{'output_file'} ne '') {
  248. my $output_filename = $self->{'output_filename'};
  249. $header .= $self->open_element('filename',['file', $output_filename])
  250. .$self->close_element('filename')."\n";
  251. }
  252. return $header;
  253. }
  254. # following is not format specific. Some infos are taken from generic XML, but
  255. # XML specific formatting is stripped.
  256. my %accents = (
  257. '=' => 'macr',
  258. # following are not entities
  259. 'H' => 'doubleacute',
  260. 'u' => 'breve',
  261. 'v' => 'caron',
  262. );
  263. # our because it is used in the xml to texi translator
  264. our %accent_types = (%Texinfo::Convert::Converter::xml_accent_entities, %accents);
  265. # no entity
  266. my @other_accents = ('dotaccent', 'tieaccent', 'ubaraccent', 'udotaccent');
  267. foreach my $accent (@other_accents) {
  268. $accent_types{$accent} = $accent;
  269. }
  270. my %misc_command_line_attributes = (
  271. 'setfilename' => 'file',
  272. 'documentencoding' => 'encoding',
  273. 'verbatiminclude' => 'file',
  274. 'documentlanguage' => 'xml:lang',
  275. );
  276. my %misc_command_numbered_arguments_attributes = (
  277. 'definfoenclose' => [ 'command', 'open', 'close' ],
  278. 'alias' => [ 'new', 'existing' ],
  279. 'syncodeindex' => [ 'from', 'to' ],
  280. 'synindex' => [ 'from', 'to' ],
  281. );
  282. my %misc_commands = %Texinfo::Common::misc_commands;
  283. foreach my $command ('item', 'headitem', 'itemx', 'tab',
  284. keys %Texinfo::Common::def_commands) {
  285. delete $misc_commands{$command};
  286. }
  287. my %default_args_code_style
  288. = %Texinfo::Convert::Converter::default_args_code_style;
  289. my %regular_font_style_commands = %Texinfo::Common::regular_font_style_commands;
  290. # our because it is used in the xml to texi translator
  291. our %commands_args_elements = (
  292. 'email' => ['emailaddress', 'emailname'],
  293. 'uref' => ['urefurl', 'urefdesc', 'urefreplacement'],
  294. 'url' => ['urefurl', 'urefdesc', 'urefreplacement'],
  295. 'inforef' => ['inforefnodename', 'inforefrefname', 'inforefinfoname'],
  296. 'image' => ['imagefile', 'imagewidth', 'imageheight',
  297. 'alttext', 'imageextension'],
  298. 'quotation' => ['quotationtype'],
  299. 'float' => ['floattype', 'floatname'],
  300. 'itemize' => ['itemprepend'],
  301. 'enumerate' => ['enumeratefirst'],
  302. );
  303. foreach my $ref_cmd ('pxref', 'xref', 'ref') {
  304. $commands_args_elements{$ref_cmd}
  305. = ['xrefnodename', 'xrefinfoname', 'xrefprinteddesc', 'xrefinfofile',
  306. 'xrefprintedname'];
  307. }
  308. foreach my $explained_command (keys(%Texinfo::Common::explained_commands)) {
  309. $commands_args_elements{$explained_command} = ["${explained_command}word",
  310. "${explained_command}desc"];
  311. }
  312. foreach my $inline_command (keys(%Texinfo::Common::inline_commands)) {
  313. $commands_args_elements{$inline_command} = ["${inline_command}format",
  314. "${inline_command}content"];
  315. }
  316. my $inline_command = 'inlinefmtifelse';
  317. $commands_args_elements{$inline_command} = ["${inline_command}format",
  318. "${inline_command}contentif", "${inline_command}contentelse"];
  319. my %commands_elements;
  320. foreach my $command (keys(%Texinfo::Common::brace_commands)) {
  321. $commands_elements{$command} = [$command];
  322. if ($commands_args_elements{$command}) {
  323. push @{$commands_elements{$command}}, @{$commands_args_elements{$command}};
  324. }
  325. }
  326. my %defcommand_name_type = (
  327. 'deffn' => 'function',
  328. 'defvr' => 'variable',
  329. 'deftypefn' => 'function',
  330. 'deftypeop' => 'operation',
  331. 'deftypevr' => 'variable',
  332. 'defcv' => 'classvar',
  333. 'deftypecv' => 'classvar',
  334. 'defop' => 'operation',
  335. 'deftp' => 'datatype',
  336. );
  337. my %ignored_types;
  338. foreach my $type (
  339. # those are put as spaces in the corresponding @-command
  340. 'empty_spaces_after_command',
  341. 'empty_spaces_before_argument',
  342. ) {
  343. $ignored_types{$type} = 1;
  344. }
  345. # this is used in IXIN, to ignore everything before first node.
  346. sub _set_ignored_type($$)
  347. {
  348. my $self = shift;
  349. my $type = shift;
  350. $ignored_types{$type} = 1;
  351. }
  352. my %type_elements = (
  353. 'paragraph' => 'para',
  354. 'preformatted' => 'pre',
  355. 'menu_entry' => 'menuentry',
  356. 'menu_entry_node' => 'menunode',
  357. 'menu_comment' => 'menucomment',
  358. 'menu_entry_description' => 'menudescription',
  359. 'menu_entry_name' => 'menutitle',
  360. 'preamble' => 'preamble',
  361. 'table_item' => 'tableitem',
  362. 'table_entry' => 'tableentry',
  363. 'table_term' => 'tableterm',
  364. 'row' => 'row',
  365. 'multitable_head' => 'thead',
  366. 'multitable_body' => 'tbody',
  367. 'def_item' => 'definitionitem',
  368. 'before_item' => 'beforefirstitem',
  369. );
  370. my %default_context_block_commands = (
  371. 'float' => 1,
  372. );
  373. sub converter_defaults($$)
  374. {
  375. return %defaults;
  376. }
  377. sub converter_initialize($)
  378. {
  379. my $self = shift;
  380. $self->{'document_context'} = [{'monospace' => [0]}];
  381. $self->{'context_block_commands'} = {%default_context_block_commands};
  382. foreach my $raw (keys (%Texinfo::Common::format_raw_commands)) {
  383. $self->{'context_block_commands'}->{$raw} = 1
  384. if $self->{'expanded_formats_hash'}->{$raw};
  385. }
  386. if ($self->{'parser'}) {
  387. $self->{'index_names'} = $self->{'parser'}->indices_information();
  388. }
  389. }
  390. # Main output function for the XML file.
  391. sub output($$)
  392. {
  393. my $self = shift;
  394. my $root = shift;
  395. $self->_set_outfile();
  396. return undef unless $self->_create_destination_directory();
  397. my $fh;
  398. if (! $self->{'output_file'} eq '') {
  399. $fh = $self->Texinfo::Common::open_out($self->{'output_file'});
  400. if (!$fh) {
  401. $self->document_error(sprintf($self->__("could not open %s for writing: %s"),
  402. $self->{'output_file'}, $!));
  403. return undef;
  404. }
  405. }
  406. $self->_set_global_multiple_commands(-1);
  407. my $result = '';
  408. $result .= $self->_output_text($self->format_header(), $fh);
  409. if ($self->get_conf('USE_NODES')) {
  410. $result .= $self->convert_document_nodes($root, $fh);
  411. } else {
  412. $result .= $self->convert_document_sections($root, $fh);
  413. }
  414. $result .= $self->_output_text($self->close_element('texinfo')."\n", $fh);
  415. if ($fh and $self->{'output_file'} ne '-') {
  416. $self->register_close_file($self->{'output_file'});
  417. if (!close ($fh)) {
  418. $self->document_error(sprintf($self->__("error on closing %s: %s"),
  419. $self->{'output_file'}, $!));
  420. }
  421. }
  422. return $result;
  423. }
  424. sub _format_command($$)
  425. {
  426. my $self = shift;
  427. my $command = shift;
  428. if (! ref($commands_formatting{$command})) {
  429. return $self->format_atom($command);
  430. } else {
  431. my @spec = @{$commands_formatting{$command}};
  432. my $element_name = shift @spec;
  433. return $self->element($element_name, \@spec);
  434. }
  435. }
  436. sub _index_entry($$)
  437. {
  438. my $self = shift;
  439. my $root = shift;
  440. if ($root->{'extra'} and $root->{'extra'}->{'index_entry'}) {
  441. my $index_entry = $root->{'extra'}->{'index_entry'};
  442. my $attribute = ['index', $index_entry->{'index_name'}];
  443. push @$attribute, ('number', $index_entry->{'number'})
  444. if (defined($index_entry->{'number'}));
  445. # in case the index is not a default index, or the style of the
  446. # entry (in code or not) is not the default for this index
  447. if ($self->{'index_names'}) {
  448. my $in_code = $self->{'index_names'}->{$index_entry->{'index_name'}}->{'in_code'};
  449. if (!$Texinfo::Common::index_names{$index_entry->{'index_name'}}
  450. or $in_code != $Texinfo::Common::index_names{$index_entry->{'index_name'}}->{'in_code'}) {
  451. push @$attribute, ('incode', $in_code);
  452. }
  453. if ($self->{'index_names'}->{$index_entry->{'index_name'}}->{'merged_in'}) {
  454. push @$attribute, ('mergedindex',
  455. $self->{'index_names'}->{$index_entry->{'index_name'}}->{'merged_in'});
  456. }
  457. }
  458. my $result = $self->open_element('indexterm', $attribute);
  459. push @{$self->{'document_context'}}, {'monospace' => [0]};
  460. $self->{'document_context'}->[-1]->{'monospace'}->[-1] = 1
  461. if ($index_entry->{'in_code'});
  462. $result .= $self->_convert({'contents' => $index_entry->{'content'}});
  463. pop @{$self->{'document_context'}};
  464. $result .= $self->close_element('indexterm');
  465. return $result;
  466. }
  467. return '';
  468. }
  469. sub _infoenclose_attribute($$) {
  470. my $self = shift;
  471. my $root = shift;
  472. my @attribute = ();
  473. return @attribute if (!$root->{'extra'});
  474. push @attribute, ('begin', $root->{'extra'}->{'begin'})
  475. if (defined($root->{'extra'}->{'begin'}));
  476. push @attribute, ('end', $root->{'extra'}->{'end'})
  477. if (defined($root->{'extra'}->{'end'}));
  478. return @attribute;
  479. }
  480. sub _accent($$;$$$)
  481. {
  482. my $self = shift;
  483. my $text = shift;
  484. my $root = shift;
  485. my $in_upper_case = shift;
  486. my $attributes = shift;
  487. $attributes = [] if (!defined($attributes));
  488. unshift @$attributes, ('type', $accent_types{$root->{'cmdname'}});
  489. my $result = $self->open_element('accent', $attributes);
  490. $result .= $text;
  491. $result .= $self->close_element('accent');
  492. return $result;
  493. }
  494. sub convert($$;$)
  495. {
  496. my $self = shift;
  497. my $root = shift;
  498. my $fh = shift;
  499. return $self->convert_document_sections($root, $fh);
  500. }
  501. sub convert_tree($$)
  502. {
  503. my $self = shift;
  504. my $root = shift;
  505. return $self->_convert($root);
  506. }
  507. sub _protect_in_spaces($)
  508. {
  509. my $text = shift;
  510. $text =~ s/\n/\\n/g;
  511. $text =~ s/\f/\\f/g;
  512. return $text;
  513. }
  514. sub _leading_spaces($)
  515. {
  516. my $root = shift;
  517. if ($root->{'extra'} and $root->{'extra'}->{'spaces_after_command'}
  518. and $root->{'extra'}->{'spaces_after_command'}->{'type'} eq 'empty_spaces_after_command') {
  519. return ('spaces', _protect_in_spaces(
  520. $root->{'extra'}->{'spaces_after_command'}->{'text'}));
  521. } else {
  522. return ();
  523. }
  524. }
  525. sub _leading_spaces_before_argument($)
  526. {
  527. my $root = shift;
  528. if ($root->{'extra'} and $root->{'extra'}->{'spaces_before_argument'}
  529. and $root->{'extra'}->{'spaces_before_argument'}->{'type'} eq 'empty_spaces_before_argument'
  530. and $root->{'extra'}->{'spaces_before_argument'}->{'text'} ne '') {
  531. return ('spaces', _protect_in_spaces(
  532. $root->{'extra'}->{'spaces_before_argument'}->{'text'}));
  533. } else {
  534. return ();
  535. }
  536. }
  537. sub _end_line_spaces($$)
  538. {
  539. my $root = shift;
  540. my $type = shift;
  541. my $end_spaces = undef;
  542. if ($root->{'args'}->[-1]->{'contents'}) {
  543. my $index = -1;
  544. if ($root->{'args'}->[-1]->{'contents'}->[-1]->{'cmdname'}
  545. and ($root->{'args'}->[-1]->{'contents'}->[-1]->{'cmdname'} eq 'c'
  546. or $root->{'args'}->[-1]->{'contents'}->[-1]->{'cmdname'} eq 'comment')) {
  547. $index = -2;
  548. }
  549. if ($root->{'args'}->[-1]->{'contents'}->[$index]
  550. and $root->{'args'}->[-1]->{'contents'}->[$index]->{'type'}
  551. and $root->{'args'}->[-1]->{'contents'}->[$index]->{'type'} eq $type
  552. and defined($root->{'args'}->[-1]->{'contents'}->[$index]->{'text'})
  553. and $root->{'args'}->[-1]->{'contents'}->[$index]->{'text'} !~ /\S/) {
  554. $end_spaces = $root->{'args'}->[-1]->{'contents'}->[$index]->{'text'};
  555. chomp $end_spaces;
  556. }
  557. }
  558. return $end_spaces;
  559. }
  560. sub _arg_line($)
  561. {
  562. my $self = shift;
  563. my $root = shift;
  564. if ($root->{'extra'} and defined($root->{'extra'}->{'arg_line'})) {
  565. my $line = $root->{'extra'}->{'arg_line'};
  566. chomp($line);
  567. if ($line ne '') {
  568. return ('line', $line);
  569. }
  570. }
  571. return ();
  572. }
  573. sub _trailing_spaces_arg($$)
  574. {
  575. my $self = shift;
  576. my $root = shift;
  577. my @spaces = $self->_collect_leading_trailing_spaces_arg($root);
  578. if (defined($spaces[1])) {
  579. chomp($spaces[1]);
  580. if ($spaces[1] ne '') {
  581. return ('trailingspaces', _protect_in_spaces($spaces[1]));
  582. }
  583. }
  584. return ();
  585. }
  586. sub _leading_spaces_arg($$)
  587. {
  588. my $self = shift;
  589. my $root = shift;
  590. my @result = ();
  591. my @spaces = $self->_collect_leading_trailing_spaces_arg($root);
  592. if (defined($spaces[0]) and $spaces[0] ne '') {
  593. @result = ('spaces', _protect_in_spaces($spaces[0]));
  594. }
  595. return @result;
  596. }
  597. sub _leading_trailing_spaces_arg($$)
  598. {
  599. my $self = shift;
  600. my $root = shift;
  601. my @result;
  602. my @spaces = $self->_collect_leading_trailing_spaces_arg($root);
  603. if (defined($spaces[0]) and $spaces[0] ne '') {
  604. push @result, ('spaces', _protect_in_spaces($spaces[0]));
  605. }
  606. if (defined($spaces[1])) {
  607. chomp($spaces[1]);
  608. if ($spaces[1] ne '') {
  609. push @result, ('trailingspaces', _protect_in_spaces($spaces[1]));
  610. }
  611. }
  612. return @result;
  613. }
  614. sub _texinfo_line($$)
  615. {
  616. my $self = shift;
  617. my $root = shift;
  618. my ($comment, $tree) = Texinfo::Convert::Converter::_tree_without_comment(
  619. $root);
  620. my $line = Texinfo::Convert::Texinfo::convert($tree);
  621. chomp($line);
  622. if ($line ne '') {
  623. return ('line', $line);
  624. } else {
  625. return ();
  626. }
  627. }
  628. my @node_directions = ('Next', 'Prev', 'Up');
  629. # not used here, but it is consistent with other %commands_args_elements
  630. # entries and may be used by XML to Texinfo converters
  631. $commands_args_elements{'node'} = ['nodename'];
  632. foreach my $direction (@node_directions) {
  633. push @{$commands_args_elements{'node'}}, 'node'.lc($direction);
  634. }
  635. sub _convert($$;$);
  636. sub _convert($$;$)
  637. {
  638. my $self = shift;
  639. my $root = shift;
  640. if (0) {
  641. #if (1) { #}
  642. print STDERR "root\n";
  643. print STDERR " Command: $root->{'cmdname'}\n" if ($root->{'cmdname'});
  644. print STDERR " Type: $root->{'type'}\n" if ($root->{'type'});
  645. print STDERR " Text: $root->{'text'}\n" if (defined($root->{'text'}));
  646. #print STDERR " Special def_command: $root->{'extra'}->{'def_command'}\n"
  647. # if (defined($root->{'extra'}) and $root->{'extra'}->{'def_command'});
  648. }
  649. return '' if ($root->{'type'} and $ignored_types{$root->{'type'}});
  650. my $result = '';
  651. if (defined($root->{'text'})) {
  652. if ($self->{'document_context'}->[-1]->{'raw'}) {
  653. # ignore the newline at the end of the @xml line, and the last in xml
  654. if ($root->{'type'} and ($root->{'type'} eq 'empty_line_after_command'
  655. or $root->{'type'} eq 'last_raw_newline')) {
  656. return '';
  657. } else {
  658. return $root->{'text'};
  659. }
  660. } elsif ($root->{'type'}
  661. and $root->{'type'} eq 'empty_line_after_command'
  662. and $root->{'extra'}->{'command'}) {
  663. my $command_name = $root->{'extra'}->{'command'}->{'cmdname'};
  664. if ($Texinfo::Common::format_raw_commands{$command_name} and
  665. $self->{'expanded_formats_hash'}->{$command_name}) {
  666. return '';
  667. }
  668. }
  669. $result = $self->format_text($root);
  670. return $result;
  671. }
  672. my @close_elements;
  673. if ($root->{'cmdname'}) {
  674. if (defined($commands_formatting{$root->{'cmdname'}})) {
  675. if ($root->{'cmdname'} eq 'click'
  676. and $root->{'extra'}
  677. and defined($root->{'extra'}->{'clickstyle'})) {
  678. return $self->element('click', ['command', $root->{'extra'}->{'clickstyle'}]);;
  679. }
  680. if ($self->{'itemize_line'} and $root->{'type'}
  681. and $root->{'type'} eq 'command_as_argument'
  682. and !$root->{'args'}) {
  683. return $self->element('formattingcommand', ['command', $root->{'cmdname'}]);
  684. }
  685. return $self->_format_command($root->{'cmdname'});
  686. } elsif ($accent_types{$root->{'cmdname'}}) {
  687. if ($self->get_conf('ENABLE_ENCODING')) {
  688. return $self->convert_accents($root, \&_accent);
  689. } else {
  690. my $attributes = [];
  691. if (!$root->{'args'}) {
  692. $result = '';
  693. } else {
  694. $result = $self->_convert($root->{'args'}->[0]);
  695. if ($root->{'extra'} and $root->{'extra'}->{'spaces'}) {
  696. push @$attributes, ('spaces', $root->{'extra'}->{'spaces'});
  697. }
  698. if ($root->{'args'}->[0]->{'type'} eq 'following_arg') {
  699. push @$attributes, ('bracketed', 'off');
  700. }
  701. }
  702. return $self->_accent($result, $root, undef, $attributes);
  703. }
  704. } elsif ($root->{'cmdname'} eq 'item' or $root->{'cmdname'} eq 'itemx'
  705. or $root->{'cmdname'} eq 'headitem' or $root->{'cmdname'} eq 'tab') {
  706. if ($root->{'cmdname'} eq 'item'
  707. and $root->{'parent'}->{'cmdname'}
  708. and ($root->{'parent'}->{'cmdname'} eq 'itemize'
  709. or $root->{'parent'}->{'cmdname'} eq 'enumerate')) {
  710. $result .= $self->open_element('listitem', [_leading_spaces($root)]);
  711. if ($root->{'parent'}->{'cmdname'} eq 'itemize'
  712. and $root->{'parent'}->{'extra'}
  713. and $root->{'parent'}->{'extra'}->{'block_command_line_contents'}
  714. and $root->{'parent'}->{'extra'}->{'block_command_line_contents'}->[0]) {
  715. $result .= $self->open_element('prepend')
  716. .$self->_convert({'contents'
  717. => $root->{'parent'}->{'extra'}->{'block_command_line_contents'}->[0]})
  718. .$self->close_element('prepend');
  719. }
  720. unshift @close_elements, 'listitem';
  721. } elsif (($root->{'cmdname'} eq 'item' or $root->{'cmdname'} eq 'itemx')
  722. and $root->{'parent'}->{'type'}
  723. and $root->{'parent'}->{'type'} eq 'table_term') {
  724. my $table_command = $root->{'parent'}->{'parent'}->{'parent'};
  725. my $format_item_command;
  726. my $attribute = [];
  727. if ($table_command->{'extra'}
  728. and $table_command->{'extra'}->{'command_as_argument'}) {
  729. $format_item_command
  730. = $table_command->{'extra'}->{'command_as_argument'}->{'cmdname'};
  731. $attribute
  732. = [$self->_infoenclose_attribute($table_command->{'extra'}->{'command_as_argument'})];
  733. }
  734. $result .= $self->open_element($root->{'cmdname'}, [_leading_spaces($root)]);
  735. if ($format_item_command) {
  736. $result .= $self->open_element('itemformat', ['command', $format_item_command, @$attribute]);
  737. }
  738. $result .= $self->_index_entry($root);
  739. my $in_code;
  740. $in_code = 1
  741. if ($format_item_command
  742. and defined($default_args_code_style{$format_item_command})
  743. and $default_args_code_style{$format_item_command}->[0]);
  744. my $in_monospace_not_normal;
  745. if ($format_item_command) {
  746. if (defined($default_args_code_style{$format_item_command})
  747. and $default_args_code_style{$format_item_command}->[0]) {
  748. $in_monospace_not_normal = 1;
  749. } elsif ($regular_font_style_commands{$format_item_command}) {
  750. $in_monospace_not_normal = 0;
  751. }
  752. }
  753. push @{$self->{'document_context'}->[-1]->{'monospace'}},
  754. $in_monospace_not_normal
  755. if (defined($in_monospace_not_normal));
  756. $result .= $self->_convert($root->{'args'}->[0]);
  757. pop @{$self->{'document_context'}->[-1]->{'monospace'}}
  758. if (defined($in_monospace_not_normal));
  759. chomp ($result);
  760. if ($format_item_command) {
  761. $result .= $self->close_element('itemformat');
  762. }
  763. $result .= $self->close_element($root->{'cmdname'})."\n";
  764. } else {
  765. unless (($root->{'cmdname'} eq 'item'
  766. or $root->{'cmdname'} eq 'headitem'
  767. or $root->{'cmdname'} eq 'tab')
  768. and $root->{'parent'}->{'type'}
  769. and $root->{'parent'}->{'type'} eq 'row') {
  770. print STDERR "BUG: multitable cell command not in a row "
  771. .Texinfo::Common::_print_current($root);
  772. }
  773. $result .= $self->open_element('entry', ['command',
  774. $root->{'cmdname'}, _leading_spaces($root)]);
  775. unshift @close_elements, 'entry';
  776. }
  777. } elsif ($root->{'type'} and $root->{'type'} eq 'index_entry_command') {
  778. my $element;
  779. my $attribute = [];
  780. if (exists $Texinfo::Common::misc_commands{$root->{'cmdname'}}) {
  781. $element = $root->{'cmdname'};
  782. } else {
  783. $element = 'indexcommand';
  784. $attribute = ['command', $root->{'cmdname'}];
  785. }
  786. push @$attribute, ('index', $root->{'extra'}->{'index_entry'}->{'index_name'});
  787. push @$attribute, _leading_spaces($root);
  788. my $end_line;
  789. if ($root->{'args'}->[0]) {
  790. $end_line = $self->_end_line_or_comment($root->{'args'}->[0]->{'contents'});
  791. } else {
  792. # May that happen?
  793. $end_line = '';
  794. }
  795. return $self->open_element($element, ${attribute}).
  796. $self->_index_entry($root).$self->close_element($element).${end_line};
  797. } elsif (exists($misc_commands{$root->{'cmdname'}})) {
  798. my $command = $root->{'cmdname'};
  799. my $type = $misc_commands{$root->{'cmdname'}};
  800. if ($type eq 'text') {
  801. return '' if ($root->{'cmdname'} eq 'end');
  802. my $attribute;
  803. if ($misc_command_line_attributes{$root->{'cmdname'}}) {
  804. if ($root->{'extra'} and defined($root->{'extra'}->{'text_arg'})) {
  805. push @$attribute, ($misc_command_line_attributes{$root->{'cmdname'}},
  806. $root->{'extra'}->{'text_arg'});
  807. }
  808. }
  809. my ($arg, $end_line)
  810. = $self->_convert_argument_and_end_line($root->{'args'}->[0]);
  811. push @$attribute, _leading_spaces($root);
  812. return $self->open_element($command, $attribute).$arg
  813. .$self->close_element($command).${end_line};
  814. } elsif ($type eq 'line') {
  815. if ($root->{'cmdname'} eq 'node') {
  816. my $nodename;
  817. if (defined($root->{'extra'}->{'normalized'})) {
  818. $nodename = $root->{'extra'}->{'normalized'};
  819. } else {
  820. $nodename = '';
  821. }
  822. # FIXME avoid protection, here?
  823. $result .= $self->open_element('node', ['name', $nodename, _leading_spaces($root)]);
  824. push @{$self->{'document_context'}->[-1]->{'monospace'}}, 1;
  825. $result .= $self->open_element('nodename',
  826. [$self->_trailing_spaces_arg($root->{'args'}->[0])])
  827. .$self->_convert({'contents' => $root->{'extra'}->{'node_content'}})
  828. .$self->close_element('nodename');
  829. # first arg is the node name.
  830. my $direction_index = 1;
  831. my $pending_empty_directions = '';
  832. foreach my $direction(@node_directions) {
  833. my $element = 'node'.lc($direction);
  834. if ($root->{'node_'.lc($direction)}) {
  835. my $node_direction = $root->{'node_'.lc($direction)};
  836. my $node_name = '';
  837. my $attribute = [];
  838. if (! defined($root->{'extra'}->{'nodes_manuals'}->[$direction_index])) {
  839. push @$attribute, ('automatic', 'on');
  840. }
  841. if ($root->{'args'}->[$direction_index]) {
  842. push @$attribute, $self->_leading_trailing_spaces_arg(
  843. $root->{'args'}->[$direction_index]);
  844. }
  845. if ($node_direction->{'extra'}->{'manual_content'}) {
  846. $node_name .= $self->_convert({
  847. 'contents' => [{'text' => '('},
  848. @{$node_direction->{'extra'}->{'manual_content'}},
  849. {'text' => ')'}]});
  850. }
  851. if ($node_direction->{'extra'}->{'node_content'}) {
  852. $node_name .= Texinfo::Common::normalize_top_node_name($self->_convert({
  853. 'contents' => $node_direction->{'extra'}->{'node_content'}}));
  854. }
  855. $result .= "$pending_empty_directions".
  856. $self->open_element($element, ${attribute}).$node_name.
  857. $self->close_element($element);
  858. $pending_empty_directions = '';
  859. } else {
  860. if ($root->{'args'}->[$direction_index]) {
  861. my $spaces_attribute = $self->_leading_trailing_spaces_arg(
  862. $root->{'args'}->[$direction_index]);
  863. $pending_empty_directions .= $self->open_element($element,
  864. [$self->_leading_trailing_spaces_arg(
  865. $root->{'args'}->[$direction_index])])
  866. .$self->close_element($element);
  867. }
  868. }
  869. $direction_index++;
  870. }
  871. my $end_line;
  872. if ($root->{'args'}->[0]) {
  873. $end_line
  874. = $self->_end_line_or_comment($root->{'args'}->[-1]->{'contents'});
  875. } else {
  876. $end_line = "\n";
  877. }
  878. if (! $self->get_conf('USE_NODES')) {
  879. $result .= $self->close_element('node');
  880. }
  881. $result .= ${end_line};
  882. pop @{$self->{'document_context'}->[-1]->{'monospace'}};
  883. } elsif ($Texinfo::Common::root_commands{$root->{'cmdname'}}) {
  884. my $attribute = [_leading_spaces($root)];
  885. $command = $self->_level_corrected_section($root);
  886. if ($command ne $root->{'cmdname'}) {
  887. unshift @$attribute, ('originalcommand', $root->{'cmdname'});
  888. }
  889. $result .= $self->open_element($command, $attribute);
  890. my $closed_section_element;
  891. if ($self->get_conf('USE_NODES')) {
  892. $closed_section_element = $self->close_element($command);
  893. } else {
  894. $closed_section_element = '';
  895. }
  896. if ($root->{'args'} and $root->{'args'}->[0]) {
  897. my ($arg, $end_line)
  898. = $self->_convert_argument_and_end_line($root->{'args'}->[0]);
  899. $result .= $self->open_element('sectiontitle').$arg
  900. .$self->close_element('sectiontitle')
  901. .$closed_section_element.$end_line;
  902. } else {
  903. $result .= $closed_section_element;
  904. }
  905. } else {
  906. my $attribute = [_leading_spaces($root)];
  907. if ($root->{'cmdname'} eq 'listoffloats' and $root->{'extra'}
  908. and $root->{'extra'}->{'type'}
  909. and defined($root->{'extra'}->{'type'}->{'normalized'})) {
  910. unshift @$attribute, ('type', $root->{'extra'}->{'type'}->{'normalized'});
  911. }
  912. my ($arg, $end_line)
  913. = $self->_convert_argument_and_end_line($root->{'args'}->[0]);
  914. return $self->open_element($command, ${attribute}).$arg
  915. .$self->close_element($command).$end_line;
  916. }
  917. } elsif ($type eq 'skipline') {
  918. # the command associated with an element is closed at the end of the
  919. # element. @bye is withing the element, but we want it to appear after
  920. # the comand closing. So we delay the output of @bye, and store it.
  921. if ($root->{'cmdname'} eq 'bye' and $root->{'parent'}
  922. and $root->{'parent'}->{'type'}
  923. and $root->{'parent'}->{'type'} eq 'element'
  924. and !($root->{'parent'}->{'extra'}
  925. and ($root->{'parent'}->{'extra'}->{'no_section'}
  926. or $root->{'parent'}->{'extra'}->{'no_node'}))) {
  927. #print STDERR "$root->{'parent'} $root->{'parent'}->{'type'}\n";
  928. $self->{'pending_bye'} = $self->open_element($command)
  929. .$self->close_element($command)."\n";
  930. return '';
  931. }
  932. my $attribute = [];
  933. if ($root->{'args'} and $root->{'args'}->[0]
  934. and defined($root->{'args'}->[0]->{'text'})) {
  935. my $line = $root->{'args'}->[0]->{'text'};
  936. chomp($line);
  937. $attribute = ['line', $line]
  938. if ($line ne '');
  939. }
  940. return $self->open_element($command, $attribute)
  941. .$self->close_element($command)."\n";
  942. } elsif ($type eq 'noarg' or $type eq 'skipspace') {
  943. my $spaces = '';
  944. $spaces = $root->{'extra'}->{'spaces_after_command'}->{'text'}
  945. if ($root->{'extra'} and $root->{'extra'}->{'spaces_after_command'}
  946. and $root->{'extra'}->{'spaces_after_command'}->{'type'} eq 'empty_spaces_after_command');
  947. return $self->open_element($command)
  948. .$self->close_element($command).$spaces;
  949. } elsif ($type eq 'special') {
  950. if ($root->{'cmdname'} eq 'clear' or $root->{'cmdname'} eq 'set') {
  951. my $attribute = [];
  952. if ($root->{'args'} and $root->{'args'}->[0]
  953. and defined($root->{'args'}->[0]->{'text'})) {
  954. push @$attribute, ('name', $root->{'args'}->[0]->{'text'});
  955. }
  956. my $value = '';
  957. if ($root->{'cmdname'} eq 'set' and $root->{'args'} and $root->{'args'}->[1]
  958. and defined($root->{'args'}->[1]->{'text'})) {
  959. $value = $self->protect_text($root->{'args'}->[1]->{'text'});
  960. }
  961. push @$attribute, $self->_arg_line($root);
  962. return $self->open_element($command, $attribute)
  963. .$value.$self->close_element($command)."\n";
  964. } elsif ($root->{'cmdname'} eq 'clickstyle') {
  965. my $attribute = [$self->_arg_line($root)];
  966. my $value = '';
  967. if ($root->{'args'} and $root->{'args'}->[0]
  968. and defined($root->{'args'}->[0]->{'text'})) {
  969. my $click_command = $root->{'args'}->[0]->{'text'};
  970. $click_command =~ s/^\@//;
  971. unshift @$attribute, ('command', $click_command);
  972. $value = $self->protect_text($root->{'args'}->[0]->{'text'});
  973. };
  974. return $self->open_element($command, $attribute)
  975. .$value.$self->close_element($command)."\n";
  976. } else {
  977. # should only be unmacro
  978. my $attribute = [$self->_arg_line($root)];
  979. if ($root->{'args'} and $root->{'args'}->[0]
  980. and defined($root->{'args'}->[0]->{'text'})) {
  981. unshift @$attribute, ('name', $root->{'args'}->[0]->{'text'});
  982. }
  983. return $self->open_element($command, $attribute)
  984. .$self->close_element($command)."\n";
  985. }
  986. } elsif ($type eq 'lineraw') {
  987. if ($root->{'cmdname'} eq 'c' or $root->{'cmdname'} eq 'comment') {
  988. return $self->format_comment(" $root->{'cmdname'}".$root->{'args'}->[0]->{'text'})
  989. } else {
  990. my $value = '';
  991. if ($root->{'args'} and $root->{'args'}->[0]
  992. and defined($root->{'args'}->[0]->{'text'})) {
  993. $value = $self->protect_text($root->{'args'}->[0]->{'text'});
  994. }
  995. chomp ($value);
  996. return $self->open_element($command).$value
  997. .$self->close_element($command)."\n";
  998. }
  999. } else {
  1000. print STDERR "BUG: unknown misc_command style $type\n" if ($type !~ /^\d$/);
  1001. my $args_attributes;
  1002. if ($misc_command_numbered_arguments_attributes{$root->{'cmdname'}}) {
  1003. $args_attributes = $misc_command_numbered_arguments_attributes{$root->{'cmdname'}};
  1004. } else {
  1005. $args_attributes = ['value'];
  1006. }
  1007. my $attribute = [];
  1008. my $arg_index = 0;
  1009. if (defined($root->{'extra'})
  1010. and defined($root->{'extra'}->{'misc_args'})) {
  1011. foreach my $arg_attribute (@{$args_attributes}) {
  1012. if (defined ($root->{'extra'}->{'misc_args'}->[$arg_index])) {
  1013. push @$attribute, ( $arg_attribute,
  1014. $root->{'extra'}->{'misc_args'}->[$arg_index]);
  1015. }
  1016. $arg_index++;
  1017. }
  1018. }
  1019. my $end_line;
  1020. if ($root->{'args'}->[0]) {
  1021. $end_line = $self->_end_line_or_comment(
  1022. $root->{'args'}->[0]->{'contents'});
  1023. push @$attribute, $self->_texinfo_line($root->{'args'}->[0]);
  1024. } else {
  1025. $end_line = "\n";
  1026. }
  1027. return $self->open_element($command, $attribute)
  1028. .$self->close_element($command).$end_line;
  1029. }
  1030. } elsif ($root->{'type'}
  1031. and $root->{'type'} eq 'definfoenclose_command') {
  1032. my $in_monospace_not_normal;
  1033. if (defined($default_args_code_style{$root->{'cmdname'}})
  1034. and $default_args_code_style{$root->{'cmdname'}}->[0]) {
  1035. $in_monospace_not_normal = 1;
  1036. } elsif ($regular_font_style_commands{$root->{'cmdname'}}) {
  1037. $in_monospace_not_normal = 0;
  1038. }
  1039. push @{$self->{'document_context'}->[-1]->{'monospace'}},
  1040. $in_monospace_not_normal
  1041. if (defined($in_monospace_not_normal));
  1042. my $arg = $self->_convert($root->{'args'}->[0]);
  1043. $result .= $self->open_element('infoenclose', ['command', $root->{'cmdname'},
  1044. $self->_infoenclose_attribute($root)])
  1045. .$arg.$self->close_element('infoenclose');
  1046. pop @{$self->{'document_context'}->[-1]->{'monospace'}}
  1047. if (defined($in_monospace_not_normal));
  1048. } elsif ($root->{'args'}
  1049. and exists($Texinfo::Common::brace_commands{$root->{'cmdname'}})) {
  1050. if ($Texinfo::Common::context_brace_commands{$root->{'cmdname'}}) {
  1051. push @{$self->{'document_context'}}, {'monospace' => [0]};
  1052. }
  1053. if ($Texinfo::Common::inline_format_commands{$root->{'cmdname'}}
  1054. and $root->{'extra'} and $root->{'extra'}->{'format'}
  1055. and $self->{'expanded_formats_hash'}->{$root->{'extra'}->{'format'}}) {
  1056. if ($root->{'cmdname'} eq 'inlineraw') {
  1057. push @{$self->{'document_context'}}, {'monospace' => [0]};
  1058. $self->{'document_context'}->[-1]->{'raw'} = 1;
  1059. }
  1060. if (scalar (@{$root->{'extra'}->{'brace_command_contents'}}) == 2
  1061. and defined($root->{'extra'}->{'brace_command_contents'}->[-1])) {
  1062. $result .= $self->_convert({'contents'
  1063. => $root->{'extra'}->{'brace_command_contents'}->[-1]});
  1064. }
  1065. if ($root->{'cmdname'} eq 'inlineraw') {
  1066. pop @{$self->{'document_context'}};
  1067. }
  1068. return $result;
  1069. }
  1070. my @elements = @{$commands_elements{$root->{'cmdname'}}};
  1071. my $command;
  1072. if (scalar(@elements) > 1) {
  1073. $command = shift @elements;
  1074. }
  1075. # this is used for commands without args, or associated to the
  1076. # first argument
  1077. my $attribute = [];
  1078. if ($root->{'cmdname'} eq 'verb') {
  1079. push @$attribute, ('delimiter', $root->{'type'});
  1080. } elsif ($root->{'cmdname'} eq 'anchor') {
  1081. my $anchor_name;
  1082. if (defined($root->{'extra'}->{'normalized'})) {
  1083. $anchor_name = $root->{'extra'}->{'normalized'};
  1084. } else {
  1085. $anchor_name = '';
  1086. }
  1087. push @$attribute, ('name', $anchor_name);
  1088. }
  1089. my $arg_index = 0;
  1090. foreach my $element (@elements) {
  1091. if (defined($root->{'args'}->[$arg_index])) {
  1092. my $in_monospace_not_normal;
  1093. if (defined($default_args_code_style{$root->{'cmdname'}})
  1094. and $default_args_code_style{$root->{'cmdname'}}->[$arg_index]) {
  1095. $in_monospace_not_normal = 1;
  1096. } elsif ($regular_font_style_commands{$root->{'cmdname'}}) {
  1097. $in_monospace_not_normal = 0;
  1098. }
  1099. push @{$self->{'document_context'}->[-1]->{'monospace'}},
  1100. $in_monospace_not_normal
  1101. if (defined($in_monospace_not_normal));
  1102. my $arg = $self->_convert($root->{'args'}->[$arg_index]);
  1103. if ($arg_index > 0) {
  1104. push @$attribute,
  1105. $self->_leading_spaces_arg($root->{'args'}->[$arg_index]);
  1106. }
  1107. if (!defined($command) or $arg ne '' or scalar(@$attribute) > 0) {
  1108. # ${attribute} is only set for @verb
  1109. push @$attribute, _leading_spaces_before_argument($root)
  1110. if (!defined($command));
  1111. $result .= $self->open_element($element, $attribute).$arg
  1112. .$self->close_element($element);
  1113. }
  1114. $attribute = [];
  1115. pop @{$self->{'document_context'}->[-1]->{'monospace'}}
  1116. if (defined($in_monospace_not_normal));
  1117. } else {
  1118. last;
  1119. }
  1120. $arg_index++;
  1121. }
  1122. # This is for the main command
  1123. $attribute = [];
  1124. if ($root->{'cmdname'} eq 'image') {
  1125. if ($self->_is_inline($root)) {
  1126. push @$attribute, ('where', 'inline');
  1127. }
  1128. } elsif ($Texinfo::Common::ref_commands{$root->{'cmdname'}}) {
  1129. if ($root->{'extra'}->{'brace_command_contents'}) {
  1130. my $normalized;
  1131. if ($root->{'extra'}->{'node_argument'}
  1132. and $root->{'extra'}->{'node_argument'}->{'node_content'}) {
  1133. my $normalized;
  1134. if (defined($root->{'extra'}->{'node_argument'}->{'normalized'})) {
  1135. $normalized = $root->{'extra'}->{'node_argument'}->{'normalized'};
  1136. } else {
  1137. $normalized = Texinfo::Convert::NodeNameNormalization::normalize_node( {'contents' => $root->{'extra'}->{'node_argument'}->{'node_content'} } );
  1138. }
  1139. if ($normalized) {
  1140. push @$attribute, ('label', $normalized);
  1141. }
  1142. }
  1143. my $manual;
  1144. my $manual_arg_index = 3;
  1145. if ($root->{'cmdname'} eq 'inforef') {
  1146. $manual_arg_index = 2;
  1147. }
  1148. if ($root->{'extra'}->{'brace_command_contents'}->[$manual_arg_index]) {
  1149. $manual = Texinfo::Convert::Text::convert({'contents'
  1150. => $root->{'extra'}->{'brace_command_contents'}->[$manual_arg_index]},
  1151. {'code' => 1,
  1152. Texinfo::Common::_convert_text_options($self)});
  1153. }
  1154. if (!defined($manual) and $root->{'extra'}->{'node_argument'}
  1155. and $root->{'extra'}->{'node_argument'}->{'manual_content'}) {
  1156. $manual = Texinfo::Convert::Text::convert({'contents'
  1157. => $root->{'extra'}->{'node_argument'}->{'manual_content'}},
  1158. {'code' => 1, Texinfo::Common::_convert_text_options($self)});
  1159. }
  1160. if (defined($manual)) {
  1161. my $manual_base = $manual;
  1162. $manual_base =~ s/\.[^\.]*$//;
  1163. $manual_base =~ s/^.*\///;
  1164. push @$attribute, ('manual', $manual_base)
  1165. if ($manual_base ne '');
  1166. }
  1167. }
  1168. }
  1169. if (defined($command)) {
  1170. push @$attribute, _leading_spaces_before_argument($root);
  1171. $result = $self->open_element($command, $attribute).$result
  1172. .$self->close_element($command);
  1173. }
  1174. if ($Texinfo::Common::context_brace_commands{$root->{'cmdname'}}) {
  1175. pop @{$self->{'document_context'}};
  1176. }
  1177. } elsif (exists($Texinfo::Common::block_commands{$root->{'cmdname'}})) {
  1178. if ($self->{'context_block_commands'}->{$root->{'cmdname'}}) {
  1179. push @{$self->{'document_context'}}, {'monospace' => [0]};
  1180. }
  1181. my $prepended_elements = '';
  1182. my $attribute = [];
  1183. $self->{'itemize_line'} = 1 if ($root->{'cmdname'} eq 'itemize');
  1184. if ($root->{'extra'} and $root->{'extra'}->{'command_as_argument'}) {
  1185. my $command_as_arg = $root->{'extra'}->{'command_as_argument'};
  1186. push @$attribute,
  1187. ('commandarg', $command_as_arg->{'cmdname'},
  1188. $self->_infoenclose_attribute($command_as_arg));
  1189. } elsif ($root->{'extra'}
  1190. and $root->{'extra'}->{'enumerate_specification'}) {
  1191. push @$attribute,('first', $root->{'extra'}->{'enumerate_specification'});
  1192. } elsif ($root->{'cmdname'} eq 'float' and $root->{'extra'}) {
  1193. if (defined($root->{'extra'}->{'node_content'})) {
  1194. my $normalized =
  1195. Texinfo::Convert::NodeNameNormalization::normalize_node (
  1196. { 'contents' => $root->{'extra'}->{'node_content'} });
  1197. push @$attribute, ('name', $normalized);
  1198. }
  1199. if ($root->{'extra'}->{'type'} and
  1200. defined($root->{'extra'}->{'type'}->{'normalized'})) {
  1201. push @$attribute, ('type', $root->{'extra'}->{'type'}->{'normalized'});
  1202. }
  1203. if (defined($root->{'number'})) {
  1204. push @$attribute, ('number', $root->{'number'});
  1205. }
  1206. } elsif ($root->{'cmdname'} eq 'verbatim') {
  1207. push @$attribute, ('xml:space', 'preserve');
  1208. } elsif ($root->{'cmdname'} eq 'macro'
  1209. or $root->{'cmdname'} eq 'rmacro') {
  1210. if (defined($root->{'args'})) {
  1211. my @args = @{$root->{'args'}};
  1212. my $name_arg = shift @args;
  1213. if (defined($name_arg) and defined($name_arg->{'text'})) {
  1214. push @$attribute, ('name', $name_arg->{'text'});
  1215. }
  1216. while (@args) {
  1217. my $formal_arg = shift @args;
  1218. $prepended_elements .= $self->open_element('formalarg')
  1219. .$self->protect_text($formal_arg->{'text'})
  1220. .$self->close_element('formalarg');
  1221. }
  1222. }
  1223. push @$attribute, $self->_arg_line($root);
  1224. }
  1225. if ($self->{'expanded_formats_hash'}->{$root->{'cmdname'}}) {
  1226. $self->{'document_context'}->[-1]->{'raw'} = 1;
  1227. } else {
  1228. my $end_command = $root->{'extra'}->{'end_command'};
  1229. my $end_command_space = [_leading_spaces($end_command)];
  1230. if (scalar(@$end_command_space)) {
  1231. $end_command_space->[0] = 'endspaces';
  1232. }
  1233. $result .= $self->open_element($root->{'cmdname'}, [@$attribute,
  1234. _leading_spaces($root), @$end_command_space])
  1235. .${prepended_elements};
  1236. my $end_line = '';
  1237. if ($root->{'args'}) {
  1238. if ($commands_args_elements{$root->{'cmdname'}}) {
  1239. my $arg_index = 0;
  1240. foreach my $element (@{$commands_args_elements{$root->{'cmdname'}}}) {
  1241. if (defined($root->{'args'}->[$arg_index])) {
  1242. my $in_code;
  1243. $in_code = 1
  1244. if (defined($default_args_code_style{$root->{'cmdname'}})
  1245. and $default_args_code_style{$root->{'cmdname'}}->[$arg_index]);
  1246. push @{$self->{'document_context'}->[-1]->{'monospace'}}, 1
  1247. if ($in_code);
  1248. my $arg;
  1249. if ($arg_index+1 eq scalar(@{$root->{'args'}})) {
  1250. # last argument
  1251. ($arg, $end_line)
  1252. = $self->_convert_argument_and_end_line($root->{'args'}->[$arg_index]);
  1253. } else {
  1254. $arg = $self->_convert($root->{'args'}->[$arg_index]);
  1255. }
  1256. my $spaces = [];
  1257. if ($arg_index != 0) {
  1258. push @$spaces, $self->_leading_spaces_arg(
  1259. $root->{'args'}->[$arg_index]);
  1260. }
  1261. if ($arg ne '' or scalar(@$spaces)) {
  1262. $result .= $self->open_element($element, $spaces).$arg
  1263. .$self->close_element($element);
  1264. }
  1265. pop @{$self->{'document_context'}->[-1]->{'monospace'}}
  1266. if ($in_code);
  1267. } else {
  1268. last;
  1269. }
  1270. $arg_index++;
  1271. }
  1272. } else {
  1273. my $contents_possible_comment;
  1274. # in that case the end of line is in the columnfractions line
  1275. # or in the columnprototypes.
  1276. if ($root->{'cmdname'} eq 'multitable') {
  1277. if (not $root->{'extra'}->{'columnfractions'}) {
  1278. # Like 'prototypes' extra value, but keeping spaces information
  1279. my @prototype_line;
  1280. if (defined $root->{'args'}[0]
  1281. and defined $root->{'args'}[0]->{'type'}
  1282. and $root->{'args'}[0]->{'type'} eq 'block_line_arg') {
  1283. foreach my $content (@{$root->{'args'}[0]{'contents'}}) {
  1284. if ($content->{'type'} and $content->{'type'} eq 'bracketed') {
  1285. push @prototype_line, $content;
  1286. } elsif ($content->{'text'}) {
  1287. # The regexp breaks between characters, with a non space followed
  1288. # by a space or a space followed by non space. It is like \b, but
  1289. # for \s \S, and not \w \W.
  1290. foreach my $prototype_or_space (split /(?<=\S)(?=\s)|(?=\S)(?<=\s)/,
  1291. $content->{'text'}) {
  1292. if ($prototype_or_space =~ /\S/) {
  1293. push @prototype_line, {'text' => $prototype_or_space,
  1294. 'type' => 'row_prototype' };
  1295. } elsif ($prototype_or_space =~ /\s/) {
  1296. push @prototype_line, {'text' => $prototype_or_space,
  1297. 'type' => 'prototype_space' };
  1298. }
  1299. }
  1300. } else {
  1301. # FIXME could this happen? Should be a debug message?
  1302. if (!$content->{'cmdname'}) {
  1303. } elsif ($content->{'cmdname'} eq 'c'
  1304. or $content->{'cmdname'} eq 'comment') {
  1305. } else {
  1306. push @prototype_line, $content;
  1307. }
  1308. }
  1309. }
  1310. $root->{'extra'}->{'prototypes_line'} = \@prototype_line;
  1311. }
  1312. }
  1313. if ($root->{'extra'}
  1314. and $root->{'extra'}->{'prototypes_line'}) {
  1315. $result .= $self->open_element('columnprototypes');
  1316. my $first_proto = 1;
  1317. foreach my $prototype (@{$root->{'extra'}->{'prototypes_line'}}) {
  1318. if ($prototype->{'text'} and $prototype->{'text'} !~ /\S/) {
  1319. if (!$first_proto) {
  1320. my $spaces = $prototype->{'text'};
  1321. chomp($spaces);
  1322. $result .= $spaces;
  1323. }
  1324. } else {
  1325. my $attribute = [];
  1326. if ($prototype->{'type'}
  1327. and $prototype->{'type'} eq 'bracketed') {
  1328. push @$attribute, ('bracketed', 'on');
  1329. push @$attribute, _leading_spaces_before_argument($prototype);
  1330. }
  1331. $result .= $self->open_element('columnprototype', $attribute)
  1332. .$self->_convert($prototype)
  1333. .$self->close_element('columnprototype');
  1334. }
  1335. $first_proto = 0;
  1336. }
  1337. $result .= $self->close_element('columnprototypes');
  1338. $contents_possible_comment
  1339. = $root->{'args'}->[-1]->{'contents'};
  1340. } elsif ($root->{'extra'}
  1341. and $root->{'extra'}->{'columnfractions'}) {
  1342. my $cmd;
  1343. foreach my $content (@{$root->{'args'}->[0]->{'contents'}}) {
  1344. if ($content->{'cmdname'}
  1345. and $content->{'cmdname'} eq 'columnfractions') {
  1346. $cmd = $content;
  1347. last;
  1348. }
  1349. }
  1350. my $attribute = [$self->_texinfo_line($cmd->{'args'}->[0])];
  1351. $result .= $self->open_element('columnfractions', $attribute);
  1352. foreach my $fraction (@{$root->{'extra'}->{'columnfractions'}}) {
  1353. $result .= $self->open_element('columnfraction',
  1354. ['value', $fraction])
  1355. .$self->close_element('columnfraction');
  1356. }
  1357. $result .= $self->close_element('columnfractions');
  1358. $contents_possible_comment
  1359. = $root->{'args'}->[-1]->{'contents'}->[-1]->{'args'}->[-1]->{'contents'}
  1360. if ($root->{'args'}->[-1]->{'contents'}
  1361. and $root->{'args'}->[-1]->{'contents'}->[-1]->{'args'}
  1362. and $root->{'args'}->[-1]->{'contents'}->[-1]->{'args'}->[-1]->{'contents'});
  1363. } else { # bogus multitable
  1364. $result .= "\n";
  1365. }
  1366. } else {
  1367. # get end of lines from @*table.
  1368. my $end_spaces = _end_line_spaces($root,
  1369. 'space_at_end_block_command');
  1370. if (defined($end_spaces)) {
  1371. $end_line .= $end_spaces
  1372. # This also catches block @-commands with no argument that
  1373. # have a bogus argument, such as text on @example line
  1374. #print STDERR "NOT xtable: $root->{'cmdname'}\n"
  1375. # if (!$Texinfo::Common::item_line_commands{$root->{'cmdname'}});
  1376. }
  1377. $contents_possible_comment = $root->{'args'}->[-1]->{'contents'}
  1378. if ($root->{'args'}->[-1]->{'contents'});
  1379. }
  1380. $end_line .= $self->_end_line_or_comment($contents_possible_comment);
  1381. }
  1382. }
  1383. $result .= $end_line;
  1384. unshift @close_elements, $root->{'cmdname'};
  1385. }
  1386. delete $self->{'itemize_line'} if ($self->{'itemize_line'});
  1387. }
  1388. }
  1389. if ($root->{'type'}) {
  1390. if (defined($type_elements{$root->{'type'}})) {
  1391. my $attribute = [];
  1392. if ($root->{'type'} eq 'preformatted') {
  1393. push @$attribute, ('xml:space', 'preserve');
  1394. } elsif ($root->{'type'} eq 'menu_entry') {
  1395. push @$attribute, ('leadingtext', $self->_convert($root->{'args'}->[0]));
  1396. } elsif (($root->{'type'} eq 'menu_entry_node'
  1397. or $root->{'type'} eq 'menu_entry_name')
  1398. and $self->{'pending_menu_entry_separator'}) {
  1399. push @$attribute, ('separator',
  1400. $self->_convert($self->{'pending_menu_entry_separator'}));
  1401. delete $self->{'pending_menu_entry_separator'};
  1402. }
  1403. $result .= $self->open_element($type_elements{$root->{'type'}}, $attribute);
  1404. }
  1405. if ($root->{'type'} eq 'def_line') {
  1406. if ($root->{'cmdname'}) {
  1407. $result .= $self->open_element($root->{'cmdname'}, [_leading_spaces($root)]);
  1408. }
  1409. $result .= $self->open_element('definitionterm');
  1410. $result .= $self->_index_entry($root);
  1411. push @{$self->{'document_context'}->[-1]->{'monospace'}}, 1;
  1412. if ($root->{'extra'} and $root->{'extra'}->{'def_args'}) {
  1413. my $main_command;
  1414. my $alias;
  1415. if ($Texinfo::Common::def_aliases{$root->{'extra'}->{'def_command'}}) {
  1416. $main_command = $Texinfo::Common::def_aliases{$root->{'extra'}->{'def_command'}};
  1417. $alias = 1;
  1418. } else {
  1419. $main_command = $root->{'extra'}->{'def_command'};
  1420. $alias = 0;
  1421. }
  1422. foreach my $arg (@{$root->{'extra'}->{'def_args'}}) {
  1423. my $type = $arg->[0];
  1424. my $content = $self->_convert($arg->[1]);
  1425. if ($type eq 'spaces') {
  1426. $result .= $content;
  1427. } else {
  1428. my $attribute = [];
  1429. if ($type eq 'category' and $alias) {
  1430. push @$attribute, ('automatic', 'on');
  1431. }
  1432. my $element;
  1433. if ($type eq 'name') {
  1434. $element = $defcommand_name_type{$main_command};
  1435. } elsif ($type eq 'arg') {
  1436. $element = 'param';
  1437. } elsif ($type eq 'typearg') {
  1438. $element = 'paramtype';
  1439. } else {
  1440. $element = $type;
  1441. }
  1442. if ($arg->[1]->{'type'}
  1443. and $arg->[1]->{'type'} eq 'bracketed_def_content') {
  1444. push @$attribute, ('bracketed', 'on');
  1445. push @$attribute, _leading_spaces_before_argument($arg->[1]);
  1446. }
  1447. $result .= $self->open_element("def$element", $attribute).$content
  1448. .$self->close_element("def$element");
  1449. }
  1450. }
  1451. }
  1452. pop @{$self->{'document_context'}->[-1]->{'monospace'}};
  1453. $result .= $self->close_element('definitionterm');
  1454. if ($root->{'cmdname'}) {
  1455. $result .= $self->close_element($root->{'cmdname'});
  1456. }
  1457. chomp ($result);
  1458. $result .= "\n";
  1459. }
  1460. }
  1461. if ($root->{'contents'}) {
  1462. my $in_code;
  1463. if ($root->{'cmdname'}
  1464. and $Texinfo::Common::preformatted_code_commands{$root->{'cmdname'}}) {
  1465. $in_code = 1;
  1466. }
  1467. push @{$self->{'document_context'}->[-1]->{'monospace'}}, 1
  1468. if ($in_code);
  1469. if (ref($root->{'contents'}) ne 'ARRAY') {
  1470. cluck "contents not an array($root->{'contents'}).";
  1471. }
  1472. foreach my $content (@{$root->{'contents'}}) {
  1473. $result .= $self->_convert($content);
  1474. }
  1475. pop @{$self->{'document_context'}->[-1]->{'monospace'}}
  1476. if ($in_code);
  1477. }
  1478. my $arg_nr = -1;
  1479. if ($root->{'type'} and $root->{'type'} eq 'menu_entry') {
  1480. foreach my $arg (@{$root->{'args'}}) {
  1481. $arg_nr++;
  1482. # menu_entry_leading_text is added as attribute leadingtext of menu_entry
  1483. # menu_entry_separator is recorded here and then added ass attribute
  1484. # separator
  1485. next if ($arg->{'type'} eq 'menu_entry_leading_text'
  1486. or $arg->{'type'} eq 'menu_entry_separator');
  1487. if ($root->{'args'}->[$arg_nr +1]
  1488. and $root->{'args'}->[$arg_nr +1]->{'type'}
  1489. and $root->{'args'}->[$arg_nr +1]->{'type'} eq 'menu_entry_separator') {
  1490. $self->{'pending_menu_entry_separator'} = $root->{'args'}->[$arg_nr +1];
  1491. }
  1492. my $in_code;
  1493. if ($arg->{'type'} eq 'menu_entry_node') {
  1494. $in_code = 1;
  1495. }
  1496. push @{$self->{'document_context'}->[-1]->{'monospace'}}, 1
  1497. if ($in_code);
  1498. $result .= $self->_convert($arg);
  1499. pop @{$self->{'document_context'}->[-1]->{'monospace'}}
  1500. if ($in_code);
  1501. }
  1502. }
  1503. if ($root->{'type'}) {
  1504. if (defined($type_elements{$root->{'type'}})) {
  1505. $result .= $self->close_element($type_elements{$root->{'type'}});
  1506. }
  1507. }
  1508. $result = '{'.$result.'}'
  1509. if ($root->{'type'} and $root->{'type'} eq 'bracketed'
  1510. and (!$root->{'parent'}->{'type'} or
  1511. ($root->{'parent'}->{'type'} ne 'block_line_arg'
  1512. and $root->{'parent'}->{'type'} ne 'misc_line_arg')));
  1513. foreach my $element (@close_elements) {
  1514. $result .= $self->close_element($element);
  1515. }
  1516. if ($root->{'cmdname'}
  1517. and exists($Texinfo::Common::block_commands{$root->{'cmdname'}})) {
  1518. my $end_command = $root->{'extra'}->{'end_command'};
  1519. if ($self->{'expanded_formats_hash'}->{$root->{'cmdname'}}) {
  1520. } else {
  1521. my $end_line = '';
  1522. if ($end_command) {
  1523. my $end_spaces = _end_line_spaces($end_command, 'spaces_at_end');
  1524. $end_line .= $end_spaces if (defined($end_spaces));
  1525. $end_line
  1526. .= $self->_end_line_or_comment($end_command->{'args'}->[0]->{'contents'})
  1527. if ($end_command->{'args'}->[0]
  1528. and $end_command->{'args'}->[0]->{'contents'});
  1529. } else {
  1530. #$end_line = "\n";
  1531. }
  1532. $result .= $end_line;
  1533. }
  1534. if ($self->{'context_block_commands'}->{$root->{'cmdname'}}) {
  1535. pop @{$self->{'document_context'}};
  1536. }
  1537. # The command is closed either when the corresponding tree element
  1538. # is done, and the command is not associated to an element, or when
  1539. # the element is closed.
  1540. } elsif ((($root->{'type'} and $root->{'type'} eq 'element'
  1541. and $root->{'extra'} and $root->{'extra'}->{'element_command'}
  1542. and !($root->{'extra'}->{'element_command'}->{'cmdname'}
  1543. and $root->{'extra'}->{'element_command'}->{'cmdname'} eq 'node'))
  1544. or ($root->{'cmdname'}
  1545. and $Texinfo::Common::root_commands{$root->{'cmdname'}}
  1546. and $root->{'cmdname'} ne 'node'
  1547. and !($root->{'parent'} and $root->{'parent'}->{'type'}
  1548. and $root->{'parent'}->{'type'} eq 'element'
  1549. and $root->{'parent'}->{'extra'}
  1550. and $root->{'parent'}->{'extra'}->{'element_command'}
  1551. and $root->{'parent'}->{'extra'}->{'element_command'} eq $root)))
  1552. and !$self->get_conf('USE_NODES')) {
  1553. if ($root->{'type'} and $root->{'type'} eq 'element') {
  1554. $root = $root->{'extra'}->{'element_command'};
  1555. }
  1556. my $command = $self->_level_corrected_section($root);
  1557. if (!($root->{'section_childs'} and scalar(@{$root->{'section_childs'}}))
  1558. or $command eq 'top') {
  1559. $result .= $self->close_element($command)."\n";
  1560. my $current = $root;
  1561. while ($current->{'section_up'}
  1562. # the most up element is a virtual sectioning root element, this
  1563. # condition avoids getting into it
  1564. and $current->{'section_up'}->{'cmdname'}
  1565. and !$current->{'section_next'}
  1566. and $self->_level_corrected_section($current->{'section_up'}) ne 'top') {
  1567. $current = $current->{'section_up'};
  1568. $result .= $self->close_element($self->_level_corrected_section($current)) ."\n";
  1569. }
  1570. }
  1571. if ($self->{'pending_bye'}) {
  1572. $result .= $self->{'pending_bye'};
  1573. delete $self->{'pending_bye'};
  1574. }
  1575. } elsif ((($root->{'type'} and $root->{'type'} eq 'element'
  1576. and $root->{'extra'} and $root->{'extra'}->{'element_command'}
  1577. and $root->{'extra'}->{'element_command'}->{'cmdname'}
  1578. and $root->{'extra'}->{'element_command'}->{'cmdname'} eq 'node')
  1579. or ($root->{'cmdname'}
  1580. and $root->{'cmdname'} eq 'node'
  1581. and !($root->{'parent'} and $root->{'parent'}->{'type'}
  1582. and $root->{'parent'}->{'type'} eq 'element'
  1583. and $root->{'parent'}->{'extra'}
  1584. and $root->{'parent'}->{'extra'}->{'element_command'}
  1585. and $root->{'parent'}->{'extra'}->{'element_command'} eq $root)))
  1586. and $self->get_conf('USE_NODES')) {
  1587. #if ($root->{'type'} and $root->{'type'} eq 'element') {
  1588. # $root = $root->{'extra'}->{'element_command'};
  1589. #}
  1590. $result .= $self->close_element('node');
  1591. if ($self->{'pending_bye'}) {
  1592. $result .= $self->{'pending_bye'};
  1593. delete $self->{'pending_bye'};
  1594. }
  1595. }
  1596. return $result;
  1597. }
  1598. 1;
  1599. __END__
  1600. # $Id: template.pod 6140 2015-02-22 23:34:38Z karl $
  1601. # Automatically generated from maintain/template.pod
  1602. =head1 NAME
  1603. Texinfo::Convert::TexinfoXML - Convert Texinfo tree to TexinfoXML
  1604. =head1 SYNOPSIS
  1605. my $converter
  1606. = Texinfo::Convert::TexinfoXML->converter({'parser' => $parser});
  1607. $converter->output($tree);
  1608. $converter->convert($tree);
  1609. $converter->convert_tree($tree);
  1610. =head1 DESCRIPTION
  1611. Texinfo::Convert::TexinfoXML converts a Texinfo tree to TexinfoXML.
  1612. =head1 METHODS
  1613. =over
  1614. =item $converter = Texinfo::Convert::TexinfoXML->converter($options)
  1615. Initialize converter from Texinfo to TexinfoXML.
  1616. The I<$options> hash reference holds options for the converter. In
  1617. this option hash reference a parser object may be associated with the
  1618. I<parser> key. The other options should be configuration options
  1619. described in the Texinfo manual. Those options, when appropriate,
  1620. override the document content.
  1621. See L<Texinfo::Convert::Converter> for more informations.
  1622. =item $converter->output($tree)
  1623. Convert a Texinfo tree I<$tree> and output the result in files as
  1624. described in the Texinfo manual.
  1625. =item $result = $converter->convert($tree)
  1626. Convert a Texinfo tree I<$tree> or tree portion and return
  1627. the resulting output.
  1628. =item $result = $converter->convert_tree($tree)
  1629. Convert a Texinfo tree portion I<$tree> and return the resulting
  1630. output. This function does not try to output a full document but only
  1631. portions. For a full document use C<convert>.
  1632. =back
  1633. =head1 AUTHOR
  1634. Patrice Dumas, E<lt>pertusus@free.frE<gt>
  1635. =head1 COPYRIGHT AND LICENSE
  1636. Copyright 2015 Free Software Foundation, Inc.
  1637. This library is free software; you can redistribute it and/or modify
  1638. it under the terms of the GNU General Public License as published by
  1639. the Free Software Foundation; either version 3 of the License, or (at
  1640. your option) any later version.
  1641. =cut