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 |