| blib/lib/Marek/Pod/HTML.pm | |||
|---|---|---|---|
| Criterion | Covered | Total | % |
| statement | 492 | 708 | 69.4 |
| branch | 135 | 284 | 47.5 |
| condition | 27 | 115 | 23.4 |
| subroutine | 32 | 41 | 78.0 |
| pod | 6 | 14 | 42.8 |
| total | 692 | 1162 | 59.5 |
| line | stmt | bran | cond | sub | pod | time | code |
|---|---|---|---|---|---|---|---|
| 1 | # -*- perl -*- | ||||||
| 2 | ############################################################################# | ||||||
| 3 | # Pod/HTML.pm -- converts Pod to HTML | ||||||
| 4 | # | ||||||
| 5 | # Copyright (C) 1999,2000 by Marek Rouchal. All rights reserved. | ||||||
| 6 | # This package is free software; you can redistribute it and/or modify | ||||||
| 7 | # it under the same terms as Perl itself. | ||||||
| 8 | ############################################################################# | ||||||
| 9 | |||||||
| 10 | package Marek::Pod::HTML; | ||||||
| 11 | |||||||
| 12 | =head1 NAME | ||||||
| 13 | |||||||
| 14 | Marek::Pod::HTML - convert Perl POD documents to HTML | ||||||
| 15 | |||||||
| 16 | =head1 SYNOPSIS | ||||||
| 17 | |||||||
| 18 | use Marek::Pod::HTML; | ||||||
| 19 | pod2html( { -dir => 'html' }, | ||||||
| 20 | { '/usr/lib/perl5/Pod/HTML.pm' => 'Pod::HTML' }); | ||||||
| 21 | |||||||
| 22 | =head1 DESCRIPTION | ||||||
| 23 | |||||||
| 24 | THIS IS PRELIMINARY SOFTWARE! The C |
||||||
| 25 | preliminary until a regular place in CPAN is found. | ||||||
| 26 | |||||||
| 27 | B |
||||||
| 28 | files. This is meant to be a successor of Tom Christiansen's original | ||||||
| 29 | Pod::HTML. However it is not a plug-in replacement as there are | ||||||
| 30 | significant differences. | ||||||
| 31 | |||||||
| 32 | When no document is specified, this script acts as a filter | ||||||
| 33 | (from STDIN to STDOUT). No index or table of contents is generated. | ||||||
| 34 | In any other case one or more corresponding F<.html> file(s) is/are | ||||||
| 35 | created. | ||||||
| 36 | |||||||
| 37 | Optionally B |
||||||
| 38 | As it makes use of the L |
||||||
| 39 | also generate Postscript output using L |
||||||
| 40 | |||||||
| 41 | There is a hook for customization of the translation result before | ||||||
| 42 | writing the actual HTML. | ||||||
| 43 | |||||||
| 44 | =head2 Pod directives and their translation | ||||||
| 45 | |||||||
| 46 | The following section gives an overview of the translation equivalences. | ||||||
| 47 | |||||||
| 48 | =over 4 | ||||||
| 49 | |||||||
| 50 | =item C<=head>I |
||||||
| 51 | |||||||
| 52 | A heading is turned into a HTML heading, e.g. C<=head1> corresponds to | ||||||
| 53 | C |
||||||
| 54 | |||||||
| 55 | =item S |
||||||
| 56 | |||||||
| 57 | Itemized lists are turned into either C |
||||||
| 58 | C |
||||||
| 59 | depending on whether the first item in the list starts with a digit, | ||||||
| 60 | a number or nothing, or anything else, respectively. | ||||||
| 61 | |||||||
| 62 | =item C |
||||||
| 63 | |||||||
| 64 | Paragraphs starting with C<=for html> or encapsulated in | ||||||
| 65 | C |
||||||
| 66 | All other C<=for>/C<=begin> paragraphs are ignored. | ||||||
| 67 | |||||||
| 68 | =item C |
||||||
| 69 | |||||||
| 70 | Turned into bold text using E |
||||||
| 71 | |||||||
| 72 | =item C |
||||||
| 73 | |||||||
| 74 | Turned into italic text using E |
||||||
| 75 | |||||||
| 76 | =item C |
||||||
| 77 | |||||||
| 78 | Turned into monospaced (typewriter) text using | ||||||
| 79 | E |
||||||
| 80 | |||||||
| 81 | =item C |
||||||
| 82 | |||||||
| 83 | Pod entities are mapped to the corresponding HTML characters or | ||||||
| 84 | entities. The most important HTML entities (e.g. C |
||||||
| 85 | are recognized. See also L |
||||||
| 86 | |||||||
| 87 | =item C |
||||||
| 88 | |||||||
| 89 | All whitespace in this sequence is turned into C< >, i.e. | ||||||
| 90 | non-breakable spaces. | ||||||
| 91 | |||||||
| 92 | =item C |
||||||
| 93 | |||||||
| 94 | The text of this sequence is included in the index (along with all | ||||||
| 95 | non-trivial C<=item> entries), pointing to the place of its ocurrence | ||||||
| 96 | in the text. | ||||||
| 97 | |||||||
| 98 | =item C |
||||||
| 99 | |||||||
| 100 | Pod hyperlinks are turned into active HTML hyperlinks if the destination | ||||||
| 101 | has been found in the Pod documents processed in this conversion session. | ||||||
| 102 | Otherwise the link text is simply underlined. | ||||||
| 103 | |||||||
| 104 | Note: There is no caching mechanism for deliberate reasons: a) One does | ||||||
| 105 | not run huge conversion jobs three times a day, so performance is not | ||||||
| 106 | the most important goal, b) caching is hard to code, and c) although | ||||||
| 107 | following conversion jobs could make profit of the existing cache of | ||||||
| 108 | destination nodes in the already converted documents, these will not | ||||||
| 109 | notice that some of their previously unresolved links may now be ok | ||||||
| 110 | because the required document has been converted. Conclusion: Run | ||||||
| 111 | B |
||||||
| 112 | you will have a consistent state. | ||||||
| 113 | |||||||
| 114 | As a special unofficial feature HTML hyperlinks are also supported: | ||||||
| 115 | C |
||||||
| 116 | |||||||
| 117 | =back | ||||||
| 118 | |||||||
| 119 | =head2 Options | ||||||
| 120 | |||||||
| 121 | B |
||||||
| 122 | B |
||||||
| 123 | |||||||
| 124 | =over 4 | ||||||
| 125 | |||||||
| 126 | =item B<-converter> I |
||||||
| 127 | |||||||
| 128 | The converter class to use, defaults to C |
||||||
| 129 | for simple customization, see also L<"Customizing">. | ||||||
| 130 | |||||||
| 131 | =item B<-suffix> I |
||||||
| 132 | |||||||
| 133 | Use this string for links to other converted Pod documents. The default | ||||||
| 134 | is C<.html> and also sets the filename suffix unless B<-filesuffix> has | ||||||
| 135 | been specified. The dot must be included! | ||||||
| 136 | |||||||
| 137 | =item B<-filesuffix> I |
||||||
| 138 | |||||||
| 139 | Use this string as a suffix for the output HTML files. This does not | ||||||
| 140 | change the suffix used in the hyperlinks to different documents. This | ||||||
| 141 | feature is meant to be used if some (Makefile based) postprocessing | ||||||
| 142 | of the generated files has to be performed, but without having to | ||||||
| 143 | adapt the links. | ||||||
| 144 | |||||||
| 145 | =item B<-dir> I |
||||||
| 146 | |||||||
| 147 | Write the generated HTML files (can be a directory hierarchy) to this | ||||||
| 148 | path. The default is the current working directory. | ||||||
| 149 | |||||||
| 150 | =item B<-libpods> I |
||||||
| 151 | |||||||
| 152 | This option activates a highly magical feature: The C<=item> nodes of | ||||||
| 153 | the specified Pod documents (given by Pod name, e.g. C |
||||||
| 154 | serve as destinations for highlighted text in all converted Pod | ||||||
| 155 | documents. Typical usage: When converting your Perl installation's | ||||||
| 156 | documentation, you may want to say | ||||||
| 157 | |||||||
| 158 | pod2html -libpods perlfunc,perlvar,perlrun -script -inc | ||||||
| 159 | |||||||
| 160 | then you will get a hyperlink to L |
||||||
| 161 | C |
||||||
| 162 | |||||||
| 163 | =item B<-localtoc> I |
||||||
| 164 | |||||||
| 165 | This is by default true, so that at the top of the page a local | ||||||
| 166 | table of contents with all the C<=head>I |
||||||
| 167 | |||||||
| 168 | =item B<-navigation> I |
||||||
| 169 | |||||||
| 170 | When using the default customization, this flag enables or disables | ||||||
| 171 | the navigation in the header of each Pod document. | ||||||
| 172 | |||||||
| 173 | =item B<-toc> I |
||||||
| 174 | |||||||
| 175 | If true, a table of contents is built from the processed Pod documents. | ||||||
| 176 | |||||||
| 177 | =item B<-idx> I |
||||||
| 178 | |||||||
| 179 | If true, an index is built from all C<=item>s of the processed Pod | ||||||
| 180 | documents. | ||||||
| 181 | |||||||
| 182 | =item B<-idxopt> I |
||||||
| 183 | |||||||
| 184 | Options for index building. Default is "item,x", which means that | ||||||
| 185 | item strings as well as text marked up with C |
||||||
| 186 | generate entries in the index. | ||||||
| 187 | |||||||
| 188 | =item B<-tocname> I |
||||||
| 189 | |||||||
| 190 | Use I |
||||||
| 191 | F |
||||||
| 192 | |||||||
| 193 | =item B<-idxname> I |
||||||
| 194 | |||||||
| 195 | Use I |
||||||
| 196 | F |
||||||
| 197 | |||||||
| 198 | =item B<-toctitle> I |
||||||
| 199 | |||||||
| 200 | The string that is used as the heading of the table of contents. | ||||||
| 201 | Default is `Table of Contents'. | ||||||
| 202 | |||||||
| 203 | =item B<-idxtitle> I |
||||||
| 204 | |||||||
| 205 | The string that is used as the heading of the table of contents. | ||||||
| 206 | Default is `Index'. | ||||||
| 207 | |||||||
| 208 | =item B<-ps> I |
||||||
| 209 | |||||||
| 210 | In addition to HTML, generate also Postscript output. The suffix is | ||||||
| 211 | F<.ps>. | ||||||
| 212 | |||||||
| 213 | =item B<-psdir> | ||||||
| 214 | |||||||
| 215 | The root directory where to write Postscript files. Defaults to the | ||||||
| 216 | same as B<-dir>. | ||||||
| 217 | |||||||
| 218 | =item B<-psfont> I |
||||||
| 219 | |||||||
| 220 | Generate Postscript files using the font I |
||||||
| 221 | `Helvetica'. | ||||||
| 222 | |||||||
| 223 | =item B<-papersize> I |
||||||
| 224 | |||||||
| 225 | Generate Postscript files using the paper size I |
||||||
| 226 | `A4'. | ||||||
| 227 | |||||||
| 228 | =item B<-warnings> I |
||||||
| 229 | |||||||
| 230 | When processing the first pass, print warnings. See L |
||||||
| 231 | for more information on warnings. Note: This can procude a lot of | ||||||
| 232 | output if the Pod source does not correspond to strict guidelines. | ||||||
| 233 | |||||||
| 234 | =item B<-stylesheet> I | ||||||
| 235 | |||||||
| 236 | The (optional) link to a style sheet, which is included in the resulting HTML | ||||||
| 237 | as | ||||||
| 238 | |||||||
| 239 | |||||||
| 240 | |||||||
| 241 | =item B<-banner> I |
||||||
| 242 | |||||||
| 243 | If true, a banner is included at the bottom of the generated | ||||||
| 244 | page. Default is true. | ||||||
| 245 | |||||||
| 246 | =back | ||||||
| 247 | |||||||
| 248 | =cut | ||||||
| 249 | |||||||
| 250 | 4 | 4 | 1142 | use strict; | |||
| 4 | 9 | ||||||
| 4 | 214 | ||||||
| 251 | 4 | 4 | 20 | use vars qw($VERSION @ISA @EXPORT @EXPORT_OK); | |||
| 4 | 9 | ||||||
| 4 | 443 | ||||||
| 252 | |||||||
| 253 | require Exporter; | ||||||
| 254 | 4 | 4 | 30 | use File::Basename; | |||
| 4 | 10 | ||||||
| 4 | 320 | ||||||
| 255 | 4 | 4 | 23 | use File::Path; | |||
| 4 | 8 | ||||||
| 4 | 244 | ||||||
| 256 | 4 | 4 | 22 | use Pod::Parser; | |||
| 4 | 13 | ||||||
| 4 | 168 | ||||||
| 257 | 4 | 4 | 5044 | use Pod::Checker; | |||
| 4 | 42500 | ||||||
| 4 | 548 | ||||||
| 258 | 4 | 4 | 3936 | use HTML::Entities; | |||
| 4 | 23705 | ||||||
| 4 | 340 | ||||||
| 259 | 4 | 4 | 4660 | use HTML::TreeBuilder; | |||
| 4 | 110895 | ||||||
| 4 | 56 | ||||||
| 260 | |||||||
| 261 | $VERSION = '0.49'; | ||||||
| 262 | @ISA = qw(Exporter Pod::Parser); | ||||||
| 263 | |||||||
| 264 | @EXPORT = qw(); | ||||||
| 265 | @EXPORT_OK = qw(&pod2html &_construct_file_name); | ||||||
| 266 | |||||||
| 267 | ############################################################################## | ||||||
| 268 | |||||||
| 269 | # this is used everywhere | ||||||
| 270 | my $NBSP = HTML::Entities::decode_entities(' '); | ||||||
| 271 | |||||||
| 272 | # This makes HTML::Element print properly opened and closed tags |
||||||
| 273 | $HTML::Tagset::optionalEndTag{'p'} = 0; | ||||||
| 274 | |||||||
| 275 | ##--------------------------------- | ||||||
| 276 | ## Function definitions begin here | ||||||
| 277 | ##--------------------------------- | ||||||
| 278 | |||||||
| 279 | sub pod2html { | ||||||
| 280 | 2 | 2 | 0 | 4 | my (%opts,%PODS); | ||
| 281 | # options hash | ||||||
| 282 | 2 | 50 | 8 | if(ref $_[0]) { | |||
| 283 | 2 | 3 | %opts = %{shift()}; | ||||
| 2 | 23 | ||||||
| 284 | } | ||||||
| 285 | # PODs hash | ||||||
| 286 | 2 | 50 | 10 | if(ref $_[0]) { | |||
| 287 | 2 | 3 | %PODS = %{shift()}; | ||||
| 2 | 7 | ||||||
| 288 | } | ||||||
| 289 | else { | ||||||
| 290 | 0 | 0 | %PODS = map { $_ => do { | ||||
| 0 | 0 | ||||||
| 291 | 0 | 0 | 0 | my $name = ref($_) ? 'STDIN' : $_; | |||
| 292 | 0 | 0 | $name =~ s:^.*/::; | ||||
| 293 | 0 | 0 | $name =~ s:\.(pod|pm|pl)$::i; | ||||
| 294 | 0 | 0 | 0 | $name =~ s:\.(bat|exe|cmd)$::i if($^O =~ /win|os2/i); | |||
| 295 | 0 | 0 | $name; | ||||
| 296 | } } @_; | ||||||
| 297 | } | ||||||
| 298 | # set defaults | ||||||
| 299 | 2 | 13 | _default(\%opts, '-converter', 'Marek::Pod::HTML'); | ||||
| 300 | 2 | 5 | _default(\%opts, '-filter', 0); | ||||
| 301 | 2 | 5 | _default(\%opts, '-suffix', '.html'); | ||||
| 302 | 2 | 6 | _default(\%opts, '-filesuffix', $opts{-suffix}); | ||||
| 303 | 2 | 5 | _default(\%opts, '-dir', '.'); | ||||
| 304 | 2 | 6 | _default(\%opts, '-libpods', ''); | ||||
| 305 | 2 | 5 | _default(\%opts, '-localtoc', 1); | ||||
| 306 | 2 | 5 | _default(\%opts, '-navigation', 1); | ||||
| 307 | 2 | 4 | _default(\%opts, '-toc', 1); | ||||
| 308 | 2 | 4 | _default(\%opts, '-idx', 1); | ||||
| 309 | 2 | 5 | _default(\%opts, '-tocname', 'podtoc'); | ||||
| 310 | 2 | 4 | _default(\%opts, '-idxname', 'podindex'); | ||||
| 311 | 2 | 4 | _default(\%opts, '-toctitle', 'Table of Contents'); | ||||
| 312 | 2 | 4 | _default(\%opts, '-idxtitle', 'Index'); | ||||
| 313 | 2 | 5 | _default(\%opts, '-ps', 0); | ||||
| 314 | 2 | 6 | _default(\%opts, '-psdir', $opts{-dir}); | ||||
| 315 | 2 | 5 | _default(\%opts, '-psfont', 'Helvetica'); | ||||
| 316 | 2 | 5 | _default(\%opts, '-papersize', 'A4'); | ||||
| 317 | 2 | 5 | _default(\%opts, '-warnings', 0); | ||||
| 318 | 2 | 4 | _default(\%opts, '-verbose', 0); | ||||
| 319 | 2 | 5 | _default(\%opts, '-stylesheet', ''); | ||||
| 320 | 2 | 5 | _default(\%opts, '-banner', 1); | ||||
| 321 | 2 | 6 | _default(\%opts, '-idxopt', 'item,x'); | ||||
| 322 | # only a single file? | ||||||
| 323 | 2 | 50 | 11 | if($opts{-filter}) { | |||
| 324 | 0 | 0 | $opts{-toc} = $opts{-idx} = 0; | ||||
| 325 | } | ||||||
| 326 | # nothing to do | ||||||
| 327 | 2 | 50 | 16 | return 0 unless(keys %PODS); | |||
| 328 | |||||||
| 329 | ################################################### | ||||||
| 330 | # first pass: run Pod::Checker on all the files | ||||||
| 331 | # and extract hyperlink nodes | ||||||
| 332 | ################################################### | ||||||
| 333 | |||||||
| 334 | 2 | 20 | my $cache = Pod::Cache->new(); | ||||
| 335 | 2 | 27 | foreach my $infile (sort keys %PODS) { | ||||
| 336 | 3 | 50 | 85 | warn "\n+++ Scanning $infile\n" if($opts{-verbose}); | |||
| 337 | ## Now create a pod scanner, based on Pod::Checker | ||||||
| 338 | 3 | 50 | 75 | my $scanner = Pod::Checker->new(-warnings => $opts{'-warnings'}, | |||
| 339 | -name => $PODS{$infile} || 'STDIN'); | ||||||
| 340 | |||||||
| 341 | ## Now check the pod document for errors | ||||||
| 342 | 3 | 827 | $scanner->parse_from_file($infile, \*STDERR); | ||||
| 343 | |||||||
| 344 | ## Return the number of errors found | ||||||
| 345 | 3 | 10641 | my $errs = $scanner->num_errors(); | ||||
| 346 | 3 | 50 | 28 | if($errs == -1) { | |||
| 50 | |||||||
| 347 | 0 | 0 | 0 | warn "Warning: No POD in `$infile', skipping\n" | |||
| 348 | if($opts{'-warnings'}); | ||||||
| 349 | 0 | 0 | next; | ||||
| 350 | } | ||||||
| 351 | elsif($errs > 0) { | ||||||
| 352 | 0 | 0 | warn "Warning: Conversion may be garbled because of errors above\n"; | ||||
| 353 | } | ||||||
| 354 | |||||||
| 355 | 3 | 12 | my $name = $scanner->name(); | ||||
| 356 | # also allow X<> entries as link destinations | ||||||
| 357 | 3 | 23 | my @nodes = _unique_ids($scanner->node()); #,$scanner->idx()); | ||||
| 358 | |||||||
| 359 | # hack for perlrun - get the nodes for all switches | ||||||
| 360 | 3 | 50 | 12 | if($name eq 'perlrun') { | |||
| 361 | 0 | 0 | my @addnodes = (); | ||||
| 362 | 0 | 0 | my %have = map { $_->[0] => 1 } @nodes; | ||||
| 0 | 0 | ||||||
| 363 | 0 | 0 | foreach(@nodes) { | ||||
| 364 | 0 | 0 | 0 | 0 | if($_->[0] =~ /^(-\w)\S/ && !$have{$1}++) { | ||
| 365 | 0 | 0 | push(@addnodes, [ $1 , $_->[1] ]); | ||||
| 366 | } | ||||||
| 367 | } | ||||||
| 368 | 0 | 0 | push(@nodes,@addnodes); | ||||
| 369 | } | ||||||
| 370 | |||||||
| 371 | ## remember settings | ||||||
| 372 | $cache->item( | ||||||
| 373 | 3 | 18 | -file => $infile, | ||||
| 374 | -page => $name, | ||||||
| 375 | -nodes => [ @nodes ], | ||||||
| 376 | -idx => [ _unique_ids($scanner->idx()) ]); | ||||||
| 377 | } # end first pass | ||||||
| 378 | |||||||
| 379 | # build lookup table for libpods | ||||||
| 380 | 2 | 110 | my %lib; | ||||
| 381 | 2 | 10 | foreach my $pod (split(/,/, $opts{-libpods})) { | ||||
| 382 | 0 | 0 | warn "\n+++ Adding $pod to autolink lookup table\n"; | ||||
| 383 | 0 | 0 | my $have_it = $cache->find_page($pod); | ||||
| 384 | 0 | 0 | 0 | unless($have_it) { | |||
| 385 | 0 | 0 | warn "Error: Could not find the library POD '$pod'.\n"; | ||||
| 386 | 0 | 0 | next; | ||||
| 387 | } | ||||||
| 388 | 0 | 0 | foreach ($have_it->nodes()) { | ||||
| 389 | 0 | 0 | my ($name,$id) = @$_; | ||||
| 390 | # only add significant nodes. The first libpod takes precedence | ||||||
| 391 | 0 | 0 | 0 | 0 | if($name ne '*' && !defined $lib{$name}) { | ||
| 392 | 0 | 0 | $lib{$name} = [ $have_it->page(), $id ]; | ||||
| 393 | } | ||||||
| 394 | } | ||||||
| 395 | } | ||||||
| 396 | |||||||
| 397 | ####################################################### | ||||||
| 398 | # second pass: do the conversion | ||||||
| 399 | ####################################################### | ||||||
| 400 | |||||||
| 401 | # Schwartzian transform to reduce sort effort | ||||||
| 402 | # compare case-insensitively, only in case of equality compare | ||||||
| 403 | # case sensitively | ||||||
| 404 | 3 | 50 | 15 | my @cache = map { $_->[0] } sort { $a->[1] cmp $b->[1] || $a->[0]->page() cmp $b->[0]->page() } | |||
| 1 | 13 | ||||||
| 3 | 26 | ||||||
| 405 | 2 | 18 | map { [ $_ , lc($_->page()) ] } $cache->item(); | ||||
| 406 | 2 | 5 | my @index; | ||||
| 407 | # propagate some of the options | ||||||
| 408 | my %conv_opts; | ||||||
| 409 | 2 | 6 | for(qw(-suffix -navigation -localtoc -toc -tocname -toctitle -idx | ||||
| 410 | -idxname -idxtitle -idxopt -stylesheet -verbose -banner)) { | ||||||
| 411 | 26 | 49 | $conv_opts{$_} = $opts{$_}; | ||||
| 412 | } | ||||||
| 413 | |||||||
| 414 | 2 | 7 | $conv_opts{-cache} = $cache; | ||||
| 415 | 2 | 6 | $conv_opts{-lib} = \%lib; | ||||
| 416 | 2 | 8 | $conv_opts{-mycache} = ''; | ||||
| 417 | 2 | 5 | $conv_opts{'-next'} = ''; | ||||
| 418 | 2 | 5 | $conv_opts{-prev} = ''; | ||||
| 419 | |||||||
| 420 | 2 | 9 | for(my $i = 0; $i< scalar(@cache); $i++) { | ||||
| 421 | ## Now create a pod converter | ||||||
| 422 | 3 | 39 | $_ = $cache[$i]; | ||||
| 423 | 3 | 12 | my $infile = $_->file(); | ||||
| 424 | 3 | 50 | 25 | warn "\n+++ Converting $infile\n" if($opts{-verbose}); | |||
| 425 | |||||||
| 426 | 3 | 31 | my %current_opts = %conv_opts; | ||||
| 427 | 3 | 13 | $current_opts{-name} = $_->page(); | ||||
| 428 | 3 | 19 | $current_opts{-mycache} = $_; | ||||
| 429 | 3 | 100 | 21 | $current_opts{'-next'} = ($i < $#cache) ? $cache[$i+1]->page() : | |||
| 100 | |||||||
| 430 | ($current_opts{-idx} ? $current_opts{-idxname} : ''); | ||||||
| 431 | 3 | 100 | 19 | $current_opts{-prev} = ($i > 0) ? $cache[$i-1]->page() : | |||
| 100 | |||||||
| 432 | ($current_opts{-toc} ? $current_opts{-tocname} : ''); | ||||||
| 433 | |||||||
| 434 | 3 | 39 | my $converter = $opts{-converter}->new(%current_opts); | ||||
| 435 | |||||||
| 436 | ## Now convert it | ||||||
| 437 | 3 | 10 | my $outfile; | ||||
| 438 | 3 | 11 | my $outpath = _construct_file_name($_->page(), 0, $opts{-filesuffix}); | ||||
| 439 | 3 | 50 | 12 | if($opts{-filter}) { | |||
| 440 | 0 | 0 | $outfile = \*STDOUT; | ||||
| 441 | } | ||||||
| 442 | else { | ||||||
| 443 | 3 | 50 | 15 | $outfile = $opts{-outfile} ? $opts{-outfile} : | |||
| 444 | $opts{-dir} . '/' . $outpath; | ||||||
| 445 | 3 | 151 | my $ddir = dirname($outfile); | ||||
| 446 | 3 | 50 | 56 | mkpath($ddir) unless(-d $ddir); | |||
| 447 | } | ||||||
| 448 | 3 | 647 | $converter->parse_from_file($infile,$outfile); | ||||
| 449 | 3 | 14 | $_->description($converter->description()); | ||||
| 450 | 3 | 27 | $_->path($outpath); | ||||
| 451 | 3 | 22 | push(@index, map { $$_[1] = "$outpath#$$_[1]"; $$_[2] = $current_opts{-name}; $_ } | ||||
| 12 | 27 | ||||||
| 12 | 26 | ||||||
| 12 | 69 | ||||||
| 452 | $converter->indices()); | ||||||
| 453 | # dump postscript if requested | ||||||
| 454 | 3 | 50 | 15 | if($opts{-ps}) { | |||
| 455 | 0 | 0 | my $pspath = $opts{-psdir} . '/' . _construct_file_name( | ||||
| 456 | $_->page(), 0, '.ps'); | ||||||
| 457 | 0 | 0 | my $ddir = dirname($pspath); | ||||
| 458 | 0 | 0 | 0 | mkpath($ddir) unless(-d $ddir); | |||
| 459 | 0 | 0 | _write_ps($pspath,$converter->{_html},\%opts); | ||||
| 460 | } | ||||||
| 461 | |||||||
| 462 | # kill the HTML tree, required by HTML::Element | ||||||
| 463 | 3 | 20 | $converter->{_html}->delete(); | ||||
| 464 | |||||||
| 465 | } # end second pass | ||||||
| 466 | |||||||
| 467 | ################################################ | ||||||
| 468 | # create a table of contents | ||||||
| 469 | ################################################ | ||||||
| 470 | |||||||
| 471 | 2 | 100 | 90 | if($opts{-toc}) { | |||
| 472 | # Style classes in TOC: | ||||||
| 473 | # H1 CLASS=PODTOC : Table of contents heading | ||||||
| 474 | # TD CLASS=PODTOC_NAME : POD name (appears as link) | ||||||
| 475 | # TD CLASS=PODTOC_DESC : Description | ||||||
| 476 | 1 | 50 | 5 | warn "\n+++ Creating table of contents\n" if($opts{-verbose}); | |||
| 477 | |||||||
| 478 | # create a Marek::Pod::HTML object to gain access to the customize | ||||||
| 479 | # method | ||||||
| 480 | 1 | 10 | my $tocobj = bless { %conv_opts, '-next' => $cache[0]->page() }, | ||||
| 481 | $opts{-converter}; | ||||||
| 482 | 1 | 32 | ($tocobj->{_html}, $tocobj->{_head}, $tocobj->{_body}) = | ||||
| 483 | _basic_html(); | ||||||
| 484 | 1 | 7 | $tocobj->depth(0); | ||||
| 485 | |||||||
| 486 | 1 | 5 | my $table = HTML::Element->new('table'); | ||||
| 487 | 1 | 24 | $tocobj->{_body}->push_content($table, "\n"); | ||||
| 488 | |||||||
| 489 | 1 | 25 | foreach(sort { lc $a->page() cmp lc $b->page() } $cache->item()) { | ||||
| 0 | 0 | ||||||
| 490 | 1 | 13 | my $anchor = HTML::Element->new('a', CLASS => 'POD_NAVLINK', | ||||
| 491 | href => $_->path()); | ||||||
| 492 | 1 | 122 | $anchor->push_content($_->page()); | ||||
| 493 | 1 | 18 | my $row = HTML::Element->new('tr'); | ||||
| 494 | 1 | 18 | my $name = HTML::Element->new('td', CLASS => 'PODTOC_NAME'); | ||||
| 495 | 1 | 21 | my $text = HTML::Element->new('td', CLASS => 'PODTOC_DESC'); | ||||
| 496 | 1 | 21 | $row->push_content($name, $text); | ||||
| 497 | 1 | 17 | $table->push_content($row,"\n"); | ||||
| 498 | 1 | 16 | $name->push_content($anchor); | ||||
| 499 | # $desc is either a simple string or a reference to an array | ||||||
| 500 | # of HTML::Element's | ||||||
| 501 | 1 | 50 | 12 | if(my $desc = $_->description()) { | |||
| 502 | 1 | 50 | 8 | $text->push_content(ref $desc ? @{$desc} : $desc); | |||
| 1 | 4 | ||||||
| 503 | # correct POD_LINKs | ||||||
| 504 | 1 | 15 | foreach($text->find_by_tag_name('a')) { | ||||
| 505 | 0 | 0 | my $class = $_->attr('CLASS'); | ||||
| 506 | 0 | 0 | 0 | 0 | next unless($class && $class eq 'POD_LINK'); | ||
| 507 | 0 | 0 | my $href = $_->attr('href'); | ||||
| 508 | 0 | 0 | $href =~ s:^(\.\./)+::; # the TOC is on top! | ||||
| 509 | 0 | 0 | $_->attr('href', $href); | ||||
| 510 | } | ||||||
| 511 | } | ||||||
| 512 | else { | ||||||
| 513 | # we have no description | ||||||
| 514 | 0 | 0 | $text->push_content(' |
||||
| 515 | } | ||||||
| 516 | } | ||||||
| 517 | |||||||
| 518 | # add all the HTML gimmicks | ||||||
| 519 | 1 | 29 | $tocobj->customize($opts{-toctitle}); | ||||
| 520 | |||||||
| 521 | # write HTML file | ||||||
| 522 | 1 | 19 | _write_html($tocobj->{_html}, | ||||
| 523 | "$opts{-dir}/$opts{-tocname}$opts{-filesuffix}",undef,$opts{-verbose}); | ||||||
| 524 | |||||||
| 525 | # dump postscript output | ||||||
| 526 | 1 | 50 | 6 | if($opts{-ps}) { | |||
| 527 | 0 | 0 | _write_ps("$opts{-psdir}/$opts{-tocname}.ps", | ||||
| 528 | $tocobj->{_html}, \%opts); | ||||||
| 529 | } | ||||||
| 530 | |||||||
| 531 | # remove the HTML | ||||||
| 532 | 1 | 5 | $tocobj->{_html}->delete(); | ||||
| 533 | } | ||||||
| 534 | |||||||
| 535 | ################################################ | ||||||
| 536 | # create an index | ||||||
| 537 | ################################################ | ||||||
| 538 | |||||||
| 539 | 2 | 100 | 48 | if($opts{-idx}) { | |||
| 540 | # Style classes in Index: | ||||||
| 541 | # H1 CLASS=PODIDX : Index heading | ||||||
| 542 | # H2 CLASS=PODIDX : Index section heading | ||||||
| 543 | 1 | 50 | 6 | warn "\n+++ Creating index\n" if($opts{-verbose}); | |||
| 544 | |||||||
| 545 | 1 | 7 | my $idxobj = bless { %conv_opts, '-prev' => $cache[-1]->page() }, | ||||
| 546 | $opts{-converter}; | ||||||
| 547 | 1 | 18 | ($idxobj->{_html}, $idxobj->{_head}, $idxobj->{_body}) = | ||||
| 548 | _basic_html(); | ||||||
| 549 | 1 | 4 | $idxobj->depth(0); | ||||
| 550 | |||||||
| 551 | # now generate the real index | ||||||
| 552 | |||||||
| 553 | 1 | 3 | my %idx; | ||||
| 554 | 1 | 4 | foreach(@index) { | ||||
| 555 | 7 | 14 | my ($text,$id, $page) = @$_; | ||||
| 556 | 7 | 10 | my $key; | ||||
| 557 | 7 | 50 | 26 | if($text =~ /^\W*([a-z])/i) { | |||
| 0 | |||||||
| 558 | 7 | 12 | $key = uc($1); | ||||
| 559 | } | ||||||
| 560 | elsif($text =~ /^\W*([0-9])/) { | ||||||
| 561 | 0 | 0 | $key = '0-9'; | ||||
| 562 | } | ||||||
| 563 | else { | ||||||
| 564 | 0 | 0 | $key = 'Sym'; | ||||
| 565 | } | ||||||
| 566 | 7 | 9 | push(@{$idx{$key}{$text}}, [ $id, $page ]); | ||||
| 7 | 29 | ||||||
| 567 | |||||||
| 568 | } | ||||||
| 569 | 1 | 6 | foreach my $key (qw(Sym 0-9), sort keys %idx) { | ||||
| 570 | 4 | 100 | 10 | next unless(defined $idx{$key}); | |||
| 571 | 2 | 9 | my $heading = HTML::Element->new('h2', CLASS => 'PODIDX'); | ||||
| 572 | 2 | 54 | $heading->push_content($key); | ||||
| 573 | 2 | 26 | $idxobj->{_body}->push_content($heading, "\n"); | ||||
| 574 | 2 | 34 | foreach my $text (sort {lc $a cmp lc $b} keys %{$idx{$key}}) { | ||||
| 3 | 7 | ||||||
| 2 | 10 | ||||||
| 575 | 4 | 66 | $idxobj->{_body}->push_content($text); | ||||
| 576 | 4 | 42 | foreach(@{$idx{$key}{$text}}) { | ||||
| 4 | 9 | ||||||
| 577 | 7 | 67 | my $anchor = HTML::Element->new('a', HREF => $$_[0], | ||||
| 578 | CLASS => 'POD_NAVLINK'); | ||||||
| 579 | 7 | 210 | $anchor->push_content("[$$_[1]]"); | ||||
| 580 | 7 | 94 | $idxobj->{_body}->push_content($NBSP x 2, $anchor); | ||||
| 581 | } | ||||||
| 582 | 4 | 64 | $idxobj->{_body}->push_content(HTML::Element->new('br'),"\n"); | ||||
| 583 | } | ||||||
| 584 | 2 | 60 | delete $idx{$key}; | ||||
| 585 | } | ||||||
| 586 | |||||||
| 587 | # add all the HTML gimmicks | ||||||
| 588 | 1 | 4 | $idxobj->customize($opts{-idxtitle}); | ||||
| 589 | |||||||
| 590 | 1 | 18 | _write_html($idxobj->{_html}, | ||||
| 591 | "$opts{-dir}/$opts{-idxname}$opts{-filesuffix}",undef,$opts{-verbose}); | ||||||
| 592 | |||||||
| 593 | # dump postscript if requested | ||||||
| 594 | 1 | 50 | 6 | if($opts{-ps}) { | |||
| 595 | 0 | 0 | _write_ps("$opts{-psdir}/$opts{-idxname}.ps", | ||||
| 596 | $idxobj->{_html}, \%opts); | ||||||
| 597 | } | ||||||
| 598 | |||||||
| 599 | # remove the HTML::Element objects | ||||||
| 600 | 1 | 6 | $idxobj->{_html}->delete(); | ||||
| 601 | } | ||||||
| 602 | } | ||||||
| 603 | |||||||
| 604 | # write HTML tree as PostScript | ||||||
| 605 | sub _write_ps | ||||||
| 606 | { | ||||||
| 607 | 0 | 0 | 0 | my ($file,$html,$opts) = @_; | |||
| 608 | |||||||
| 609 | 0 | 0 | 0 | warn "Writing PostScript $file\n" if($opts->{-verbose}); | |||
| 610 | 0 | 0 | 0 | unless(open(PS,">$file")) { | |||
| 611 | 0 | 0 | warn "Error: Cannot write '$file': $!\n"; | ||||
| 612 | 0 | 0 | return 0; | ||||
| 613 | } | ||||||
| 614 | 0 | 0 | require HTML::FormatPS; | ||||
| 615 | 0 | 0 | my $formatter = new HTML::FormatPS | ||||
| 616 | FontFamily => $opts->{-psfont}, | ||||||
| 617 | HorizontalMargin => HTML::FormatPS::mm(15), | ||||||
| 618 | VerticalMargin => HTML::FormatPS::mm(20), | ||||||
| 619 | PaperSize => $opts->{-papersize}; | ||||||
| 620 | 0 | 0 | print PS $formatter->format($html); | ||||
| 621 | 0 | 0 | close(PS); | ||||
| 622 | } | ||||||
| 623 | |||||||
| 624 | ##------------------------------- | ||||||
| 625 | ## Method definitions begin here | ||||||
| 626 | ##------------------------------- | ||||||
| 627 | |||||||
| 628 | =head2 OO Interface | ||||||
| 629 | |||||||
| 630 | The B |
||||||
| 631 | to customize the converter for special requirements or for | ||||||
| 632 | proprietary conversion tools. This section describes the most important | ||||||
| 633 | methods. | ||||||
| 634 | |||||||
| 635 | =over 4 | ||||||
| 636 | |||||||
| 637 | =item new() | ||||||
| 638 | |||||||
| 639 | Create a new converter object. Idiom: | ||||||
| 640 | |||||||
| 641 | my $converter = new Marek::Pod::HTML; | ||||||
| 642 | |||||||
| 643 | =cut | ||||||
| 644 | |||||||
| 645 | # set up a new object | ||||||
| 646 | sub new { | ||||||
| 647 | 3 | 3 | 1 | 5 | my $this = shift; | ||
| 648 | 3 | 33 | 18 | my $class = ref($this) || $this; | |||
| 649 | 3 | 21 | my %params = @_; | ||||
| 650 | 3 | 27 | my $self = {%params}; | ||||
| 651 | 3 | 11 | bless $self, $class; | ||||
| 652 | 3 | 9 | $self->initialize(); | ||||
| 653 | 3 | 69 | return $self; | ||||
| 654 | } | ||||||
| 655 | |||||||
| 656 | # initalize, set defaults | ||||||
| 657 | sub initialize { | ||||||
| 658 | 3 | 3 | 0 | 8 | my $self = shift; | ||
| 659 | |||||||
| 660 | ## Options | ||||||
| 661 | # the POD name | ||||||
| 662 | 3 | 50 | 23 | $self->{-name} ||= ''; | |||
| 663 | |||||||
| 664 | # the suffix for links | ||||||
| 665 | 3 | 50 | 9 | $self->{-suffix} ||= '.html'; | |||
| 666 | |||||||
| 667 | # the short description, taken from NAME | ||||||
| 668 | 3 | 50 | 16 | $self->{-description} ||= ''; | |||
| 669 | |||||||
| 670 | # generate local navigation | ||||||
| 671 | 3 | 50 | 12 | $self->{-localtoc} = 1 unless(defined $self->{-localtoc}); | |||
| 672 | |||||||
| 673 | # global navigation | ||||||
| 674 | 3 | 50 | 10 | $self->{-navigation} = 1 unless(defined $self->{-navigation}); | |||
| 675 | |||||||
| 676 | ## Internal | ||||||
| 677 | # counter for headings and items | ||||||
| 678 | 3 | 5 | $self->{_current_node} = 0; | ||||
| 679 | 3 | 8 | $self->{_current_idx} = 0; | ||||
| 680 | |||||||
| 681 | # a stack for nested lists | ||||||
| 682 | 3 | 5 | $self->{_list_stack} = []; | ||||
| 683 | |||||||
| 684 | # a stack for nested lists | ||||||
| 685 | 3 | 6 | $self->{_current_anchor} = ''; | ||||
| 686 | |||||||
| 687 | # no parser errors here, we've seen them in the first pass | ||||||
| 688 | 3 | 0 | 32 | $self->SUPER::errorsub(sub { return 1; }); | |||
| 0 | 0 | ||||||
| 689 | } | ||||||
| 690 | |||||||
| 691 | =item customize($name) | ||||||
| 692 | |||||||
| 693 | This method is called after the complete Pod source code has been | ||||||
| 694 | converted, thus allowing for customizations like title, navigation | ||||||
| 695 | and footer. I<$name> should contain the page title. | ||||||
| 696 | This method also reads properties of the current Marek::Pod::HTML object | ||||||
| 697 | to do the customizations. It is executed for each POD file processed and | ||||||
| 698 | -- if enabled -- the index and the table of contents. | ||||||
| 699 | |||||||
| 700 | X |
||||||
| 701 | customization by writing a new module that inherits from B |
||||||
| 702 | |||||||
| 703 | package POD::HTML::mystyle; | ||||||
| 704 | use Marek::Pod::HTML qw(pod2html); | ||||||
| 705 | use vars qw(@ISA @EXPORT @EXPORT_OK); | ||||||
| 706 | require Exporter; | ||||||
| 707 | @ISA = qw(Marek::Pod::HTML); | ||||||
| 708 | @EXPORT_OK = qw(&pod2html); | ||||||
| 709 | sub customize { | ||||||
| 710 | my ($self,$name) = @_; | ||||||
| 711 | # if you just want to add things, use this line first: | ||||||
| 712 | $self->SUPER::customize($name); | ||||||
| 713 | # do your own things here | ||||||
| 714 | #... | ||||||
| 715 | } | ||||||
| 716 | |||||||
| 717 | For complete customization, it is a good starting point to copy the | ||||||
| 718 | customize method from B |
||||||
| 719 | |||||||
| 720 | You can access all the converter's methods and properties through the | ||||||
| 721 | C<$self->method()> and C<$self->{-property}> syntax, respectively. | ||||||
| 722 | |||||||
| 723 | =cut | ||||||
| 724 | |||||||
| 725 | # this method can be overridden to customize the HTML output | ||||||
| 726 | sub customize { | ||||||
| 727 | 5 | 5 | 1 | 11 | my ($self,$name) = @_; | ||
| 728 | |||||||
| 729 | # set document class | ||||||
| 730 | 5 | 21 | my $root = HTML::Element->new('~declaration', text => | ||||
| 731 | 'DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN"'); | ||||||
| 732 | 5 | 134 | $root->push_content("\n", $self->{_html}); | ||||
| 733 | 5 | 91 | $self->{_html} = $root; | ||||
| 734 | |||||||
| 735 | # include stylesheet | ||||||
| 736 | 5 | 50 | 23 | if($self->{-stylesheet}) { | |||
| 737 | 0 | 0 | my $css = HTML::Element->new('link', TYPE => "text/css", | ||||
| 738 | REL => "stylesheet", HREF => $self->{-stylesheet}); | ||||||
| 739 | 0 | 0 | $self->{_head}->push_content($css, "\n"); | ||||
| 740 | } | ||||||
| 741 | |||||||
| 742 | # customize the title | ||||||
| 743 | 5 | 17 | my $title = HTML::Element->new('title'); | ||||
| 744 | 5 | 50 | 120 | $title->push_content($self->{-title} || $name || 'POD'); | |||
| 745 | 5 | 69 | $self->{_head}->push_content($title, "\n"); | ||||
| 746 | |||||||
| 747 | # prepend big heading | ||||||
| 748 | 5 | 50 | 97 | if($name) { | |||
| 749 | 5 | 19 | my $titleh = HTML::Element->new('h1', CLASS => 'POD_TITLE'); | ||||
| 750 | 5 | 133 | $titleh->push_content($name); | ||||
| 751 | 5 | 73 | $self->{_body}->unshift_content("\n",$titleh,"\n", | ||||
| 752 | HTML::Element->new('hr')); | ||||||
| 753 | } | ||||||
| 754 | |||||||
| 755 | 5 | 50 | 244 | if($self->{-navigation}) { | |||
| 756 | # add navigation | ||||||
| 757 | 5 | 22 | my $table = HTML::Element->new('table', width => '100%'); | ||||
| 758 | 5 | 124 | $self->{_body}->unshift_content("\n",$table); | ||||
| 759 | |||||||
| 760 | 5 | 89 | my $tr = HTML::Element->new('tr'); | ||||
| 761 | 5 | 88 | $table->push_content("\n",$tr,"\n"); | ||||
| 762 | |||||||
| 763 | 5 | 100 | 110 | if($self->{'-next'}) { | |||
| 764 | 3 | 13 | my $td = HTML::Element->new('td', align => 'left', width => '1%'); | ||||
| 765 | 3 | 98 | my $anchor = HTML::Element->new('a', CLASS => 'POD_NAVLINK', | ||||
| 766 | href => _construct_file_name($self->{'-next'}, $self->depth(), $self->{-suffix})); | ||||||
| 767 | 3 | 97 | $anchor->push_content('Next:', HTML::Element->new('br'), $self->{'-next'}); | ||||
| 768 | 3 | 121 | $td->push_content($anchor); | ||||
| 769 | 3 | 46 | $tr->push_content($td); | ||||
| 770 | } | ||||||
| 771 | |||||||
| 772 | 5 | 100 | 55 | if($self->{'-prev'}) { | |||
| 773 | 3 | 13 | my $td = HTML::Element->new('td', align => 'left', width => '1%'); | ||||
| 774 | 3 | 91 | my $anchor = HTML::Element->new('a', CLASS => 'POD_NAVLINK', | ||||
| 775 | href => _construct_file_name($self->{'-prev'}, $self->depth(), $self->{-suffix})); | ||||||
| 776 | 3 | 92 | $anchor->push_content('Previous:', HTML::Element->new('br'), $self->{'-prev'}); | ||||
| 777 | 3 | 109 | $td->push_content($anchor); | ||||
| 778 | 3 | 52 | $tr->push_content($td); | ||||
| 779 | } | ||||||
| 780 | |||||||
| 781 | 5 | 59 | my $filler = HTML::Element->new('td', width => '90%'); | ||||
| 782 | 5 | 120 | $filler->push_content($NBSP); | ||||
| 783 | 5 | 63 | $tr->push_content($filler); | ||||
| 784 | |||||||
| 785 | 5 | 100 | 67 | if($self->{-toc}) { | |||
| 786 | 3 | 17 | my $td = HTML::Element->new('td', align => 'right', width => '1%'); | ||||
| 787 | 3 | 92 | my $anchor = HTML::Element->new('a', CLASS => 'POD_NAVLILNK', | ||||
| 788 | href => _construct_file_name($self->{-tocname}, $self->depth(), $self->{-suffix})); | ||||||
| 789 | 3 | 85 | my $text = '['.$self->{-toctitle}.']'; | ||||
| 790 | 3 | 56 | $text =~ s/\s+/$NBSP/g; | ||||
| 791 | 3 | 11 | $anchor->push_content($text); | ||||
| 792 | 3 | 38 | $td->push_content($anchor); | ||||
| 793 | 3 | 38 | $tr->push_content($td); | ||||
| 794 | } | ||||||
| 795 | |||||||
| 796 | 5 | 100 | 51 | if($self->{-idx}) { | |||
| 797 | 3 | 10 | my $td = HTML::Element->new('td', align => 'right', width => '1%'); | ||||
| 798 | 3 | 93 | my $anchor = HTML::Element->new('a', CLASS => 'POD_NAVLINK', | ||||
| 799 | href => _construct_file_name($self->{-idxname}, $self->depth(), $self->{-suffix})); | ||||||
| 800 | 3 | 96 | my $text = '['.$self->{-idxtitle}.']'; | ||||
| 801 | 3 | 6 | $text =~ s/\s+/$NBSP/g; | ||||
| 802 | 3 | 9 | $anchor->push_content($text); | ||||
| 803 | 3 | 42 | $td->push_content($anchor); | ||||
| 804 | 3 | 44 | $tr->push_content($td); | ||||
| 805 | } | ||||||
| 806 | } # end navigation | ||||||
| 807 | |||||||
| 808 | # for finding the way back to the top | ||||||
| 809 | 5 | 49 | my $anchor = HTML::Element->new('a', CLASS => 'POD_NAVLINK', | ||||
| 810 | name => 'Pod_TOP_OF_PAGE'); | ||||||
| 811 | 5 | 156 | $self->{_body}->unshift_content("\n",$anchor); | ||||
| 812 | |||||||
| 813 | # customize the footer | ||||||
| 814 | 5 | 94 | $anchor = HTML::Element->new('a', CLASS => 'POD_NAVLINK', | ||||
| 815 | href => '#Pod_TOP_OF_PAGE'); | ||||||
| 816 | 5 | 153 | $anchor->push_content('[Top]'); | ||||
| 817 | 5 | 71 | $self->{_body}->push_content(HTML::Element->new('hr'), "\n", $anchor, "\n"); | ||||
| 818 | 5 | 50 | 464 | $self->{_body}->push_content("Generated by Marek::Pod::HTML $VERSION on " . localtime() . "\n") | |||
| 819 | if($self->{-banner}); | ||||||
| 820 | } | ||||||
| 821 | |||||||
| 822 | =item depth() | ||||||
| 823 | |||||||
| 824 | Returns how "deep" this documents is buried in the directory | ||||||
| 825 | hierarchy. This value is derived from the C<-name> property and is | ||||||
| 826 | for instance 1 for B |
||||||
| 827 | |||||||
| 828 | =cut | ||||||
| 829 | |||||||
| 830 | # which hierarchy level does this POD have? | ||||||
| 831 | sub depth { | ||||||
| 832 | 16 | 16 | 1 | 36 | my ($self,$depth) = @_; | ||
| 833 | 16 | 100 | 105 | if(defined $depth) { | |||
| 100 | |||||||
| 834 | 2 | 5 | $self->{-depth} = $depth; | ||||
| 835 | } elsif(!defined $self->{-depth}) { | ||||||
| 836 | 3 | 8 | $self->{-depth} = 0; | ||||
| 837 | 3 | 15 | $self->{-depth}++ while($self->{-name} =~ /::/g); | ||||
| 838 | } | ||||||
| 839 | 16 | 72 | $self->{-depth}; | ||||
| 840 | } | ||||||
| 841 | |||||||
| 842 | =item description() | ||||||
| 843 | |||||||
| 844 | Sets or retrieves the short description from the C<=head1 NAME> section of | ||||||
| 845 | the Pod document. Empty if there is no such section. | ||||||
| 846 | |||||||
| 847 | =cut | ||||||
| 848 | |||||||
| 849 | # The POD description, taken out of NAME if present | ||||||
| 850 | sub description { | ||||||
| 851 | 9 | 100 | 9 | 1 | 88 | return (@_ > 1) ? ($_[0]->{-description} = $_[1]) : $_[0]->{-description}; | |
| 852 | } | ||||||
| 853 | |||||||
| 854 | =item indices() | ||||||
| 855 | |||||||
| 856 | Add a new item or return the list of index entries of this document. | ||||||
| 857 | Each index is represented by an index text (in HTML) and the unique id | ||||||
| 858 | (i.e. the anchor name) of the index entry in the HTML document. | ||||||
| 859 | |||||||
| 860 | =cut | ||||||
| 861 | |||||||
| 862 | # store/retrieve index entries | ||||||
| 863 | sub indices { | ||||||
| 864 | 15 | 15 | 1 | 25 | my $self = shift; | ||
| 865 | 15 | 100 | 47 | unless(defined $self->{_indices}) { | |||
| 866 | 3 | 8 | $self->{_indices} = []; | ||||
| 867 | } | ||||||
| 868 | 15 | 100 | 31 | if(@_) { | |||
| 869 | 12 | 15 | push(@{$self->{_indices}}, [ @_ ]); | ||||
| 12 | 41 | ||||||
| 870 | 12 | 443 | return $self->{_indices}->[-1]; | ||||
| 871 | } | ||||||
| 872 | else { | ||||||
| 873 | 3 | 5 | return @{$self->{_indices}}; | ||||
| 3 | 11 | ||||||
| 874 | } | ||||||
| 875 | } | ||||||
| 876 | |||||||
| 877 | =item name() | ||||||
| 878 | |||||||
| 879 | Set/retrieve the C<-name> property, i.e. the canonical Pod name | ||||||
| 880 | (e.g. C |
||||||
| 881 | |||||||
| 882 | =back | ||||||
| 883 | |||||||
| 884 | See the F |
||||||
| 885 | you may use in your code, but beware: things may change there without | ||||||
| 886 | notice! | ||||||
| 887 | |||||||
| 888 | =cut | ||||||
| 889 | |||||||
| 890 | # set and/or retrieve canonical name of POD | ||||||
| 891 | sub name { | ||||||
| 892 | 3 | 50 | 3 | 1 | 26 | return (@_ > 1) ? ($_[0]->{-name} = $_[1]) : $_[0]->{-name}; | |
| 893 | } | ||||||
| 894 | |||||||
| 895 | ## overrides for Pod::Parser | ||||||
| 896 | |||||||
| 897 | # things to do at start of POD | ||||||
| 898 | sub begin_input { | ||||||
| 899 | 3 | 3 | 0 | 7 | my $self = shift; | ||
| 900 | |||||||
| 901 | 3 | 10 | ($self->{_html}, $self->{_head}, $self->{_body}) = | ||||
| 902 | _basic_html(); | ||||||
| 903 | 3 | 8 | $self->{_current} = $self->{_body}; | ||||
| 904 | 3 | 227 | $self->{_current_head1_title} = ''; | ||||
| 905 | } | ||||||
| 906 | |||||||
| 907 | # things to do at end of POD | ||||||
| 908 | sub end_pod { | ||||||
| 909 | 3 | 3 | 0 | 137 | my $self = shift; | ||
| 910 | 3 | 24 | my $out_fh = $self->output_handle(); | ||||
| 911 | #delete $self->{_p_for_reuse}; | ||||||
| 912 | 3 | 7 | delete $self->{_current}; | ||||
| 913 | |||||||
| 914 | # close any lists left | ||||||
| 915 | 3 | 5 | while(@{$self->{_list_stack}}) { | ||||
| 3 | 13 | ||||||
| 916 | 0 | 0 | my $list = shift(@{$self->{_list_stack}}); | ||||
| 0 | 0 | ||||||
| 917 | 0 | 0 | warn "Warning: autoclosing list at EOF\n"; | ||||
| 918 | # nothing to do thanks to HTML::Element | ||||||
| 919 | } | ||||||
| 920 | |||||||
| 921 | ## add local TOC | ||||||
| 922 | 3 | 50 | 13 | if($self->{-localtoc}) { | |||
| 923 | 3 | 12 | $self->_local_toc(); | ||||
| 924 | } | ||||||
| 925 | |||||||
| 926 | ## Do any page customizations | ||||||
| 927 | 3 | 94 | $self->customize($self->name()); | ||||
| 928 | |||||||
| 929 | # dump it | ||||||
| 930 | 3 | 76 | _write_html($self->{_html},$self->output_file(),$out_fh,$self->{-verbose}); | ||||
| 931 | 3 | 324 | 1; | ||||
| 932 | } | ||||||
| 933 | |||||||
| 934 | sub _write_html | ||||||
| 935 | { | ||||||
| 936 | 5 | 5 | 11 | my ($obj, $file, $handle,$verbose) = @_; | |||
| 937 | 5 | 50 | 23 | warn "Writing HTML $file\n" if($verbose); | |||
| 938 | 5 | 20 | my $html = $obj->as_HTML() . "\n"; | ||||
| 939 | 5 | 100 | 25400 | unless($handle) { | |||
| 940 | 2 | 50 | 221 | unless(open(OUT, ">$file")) { | |||
| 941 | 0 | 0 | warn "Error: Cannot write: $!\n"; | ||||
| 942 | 0 | 0 | return 0; | ||||
| 943 | } | ||||||
| 944 | 2 | 14 | print OUT $html; | ||||
| 945 | 2 | 82 | close(OUT); | ||||
| 946 | } else { | ||||||
| 947 | 3 | 27 | print $handle $html; | ||||
| 948 | } | ||||||
| 949 | 5 | 11 | 1; | ||||
| 950 | } | ||||||
| 951 | |||||||
| 952 | # expand a POD command | ||||||
| 953 | sub command { | ||||||
| 954 | 28 | 28 | 0 | 647 | my ($self, $command, $paragraph, $line_num, $pod_para) = @_; | ||
| 955 | 28 | 147 | my ($file, $line) = $pod_para->file_line; | ||||
| 956 | |||||||
| 957 | # Heading | ||||||
| 958 | 28 | 100 | 147 | if ($command =~ /^head(\d)/) { | |||
| 100 | |||||||
| 100 | |||||||
| 50 | |||||||
| 0 | |||||||
| 0 | |||||||
| 0 | |||||||
| 959 | 9 | 15 | my $n = $1; | ||||
| 960 | |||||||
| 961 | # close any lists left | ||||||
| 962 | 9 | 12 | while(@{$self->{_list_stack}}) { | ||||
| 9 | 28 | ||||||
| 963 | 0 | 0 | my $list = shift(@{$self->{_list_stack}}); | ||||
| 0 | 0 | ||||||
| 964 | 0 | 0 | warn "Warning: autoclosing list at $command" | ||||
| 965 | . " at line $line_num of file $file\n"; | ||||||
| 966 | 0 | 0 | $self->{_current} = $list->parent(); | ||||
| 967 | } | ||||||
| 968 | |||||||
| 969 | # expand the heading's text | ||||||
| 970 | 9 | 41 | $paragraph =~ s/[\s\n]+$//; | ||||
| 971 | 9 | 25 | my @title = $self->interpolate($paragraph, $line_num); | ||||
| 972 | |||||||
| 973 | # retrieve the heading's id | ||||||
| 974 | 9 | 69 | my $count = ($self->{_current_node})++; | ||||
| 975 | 9 | 9 | my ($node,$id) = @{$self->{-mycache}->{-nodes}->[$count]}; | ||||
| 9 | 31 | ||||||
| 976 | |||||||
| 977 | # make and |
||||||
| 978 | # levels. By special request of Achim Bohnet ;-) | ||||||
| 979 | 9 | 48 | my $heading = HTML::Element->new('h'.($n + 1), CLASS => "POD_HEAD$n"); | ||||
| 980 | 9 | 294 | my $anchor = HTML::Element->new('a', name => $id); | ||||
| 981 | 9 | 209 | $self->{_current_anchor} = $id; | ||||
| 982 | 9 | 25 | $anchor->push_content(@title); | ||||
| 983 | 9 | 146 | $heading->push_content($anchor); | ||||
| 984 | 9 | 128 | $self->{_current}->push_content($heading,"\n"); | ||||
| 985 | |||||||
| 986 | # save heading details for later reference | ||||||
| 987 | 9 | 100 | 153 | if($n == 1) { | |||
| 988 | 6 | 21 | $self->{_current_head1_title} = $heading->as_text(); | ||||
| 989 | } | ||||||
| 990 | 9 | 50 | 169 | if($self->{-localtoc}) { | |||
| 991 | 9 | 11 | push(@{$self->{_toc}}, [ $n, $id, | ||||
| 9 | 41 | ||||||
| 992 | HTML::Element->clone_list(@title) ]); | ||||||
| 993 | } | ||||||
| 994 | } | ||||||
| 995 | # Start of List | ||||||
| 996 | elsif ($command eq 'over') { | ||||||
| 997 | 4 | 8 | $self->{_current_anchor} = ''; | ||||
| 998 | 4 | 15 | $paragraph =~ s/[\s\n]+$//; | ||||
| 999 | 4 | 7 | unshift(@{$self->{_list_stack}}, | ||||
| 4 | 25 | ||||||
| 1000 | Pod::List->new(-indent => $paragraph, | ||||||
| 1001 | -parent => $self->{_current})); | ||||||
| 1002 | } | ||||||
| 1003 | |||||||
| 1004 | # a list item | ||||||
| 1005 | elsif ($command eq 'item') { | ||||||
| 1006 | # Check for an open list | ||||||
| 1007 | 11 | 50 | 14 | unless(@{$self->{_list_stack}}) { | |||
| 11 | 30 | ||||||
| 1008 | 0 | 0 | unshift(@{$self->{_list_stack}}, | ||||
| 0 | 0 | ||||||
| 1009 | Pod::List->new(-indent => 4, -parent => | ||||||
| 1010 | $self->{_current})); | ||||||
| 1011 | 0 | 0 | warn "Warning: =item without =over, auto-opening `=over 4'" | ||||
| 1012 | . " at line $line_num of file $file\n"; | ||||||
| 1013 | } | ||||||
| 1014 | 11 | 19 | my $list = $self->{_list_stack}[0]; | ||||
| 1015 | 11 | 45 | $paragraph =~ s/[\s\n]+$//; | ||||
| 1016 | 11 | 100 | 61 | unless($list->type()) { | |||
| 100 | |||||||
| 1017 | # determine type of list | ||||||
| 1018 | 4 | 50 | 66 | 65 | if($paragraph =~ s/^()\s*\d+\.?\s*/$1/) { | ||
| 100 | |||||||
| 1019 | # an ordered list | ||||||
| 1020 | 0 | 0 | $list->type('ol'); | ||||
| 1021 | 0 | 0 | $list->rx('^()\s*\d+\.?\s*'); | ||||
| 1022 | } | ||||||
| 1023 | # artificial intelligence: look behind opening tags | ||||||
| 1024 | elsif($paragraph =~ s/^((\s*\w<)*)\s*[*]\s*/$1/ || | ||||||
| 1025 | $paragraph =~ s/^\s*$//) { | ||||||
| 1026 | # a bulleted list | ||||||
| 1027 | 2 | 5 | $list->type('ul'); | ||||
| 1028 | 2 | 12 | $list->rx('^((\s*\w<)*)\s*[*]\s*'); | ||||
| 1029 | } | ||||||
| 1030 | else { | ||||||
| 1031 | # a definition list | ||||||
| 1032 | 2 | 8 | $list->type('dl'); | ||||
| 1033 | } | ||||||
| 1034 | 4 | 33 | $list->tag(HTML::Element->new($list->type(), CLASS => 'POD_LIST') | ||||
| 1035 | )->push_content("\n"); | ||||||
| 1036 | 4 | 182 | $self->{_current}->push_content($list->tag(),"\n"); | ||||
| 1037 | } elsif(my $rx = $list->rx()) { | ||||||
| 1038 | # simplify the item text | ||||||
| 1039 | 4 | 231 | $paragraph =~ s/$rx/$1/; | ||||
| 1040 | } | ||||||
| 1041 | |||||||
| 1042 | # retrieve node id | ||||||
| 1043 | 11 | 127 | my $count = ($self->{_current_node})++; | ||||
| 1044 | 11 | 15 | my ($node,$id) = @{$self->{-mycache}->{-nodes}->[$count]}; | ||||
| 11 | 41 | ||||||
| 1045 | 11 | 19 | $self->{_current_anchor} = $id; | ||||
| 1046 | |||||||
| 1047 | 11 | 26 | my @text = $self->interpolate($paragraph, $line_num); | ||||
| 1048 | |||||||
| 1049 | 11 | 92 | my $item; | ||||
| 1050 | 11 | 38 | my $anchor = HTML::Element->new('a', name => $id); | ||||
| 1051 | 11 | 100 | 375 | if($list->type() eq 'dl') { | |||
| 1052 | 5 | 29 | my $dt; | ||||
| 1053 | 5 | 16 | my $content = $list->tag()->content(); | ||||
| 1054 | 5 | 50 | 33 | 122 | if(defined $content && ref($content) && @$content && | ||
| 33 | |||||||
| 66 | |||||||
| 66 | |||||||
| 66 | |||||||
| 1055 | ref($content->[-1]) && $content->[-1]->tag() eq 'dd' && | ||||||
| 1056 | $content->[-1]->is_empty()) { | ||||||
| 1057 | 0 | 0 | $dt = $content->[-1]; | ||||
| 1058 | 0 | 0 | $dt->tag('dt'); | ||||
| 1059 | } else { | ||||||
| 1060 | 5 | 66 | $dt = HTML::Element->new('dt', CLASS => 'POD_ITEM'); | ||||
| 1061 | 5 | 126 | $list->tag()->push_content($dt); | ||||
| 1062 | } | ||||||
| 1063 | 5 | 89 | $dt->push_content($anchor,"\n"); | ||||
| 1064 | 5 | 91 | $anchor->push_content(@text); | ||||
| 1065 | 5 | 63 | $item = HTML::Element->new('dd'); | ||||
| 1066 | 5 | 131 | $self->{_last_p_by} = 'dd'; | ||||
| 1067 | } else { | ||||||
| 1068 | 6 | 43 | $item = HTML::Element->new('li', CLASS => 'POD_ITEM'); | ||||
| 1069 | 6 | 100 | 143 | if(length $paragraph) { | |||
| 1070 | 3 | 9 | my $p = HTML::Element->new('p'); | ||||
| 1071 | 3 | 52 | $p->push_content(@text); | ||||
| 1072 | 3 | 43 | $anchor->push_content($p); | ||||
| 1073 | } else { | ||||||
| 1074 | 3 | 7 | $anchor->push_content(@text); | ||||
| 1075 | } | ||||||
| 1076 | 6 | 60 | $item->push_content($anchor); | ||||
| 1077 | 6 | 84 | $item->push_content("\n"); | ||||
| 1078 | } | ||||||
| 1079 | 11 | 88 | $list->tag()->push_content($item); | ||||
| 1080 | 11 | 171 | $self->{_current} = $item; | ||||
| 1081 | |||||||
| 1082 | 11 | 50 | 54 | if($self->{-idxopt} =~ /(^|,)item(,|$)/i) { | |||
| 1083 | # save item html text for later reference | ||||||
| 1084 | 11 | 100 | 33 | 292 | $self->indices(_to_text(@text),$id) | ||
| 1085 | if($paragraph =~ /^\s*(\w<\s*)*(\S*)/ && $2); | ||||||
| 1086 | } | ||||||
| 1087 | } | ||||||
| 1088 | |||||||
| 1089 | # End of a list | ||||||
| 1090 | elsif ($command eq 'back') { | ||||||
| 1091 | 4 | 9 | $self->{_current_anchor} = ''; | ||||
| 1092 | 4 | 4 | my $list = shift(@{$self->{_list_stack}}); | ||||
| 4 | 11 | ||||||
| 1093 | 4 | 50 | 11 | unless($list) { | |||
| 1094 | 0 | 0 | warn "Warning: =back without =over, ignoring" | ||||
| 1095 | . " at line $line_num of file $file\n"; | ||||||
| 1096 | } | ||||||
| 1097 | else { | ||||||
| 1098 | 4 | 16 | $self->{_current} = $list->parent(); | ||||
| 1099 | } | ||||||
| 1100 | } | ||||||
| 1101 | |||||||
| 1102 | # 'for' converter paragraph | ||||||
| 1103 | elsif ($command eq 'for') { | ||||||
| 1104 | 0 | 0 | $self->{_current_anchor} = ''; | ||||
| 1105 | 0 | 0 | $paragraph =~ s/[\s\n]+$//s; | ||||
| 1106 | 0 | 0 | 0 | 0 | if($paragraph =~ s/^[\s\n]*(\S+)[\s\n]*// && lc($1) eq 'html') { | ||
| 1107 | 0 | 0 | my $curr = $self->{_current}; | ||||
| 1108 | 0 | 0 | my $p = _get_last_p_or_new($curr, 'POD_RAW'); | ||||
| 1109 | 0 | 0 | $self->_push_raw_html($p,$paragraph); | ||||
| 1110 | } | ||||||
| 1111 | } | ||||||
| 1112 | |||||||
| 1113 | # 'begin' converter brace | ||||||
| 1114 | elsif ($command eq 'begin') { | ||||||
| 1115 | 0 | 0 | $self->{_current_anchor} = ''; | ||||
| 1116 | 0 | 0 | 0 | unless($paragraph =~ /(\S+)/) { | |||
| 1117 | 0 | 0 | warn "Warning: =begin without parameter, ignoring" | ||||
| 1118 | . " at line $line_num of file $file\n"; | ||||||
| 1119 | } | ||||||
| 1120 | else { | ||||||
| 1121 | 0 | 0 | $self->{_begin} = lc($1); | ||||
| 1122 | 0 | 0 | 0 | if($self->{_begin} eq 'html') { | |||
| 1123 | # set up a raw HTML storage | ||||||
| 1124 | 0 | 0 | $self->{_raw_html} = ''; | ||||
| 1125 | } | ||||||
| 1126 | } | ||||||
| 1127 | } | ||||||
| 1128 | |||||||
| 1129 | # 'end' converter brace | ||||||
| 1130 | elsif ($command eq 'end') { | ||||||
| 1131 | 0 | 0 | $self->{_current_anchor} = ''; | ||||
| 1132 | 0 | 0 | $self->{_begin} = undef; | ||||
| 1133 | # do I have html? | ||||||
| 1134 | 0 | 0 | 0 | if($self->{_raw_html}) { | |||
| 1135 | # try to find a preceding tag |
||||||
| 1136 | 0 | 0 | my $curr = $self->{_current}; | ||||
| 1137 | 0 | 0 | my $p = _get_last_p_or_new($curr, 'POD_RAW'); | ||||
| 1138 | 0 | 0 | $self->_push_raw_html($p,$self->{_raw_html}); | ||||
| 1139 | 0 | 0 | delete $self->{_raw_html}; | ||||
| 1140 | } | ||||||
| 1141 | } | ||||||
| 1142 | # ignore all the rest | ||||||
| 1143 | } | ||||||
| 1144 | |||||||
| 1145 | sub _get_last_p_or_new | ||||||
| 1146 | { | ||||||
| 1147 | 0 | 0 | 0 | my ($curr,$class) = @_; | |||
| 1148 | 0 | 0 | my $p; | ||||
| 1149 | 0 | 0 | my $content = $curr->content(); | ||||
| 1150 | 0 | 0 | 0 | 0 | if(defined $content && ref($content) && @$content && | ||
| 0 | |||||||
| 0 | |||||||
| 0 | |||||||
| 1151 | ref($content->[-2]) && $content->[-2]->tag() eq 'p') { | ||||||
| 1152 | 0 | 0 | $p = $content->[-2]; | ||||
| 1153 | } else { # need a new one | ||||||
| 1154 | 0 | 0 | $p = HTML::Element->new('p', CLASS => $class); | ||||
| 1155 | 0 | 0 | $curr->push_content($p,"\n"); | ||||
| 1156 | } | ||||||
| 1157 | 0 | 0 | $p; | ||||
| 1158 | } | ||||||
| 1159 | |||||||
| 1160 | # process a verbatim paragraph | ||||||
| 1161 | sub verbatim { | ||||||
| 1162 | 1 | 1 | 0 | 2 | my ($self, $paragraph, $line_num, $pod_para) = @_; | ||
| 1163 | |||||||
| 1164 | 1 | 3 | $self->{_current_anchor} = ''; | ||||
| 1165 | # strip trailing whitespace | ||||||
| 1166 | 1 | 5 | $paragraph =~ s/[\s\n]+$//s; | ||||
| 1167 | |||||||
| 1168 | 1 | 50 | 4 | unless(length($paragraph)) { | |||
| 0 | |||||||
| 0 | |||||||
| 1169 | # just an empty line | ||||||
| 1170 | 1 | 4 | $self->{_current}->push_content(HTML::Element->new('p'), "\n"); | ||||
| 1171 | } | ||||||
| 1172 | elsif(!$self->{_begin}) { | ||||||
| 1173 | # a regular paragraph | ||||||
| 1174 | 0 | 0 | my $pre; | ||||
| 1175 | 0 | 0 | my $content = $self->{_current}->content(); | ||||
| 1176 | # reuse last if immediate predecessor |
||||||
| 1177 | 0 | 0 | 0 | 0 | if(defined $content && ref($content) && @$content && | ||
| 0 | |||||||
| 0 | |||||||
| 0 | |||||||
| 1178 | ref($content->[-2]) && $content->[-2]->tag() eq 'pre') { | ||||||
| 1179 | 0 | 0 | $pre = $content->[-2]; | ||||
| 1180 | } else { | ||||||
| 1181 | 0 | 0 | $pre = HTML::Element->new('pre', CLASS => 'POD_VERBATIM'); | ||||
| 1182 | 0 | 0 | $self->{_current}->push_content($pre,"\n"); | ||||
| 1183 | } | ||||||
| 1184 | 0 | 0 | $pre->push_content("\n"); | ||||
| 1185 | |||||||
| 1186 | 0 | 0 | 0 | 0 | if($self->{_current_head1_title} eq 'NAME' && !$self->description()) { | ||
| 1187 | # save the description for further use in TOC | ||||||
| 1188 | 0 | 0 | my $str = $paragraph; | ||||
| 1189 | 0 | 0 | $str =~ s/^[\n\s]+//; | ||||
| 1190 | 0 | 0 | 0 | $self->description($str) if($str); | |||
| 1191 | } | ||||||
| 1192 | # this is special in perl.pod | ||||||
| 1193 | 0 | 0 | foreach(split(/\n/,$paragraph)) { | ||||
| 1194 | # TODO expand tabs correctly? | ||||||
| 1195 | 0 | 0 | 0 | if(s/^(\s+)([\w:]+)(\t+)//) { | |||
| 1196 | # this is for perl.pod - an implied list | ||||||
| 1197 | 0 | 0 | my ($indent,$page,$postdent) = ($1,$2,$3); | ||||
| 1198 | 0 | 0 | my $dest = $self->{-cache}->find_page($page); | ||||
| 1199 | 0 | 0 | 0 | if($dest) { | |||
| 1200 | 0 | 0 | my $destfile = _construct_file_name( | ||||
| 1201 | $dest->page(), $self->depth(), $self->{-suffix}); | ||||||
| 1202 | 0 | 0 | my $link = HTML::Element->new('a', href => $destfile, | ||||
| 1203 | CLASS => 'POD_LINK'); | ||||||
| 1204 | 0 | 0 | $link->push_content($page); | ||||
| 1205 | 0 | 0 | $page = $link; | ||||
| 1206 | } | ||||||
| 1207 | 0 | 0 | $pre->push_content($indent,$page,$postdent,$_,"\n"); | ||||
| 1208 | } else { | ||||||
| 1209 | 0 | 0 | $pre->push_content($_,"\n"); | ||||
| 1210 | } | ||||||
| 1211 | } | ||||||
| 1212 | } | ||||||
| 1213 | # a "verbatim" =begin html paragraph | ||||||
| 1214 | elsif($self->{_begin} eq 'html') { | ||||||
| 1215 | 0 | 0 | $self->{_raw_html} .= $paragraph; | ||||
| 1216 | } | ||||||
| 1217 | } | ||||||
| 1218 | |||||||
| 1219 | # a regular text paragraph | ||||||
| 1220 | sub textblock { | ||||||
| 1221 | 22 | 22 | 0 | 365 | my ($self, $paragraph, $line_num, $pod_para) = @_; | ||
| 1222 | |||||||
| 1223 | 22 | 141 | $paragraph =~ s/[\s\n]+$//s; | ||||
| 1224 | |||||||
| 1225 | # regular context | ||||||
| 1226 | 22 | 50 | 52 | if(!$self->{_begin}) { | |||
| 0 | |||||||
| 1227 | 22 | 59 | my @text = $self->interpolate($paragraph, $line_num); | ||||
| 1228 | # remember first paragraph in NAME section | ||||||
| 1229 | 22 | 50 | 66 | 282 | if($self->{_current_head1_title} eq 'NAME' && $paragraph && | ||
| 66 | |||||||
| 1230 | !$self->description()) { | ||||||
| 1231 | # save the description for further use in TOC | ||||||
| 1232 | 3 | 12 | $self->description([ HTML::Element->clone_list(@text) ]); | ||||
| 1233 | } | ||||||
| 1234 | 22 | 38 | my $par; | ||||
| 1235 | 22 | 100 | 100 | 135 | if($self->{_last_p_by} && $self->{_last_p_by} eq 'dd') { | ||
| 50 | 66 | ||||||
| 1236 | 5 | 8 | $par = $self->{_current}; | ||||
| 1237 | 5 | 13 | delete $self->{_last_p_by}; | ||||
| 1238 | } | ||||||
| 1239 | elsif($self->{_last_p_by} && $self->{_last_p_by} eq 'beginfor') { | ||||||
| 1240 | 0 | 0 | $par = _get_last_p_or_new($self->{_current}, 'POD_TEXT'); | ||||
| 1241 | } | ||||||
| 1242 | else { | ||||||
| 1243 | 17 | 55 | $par = HTML::Element->new('p', CLASS => 'POD_TEXT'); | ||||
| 1244 | 17 | 442 | $self->{_current}->push_content($par, "\n"); | ||||
| 1245 | } | ||||||
| 1246 | 22 | 334 | $par->push_content("\n",@text,"\n"); | ||||
| 1247 | 22 | 532 | $self->{_last_p_by} = 'text'; | ||||
| 1248 | } | ||||||
| 1249 | # =begin html context | ||||||
| 1250 | elsif($self->{_begin} eq 'html') { | ||||||
| 1251 | 0 | 0 | $self->{_raw_html} .= $paragraph; | ||||
| 1252 | } | ||||||
| 1253 | # reset currrent anchor this late so that in this par no autolinks | ||||||
| 1254 | # are generated | ||||||
| 1255 | 22 | 1169 | $self->{_current_anchor} = ''; | ||||
| 1256 | } | ||||||
| 1257 | |||||||
| 1258 | # expand a POD text string | ||||||
| 1259 | sub interpolate { | ||||||
| 1260 | 42 | 42 | 0 | 115 | my ($self, $paragraph, $line) = @_; | ||
| 1261 | ## Check the interior sequences in the command-text | ||||||
| 1262 | # and return the text as array of HTML::Element's | ||||||
| 1263 | 42 | 2009 | $self->_expand_ptree( | ||||
| 1264 | $self->parse_text($paragraph,$line), $line, ''); | ||||||
| 1265 | } | ||||||
| 1266 | |||||||
| 1267 | sub _expand_ptree { | ||||||
| 1268 | 48 | 48 | 369 | my ($self,$ptree,$line,$nestlist) = @_; | |||
| 1269 | 48 | 50 | local($_); | ||||
| 1270 | 48 | 66 | my @text = (); | ||||
| 1271 | # process each node in the parse tree | ||||||
| 1272 | 48 | 77 | foreach(@$ptree) { | ||||
| 1273 | # regular text chunk | ||||||
| 1274 | 58 | 100 | 145 | unless(ref) { | |||
| 1275 | 48 | 59 | my $chunk = $_; | ||||
| 1276 | # do magic linebreaking | ||||||
| 1277 | 48 | 143 | while($chunk =~ s/^([^\n]*)\n([ \t]+)//) { | ||||
| 1278 | 0 | 0 | my ($line,$indent) = ($1,$2); | ||||
| 1279 | 0 | 0 | 0 | $line =~ s/\s/$NBSP/g if($nestlist =~ /S/); | |||
| 1280 | 0 | 0 | push(@text, $line, HTML::Element->new('br'), | ||||
| 1281 | _expand_tab($indent) ); | ||||||
| 1282 | } | ||||||
| 1283 | # escape whitespace if in S<> | ||||||
| 1284 | 48 | 50 | 88 | if($chunk) { | |||
| 1285 | 48 | 50 | 85 | $chunk =~ s/\s/$NBSP/g if($nestlist =~ /S/); | |||
| 1286 | 48 | 123 | push(@text,$chunk); | ||||
| 1287 | } | ||||||
| 1288 | 48 | 103 | next; # finished this chunk | ||||
| 1289 | } | ||||||
| 1290 | # have an interior sequence | ||||||
| 1291 | 10 | 39 | my $cmd = $_->cmd_name(); | ||||
| 1292 | 10 | 31 | my $contents = $_->parse_tree(); | ||||
| 1293 | 10 | 11 | my $file; | ||||
| 1294 | 10 | 52 | ($file,$line) = $_->file_line(); | ||||
| 1295 | |||||||
| 1296 | # an entity | ||||||
| 1297 | 10 | 50 | 63 | if($cmd eq 'E') { | |||
| 100 | |||||||
| 100 | |||||||
| 100 | |||||||
| 50 | |||||||
| 50 | |||||||
| 50 | |||||||
| 50 | |||||||
| 50 | |||||||
| 50 | |||||||
| 50 | |||||||
| 1298 | 0 | 0 | my $entity = $contents->raw_text(); | ||||
| 1299 | 0 | 0 | $entity =~ s/^[\n\s]+|[\n\s]+$//g; | ||||
| 1300 | 0 | 0 | 0 | if($entity =~ /^(0x[0-9a-f]+)$/i) { | |||
| 0 | |||||||
| 0 | |||||||
| 0 | |||||||
| 0 | |||||||
| 1301 | # hexadecimal | ||||||
| 1302 | 0 | 0 | push(@text, chr(hex($1))); | ||||
| 1303 | } | ||||||
| 1304 | elsif($entity =~ /^(0[0-7]+)$/) { | ||||||
| 1305 | # octal | ||||||
| 1306 | 0 | 0 | push(@text, chr(oct($1))); | ||||
| 1307 | } | ||||||
| 1308 | elsif($entity =~ /^(\d+)$/) { | ||||||
| 1309 | # decimal | ||||||
| 1310 | 0 | 0 | push(@text, chr($1)); | ||||
| 1311 | } | ||||||
| 1312 | elsif($entity =~ /^sol$/i) { | ||||||
| 1313 | # forward slash | ||||||
| 1314 | 0 | 0 | push(@text, '/'); | ||||
| 1315 | } | ||||||
| 1316 | elsif($entity =~ /^verbar$/i) { | ||||||
| 1317 | # vertical bar | ||||||
| 1318 | 0 | 0 | push(@text, '|'); | ||||
| 1319 | } | ||||||
| 1320 | else { | ||||||
| 1321 | # textual entity | ||||||
| 1322 | 0 | 0 | 0 | push(@text, HTML::Entities::decode_entities("&$entity;") || ''); | |||
| 1323 | } | ||||||
| 1324 | } | ||||||
| 1325 | |||||||
| 1326 | # a hyperlink | ||||||
| 1327 | elsif($cmd eq 'L') { | ||||||
| 1328 | # try to parse the hyperlink | ||||||
| 1329 | 2 | 14 | my $raw = $contents->raw_text(); | ||||
| 1330 | 2 | 12 | my $link = Pod::Hyperlink->new($raw); | ||||
| 1331 | 2 | 50 | 178 | unless(defined $link) { | |||
| 1332 | # the link cannot be parsed | ||||||
| 1333 | 0 | 0 | my $underline = HTML::Element->new('u'); | ||||
| 1334 | 0 | 0 | $underline->push_content($raw); | ||||
| 1335 | 0 | 0 | push(@text,$underline); | ||||
| 1336 | 0 | 0 | next; | ||||
| 1337 | } | ||||||
| 1338 | |||||||
| 1339 | # only underline if destination not found | ||||||
| 1340 | 2 | 4 | $self->{_link_pagemark} = 'u'; | ||||
| 1341 | 2 | 4 | $self->{_link_pageopt} = +{}; | ||||
| 1342 | 2 | 5 | $self->{_link_sectionmark} = 'u'; | ||||
| 1343 | 2 | 4 | $self->{_link_sectionopt} = +{}; | ||||
| 1344 | |||||||
| 1345 | # search for page | ||||||
| 1346 | 2 | 6 | my $page = $link->page(); | ||||
| 1347 | 2 | 11 | $page =~ s/[(]\w*[)]$//; # strip manpage section | ||||
| 1348 | 2 | 1 | my $dest; | ||||
| 1349 | 2 | 4 | my $destfile = ''; | ||||
| 1350 | 2 | 50 | 5 | if($page) { | |||
| 1351 | 2 | 9 | $dest = $self->{-cache}->find_page($page); | ||||
| 1352 | 2 | 50 | 28 | if($dest) { | |||
| 1353 | 2 | 9 | $destfile = _construct_file_name( | ||||
| 1354 | $dest->page(), $self->depth(), $self->{-suffix}); | ||||||
| 1355 | 2 | 8 | $self->{_link_pagemark} = $self->{_link_sectionmark} = 'a'; | ||||
| 1356 | 2 | 8 | $self->{_link_pageopt} = | ||||
| 1357 | $self->{_link_sectionopt} = | ||||||
| 1358 | { CLASS => 'POD_LINK', HREF => $destfile }; | ||||||
| 1359 | } | ||||||
| 1360 | else { | ||||||
| 1361 | 0 | 0 | warn "Cannot find page `$page' at L<> on line $line\n"; | ||||
| 1362 | } | ||||||
| 1363 | } else { | ||||||
| 1364 | 0 | 0 | $dest = $self->{-mycache}; | ||||
| 1365 | } | ||||||
| 1366 | |||||||
| 1367 | 2 | 50 | 8 | if($link->type() eq 'hyperlink') { | |||
| 1368 | 0 | 0 | $self->{_link_sectionmark} = 'a'; | ||||
| 1369 | 0 | 0 | $self->{_link_sectionopt} = | ||||
| 1370 | { CLASS => 'POD_LINK', HREF => $link->node() }; | ||||||
| 1371 | } else { | ||||||
| 1372 | # search for node in page | ||||||
| 1373 | 2 | 16 | my $node = ''; | ||||
| 1374 | # use Pod::Checker's expand procedure to get the link | ||||||
| 1375 | # destination node | ||||||
| 1376 | 2 | 50 | 6 | if($link->node()) { | |||
| 1377 | 2 | 42 | my $cruncher = Pod::Checker->new(-quiet => 1); | ||||
| 1378 | 2 | 0 | 82 | $cruncher->errorsub(sub { 1; }); # suppress any errors | |||
| 0 | 0 | ||||||
| 1379 | 2 | 10 | $node = $cruncher->interpolate_and_check($link->node(), | ||||
| 1380 | $line,$file); | ||||||
| 1381 | } | ||||||
| 1382 | 2 | 50 | 33 | 150 | if($dest && $node) { | ||
| 1383 | 2 | 8 | my $id = $dest->find_node($node); | ||||
| 1384 | 2 | 50 | 40 | if($id) { | |||
| 1385 | 2 | 5 | $self->{_link_sectionmark} = 'a'; | ||||
| 1386 | 2 | 8 | $self->{_link_sectionopt} = | ||||
| 1387 | { CLASS => 'POD_LINK', HREF => "$destfile#$id" }; | ||||||
| 1388 | } else { | ||||||
| 1389 | 0 | 0 | 0 | my $inpage = $page ? " in `$page'" : ''; | |||
| 1390 | 0 | 0 | warn "Cannot find node `$node'$inpage at L<> on line $line\n"; | ||||
| 1391 | } | ||||||
| 1392 | } | ||||||
| 1393 | } | ||||||
| 1394 | 2 | 8 | $link->line($line); # remember line | ||||
| 1395 | |||||||
| 1396 | # convert the link text (expand POD markup) | ||||||
| 1397 | 2 | 13 | push(@text, $self->_expand_ptree($self->parse_text( | ||||
| 1398 | $link->markup(), $line), $line, "$nestlist$cmd")); | ||||||
| 1399 | } | ||||||
| 1400 | |||||||
| 1401 | # internal: hyperlink to page | ||||||
| 1402 | elsif($cmd eq 'P') { | ||||||
| 1403 | 2 | 10 | my $tag = HTML::Element->new($self->{_link_pagemark}, | ||||
| 1404 | 2 | 5 | %{$self->{_link_pageopt}}); | ||||
| 1405 | 2 | 69 | push(@text,$tag); | ||||
| 1406 | 2 | 5 | $tag->push_content($self->_expand_ptree($contents, $line, | ||||
| 1407 | "$nestlist$cmd")); | ||||||
| 1408 | } | ||||||
| 1409 | |||||||
| 1410 | # internal: hyperlink to section | ||||||
| 1411 | elsif($cmd eq 'Q') { | ||||||
| 1412 | 2 | 11 | my $tag = HTML::Element->new($self->{_link_sectionmark}, | ||||
| 1413 | 2 | 3 | %{$self->{_link_sectionopt}}); | ||||
| 1414 | 2 | 68 | push(@text,$tag); | ||||
| 1415 | 2 | 15 | $tag->push_content($self->_expand_ptree($contents, $line, | ||||
| 1416 | "$nestlist$cmd")); | ||||||
| 1417 | } | ||||||
| 1418 | |||||||
| 1419 | # bold text | ||||||
| 1420 | elsif($cmd eq 'B') { | ||||||
| 1421 | 0 | 0 | $self->_autolink_and_highlight(\@text, $contents, $line, | ||||
| 1422 | "$nestlist$cmd", 'b', 0); | ||||||
| 1423 | } | ||||||
| 1424 | |||||||
| 1425 | # code text | ||||||
| 1426 | elsif($cmd eq 'C') { | ||||||
| 1427 | 0 | 0 | $self->_autolink_and_highlight(\@text, $contents, $line, | ||||
| 1428 | "$nestlist$cmd", 'code', 1); | ||||||
| 1429 | } | ||||||
| 1430 | |||||||
| 1431 | # file text | ||||||
| 1432 | elsif($cmd eq 'F') { | ||||||
| 1433 | 0 | 0 | $self->_autolink_and_highlight(\@text, $contents, $line, | ||||
| 1434 | "$nestlist$cmd", 'code' , 0); | ||||||
| 1435 | } | ||||||
| 1436 | |||||||
| 1437 | # italic text | ||||||
| 1438 | elsif($cmd eq 'I') { | ||||||
| 1439 | # TODO I<...I<...>...> should be expanded to | ||||||
| 1440 | # ......... - according to Achim Bohnet | ||||||
| 1441 | 0 | 0 | $self->_autolink_and_highlight(\@text, $contents, $line, | ||||
| 1442 | "$nestlist$cmd", 'i', 0); | ||||||
| 1443 | } | ||||||
| 1444 | |||||||
| 1445 | # non-breakable space | ||||||
| 1446 | elsif($cmd eq 'S') { | ||||||
| 1447 | # will be taken care of above, when expanding text chunk | ||||||
| 1448 | 0 | 0 | push(@text, $self->_expand_ptree($contents, $line, "$nestlist$cmd")); | ||||
| 1449 | } | ||||||
| 1450 | |||||||
| 1451 | # zero-size element | ||||||
| 1452 | elsif($cmd eq 'Z') { | ||||||
| 1453 | # do nothing - a comment would be nice | ||||||
| 1454 | # is the correct entity, but it won't work with the | ||||||
| 1455 | # current HTML::Entities | ||||||
| 1456 | } | ||||||
| 1457 | |||||||
| 1458 | # custom index entries | ||||||
| 1459 | # TODO these should run also through Pod::Checker and result in | ||||||
| 1460 | # valid L<...> destinations | ||||||
| 1461 | elsif($cmd eq 'X') { | ||||||
| 1462 | # set up a fast lookup cache for node ids | ||||||
| 1463 | 4 | 8 | my $count = ($self->{_current_idx})++; | ||||
| 1464 | 4 | 4 | my ($node,$id) = @{$self->{-mycache}->{-idx}->[$count]}; | ||||
| 4 | 14 | ||||||
| 1465 | 4 | 13 | my $tag = HTML::Element->new('a', name => $id); | ||||
| 1466 | #$tag->push_content(@key); | ||||||
| 1467 | 4 | 99 | push(@text,$tag); | ||||
| 1468 | 4 | 50 | 33 | $self->indices($node,$id) # $node was $txt | |||
| 1469 | if($self->{-idxopt} =~ /(^|,)x(,|$)/i); | ||||||
| 1470 | } | ||||||
| 1471 | # ignore everything else | ||||||
| 1472 | } | ||||||
| 1473 | 48 | 258 | @text; | ||||
| 1474 | } | ||||||
| 1475 | |||||||
| 1476 | ## Helpers | ||||||
| 1477 | |||||||
| 1478 | # set some default value unless already defined | ||||||
| 1479 | sub _default | ||||||
| 1480 | { | ||||||
| 1481 | 46 | 100 | 46 | 127 | $_[0]->{$_[1]} = $_[2] unless(defined $_[0]->{$_[1]}); | ||
| 1482 | } | ||||||
| 1483 | |||||||
| 1484 | # setup the basic frame for a HTML tree | ||||||
| 1485 | sub _basic_html | ||||||
| 1486 | { | ||||||
| 1487 | 5 | 5 | 68 | my $html = HTML::Element->new('html'); | |||
| 1488 | 5 | 146 | my $head = HTML::Element->new('head'); | ||||
| 1489 | 5 | 96 | $head->push_content("\n", | ||||
| 1490 | HTML::Element->new('meta', 'http-equiv' => 'Content-Type', | ||||||
| 1491 | content => 'text/html; charset=ISO-8859-1'), "\n", | ||||||
| 1492 | HTML::Element->new('meta', 'http-equiv' => 'Content-Style-Type', | ||||||
| 1493 | content => 'text/css'), "\n", | ||||||
| 1494 | HTML::Element->new('meta', 'name' => 'GENERATOR', | ||||||
| 1495 | content => "Marek::Pod::HTML $VERSION"), "\n"); | ||||||
| 1496 | 5 | 729 | $html->push_content("\n",$head,"\n"); | ||||
| 1497 | 5 | 104 | my $body = HTML::Element->new('body'); | ||||
| 1498 | 5 | 87 | $body->push_content("\n"); | ||||
| 1499 | 5 | 62 | $html->push_content($body,"\n"); | ||||
| 1500 | 5 | 94 | ($html,$head,$body); | ||||
| 1501 | } | ||||||
| 1502 | |||||||
| 1503 | # create a set of unique ids | ||||||
| 1504 | sub _unique_ids { | ||||||
| 1505 | 6 | 6 | 45 | my (@nodes) = @_; | |||
| 1506 | |||||||
| 1507 | # we need the hashes both ways... | ||||||
| 1508 | 6 | 11 | my %hash = (); | ||||
| 1509 | 6 | 8 | my %Node = (); | ||||
| 1510 | 6 | 9 | foreach my $node (@nodes) { | ||||
| 1511 | # start with string | ||||||
| 1512 | 24 | 42 | my $id = _idfy($node,\%hash); | ||||
| 1513 | 24 | 64 | $hash{$id} = 1; | ||||
| 1514 | 24 | 41 | $Node{$node} = $id; | ||||
| 1515 | 24 | 58 | $node = [ $node, $id ]; | ||||
| 1516 | } | ||||||
| 1517 | # create secondary nodes (needed mainly for perlfunc) | ||||||
| 1518 | 6 | 12 | my @addnodes = (); | ||||
| 1519 | 6 | 14 | foreach my $node (keys %Node) { | ||||
| 1520 | 19 | 100 | 61 | if($node =~ /^(\S+)\s+\S/) { # more than one word | |||
| 1521 | 3 | 50 | 14 | push(@addnodes, [ $1, $Node{$node} ]) unless(defined $Node{$1}); | |||
| 1522 | } | ||||||
| 1523 | } | ||||||
| 1524 | 6 | 44 | @nodes,@addnodes; | ||||
| 1525 | } | ||||||
| 1526 | |||||||
| 1527 | # turn a string into a unique id | ||||||
| 1528 | # hashref points to a has with already existing ids | ||||||
| 1529 | sub _idfy | ||||||
| 1530 | { | ||||||
| 1531 | 24 | 24 | 30 | my ($id,$hashref) = @_; | |||
| 1532 | |||||||
| 1533 | # collapse entities | ||||||
| 1534 | 24 | 27 | $id =~ s/E<([^>]*)>/$1/g; | ||||
| 1535 | # collapse all non-alphanum characters to _ | ||||||
| 1536 | 24 | 41 | $id =~ s/\W+/_/g; | ||||
| 1537 | # collapse multiple _ | ||||||
| 1538 | 24 | 24 | $id =~ s/_{2,}/_/g; | ||||
| 1539 | # abbreviate to 20 characters | ||||||
| 1540 | 24 | 33 | $id = substr($id,0,20); | ||||
| 1541 | # has to have some contents | ||||||
| 1542 | 24 | 100 | 36 | $id = '_' unless($id); | |||
| 1543 | 24 | 28 | my $ext = ''; | ||||
| 1544 | # find something unique | ||||||
| 1545 | 24 | 66 | $ext++ while($hashref->{$id.$ext}); | ||||
| 1546 | 24 | 44 | $id . $ext; | ||||
| 1547 | } | ||||||
| 1548 | |||||||
| 1549 | |||||||
| 1550 | # prepend a paragraph with links to an HTML object's contents | ||||||
| 1551 | sub _add_links { | ||||||
| 1552 | 0 | 0 | 0 | 1; | |||
| 1553 | } | ||||||
| 1554 | |||||||
| 1555 | # turn a POD name into a HTML file name | ||||||
| 1556 | sub _construct_file_name { | ||||||
| 1557 | 17 | 17 | 50 | my ($file,$depth,$suffix) = @_; | |||
| 1558 | 17 | 83 | $file =~ s!::!/!g; #/ | ||||
| 1559 | 17 | 50 | 39 | $file .= $suffix if($suffix); | |||
| 1560 | 17 | 101 | ('../' x $depth) . $file; | ||||
| 1561 | } | ||||||
| 1562 | |||||||
| 1563 | # check if linkable and put into appropriate tag | ||||||
| 1564 | sub _autolink_and_highlight | ||||||
| 1565 | { | ||||||
| 1566 | 0 | 0 | 0 | my ($self,$tref,$contents,$line,$nest,$type,$doit) = @_; | |||
| 1567 | |||||||
| 1568 | 0 | 0 | my $tag = HTML::Element->new($type); | ||||
| 1569 | 0 | 0 | push(@$tref,$tag); | ||||
| 1570 | # canonicalize raw_text before lookup | ||||||
| 1571 | 0 | 0 | my $cruncher = Pod::Checker->new(-quiet => 1); | ||||
| 1572 | 0 | 0 | 0 | $cruncher->errorsub(sub { 1; }); # suppress any errors | |||
| 0 | 0 | ||||||
| 1573 | 0 | 0 | my $text = $cruncher->interpolate_and_check($contents->raw_text(), | ||||
| 1574 | $line,''); | ||||||
| 1575 | 0 | 0 | $text =~ s/^\s+|\s+$//g; | ||||
| 1576 | 0 | 0 | my ($node_ref); # will contain [$page,$id] | ||||
| 1577 | # try to find text in the libpod nodes. Do not link if | ||||||
| 1578 | # currently processing the anchor paragraph itself | ||||||
| 1579 | # (avoid reciprocal links) | ||||||
| 1580 | 0 | 0 | 0 | 0 | if($doit && $self->{-lib} && | ||
| 0 | |||||||
| 0 | |||||||
| 0 | |||||||
| 1581 | ($node_ref = $self->{-lib}->{$text}) && | ||||||
| 1582 | !($$node_ref[0] eq $self->{-name} && | ||||||
| 1583 | $$node_ref[1] eq $self->{_current_anchor})) { | ||||||
| 1584 | 0 | 0 | my $anchor = HTML::Element->new('a', CLASS => 'POD_LINK', | ||||
| 1585 | href => _construct_file_name($$node_ref[0], $self->depth(), | ||||||
| 1586 | $self->{-suffix} . '#' . $$node_ref[1])); | ||||||
| 1587 | 0 | 0 | $tag->push_content($anchor); | ||||
| 1588 | 0 | 0 | $tag = $anchor; | ||||
| 1589 | } | ||||||
| 1590 | 0 | 0 | $tag->push_content($self->_expand_ptree($contents, $line, $nest)); | ||||
| 1591 | } | ||||||
| 1592 | |||||||
| 1593 | # expand blanks and tabs to an appropriate amount of non-breaking space | ||||||
| 1594 | sub _expand_tab { | ||||||
| 1595 | # TODO more magic: indent by one blank less than in $str - | ||||||
| 1596 | # this would allow for the missing E syntax |
||||||
| 1597 | 0 | 0 | 0 | my ($str, $pos) = @_; | |||
| 1598 | 0 | 0 | my $new = ''; | ||||
| 1599 | 0 | 0 | 0 | $pos ||= 0; | |||
| 1600 | 0 | 0 | while($str =~ m/([ \t])/g) { | ||||
| 1601 | 0 | 0 | 0 | if($1 eq ' ') { | |||
| 1602 | 0 | 0 | $new .= $NBSP; | ||||
| 1603 | 0 | 0 | $pos++; | ||||
| 1604 | } | ||||||
| 1605 | else { | ||||||
| 1606 | 0 | 0 | my $len = $pos % 8; | ||||
| 1607 | 0 | 0 | 0 | $len = 8 unless($len); | |||
| 1608 | 0 | 0 | $new .= $NBSP x $len; | ||||
| 1609 | 0 | 0 | $pos += $len; | ||||
| 1610 | } | ||||||
| 1611 | } | ||||||
| 1612 | 0 | 0 | $new; | ||||
| 1613 | } | ||||||
| 1614 | |||||||
| 1615 | # prepend local navigation to body | ||||||
| 1616 | sub _local_toc { | ||||||
| 1617 | 3 | 3 | 5 | my $self = shift; | |||
| 1618 | 3 | 50 | 14 | if(defined $self->{_toc}) { | |||
| 1619 | 3 | 5 | my $level = 1; | ||||
| 1620 | 3 | 11 | my @hier = ( HTML::Element->new('ul') ); | ||||
| 1621 | 3 | 60 | $hier[0]->push_content("\n"); | ||||
| 1622 | 3 | 45 | $self->{_body}->unshift_content("\n", $hier[0], "\n", | ||||
| 1623 | HTML::Element->new('hr')); | ||||||
| 1624 | 3 | 172 | foreach(@{$self->{_toc}}) { | ||||
| 3 | 8 | ||||||
| 1625 | 9 | 115 | my ($l, $id, @line) = @$_; | ||||
| 1626 | 9 | 34 | while($l > $level) { | ||||
| 1627 | # new sublevel | ||||||
| 1628 | 1 | 5 | push(@hier, HTML::Element->new('ul')); | ||||
| 1629 | 1 | 19 | $hier[-2]->push_content($hier[-1], "\n"); | ||||
| 1630 | 1 | 16 | $level++; | ||||
| 1631 | 1 | 6 | $hier[-1]->push_content("\n"); | ||||
| 1632 | } | ||||||
| 1633 | 9 | 34 | while($l < $level) { | ||||
| 1634 | 0 | 0 | pop(@hier); | ||||
| 1635 | 0 | 0 | $level--; | ||||
| 1636 | } | ||||||
| 1637 | 9 | 28 | my $item = HTML::Element->new('li'); | ||||
| 1638 | 9 | 177 | my $anchor = HTML::Element->new('a', CLASS => 'POD_NAVLINK', | ||||
| 1639 | href => "#$id"); | ||||||
| 1640 | 9 | 268 | $item->push_content($anchor); | ||||
| 1641 | 9 | 146 | $anchor->push_content(@line); | ||||
| 1642 | 9 | 117 | $hier[-1]->push_content($item, "\n"); | ||||
| 1643 | } | ||||||
| 1644 | } | ||||||
| 1645 | } | ||||||
| 1646 | |||||||
| 1647 | # push a raw HTML string on the current contents | ||||||
| 1648 | sub _push_raw_html { | ||||||
| 1649 | 0 | 0 | 0 | my ($self,$node,$str) = @_; | |||
| 1650 | 0 | 0 | my $tree = new HTML::TreeBuilder; | ||||
| 1651 | 0 | 0 | $tree->warn(1); | ||||
| 1652 | 0 | 0 | $tree->implicit_tags(1); | ||||
| 1653 | 0 | 0 | $tree->ignore_unknown(1); | ||||
| 1654 | 0 | 0 | $tree->store_comments(1); | ||||
| 1655 | 0 | 0 | $tree->p_strict(1); | ||||
| 1656 | #$tree->implicit_body_p_tag(1); | ||||||
| 1657 | 0 | 0 | $tree->parse($str); | ||||
| 1658 | 0 | 0 | $tree->eof; | ||||
| 1659 | 0 | 0 | my $head = $tree->find_by_tag_name('head'); | ||||
| 1660 | 0 | 0 | 0 | 0 | $self->{_head}->push_content(@{$head->content()},"\n") | ||
| 0 | 0 | ||||||
| 1661 | if($head && $head->content()); | ||||||
| 1662 | 0 | 0 | my $body = $tree->find_by_tag_name('body'); | ||||
| 1663 | 0 | 0 | 0 | 0 | $node->push_content(@{$body->content()}) | ||
| 0 | 0 | ||||||
| 1664 | if($body && $body->content()); | ||||||
| 1665 | # this will not delete the contents, they have been pushed | ||||||
| 1666 | # somewhere else | ||||||
| 1667 | 0 | 0 | $tree->delete(); | ||||
| 1668 | |||||||
| 1669 | # consolidate p tags, i.e. re-root them appropriately | ||||||
| 1670 | 0 | 0 | my $lastp; | ||||
| 1671 | 0 | 0 | 0 | if($node->tag() eq 'p') { | |||
| 1672 | 0 | 0 | my $root = $node->parent(); | ||||
| 1673 | 0 | 0 | foreach($node->content_refs_list) { | ||||
| 1674 | 0 | 0 | 0 | 0 | if(ref $$_ && $$_->tag() eq 'p') { | ||
| 1675 | 0 | 0 | my $parent = $$_->parent(); | ||||
| 1676 | 0 | 0 | my $pindex = $$_->pindex(); | ||||
| 1677 | 0 | 0 | my ($p,@rest) = $parent->splice_content($pindex); | ||||
| 1678 | 0 | 0 | 0 | if(@rest) { | |||
| 1679 | 0 | 0 | my %attr = $node->all_attr(); | ||||
| 1680 | 0 | 0 | my $newp = HTML::Element->new('p', $node->all_external_attr()); | ||||
| 1681 | 0 | 0 | $newp->push_content(@rest); | ||||
| 1682 | 0 | 0 | $root->push_content($p,"\n",$newp,"\n"); | ||||
| 1683 | 0 | 0 | $lastp = 'beginfor'; | ||||
| 1684 | } else { | ||||||
| 1685 | 0 | 0 | $root->push_content($p,"\n"); | ||||
| 1686 | 0 | 0 | $lastp = 'raw'; | ||||
| 1687 | } | ||||||
| 1688 | } | ||||||
| 1689 | } | ||||||
| 1690 | } | ||||||
| 1691 | 0 | 0 | 0 | $self->{_last_p_by} = $lastp || 'beginfor'; | |||
| 1692 | 0 | 0 | 1; | ||||
| 1693 | } | ||||||
| 1694 | |||||||
| 1695 | # process a part of HTML::Element into plain text | ||||||
| 1696 | sub _to_text { | ||||||
| 1697 | 8 | 8 | 13 | my @out; | |||
| 1698 | 8 | 15 | foreach(@_) { | ||||
| 1699 | 8 | 50 | 18 | if(ref $_) { | |||
| 1700 | 0 | 0 | push(@out, $_->as_text()); | ||||
| 1701 | } | ||||||
| 1702 | else { | ||||||
| 1703 | 8 | 73 | push(@out, HTML::Entities::decode_entities($_)); | ||||
| 1704 | } | ||||||
| 1705 | } | ||||||
| 1706 | 8 | 38 | join('',@out); | ||||
| 1707 | } | ||||||
| 1708 | |||||||
| 1709 | # needed to get rid of all HTML::Element's | ||||||
| 1710 | sub DESTROY { | ||||||
| 1711 | 5 | 5 | 2786 | my $self = shift; | |||
| 1712 | 5 | 50 | 27 | $self->{_html}->delete() if(defined $self->{_html}); | |||
| 1713 | } | ||||||
| 1714 | |||||||
| 1715 | =head1 SEE ALSO | ||||||
| 1716 | |||||||
| 1717 | L |
||||||
| 1718 | L |
||||||
| 1719 | L |
||||||
| 1720 | |||||||
| 1721 | =head1 AUTHOR | ||||||
| 1722 | |||||||
| 1723 | Marek Rouchal E |
||||||
| 1724 | |||||||
| 1725 | =head1 HISTORY | ||||||
| 1726 | |||||||
| 1727 | A big deal of this code has been recycled from a variety of existing | ||||||
| 1728 | Pod converters, e.g. by Tom Christiansen and Russ Allbery. A lot of | ||||||
| 1729 | ideas came from Nick Ing-Simmons' B |
||||||
| 1730 | B |
||||||
| 1731 | Without the B |
||||||
| 1732 | B |
||||||
| 1733 | |||||||
| 1734 | =cut | ||||||
| 1735 | |||||||
| 1736 | 1; | ||||||
| 1737 |