3 package Pod::Simple::HTML;
5 use Pod::Simple::PullParser ();
7 @ISA %Tagmap $Computerese $LamePad $Linearization_Limit $VERSION
8 $Perldoc_URL_Prefix $Perldoc_URL_Postfix
9 $Title_Prefix $Title_Postfix $HTML_EXTENSION %ToIndex
10 $Doctype_decl $Content_decl
12 @ISA = ('Pod::Simple::PullParser');
17 if(defined &DEBUG) { } # no-op
18 elsif( defined &Pod::Simple::DEBUG ) { *DEBUG = \&Pod::Simple::DEBUG }
19 else { *DEBUG = sub () {0}; }
22 $Doctype_decl ||= ''; # No. Just No. Don't even ask me for it.
23 # qq{<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
24 # "http://www.w3.org/TR/html4/loose.dtd">\n};
27 q{<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1" >};
29 $HTML_EXTENSION = '.html' unless defined $HTML_EXTENSION;
30 $Computerese = "" unless defined $Computerese;
31 $LamePad = '' unless defined $LamePad;
33 $Linearization_Limit = 120 unless defined $Linearization_Limit;
34 # headings/items longer than that won't get an <a name="...">
35 $Perldoc_URL_Prefix = 'http://search.cpan.org/perldoc?'
36 unless defined $Perldoc_URL_Prefix;
37 $Perldoc_URL_Postfix = ''
38 unless defined $Perldoc_URL_Postfix;
40 $Title_Prefix = '' unless defined $Title_Prefix;
41 $Title_Postfix = '' unless defined $Title_Postfix;
42 %ToIndex = map {; $_ => 1 } qw(head1 head2 head3 head4 ); # item-text
43 # 'item-text' stuff in the index doesn't quite work, and may
44 # not be a good idea anyhow.
47 __PACKAGE__->_accessorize(
49 # In turning L<Foo::Bar> into http://whatever/Foo%3a%3aBar, what
50 # to put before the "Foo%3a%3aBar".
51 # (for singleton mode only?)
52 'perldoc_url_postfix',
53 # what to put after "Foo%3a%3aBar" in the URL. Normally "".
55 'batch_mode', # whether we're in batch mode
56 'batch_mode_current_level',
57 # When in batch mode, how deep the current module is: 1 for "LWP",
58 # 2 for "LWP::Procotol", 3 for "LWP::Protocol::GHTTP", etc
60 'title_prefix', 'title_postfix',
61 # What to put before and after the title in the head.
62 # Should already be &-escaped
64 'html_header_before_title',
65 'html_header_after_title',
68 'index', # whether to add an index at the top of each page
69 # (actually it's a table-of-contents, but we'll call it an index,
70 # out of apparently longstanding habit)
72 'html_css', # URL of CSS file to point to
73 'html_javascript', # URL of CSS file to point to
75 'force_title', # should already be &-escaped
76 'default_title', # should already be &-escaped
79 #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
83 'Verbatim' => "\n<pre$Computerese>",
84 '/Verbatim' => "</pre>\n",
85 'VerbatimFormatted' => "\n<pre$Computerese>",
86 '/VerbatimFormatted' => "</pre>\n",
88 '/VerbatimB' => "</b>",
90 '/VerbatimI' => "</i>",
91 'VerbatimBI' => "<b><i>",
92 '/VerbatimBI' => "</i></b>",
98 'head1' => "\n<h1>", # And also stick in an <a name="...">
99 'head2' => "\n<h2>", # ''
100 'head3' => "\n<h3>", # ''
101 'head4' => "\n<h4>", # ''
102 '/head1' => "</a></h1>\n",
103 '/head2' => "</a></h2>\n",
104 '/head3' => "</a></h3>\n",
105 '/head4' => "</a></h4>\n",
107 'X' => "<!--\n\tINDEX: ",
116 over-block=blockquote
122 map {; m/^([-a-z]+)/s && push @_to_accept, $1; $_ }
137 ] # no point in providing a way to get <q>...</q>, I think
140 '/item-bullet' => "</li>$LamePad\n",
141 '/item-number' => "</li>$LamePad\n",
142 '/item-text' => "</a></dt>$LamePad\n",
143 'item-body' => "\n<dd>",
144 '/item-body' => "</dd>\n",
147 'B' => "<b>", '/B' => "</b>",
148 'I' => "<i>", '/I' => "</i>",
149 'F' => "<em$Computerese>", '/F' => "</em>",
150 'C' => "<code$Computerese>", '/C' => "</code>",
151 'L' => "<a href='YOU_SHOULD_NEVER_SEE_THIS'>", # ideally never used!
156 return map {; m/^([-_:0-9a-zA-Z]+)=([-_:0-9a-zA-Z]+)$/s
157 ? ( $1, => "\n<$2>", "/$1", => "</$2>\n" ) : die "Funky $_"
161 return map {; m/^([-_:0-9a-zA-Z]+)=([-_:0-9a-zA-Z]+)$/s
162 ? ( $1, => "<$2>", "/$1", => "</$2>" ) : die "Funky $_"
166 #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
167 sub go { Pod::Simple::HTML->parse_from_file(@ARGV); exit 0 }
168 # Just so we can run from the command line. No options.
169 # For that, use perldoc!
170 #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
173 my $new = shift->SUPER::new(@_);
174 #$new->nix_X_codes(1);
176 $new->accept_targets( 'html', 'HTML' );
177 $new->accept_codes('VerbatimFormatted');
178 $new->accept_codes(@_to_accept);
179 DEBUG > 2 and print "To accept: ", join(' ',@_to_accept), "\n";
181 $new->perldoc_url_prefix( $Perldoc_URL_Prefix );
182 $new->perldoc_url_postfix( $Perldoc_URL_Postfix );
183 $new->title_prefix( $Title_Prefix );
184 $new->title_postfix( $Title_Postfix );
186 $new->html_header_before_title(
187 qq[$Doctype_decl<html><head><title>]
189 $new->html_header_after_title( join "\n" =>
192 "</head>\n<body class='pod'>",
193 $new->version_tag_comment,
194 "<!-- start doc -->\n",
196 $new->html_footer( qq[\n<!-- end doc -->\n\n</body></html>\n] );
198 $new->{'Tagmap'} = {%Tagmap};
202 sub batch_mode_page_object_init {
203 my($self, $batchconvobj, $module, $infile, $outfile, $depth) = @_;
204 DEBUG and print "Initting $self\n for $module\n",
205 " in $infile\n out $outfile\n depth $depth\n";
206 $self->batch_mode(1);
207 $self->batch_mode_current_level($depth);
213 return $self->do_middle if $self->bare_output;
215 $self->do_beginning && $self->do_middle && $self->do_end;
218 #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
225 if(defined $self->force_title) {
226 $title = $self->force_title;
227 DEBUG and print "Forcing title to be $title\n";
229 # Actually try looking for the title in the document:
230 $title = $self->get_short_title();
231 unless($self->content_seen) {
232 DEBUG and print "No content seen in search for title.\n";
235 $self->{'Title'} = $title;
237 if(defined $title and $title =~ m/\S/) {
238 $title = $self->title_prefix . esc($title) . $self->title_postfix;
240 $title = $self->default_title;
241 $title = '' unless defined $title;
242 DEBUG and print "Title defaults to $title\n";
247 my $after = $self->html_header_after_title || '';
248 if($self->html_css) {
250 $self->html_css =~ m/</
251 ? $self->html_css # It's a big blob of markup, let's drop it in
252 : sprintf( # It's just a URL, so let's wrap it up
253 qq[<link rel="stylesheet" type="text/css" title="pod_stylesheet" href="%s">\n],
256 $after =~ s{(</head>)}{$link\n$1}i; # otherwise nevermind
258 $self->_add_top_anchor(\$after);
260 if($self->html_javascript) {
262 $self->html_javascript =~ m/</
263 ? $self->html_javascript # It's a big blob of markup, let's drop it in
264 : sprintf( # It's just a URL, so let's wrap it up
265 qq[<script type="text/javascript" src="%s"></script>\n],
266 $self->html_javascript,
268 $after =~ s{(</head>)}{$link\n$1}i; # otherwise nevermind
271 print {$self->{'output_fh'}}
272 $self->html_header_before_title || '',
273 $title, # already escaped
277 DEBUG and print "Returning from do_beginning...\n";
281 sub _add_top_anchor {
282 my($self, $text_r) = @_;
283 unless($$text_r and $$text_r =~ m/name=['"]___top['"]/) { # a hack
284 $$text_r .= "<a name='___top' class='dummyTopAnchor' ></a>\n";
289 sub version_tag_comment {
292 "<!--\n generated by %s v%s,\n using %s v%s,\n under Perl v%s at %s GMT.\n\n %s\n\n-->\n",
294 ref($self), $self->VERSION(), $ISA[0], $ISA[0]->VERSION(),
296 ), $self->_modnote(),
301 my $class = ref($_[0]) || $_[0];
302 return join "\n " => grep m/\S/, split "\n",
305 If you want to change this HTML document, you probably shouldn't do that
306 by changing it directly. Instead, see about changing the calling options
307 to $class, and/or subclassing $class,
308 then reconverting this document from the Pod source.
309 When in doubt, email the author of $class for advice.
310 See 'perldoc $class' for more info.
317 print {$self->{'output_fh'}} $self->html_footer || '';
321 # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
322 # Normally this would just be a call to _do_middle_main_loop -- but we
323 # have to do some elaborate things to emit all the content and then
324 # summarize it and output it /before/ the content that it's a summary of.
328 return $self->_do_middle_main_loop unless $self->index;
330 if( $self->output_string ) {
332 my $out = $self->output_string; #it's a reference to it
333 my $sneakytag = "\f\f\e\e\b\bIndex Here\e\e\b\b\f\f\n";
335 $self->_do_middle_main_loop;
336 $sneakytag = quotemeta($sneakytag);
337 my $index = $self->index_as_html();
338 if( $$out =~ s/$sneakytag/$index/s ) {
340 DEBUG and print "Inserted ", length($index), " bytes of index HTML into $out.\n";
342 DEBUG and print "Odd, couldn't find where to insert the index in the output!\n";
343 # I don't think this should ever happen.
348 unless( $self->output_fh ) {
350 Carp::confess("Parser object \$p doesn't seem to have any output object! I don't know how to deal with that.");
353 # If we get here, we're outputting to a FH. So we need to do some magic.
354 # Namely, divert all content to a string, which we output after the index.
355 my $fh = $self->output_fh;
358 # Our horrible bait and switch:
359 $self->output_string( \$content );
360 $self->_do_middle_main_loop;
361 $self->abandon_output_string();
362 $self->output_fh($fh);
364 print $fh $self->index_as_html();
370 ###########################################################################
374 # This is meant to be called AFTER the input document has been parsed!
376 my $points = $self->{'PSHTML_index_points'} || [];
378 @$points > 1 or return qq[<div class='indexgroupEmpty'></div>\n];
379 # There's no point in having a 0-item or 1-item index, I dare say.
381 my(@out) = qq{\n<div class='indexgroup'>};
384 my( $target_level, $previous_tagname, $tagname, $text, $anchorname, $indent);
385 foreach my $p (@$points, ['head0', '(end)']) {
386 ($tagname, $text) = @$p;
387 $anchorname = $self->section_escape($text);
388 if( $tagname =~ m{^head(\d+)$} ) {
389 $target_level = 0 + $1;
390 } else { # must be some kinda list item
391 if($previous_tagname =~ m{^head\d+$} ) {
392 $target_level = $level + 1;
394 $target_level = $level; # no change needed
398 # Get to target_level by opening or closing ULs
399 while($level > $target_level)
400 { --$level; push @out, (" " x $level) . "</ul>"; }
401 while($level < $target_level)
402 { ++$level; push @out, (" " x ($level-1))
403 . "<ul class='indexList indexList$level'>"; }
405 $previous_tagname = $tagname;
408 $indent = ' ' x $level;
410 "%s<li class='indexItem indexItem%s'><a href='#%s'>%s</a>",
411 $indent, $level, $anchorname, esc($text)
414 push @out, "</div>\n";
415 return join "\n", @out;
418 ###########################################################################
420 sub _do_middle_main_loop {
422 my $fh = $self->{'output_fh'};
423 my $tagmap = $self->{'Tagmap'};
425 my($token, $type, $tagname, $linkto, $linktype);
429 while($token = $self->get_token) {
431 # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
432 if( ($type = $token->type) eq 'start' ) {
433 if(($tagname = $token->tagname) eq 'L') {
434 $linktype = $token->attr('type') || 'insane';
436 $linkto = $self->do_link($token);
438 if(defined $linkto and length $linkto) {
440 # (Yes, SGML-escaping applies on top of %-escaping!
441 # But it's rarely noticeable in practice.)
442 print $fh qq{<a href="$linkto" class="podlink$linktype"\n>};
444 print $fh "<a>"; # Yes, an 'a' element with no attributes!
447 } elsif ($tagname eq 'item-text' or $tagname =~ m/^head\d$/s) {
448 print $fh $tagmap->{$tagname} || next;
452 push @to_unget, $self->get_token;
453 last if $to_unget[-1]->is_end
454 and $to_unget[-1]->tagname eq $tagname;
456 # TODO: support for X<...>'s found in here? (maybe hack into linearize_tokens)
459 my $name = $self->linearize_tokens(@to_unget);
462 print $fh "class='u' href='#___top' title='click to go to top of document'\n"
463 if $tagname =~ m/^head\d$/s;
466 my $esc = esc( $self->section_name_tidy( $name ) );
467 print $fh qq[name="$esc"];
468 DEBUG and print "Linearized ", scalar(@to_unget),
469 " tokens as \"$name\".\n";
470 push @{ $self->{'PSHTML_index_points'} }, [$tagname, $name]
471 if $ToIndex{ $tagname };
472 # Obviously, this discards all formatting codes (saving
473 # just their content), but ahwell.
475 } else { # ludicrously long, so nevermind
476 DEBUG and print "Linearized ", scalar(@to_unget),
477 " tokens, but it was too long, so nevermind.\n";
480 $self->unget_token(@to_unget);
482 } elsif ($tagname eq 'Data') {
483 my $next = $self->get_token;
484 next unless defined $next;
485 unless( $next->type eq 'text' ) {
486 $self->unget_token($next);
489 DEBUG and print " raw text ", $next->text, "\n";
490 printf $fh "\n" . $next->text . "\n";
494 if( $tagname =~ m/^over-/s ) {
496 } elsif( $tagname =~ m/^item-/s and @stack and $stack[-1] ) {
497 print $fh $stack[-1];
500 print $fh $tagmap->{$tagname} || next;
501 ++$dont_wrap if $tagname eq 'Verbatim' or $tagname eq "VerbatimFormatted"
505 # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
506 } elsif( $type eq 'end' ) {
507 if( ($tagname = $token->tagname) =~ m/^over-/s ) {
508 if( my $end = pop @stack ) {
511 } elsif( $tagname =~ m/^item-/s and @stack) {
512 $stack[-1] = $tagmap->{"/$tagname"};
513 if( $tagname eq 'item-text' and defined(my $next = $self->get_token) ) {
514 $self->unget_token($next);
515 if( $next->type eq 'start' and $next->tagname !~ m/^item-/s ) {
516 print $fh $tagmap->{"/item-text"},$tagmap->{"item-body"};
517 $stack[-1] = $tagmap->{"/item-body"};
522 print $fh $tagmap->{"/$tagname"} || next;
523 --$dont_wrap if $tagname eq 'Verbatim' or $tagname eq 'X';
525 # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
526 } elsif( $type eq 'text' ) {
527 esc($type = $token->text); # reuse $type, why not
528 $type =~ s/([\?\!\"\'\.\,]) /$1\n/g unless $dont_wrap;
536 ###########################################################################
540 my($self, $token) = @_;
541 my $type = $token->attr('type');
543 $self->whine("Typeless L!?", $token->attr('start_line'));
544 } elsif( $type eq 'pod') { return $self->do_pod_link($token);
545 } elsif( $type eq 'url') { return $self->do_url_link($token);
546 } elsif( $type eq 'man') { return $self->do_man_link($token);
548 $self->whine("L of unknown type $type!?", $token->attr('start_line'));
550 return 'FNORG'; # should never get called
553 # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
555 sub do_url_link { return $_[1]->attr('to') }
557 sub do_man_link { return undef }
558 # But subclasses are welcome to override this if they have man
559 # pages somewhere URL-accessible.
563 # And now things get really messy...
564 my($self, $link) = @_;
565 my $to = $link->attr('to');
566 my $section = $link->attr('section');
567 return undef unless( # should never happen
568 (defined $to and length $to) or
569 (defined $section and length $section)
572 $section = $self->section_escape($section)
573 if defined $section and length($section .= ''); # (stringify)
575 DEBUG and printf "Resolving \"%s\" \"%s\"...\n",
576 $to || "(nil)", $section || "(nil)";
580 my $complete_url = $self->resolve_pod_link_by_table($to, $section);
581 if( $complete_url ) {
582 DEBUG > 1 and print "resolve_pod_link_by_table(T,S) gives ",
583 $complete_url, "\n (Returning that.)\n";
584 return $complete_url;
586 DEBUG > 4 and print " resolve_pod_link_by_table(T,S)",
587 " didn't return anything interesting.\n";
591 if(defined $to and length $to) {
592 # Give this routine first hack again
593 my $there = $self->resolve_pod_link_by_table($to);
594 if(defined $there and length $there) {
596 and print "resolve_pod_link_by_table(T) gives $there\n";
599 $self->resolve_pod_page_link($to, $section);
600 # (I pass it the section value, but I don't see a
601 # particular reason it'd use it.)
602 DEBUG > 1 and print "resolve_pod_page_link gives ", $to || "(nil)", "\n";
603 unless( defined $there and length $there ) {
604 DEBUG and print "Can't resolve $to\n";
607 # resolve_pod_page_link returning undef is how it
608 # can signal that it gives up on making a link
613 #DEBUG and print "So far [", $to||'nil', "] [", $section||'nil', "]\n";
615 my $out = (defined $to and length $to) ? $to : '';
616 $out .= "#" . $section if defined $section and length $section;
618 unless(length $out) { # sanity check
619 DEBUG and printf "Oddly, couldn't resolve \"%s\" \"%s\"...\n",
620 $to || "(nil)", $section || "(nil)";
624 DEBUG and print "Resolved to $out\n";
629 # . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
632 my($self, $section) = @_;
633 return $self->section_url_escape(
634 $self->section_name_tidy($section)
638 sub section_name_tidy {
639 my($self, $section) = @_;
641 $section =~ tr/\x00-\x1F\x80-\x9F//d if 'A' eq chr(65); # drop crazy characters
642 $section = $self->unicode_escape_url($section);
643 $section = '_' unless length $section;
647 sub section_url_escape { shift->general_url_escape(@_) }
648 sub pagepath_url_escape { shift->general_url_escape(@_) }
650 sub general_url_escape {
651 my($self, $string) = @_;
653 $string =~ s/([^\x00-\xFF])/join '', map sprintf('%%%02X',$_), unpack 'C*', $1/eg;
654 # express Unicode things as urlencode(utf(orig)).
656 # A pretty conservative escaping, behoovey even for query components
657 # of a URL (see RFC 2396)
659 $string =~ s/([^-_\.!~*()abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789])/sprintf('%%%02X',ord($1))/eg;
660 # Yes, stipulate the list without a range, so that this can work right on
661 # all charsets that this module happens to run under.
662 # Altho, hmm, what about that ord? Presumably that won't work right
663 # under non-ASCII charsets. Something should be done
664 # about that, I guess?
669 #--------------------------------------------------------------------------
671 # Oh look, a yawning portal to Hell! Let's play touch football right by it!
674 sub resolve_pod_page_link {
675 # resolve_pod_page_link must return a properly escaped URL
677 return $self->batch_mode()
678 ? $self->resolve_pod_page_link_batch_mode(@_)
679 : $self->resolve_pod_page_link_singleton_mode(@_)
683 sub resolve_pod_page_link_singleton_mode {
685 return undef unless defined $it and length $it;
686 my $url = $self->pagepath_url_escape($it);
688 $url =~ s{::$}{}s; # probably never comes up anyway
689 $url =~ s{::}{/}g unless $self->perldoc_url_prefix =~ m/\?/s; # sane DWIM?
691 return undef unless length $url;
692 return $self->perldoc_url_prefix . $url . $self->perldoc_url_postfix;
695 sub resolve_pod_page_link_batch_mode {
697 DEBUG > 1 and print " During batch mode, resolving $to ...\n";
698 my @path = grep length($_), split m/::/s, $to, -1;
699 unless( @path ) { # sanity
700 DEBUG and print "Very odd! Splitting $to gives (nil)!\n";
703 $self->batch_mode_rectify_path(\@path);
704 my $out = join('/', map $self->pagepath_url_escape($_), @path)
706 DEBUG > 1 and print " => $out\n";
710 sub batch_mode_rectify_path {
711 my($self, $pathbits) = @_;
712 my $level = $self->batch_mode_current_level;
713 $level--; # how many levels up to go to get to the root
715 unshift @$pathbits, '.'; # just to be pretty
717 unshift @$pathbits, ('..') x $level;
722 #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
724 sub resolve_pod_link_by_table {
725 # A crazy hack to allow specifying custom L<foo> => URL mappings
727 return unless $_[0]->{'podhtml_LOT'}; # An optimizy shortcut
729 my($self, $to, $section) = @_;
731 # TODO: add a method that actually populates podhtml_LOT from a file?
733 if(defined $section) {
734 $to = '' unless defined $to and length $to;
735 return $self->{'podhtml_LOT'}{"$to#$section"}; # quite possibly undef!
737 return $self->{'podhtml_LOT'}{$to}; # quite possibly undef!
742 ###########################################################################
744 sub linearize_tokens { # self, tokens
749 while($t = shift @_) {
750 if(!ref $t or !UNIVERSAL::can($t, 'is_text')) {
751 $out .= $t; # a string, or some insane thing
752 } elsif($t->is_text) {
754 } elsif($t->is_start and $t->tag eq 'X') {
755 # Ignore until the end of this X<...> sequence:
758 next if( ($t = shift @_)->is_text );
759 if( $t->is_start and $t->tag eq 'X') { ++$x_open }
760 elsif($t->is_end and $t->tag eq 'X') { --$x_open }
764 return undef if length $out > $Linearization_Limit;
768 #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
770 sub unicode_escape_url {
771 my($self, $string) = @_;
772 $string =~ s/([^\x00-\xFF])/'('.ord($1).')'/eg;
773 # Turn char 1234 into "(1234)"
777 #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
778 sub esc { # a function.
779 if(defined wantarray) {
781 @_ = splice @_; # break aliasing
784 $x =~ s/([^-\n\t !\#\$\%\(\)\*\+,\.\~\/\:\;=\?\@\[\\\]\^_\`\{\|\}abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789])/'&#'.(ord($1)).';'/eg;
789 # Escape things very cautiously:
790 $x =~ s/([^-\n\t !\#\$\%\(\)\*\+,\.\~\/\:\;=\?\@\[\\\]\^_\`\{\|\}abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789])/'&#'.(ord($1)).';'/eg
792 # Leave out "- so that "--" won't make it thru in X-generated comments
795 # Yes, stipulate the list without a range, so that this can work right on
796 # all charsets that this module happens to run under.
797 # Altho, hmm, what about that ord? Presumably that won't work right
798 # under non-ASCII charsets. Something should be done about that.
803 #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
810 Pod::Simple::HTML - convert Pod to HTML
814 perl -MPod::Simple::HTML -e Pod::Simple::HTML::go thingy.pod
819 This class is for making an HTML rendering of a Pod document.
821 This is a subclass of L<Pod::Simple::PullParser> and inherits all its
822 methods (and options).
824 Note that if you want to do a batch conversion of a lot of Pod
825 documents to HTML, you should see the module L<Pod::Simple::HTMLBatch>.
829 =head1 CALLING FROM THE COMMAND LINE
833 perl -MPod::Simple::HTML -e Pod::Simple::HTML::go Thing.pod Thing.html
837 =head1 CALLING FROM PERL
839 TODO make a new object, set any options, and use parse_from_file
845 all (most?) accessorized methods
852 can just set any of: html_css html_javascript title_prefix
853 'html_header_before_title',
854 'html_header_after_title',
857 maybe override do_pod_link
859 maybe override do_beginning do_end
865 L<Pod::Simple>, L<Pod::Simple::HTMLBatch>
868 TODO: a corpus of sample Pod input and HTML output? Or common
873 =head1 COPYRIGHT AND DISCLAIMERS
875 Copyright (c) 2002-2004 Sean M. Burke. All rights reserved.
877 This library is free software; you can redistribute it and/or modify it
878 under the same terms as Perl itself.
880 This program is distributed in the hope that it will be useful, but
881 without any warranty; without even the implied warranty of
882 merchantability or fitness for a particular purpose.
886 Sean M. Burke C<sburke@cpan.org>