1package Pod::POM::View::HTML::Filter; 2use Pod::POM::View::HTML; 3our @ISA = qw( Pod::POM::View::HTML ); 4 5use warnings; 6use strict; 7use Carp; 8 9our $VERSION = '0.09'; 10 11my %filter; 12our %builtin = ( 13 default => { 14 code => sub { 15 my $s = shift; 16 $s =~ s/&/&/g; 17 $s =~ s/</</g; 18 $s =~ s/>/>/g; 19 $s; 20 }, 21 verbatim => 1, 22 }, 23 perl_tidy => { 24 code => \&perl_tidy_filter, 25 requires => [qw( Perl::Tidy )], 26 verbatim => 1, 27 alias => [qw( perl )], 28 }, 29 perl_ppi => { 30 code => \&perl_ppi_filter, 31 requires => [qw( PPI PPI::HTML )], 32 verbatim => 1, 33 alias => [qw( ppi )], 34 }, 35 html => { 36 code => \&html_filter, 37 requires => [qw( Syntax::Highlight::HTML )], 38 verbatim => 1, 39 }, 40 shell => { 41 code => \&shell_filter, 42 requires => [qw( Syntax::Highlight::Shell )], 43 verbatim => 1, 44 }, 45 kate => { 46 code => \&kate_filter, 47 requires => [qw( Syntax::Highlight::Engine::Kate )], 48 verbatim => 1, 49 }, 50 wiki => { 51 code => \&wiki_filter, 52 requires => [qw( Text::WikiFormat )], 53 verbatim => 0, 54 }, 55 wikimedia => { 56 code => \&wikimedia_filter, 57 requires => [qw( Text::MediawikiFormat )], 58 verbatim => 0, 59 }, 60); 61 62# automatically register built-in handlers 63my $INIT = 1; 64Pod::POM::View::HTML::Filter->add( %builtin ); 65$INIT = 0; 66 67# 68# Specific methods 69# 70sub new { 71 my $class = shift; 72 return $class->SUPER::new( 73 auto_unindent => 1, 74 @_, 75 filter => {}, # instance filters 76 FILTER => [], # stack maintaining info for filters 77 ); 78} 79 80sub add { 81 my ($self, %args) = @_; 82 my $filter = $self->__filter(); 83 84 for my $lang ( keys %args ) { 85 my $nok = 0; 86 if( exists $args{$lang}{requires} ) { 87 for ( @{ $args{$lang}{requires} } ) { 88 eval "require $_;"; 89 if ($@) { 90 $nok++; 91 carp "$lang\: pre-requisite $_ could not be loaded" 92 unless $INIT; # don't warn for built-ins 93 } 94 } 95 } 96 croak "$lang: no code parameter given" 97 unless exists $args{$lang}{code}; 98 99 if ( !$nok ) { 100 $filter->{$lang} = $args{$lang}; 101 if ( $args{$lang}{alias} ) { 102 $filter->{$_} = $args{$lang} for @{ $args{$lang}{alias} }; 103 } 104 } 105 } 106} 107 108sub delete { 109 my ( $self, $lang ) = @_; 110 my $filter = $self->__filter(); 111 my $old = $self->_filter()->{$lang}; 112 $filter->{$lang} = undef; 113 return $old; 114} 115 116# return a hashref of current filters for the class|instance 117sub _filter { 118 my ($self) = @_; 119 my $filter = 120 ref $self 121 && UNIVERSAL::isa( $self, 'Pod::POM::View::HTML::Filter' ) 122 ? { %filter, %{ $self->{filter} } } 123 : \%filter; 124 $filter->{$_} || delete $filter->{$_} for keys %$filter; 125 return $filter; 126} 127 128# return the real inner filter list for the class|instance 129sub __filter { 130 my ($self) = @_; 131 return 132 ref $self 133 && UNIVERSAL::isa( $self, 'Pod::POM::View::HTML::Filter' ) 134 ? $self->{filter} 135 : \%filter; 136} 137 138sub know { 139 my ($self, $lang) = @_; 140 return exists $self->_filter()->{$lang}; 141} 142 143sub filters { keys %{ $_[0]->_filter() }; } 144 145# 146# overridden Pod::POM::View::HTML methods 147# 148sub view_for { 149 my ($self, $for) = @_; 150 my $format = $for->format; 151 my $filter = $self->_filter(); 152 153 return $for->text() . "\n\n" if $format =~ /^html\b/; 154 155 if ( $format =~ /^filter\b/ ) { 156 my $args = (split '=', $format, 2)[1]; 157 return '' unless defined $args; # silently skip 158 159 my $text = $for->text; 160 my $verbatim = 0; 161 162 # select the filters and options 163 my @langs; 164 for my $lang (split /\|/, $args) { 165 ( $lang, my $opts ) = ( split( ':', $lang, 2 ), '' ); 166 $opts =~ y/:/ /; 167 $lang = exists $filter->{$lang} ? $lang : 'default'; 168 push @langs, [ $lang, $opts ]; 169 $verbatim++ if $filter->{$lang}{verbatim}; 170 } 171 172 # cancel filtering if one filter is missing 173 @langs = ( grep { $_->[0] eq 'default' } @langs ) 174 ? ( [ 'default', '' ] ) 175 : @langs; 176 177 # process the text 178 $text = $filter->{ $_->[0] }{code}->( $text, $_->[1] ) for @langs; 179 180 return $verbatim ? "<pre>$text</pre>\n" : "$text\n"; 181 } 182 183 # fall-through 184 return ''; 185} 186 187sub view_begin { 188 my ($self, $begin) = @_; 189 my ($format, $args) = split(' ', $begin->format(), 2); 190 my $filter = $self->_filter(); 191 192 if ( $format eq 'html' ) { 193 return $self->SUPER::view_begin( $begin ); 194 } 195 elsif( $format eq 'filter' ) { 196 my @filters = map { s/^\s*|\s*$//g; $_ } split /\|/, $args; 197 198 # fetch the text and verbatim blocks in the begin section 199 # and remember the type of each block 200 my $verbatim = 0; 201 my $prev = ''; 202 my $text = ''; 203 for my $item ( @{ $begin->content } ) { 204 $text .= ($prev ? "\n\n" :'') . $item->text(); 205 $prev = 1; 206 $verbatim++ if $item->type eq 'verbatim'; 207 } 208 209 # a block is verbatim only if all subblocks are verbatim 210 $verbatim = 0 if $verbatim != @{ $begin->content }; 211 212 # select the filters and options 213 my @langs; 214 for my $f (@filters) { 215 my ( $lang, $opts ) = split( ' ', $f, 2 ); 216 $lang = exists $filter->{$lang} ? $lang : 'default'; 217 push @langs, [ $lang, $opts ]; 218 $verbatim++ if $filter->{$lang}{verbatim}; 219 } 220 221 # cancel filtering if one filter is missing 222 @langs = ( grep { $_->[0] eq 'default' } @langs ) 223 ? ( [ 'default', '' ] ) 224 : @langs; 225 226 # process the text 227 ( my $indent, $text ) = _unindent($text) 228 if $self->{auto_unindent}; 229 $text = $filter->{ $_->[0] }{code}->( $text, $_->[1] ) for @langs; 230 $text =~ s/^(?=.+)/$indent/gm 231 if $self->{auto_unindent}; 232 233 # the enclosing tags depend on the block and the last filter 234 return $verbatim ? "<pre>$text</pre>\n" : "$text\n"; 235 } 236 237 # fall-through 238 return ''; 239} 240 241# 242# utility functions 243# 244 245# a simple filter output cleanup routine 246sub _cleanup { 247 local $_ = shift; 248 s!\A<pre>\n?|\n?</pre>\n\z!!gm; # remove <pre></pre> 249 $_; 250} 251 252sub _unindent { 253 my $str = shift; 254 my $indent; 255 while ( $str =~ /^( *)\S/gmc ) { 256 $indent = 257 !defined $indent ? $1 258 : length($1) < length($indent) ? $1 259 : $indent; 260 } 261 $indent ||= ''; 262 $str =~ s/^$indent//gm; 263 return ( $indent, $str ); 264} 265 266# 267# builtin filters 268# 269 270# a cache for multiple parsers with the same options 271my %filter_parser; 272 273# Perl highlighting, thanks to Perl::Tidy 274sub perl_tidy_filter { 275 my ($code, $opts) = ( shift, shift || "" ); 276 my $output = ""; 277 278 # Perl::Tidy 20031021 uses Getopt::Long and expects the default config 279 # this is a workaround (a patch was sent to Perl::Tidy's author) 280 my $glc = Getopt::Long::Configure(); 281 Getopt::Long::ConfigDefaults(); 282 283 Perl::Tidy::perltidy( 284 source => \$code, 285 destination => \$output, 286 argv => "-html -pre -nopod2html " . $opts, 287 stderr => '-', 288 errorfile => '-', 289 ); 290 $output = _cleanup( $output ); # remove <pre></pre> 291 292 # put back Getopt::Long previous configuration, if needed 293 Getopt::Long::Configure( $glc ); 294 295 return $output; 296} 297 298# Perl highlighting, thanks to PPI::HTML 299sub perl_ppi_filter { 300 my ($code, $opts) = ( shift, shift || ''); 301 302 # PPI::HTML options 303 my %ppi_opt = map { !/=/ && s/$/=1/ ; split /=/, $_, 2 } split / /, $opts; 304 305 # create PPI::HTML syntax highlighter 306 my $highlighter = $filter_parser{ppi}{$opts} ||= PPI::HTML->new(%ppi_opt); 307 308 # highlight the code and clean up the resulting HTML 309 my $pretty = $highlighter->html(\$code); 310 $pretty =~ s/<br>$//gsm; 311 312 return $pretty; 313} 314 315# HTML highlighting thanks to Syntax::Highlight::HTML 316sub html_filter { 317 my ($code, $opts) = ( shift, shift || "" ); 318 319 my $parser = $filter_parser{html}{$opts} 320 ||= Syntax::Highlight::HTML->new( map { (split /=/) } split ' ', $opts ); 321 return _cleanup( $parser->parse($code) ); 322} 323 324# Shell highlighting thanks to Syntax::Highlight::Shell 325sub shell_filter { 326 my ($code, $opts) = ( shift, shift || "" ); 327 328 my $parser = $filter_parser{shell}{$opts} 329 ||= Syntax::Highlight::Shell->new( map { (split /=/) } split ' ', $opts ); 330 return _cleanup( $parser->parse($code) ); 331} 332 333# Kate highligthing thanks to Syntax::Highlight::Engine::Kate 334sub kate_filter { 335 my ($code, $opts) = @_; 336 my ($lang) = split ' ', $opts || ''; 337 338 my $parser = $filter_parser{kate}{$lang} 339 ||= Syntax::Highlight::Engine::Kate->new( 340 language => $lang, 341 substitutions => { 342 '<' => '<', 343 '>' => '>', 344 '&' => '&', 345 }, 346 format_table => { 347 Alert => [ '<span class="k-alert">', '</span>' ], 348 BaseN => [ '<span class="k-basen">', '</span>' ], 349 BString => [ '<span class="k-bstring">', '</span>' ], 350 Char => [ '<span class="k-char">', '</span>' ], 351 Comment => [ '<span class="k-comment">', '</span>' ], 352 DataType => [ '<span class="k-datatype">', '</span>' ], 353 DecVal => [ '<span class="k-decval">', '</span>' ], 354 Error => [ '<span class="k-error">', '</span>' ], 355 Float => [ '<span class="k-float">', '</span>' ], 356 Function => [ '<span class="k-function">', '</span>' ], 357 IString => [ '<span class="k-istring">', '</span>' ], 358 Keyword => [ '<span class="k-keyword">', '</span>' ], 359 Normal => [ '', '' ], 360 Operator => [ '<span class="k-operator">', '</span>' ], 361 Others => [ '<span class="k-others">', '</span>' ], 362 RegionMarker => [ '<span class="k-regionmarker">', '</span>' ], 363 Reserved => [ '<span class="k-reserved">', '</span>' ], 364 String => [ '<span class="k-string">', '</span>' ], 365 Variable => [ '<span class="k-variable">', '</span>' ], 366 Warning => [ '<span class="k-warning">', '</span>' ], 367 }, 368 ); 369 370 return $parser->highlightText($code); 371} 372 373sub wiki_filter { 374 my ($code, $opts) = (shift, shift || ''); 375 return Text::WikiFormat::format( $code , {}, 376 { map { ( split /=/ ) } split ' ', $opts } ); 377} 378 379sub wikimedia_filter { 380 my ($code, $opts) = (shift, shift || ''); 381 return Text::MediawikiFormat::format( $code , {}, 382 { map { ( split /=/ ) } split ' ', $opts } ); 383} 384 3851; 386 387__END__ 388 389=head1 NAME 390 391Pod::POM::View::HTML::Filter - Use filters on sections of your pod documents 392 393=cut 394 395=head1 SYNOPSIS 396 397In your POD: 398 399 Some coloured Perl code: 400 401 =begin filter perl 402 403 # now in full colour! 404 $A++; 405 406 =end filter 407 408 =for filter=perl $A++; # this works too 409 410 This should read C<bar bar bar>: 411 412 =begin filter foo 413 414 bar foo bar 415 416 =end filter 417 418In your code: 419 420 my $view = Pod::POM::View::HTML::Filter->new; 421 $view->add( 422 foo => { 423 code => sub { my $s = shift; $s =~ s/foo/bar/gm; $s }, 424 # other options are available 425 } 426 ); 427 428 my $pom = Pod::POM->parse_file( '/my/pod/file' ); 429 $pom->present($view); 430 431=begin html 432 433<style type="text/css"> 434<!-- 435/* HTML colouring styles */ 436.h-decl { color: #336699; font-style: italic; } /* doctype declaration */ 437.h-pi { color: #336699; } /* process instruction */ 438.h-com { color: #338833; font-style: italic; } /* comment */ 439.h-ab { color: #000000; font-weight: bold; } /* angles as tag delim. */ 440.h-tag { color: #993399; font-weight: bold; } /* tag name */ 441.h-attr { color: #000000; font-weight: bold; } /* attribute name */ 442.h-attv { color: #333399; } /* attribute value */ 443.h-ent { color: #cc3333; } /* entity */ 444.h-lno { color: #aaaaaa; background: #f7f7f7;} /* line numbers */ 445 446/* Perl colouring styles */ 447.c { color: #228B22;} /* comment */ 448.cm { color: #000000;} /* comma */ 449.co { color: #000000;} /* colon */ 450.h { color: #CD5555; font-weight:bold;} /* here-doc-target */ 451.hh { color: #CD5555; font-style:italic;} /* here-doc-text */ 452.i { color: #00688B;} /* identifier */ 453.j { color: #CD5555; font-weight:bold;} /* label */ 454.k { color: #8B008B; font-weight:bold;} /* keyword */ 455.m { color: #FF0000; font-weight:bold;} /* subroutine */ 456.n { color: #B452CD;} /* numeric */ 457.p { color: #000000;} /* paren */ 458.pd { color: #228B22; font-style:italic;} /* pod-text */ 459.pu { color: #000000;} /* punctuation */ 460.q { color: #CD5555;} /* quote */ 461.s { color: #000000;} /* structure */ 462.sc { color: #000000;} /* semicolon */ 463.v { color: #B452CD;} /* v-string */ 464.w { color: #000000;} /* bareword */ 465--> 466</style> 467 468<p>The resulting HTML will look like this (modulo the stylesheet):</p> 469 470<pre> <span class="c"># now in full colour!</span> 471 <span class="i">$A</span>++<span class="sc">;</span></pre> 472<pre><span class="i">$A</span>++<span class="sc">;</span> <span class="c"># this works too</span></pre> 473<p>This should read <code>bar bar bar</code>:</p> 474<p>bar bar bar</p> 475 476=end html 477 478=head1 DESCRIPTION 479 480This module is a subclass of C<Pod::POM::View::HTML> that support the 481C<filter> extension. This can be used in C<=begin> / C<=end> and 482C<=for> pod blocks. 483 484Please note that since the view maintains an internal state, only 485an instance of the view can be used to present the POM object. 486Either use: 487 488 my $view = Pod::POM::View::HTML::Filter->new; 489 $pom->present( $view ); 490 491or 492 493 $Pod::POM::DEFAULT_VIEW = Pod::POM::View::HTML::Filter->new; 494 $pom->present; 495 496Even though the module was specifically designed 497for use with C<Perl::Tidy>, you can write your own filters quite 498easily (see L<Writing your own filters>). 499 500=head1 FILTERING POD? 501 502The whole idea of this module is to take advantage of all the syntax 503colouring modules that exist (actually, C<Perl::Tidy> was my first target) 504to produce colourful code examples in a POD document (after conversion 505to HTML). 506 507Filters can be used in two different POD constructs: 508 509=over 4 510 511=item C<=begin filter I<filter>> 512 513The data in the C<=begin filter> ... C<=end filter> region is passed to 514the filter and the result is output in place in the document. 515 516The general form of a C<=begin filter> block is as follow: 517 518 =begin filter lang optionstring 519 520 # some text to process with filter "lang" 521 522 =end filter 523 524The optionstring is trimed for whitespace and passed as a single string 525to the filter routine which must perform its own parsing. 526 527=item C<=for filter=I<filter>> 528 529C<=for> filters work just like C<=begin>/C=<end> filters, except that 530a single paragraph is the target. 531 532The general form of a C<=for filter> block is as follow: 533 534 =for filter=lang:option1:option2 535 # some code in language lang 536 537The option string sent to the filter C<lang> would be C<option1 option2> 538(colons are replaced with spaces). 539 540=back 541 542=head2 Options 543 544Some filters may accept options that alter their behaviour. 545Options are separated by whitespace, and appear after the name of the 546filter. For example, the following code will be rendered in colour and 547with line numbers: 548 549 =begin filter perl -nnn 550 551 $a = 123; 552 $b = 3; 553 print $a * $b; # prints 369 554 print $a x $b; # prints 123123123 555 556 =end filter 557 558C<=for> filters can also accept options, but the syntax is less clear. 559(This is because C<=for> expects the I<formatname> to match C<\S+>.) 560 561The syntax is the following: 562 563 =for filter=html:nnn=1 564 <center><img src="camel.png" /> 565 A camel</center> 566 567In summary, options are separated by space for C<=begin> blocks and by 568colons for C<=for> paragraphs. 569 570The options and their paramater depend on the filter, but they cannot contain 571the pipe (C<|>) or colon (C<:>) character, for obvious reasons. 572 573=head2 Pipes 574 575Having filter to modify a block of text is usefule, but what's more useful 576(and fun) than a filter? Answer: a stack of filters piped together! 577 578Take the imaginary filters C<foo> (which does a simple C<s/foo/bar/g>) 579and C<bang> (which does an even simpler C<tr/r/!/>). The following block 580 581 =begin filter foo|bar 582 583 foo bar baz 584 585 =end 586 587will become C<ba! ba! baz>. 588 589And naturally, 590 591 =for filter=bar|foo 592 foo bar baz 593 594will return C<bar ba! baz>. 595 596=head2 A note on verbatim and text blocks 597 598B<Note:> The fact that I mention I<verbatim> and I<paragraph> in 599this section is due to an old bug in C<Pod::POM>, which parses the 600content of C<begin>/C<end> sections as the usual POD paragraph 601and verbatim blocks. This is a bug in C<Pod::POM>, around which 602C<Pod::POM::View::HTML::Filter> tries to work around. 603 604As from version 0.06, C<Pod::POM::View::HTML::Filter> gets to the 605original text contained in the C<=begin> / C<=end> block (it was 606easier than I thought, actually) and put that string throught all 607the filters. 608 609If any filter in the stack is defined as C<verbatim>, or if C<Pod::POM> 610detect any block in the C<=begin> / C<=end> block as verbatim, then 611the output will be produced between C<< <pre> >> and C<< </pre> >> tags. 612Otherwise, no special tags will be added (his is left to the formatter). 613 614=head2 Examples 615 616An example of the power of pipes can be seen in the following example. 617Take a bit of Perl code to colour: 618 619 =begin filter perl 620 621 "hot cross buns" =~ /cross/; 622 print "Matched: <$`> $& <$'>\n"; # Matched: <hot > cross < buns> 623 print "Left: <$`>\n"; # Left: <hot > 624 print "Match: <$&>\n"; # Match: <cross> 625 print "Right: <$'>\n"; # Right: < buns> 626 627 =end 628 629This will produce the following HTML code: 630 631 <pre> <span class="q">"hot cross buns"</span> =~ <span class="q">/cross/</span><span class="sc">;</span> 632 <span class="k">print</span> <span class="q">"Matched: <$`> $& <$'>\n"</span><span class="sc">;</span> <span class="c"># Matched: <hot > cross < buns></span> 633 <span class="k">print</span> <span class="q">"Left: <$`>\n"</span><span class="sc">;</span> <span class="c"># Left: <hot ></span> 634 <span class="k">print</span> <span class="q">"Match: <$&>\n"</span><span class="sc">;</span> <span class="c"># Match: <cross></span> 635 <span class="k">print</span> <span class="q">"Right: <$'>\n"</span><span class="sc">;</span> <span class="c"># Right: < buns></span></pre> 636 637=begin html 638 639<p>Which your browser will render as:</p> 640 641<pre> <span class="q">"hot cross buns"</span> =~ <span class="q">/cross/</span><span class="sc">;</span> 642 <span class="k">print</span> <span class="q">"Matched: <$`> $& <$'>\n"</span><span class="sc">;</span> <span class="c"># Matched: <hot > cross < buns></span> 643 <span class="k">print</span> <span class="q">"Left: <$`>\n"</span><span class="sc">;</span> <span class="c"># Left: <hot ></span> 644 <span class="k">print</span> <span class="q">"Match: <$&>\n"</span><span class="sc">;</span> <span class="c"># Match: <cross></span> 645 <span class="k">print</span> <span class="q">"Right: <$'>\n"</span><span class="sc">;</span> <span class="c"># Right: < buns></span></pre> 646 647=end html 648 649Now if you want to colour and number the HTML code produced, it's as simple 650as tackling the C<html> on top of the C<perl> filter: 651 652 =begin filter perl | html nnn=1 653 654 "hot cross buns" =~ /cross/; 655 print "Matched: <$`> $& <$'>\n"; # Matched: <hot > cross < buns> 656 print "Left: <$`>\n"; # Left: <hot > 657 print "Match: <$&>\n"; # Match: <cross> 658 print "Right: <$'>\n"; # Right: < buns> 659 660 =end 661 662Which produces the rather unreadable piece of HTML: 663 664 <pre><span class="h-lno"> 1</span> <span class="h-ab"><</span><span class="h-tag">span</span> <span class="h-attr">class</span>=<span class="h-attv">"q</span>"<span class="h-ab">></span><span class="h-ent">&quot;</span>hot cross buns<span class="h-ent">&quot;</span><span class="h-ab"></</span><span class="h-tag">span</span><span class="h-ab">></span> =~ <span class="h-ab"><</span><span class="h-tag">span</span> <span class="h-attr">class</span>=<span class="h-attv">"q</span>"<span class="h-ab">></span>/cross/<span class="h-ab"></</span><span class="h-tag">span</span><span class="h-ab">></span><span class="h-ab"><</span><span class="h-tag">span</span> <span class="h-attr">class</span>=<span class="h-attv">"sc</span>"<span class="h-ab">></span>;<span class="h-ab"></</span><span class="h-tag">span</span><span class="h-ab">></span> 665 <span class="h-lno"> 2</span> <span class="h-ab"><</span><span class="h-tag">span</span> <span class="h-attr">class</span>=<span class="h-attv">"k</span>"<span class="h-ab">></span>print<span class="h-ab"></</span><span class="h-tag">span</span><span class="h-ab">></span> <span class="h-ab"><</span><span class="h-tag">span</span> <span class="h-attr">class</span>=<span class="h-attv">"q</span>"<span class="h-ab">></span><span class="h-ent">&quot;</span>Matched: <span class="h-ent">&lt;</span>$`<span class="h-ent">&gt;</span> $<span class="h-ent">&amp;</span> <span class="h-ent">&lt;</span>$'<span class="h-ent">&gt;</span>\n<span class="h-ent">&quot;</span><span class="h-ab"></</span><span class="h-tag">span</span><span class="h-ab">></span><span class="h-ab"><</span><span class="h-tag">span</span> <span class="h-attr">class</span>=<span class="h-attv">"sc</span>"<span class="h-ab">></span>;<span class="h-ab"></</span><span class="h-tag">span</span><span class="h-ab">></span> <span class="h-ab"><</span><span class="h-tag">span</span> <span class="h-attr">class</span>=<span class="h-attv">"c</span>"<span class="h-ab">></span># Matched: <span class="h-ent">&lt;</span>hot <span class="h-ent">&gt;</span> cross <span class="h-ent">&lt;</span> buns<span class="h-ent">&gt;</span><span class="h-ab"></</span><span class="h-tag">span</span><span class="h-ab">></span> 666 <span class="h-lno"> 3</span> <span class="h-ab"><</span><span class="h-tag">span</span> <span class="h-attr">class</span>=<span class="h-attv">"k</span>"<span class="h-ab">></span>print<span class="h-ab"></</span><span class="h-tag">span</span><span class="h-ab">></span> <span class="h-ab"><</span><span class="h-tag">span</span> <span class="h-attr">class</span>=<span class="h-attv">"q</span>"<span class="h-ab">></span><span class="h-ent">&quot;</span>Left: <span class="h-ent">&lt;</span>$`<span class="h-ent">&gt;</span>\n<span class="h-ent">&quot;</span><span class="h-ab"></</span><span class="h-tag">span</span><span class="h-ab">></span><span class="h-ab"><</span><span class="h-tag">span</span> <span class="h-attr">class</span>=<span class="h-attv">"sc</span>"<span class="h-ab">></span>;<span class="h-ab"></</span><span class="h-tag">span</span><span class="h-ab">></span> <span class="h-ab"><</span><span class="h-tag">span</span> <span class="h-attr">class</span>=<span class="h-attv">"c</span>"<span class="h-ab">></span># Left: <span class="h-ent">&lt;</span>hot <span class="h-ent">&gt;</span><span class="h-ab"></</span><span class="h-tag">span</span><span class="h-ab">></span> 667 <span class="h-lno"> 4</span> <span class="h-ab"><</span><span class="h-tag">span</span> <span class="h-attr">class</span>=<span class="h-attv">"k</span>"<span class="h-ab">></span>print<span class="h-ab"></</span><span class="h-tag">span</span><span class="h-ab">></span> <span class="h-ab"><</span><span class="h-tag">span</span> <span class="h-attr">class</span>=<span class="h-attv">"q</span>"<span class="h-ab">></span><span class="h-ent">&quot;</span>Match: <span class="h-ent">&lt;</span>$<span class="h-ent">&amp;</span><span class="h-ent">&gt;</span>\n<span class="h-ent">&quot;</span><span class="h-ab"></</span><span class="h-tag">span</span><span class="h-ab">></span><span class="h-ab"><</span><span class="h-tag">span</span> <span class="h-attr">class</span>=<span class="h-attv">"sc</span>"<span class="h-ab">></span>;<span class="h-ab"></</span><span class="h-tag">span</span><span class="h-ab">></span> <span class="h-ab"><</span><span class="h-tag">span</span> <span class="h-attr">class</span>=<span class="h-attv">"c</span>"<span class="h-ab">></span># Match: <span class="h-ent">&lt;</span>cross<span class="h-ent">&gt;</span><span class="h-ab"></</span><span class="h-tag">span</span><span class="h-ab">></span> 668 <span class="h-lno"> 5</span> <span class="h-ab"><</span><span class="h-tag">span</span> <span class="h-attr">class</span>=<span class="h-attv">"k</span>"<span class="h-ab">></span>print<span class="h-ab"></</span><span class="h-tag">span</span><span class="h-ab">></span> <span class="h-ab"><</span><span class="h-tag">span</span> <span class="h-attr">class</span>=<span class="h-attv">"q</span>"<span class="h-ab">></span><span class="h-ent">&quot;</span>Right: <span class="h-ent">&lt;</span>$'<span class="h-ent">&gt;</span>\n<span class="h-ent">&quot;</span><span class="h-ab"></</span><span class="h-tag">span</span><span class="h-ab">></span><span class="h-ab"><</span><span class="h-tag">span</span> <span class="h-attr">class</span>=<span class="h-attv">"sc</span>"<span class="h-ab">></span>;<span class="h-ab"></</span><span class="h-tag">span</span><span class="h-ab">></span> <span class="h-ab"><</span><span class="h-tag">span</span> <span class="h-attr">class</span>=<span class="h-attv">"c</span>"<span class="h-ab">></span># Right: <span class="h-ent">&lt;</span> buns<span class="h-ent">&gt;</span><span class="h-ab"></</span><span class="h-tag">span</span><span class="h-ab">></span></pre> 669 670=begin html 671 672<p>But your your browser will render it as:</p> 673 674 675<pre><span class="h-lno"> 1</span> <span class="h-ab"><</span><span class="h-tag">span</span> <span class="h-attr">class</span>=<span class="h-attv">"q</span>"<span class="h-ab">></span><span class="h-ent">&quot;</span>hot cross buns<span class="h-ent">&quot;</span><span class="h-ab"></</span><span class="h-tag">span</span><span class="h-ab">></span> =~ <span class="h-ab"><</span><span class="h-tag">span</span> <span class="h-attr">class</span>=<span class="h-attv">"q</span>"<span class="h-ab">></span>/cross/<span class="h-ab"></</span><span class="h-tag">span</span><span class="h-ab">></span><span class="h-ab"><</span><span class="h-tag">span</span> <span class="h-attr">class</span>=<span class="h-attv">"sc</span>"<span class="h-ab">></span>;<span class="h-ab"></</span><span class="h-tag">span</span><span class="h-ab">></span> 676<span class="h-lno"> 2</span> <span class="h-ab"><</span><span class="h-tag">span</span> <span class="h-attr">class</span>=<span class="h-attv">"k</span>"<span class="h-ab">></span>print<span class="h-ab"></</span><span class="h-tag">span</span><span class="h-ab">></span> <span class="h-ab"><</span><span class="h-tag">span</span> <span class="h-attr">class</span>=<span class="h-attv">"q</span>"<span class="h-ab">></span><span class="h-ent">&quot;</span>Matched: <span class="h-ent">&lt;</span>$`<span class="h-ent">&gt;</span> $<span class="h-ent">&amp;</span> <span class="h-ent">&lt;</span>$'<span class="h-ent">&gt;</span>\n<span class="h-ent">&quot;</span><span class="h-ab"></</span><span class="h-tag">span</span><span class="h-ab">></span><span class="h-ab"><</span><span class="h-tag">span</span> <span class="h-attr">class</span>=<span class="h-attv">"sc</span>"<span class="h-ab">></span>;<span class="h-ab"></</span><span class="h-tag">span</span><span class="h-ab">></span> <span class="h-ab"><</span><span class="h-tag">span</span> <span class="h-attr">class</span>=<span class="h-attv">"c</span>"<span class="h-ab">></span># Matched: <span class="h-ent">&lt;</span>hot <span class="h-ent">&gt;</span> cross <span class="h-ent">&lt;</span> buns<span class="h-ent">&gt;</span><span class="h-ab"></</span><span class="h-tag">span</span><span class="h-ab">></span> 677<span class="h-lno"> 3</span> <span class="h-ab"><</span><span class="h-tag">span</span> <span class="h-attr">class</span>=<span class="h-attv">"k</span>"<span class="h-ab">></span>print<span class="h-ab"></</span><span class="h-tag">span</span><span class="h-ab">></span> <span class="h-ab"><</span><span class="h-tag">span</span> <span class="h-attr">class</span>=<span class="h-attv">"q</span>"<span class="h-ab">></span><span class="h-ent">&quot;</span>Left: <span class="h-ent">&lt;</span>$`<span class="h-ent">&gt;</span>\n<span class="h-ent">&quot;</span><span class="h-ab"></</span><span class="h-tag">span</span><span class="h-ab">></span><span class="h-ab"><</span><span class="h-tag">span</span> <span class="h-attr">class</span>=<span class="h-attv">"sc</span>"<span class="h-ab">></span>;<span class="h-ab"></</span><span class="h-tag">span</span><span class="h-ab">></span> <span class="h-ab"><</span><span class="h-tag">span</span> <span class="h-attr">class</span>=<span class="h-attv">"c</span>"<span class="h-ab">></span># Left: <span class="h-ent">&lt;</span>hot <span class="h-ent">&gt;</span><span class="h-ab"></</span><span class="h-tag">span</span><span class="h-ab">></span> 678<span class="h-lno"> 4</span> <span class="h-ab"><</span><span class="h-tag">span</span> <span class="h-attr">class</span>=<span class="h-attv">"k</span>"<span class="h-ab">></span>print<span class="h-ab"></</span><span class="h-tag">span</span><span class="h-ab">></span> <span class="h-ab"><</span><span class="h-tag">span</span> <span class="h-attr">class</span>=<span class="h-attv">"q</span>"<span class="h-ab">></span><span class="h-ent">&quot;</span>Match: <span class="h-ent">&lt;</span>$<span class="h-ent">&amp;</span><span class="h-ent">&gt;</span>\n<span class="h-ent">&quot;</span><span class="h-ab"></</span><span class="h-tag">span</span><span class="h-ab">></span><span class="h-ab"><</span><span class="h-tag">span</span> <span class="h-attr">class</span>=<span class="h-attv">"sc</span>"<span class="h-ab">></span>;<span class="h-ab"></</span><span class="h-tag">span</span><span class="h-ab">></span> <span class="h-ab"><</span><span class="h-tag">span</span> <span class="h-attr">class</span>=<span class="h-attv">"c</span>"<span class="h-ab">></span># Match: <span class="h-ent">&lt;</span>cross<span class="h-ent">&gt;</span><span class="h-ab"></</span><span class="h-tag">span</span><span class="h-ab">></span> 679<span class="h-lno"> 5</span> <span class="h-ab"><</span><span class="h-tag">span</span> <span class="h-attr">class</span>=<span class="h-attv">"k</span>"<span class="h-ab">></span>print<span class="h-ab"></</span><span class="h-tag">span</span><span class="h-ab">></span> <span class="h-ab"><</span><span class="h-tag">span</span> <span class="h-attr">class</span>=<span class="h-attv">"q</span>"<span class="h-ab">></span><span class="h-ent">&quot;</span>Right: <span class="h-ent">&lt;</span>$'<span class="h-ent">&gt;</span>\n<span class="h-ent">&quot;</span><span class="h-ab"></</span><span class="h-tag">span</span><span class="h-ab">></span><span class="h-ab"><</span><span class="h-tag">span</span> <span class="h-attr">class</span>=<span class="h-attv">"sc</span>"<span class="h-ab">></span>;<span class="h-ab"></</span><span class="h-tag">span</span><span class="h-ab">></span> <span class="h-ab"><</span><span class="h-tag">span</span> <span class="h-attr">class</span>=<span class="h-attv">"c</span>"<span class="h-ab">></span># Right: <span class="h-ent">&lt;</span> buns<span class="h-ent">&gt;</span><span class="h-ab"></</span><span class="h-tag">span</span><span class="h-ab">></span></pre> 680 681=end html 682 683=head2 Caveats 684 685There were a few things to keep in mind when mixing verbatim and text paragraphs 686in a C<=begin> block. These problems do not exist any more as from version 6870.06. 688 689=over 4 690 691=item Text paragraphs are not processed for POD escapes any more 692 693Because the C<=begin> / C<=end> block is now processed as a single 694string of text, the following block: 695 696 =begin filter html 697 698 B<foo> 699 700 =end 701 702will not be transformed into C< <b>foo</b> > before being passed to the 703filters, but will produce the expected: 704 705 <pre>B<span class="h-ab"><</span><span class="h-tag">foo</span><span class="h-ab">></span></pre> 706 707=begin html 708 709<p>This will be rendered by your web browser as:</p> 710 711 <pre>B<span class="h-ab"><</span><span class="h-tag">foo</span><span class="h-ab">></span></pre> 712 713=end html 714 715And the same text in a verbatim block 716 717 =begin filter html 718 719 B<foo> 720 721 =end 722 723will produce the same results. 724 725 <pre> B<span class="h-ab"><</span><span class="h-tag">foo</span><span class="h-ab">></span></pre> 726 727=begin html 728 729<p>Which a web browser will render as:</p> 730 731 <pre> B<span class="h-ab"><</span><span class="h-tag">foo</span><span class="h-ab">></span></pre> 732 733=end html 734 735Which looks quite the same, doesn't it? 736 737=item Separate paragraphs aren't filtered separately any more 738 739As seen in L<A note on verbatim and text blocks>, the filter now processes 740the begin block as a single string of text. So, if you have a filter 741that replace each C<*> character with an auto-incremented number in 742square brackets, like this: 743 744 $view->add( 745 notes => { 746 code => sub { 747 my ( $text, $opt ) = @_; 748 my $n = $opt =~ /(\d+)/ ? $1 : 1; 749 $text =~ s/\*/'[' . $n++ . ']'/ge; 750 $text; 751 } 752 } 753 ); 754 755And you try to process the following block: 756 757 =begin filter notes 2 758 759 TIMTOWDI*, but your library should DWIM* when possible. 760 761 You can't always claims that PICNIC*, can you? 762 763 =end filter 764 765You'll get the expected result (contrary to previous versions): 766 767 <p>TIMTOWDI[2], but your library should DWIM[3] when possible. 768 769 You can't always claims that PICNIC[4], can you?</p> 770 771The filter was really called only once, starting at C<2>, just like requested. 772 773Future versions of C<Pod::POM::View::HTML::Filter> I<may> support 774C<init>, C<begin> and C<end> callbacks to run filter initialisation and 775clean up code. 776 777=back 778 779=head1 METHODS 780 781=head2 Public methods 782 783The following methods are available: 784 785=over 4 786 787=item C<< add( lang => { I<options> }, ... ) >> 788 789Add support for one or more languages. Options are passed in a hash 790reference. 791 792The required C<code> option is a reference to the filter routine. The 793filter must take a string as its only argument and return the formatted 794HTML string (coloured accordingly to the language grammar, hopefully). 795 796Available options are: 797 798 Name Type Content 799 ---- ---- ------- 800 801 code CODEREF filter implementation 802 803 verbatim BOOLEAN if true, force the full content of the 804 =begin/=end block to be passed verbatim 805 to the filter 806 807 requires ARRAYREF list of required modules for this filter 808 809Note that C<add()> is both a class and an instance method. 810 811When used as a class method, the new language is immediately available 812for all future and existing instances. 813 814When used as an instance method, the new language is only available for 815the instance itself. 816 817=item C<delete( $lang )> 818 819Remove the given language from the list of class or instance filters. 820The deleted filter is returned by this method. 821 822C<delete()> is both a class and an instance method, just like C<add()>. 823 824=item C<filters()> 825 826Return the list of languages supported. 827 828=item C<know( I<$lang> )> 829 830Return true if the view knows how to handle language C<$lang>. 831 832=back 833 834=head2 Overloaded methods 835 836The following C<Pod::POM::View::HTML> methods are overridden in 837C<Pod::POM::View::HTML::Filter>: 838 839=over 4 840 841=item C<new()> 842 843The overloaded constructor initialises some internal structures. 844This means that you'll have to use a instance of the class as a 845view for your C<Pod::POM> object. Therefore you must use C<new>. 846 847 $Pod::POM::DEFAULT_VIEW = 'Pod::POM::View::HTML::Filter'; # WRONG 848 $pom->present( 'Pod::POM::View::HTML::Filter' ); # WRONG 849 850 # this is CORRECT 851 $Pod::POM::DEFAULT_VIEW = Pod::POM::View::HTML::Filter->new; 852 853 # this is also CORRECT 854 my $view = Pod::POM::View::HTML::Filter->new; 855 $pom->present( $view ); 856 857The only option at this time is C<auto_unindent>, which is enabled by 858default. This option remove leading indentation from all verbatim blocks 859within the begin blocks, and put it back after highlighting. 860 861=item C<view_begin()> 862 863=item C<view_for()> 864 865These are the methods that support the C<filter> format. 866 867=back 868 869=head1 FILTERS 870 871=head2 Built-in filters 872 873C<Pod::POM::View::HTML::Filter> is shipped with a few built-in filters. 874 875The name for the filter is obtained by removing C<_filter> from the 876names listed below (except for C<default>): 877 878=over 4 879 880=item default 881 882This filter is called when the required filter is not known by 883C<Pod::POM::View::HTML::Filter>. It does nothing more than normal POD 884processing (POD escapes for text paragraphs and C<< <pre> >> for 885verbatim paragraphs. 886 887You can use the C<delete()> method to remove a filter and therefore 888make it behave like C<default>. 889 890=item perl_tidy_filter 891 892This filter does Perl syntax highlighting with a lot of help from 893C<Perl::Tidy>. 894 895It accepts options to C<Perl::Tidy>, such as C<-nnn> to number lines of 896code. Check C<Perl::Tidy>'s documentation for more information about 897those options. 898 899=item perl_ppi_filter 900 901This filter does Perl syntax highlighting using C<PPI::HTML>, which is 902itself based on the incredible C<PPI>. 903 904It accepts the same options as C<PPI::HTML>, which at this time solely 905consist of C<line_numbers> to, as one may guess, add line numbers to the 906output. 907 908=item html_filter 909 910This filter does HTML syntax highlighting with the help of 911C<Syntax::Highlight::HTML>. 912 913The filter supports C<Syntax::Highlight::HTML> options: 914 915 =begin filter html nnn=1 916 917 <p>The lines of the HTML code will be numbered.</p> 918 <p>This is line 2.</p> 919 920 =end filter 921 922See C<Syntax::Highlight::HTML> for the list of supported options. 923 924=item shell_filter 925 926This filter does shell script syntax highlighting with the help of 927C<Syntax::Highlight::Shell>. 928 929The filter supports C<Syntax::Highlight::Shell> options: 930 931 =begin filter shell nnn=1 932 933 #!/bin/sh 934 echo "This is a foo test" | sed -e 's/foo/shell/' 935 936 =end filter 937 938See C<Syntax::Highlight::Shell> for the list of supported options. 939 940=item kate_filter 941 942This filter support syntax highlighting for numerous languages 943with the help of C<Syntax::Highlight::Engine::Kate>. 944 945The filter supports C<Syntax::Highlight::Engine::Kate> languages as options: 946 947 =begin filter kate Diff 948 949 Index: lib/Pod/POM/View/HTML/Filter.pm 950 =================================================================== 951 --- lib/Pod/POM/View/HTML/Filter.pm (revision 99) 952 +++ lib/Pod/POM/View/HTML/Filter.pm (working copy) 953 @@ -27,6 +27,11 @@ 954 requires => [qw( Syntax::Highlight::Shell )], 955 verbatim => 1, 956 }, 957 + kate => { 958 + code => \&kate_filter, 959 + requires => [qw( Syntax::Highlight::Engine::Kate )], 960 + verbatim => 1, 961 + }, 962 ); 963 964 my $HTML_PROTECT = 0; 965 966 =end filter 967 968Check the C<Syntax::Highlight::Engine::Kate> documentation for the full 969list of supported languages. Please note that some of them aren't well 970supported yet (by C<Syntax::Highlight::Engine::Kate>), so the output 971may not be what you expect. 972 973Here is a list of languages we have successfully tested with 974C<Syntax::Highlight::Engine::Kate> version 0.02: 975C<C>, C<Diff>, C<Fortran>, C<JavaScript>, C<LDIF>, C<SQL>. 976 977=item wiki_filter 978 979This filter converts the wiki format parsed by C<Text::WikiFormat> 980in HTML. 981 982The supported options are: C<prefix>, C<extended>, C<implicit_links>, 983C<absolute_links>. The option and value are separated by a C<=> character, 984as in the example below: 985 986 =begin filter wiki extended=1 987 988 [link|title] 989 990 =end 991 992=item wikimedia_filter 993 994This filter converts the wiki format parsed by C<Text::MediawikiFormat> 995in HTML. 996 997The supported options are: C<prefix>, C<extended>, C<implicit_links>, 998C<absolute_links> and C<process_html>. The option and value are separated 999by a C<=> character. 1000 1001=back 1002 1003=head2 Writing your own filters 1004 1005Write a filter is quite easy: a filter is a subroutine that takes two 1006arguments (text to parse and option string) and returns the filtered 1007string. 1008 1009The filter is added to C<Pod::POM::View::HTML::Filter>'s internal filter 1010list with the C<add()> method: 1011 1012 $view->add( 1013 foo => { 1014 code => \&foo_filter, 1015 requires => [], 1016 } 1017 ); 1018 1019When presenting the following piece of pod, 1020 1021 =begin filter foo bar baz 1022 1023 Some text to filter. 1024 1025 =end filter 1026 1027the C<foo_filter()> routine will be called with two arguments, like this: 1028 1029 foo_filter( "Some text to filter.", "bar baz" ); 1030 1031If you have a complex set of options, your routine will have to parse 1032the option string by itself. 1033 1034Please note that in a C<=for> construct, whitespace in the option string 1035must be replaced with colons: 1036 1037 =for filter=foo:bar:baz Some text to filter. 1038 1039The C<foo_filter()> routine will be called with the same two arguments 1040as before. 1041 1042=head1 BUILT-IN FILTERS CSS STYLES 1043 1044Each filter uses its own CSS classes, so that one can define their 1045favourite colours in a custom CSS file. 1046 1047=head2 C<perl> filter 1048 1049C<Perl::Tidy>'s HTML code looks like: 1050 1051 <span class="i">$A</span>++<span class="sc">;</span> 1052 1053Here are the classes used by C<Perl::Tidy>: 1054 1055 n numeric 1056 p paren 1057 q quote 1058 s structure 1059 c comment 1060 v v-string 1061 cm comma 1062 w bareword 1063 co colon 1064 pu punctuation 1065 i identifier 1066 j label 1067 h here-doc-target 1068 hh here-doc-text 1069 k keyword 1070 sc semicolon 1071 m subroutine 1072 pd pod-text 1073 1074=head2 C<ppi> filter 1075 1076C<PPI::HTML> uses the following CSS classes: 1077 1078 comment 1079 double 1080 heredoc_content 1081 interpolate 1082 keyword for language keywords (my, use 1083 line_number 1084 number 1085 operator for language operators 1086 pragma for pragmatas (strict, warnings) 1087 single 1088 structure for syntaxic symbols 1089 substitute 1090 symbol 1091 word for module, function and method names 1092 words 1093 match 1094 1095=head2 C<html> filter 1096 1097C<Syntax::Highlight::HTML> makes use of the following classes: 1098 1099 h-decl declaration # declaration <!DOCTYPE ...> 1100 h-pi process # process instruction <?xml ...?> 1101 h-com comment # comment <!-- ... --> 1102 h-ab angle_bracket # the characters '<' and '>' as tag delimiters 1103 h-tag tag_name # the tag name of an element 1104 h-attr attr_name # the attribute name 1105 h-attv attr_value # the attribute value 1106 h-ent entity # any entities: é « 1107 1108=head2 C<shell> filter 1109 1110C<Syntax::Highlight::Shell> makes use of the following classes: 1111 1112 s-key # shell keywords (like if, for, while, do...) 1113 s-blt # the builtins commands 1114 s-cmd # the external commands 1115 s-arg # the command arguments 1116 s-mta # shell metacharacters (|, >, \, &) 1117 s-quo # the single (') and double (") quotes 1118 s-var # expanded variables: $VARIABLE 1119 s-avr # assigned variables: VARIABLE=value 1120 s-val # shell values (inside quotes) 1121 s-cmt # shell comments 1122 1123=head2 C<kate> filter 1124 1125Output formatted with C<Syntax::Highlight::Engine::Kate> makes use 1126of the following classes: 1127 1128 k-alert # Alert 1129 k-basen # BaseN 1130 k-bstring # BString 1131 k-char # Char 1132 k-comment # Comment 1133 k-datatype # DataType 1134 k-decval # DecVal 1135 k-error # Error 1136 k-float # Float 1137 k-function # Function 1138 k-istring # IString 1139 k-keyword # Keyword 1140 k-normal # Normal 1141 k-operator # Operator 1142 k-others # Others 1143 k-regionmarker # RegionMarker 1144 k-reserved # Reserved 1145 k-string # String 1146 k-variable # Variable 1147 k-warning # Warning 1148 1149=head1 HISTORY 1150 1151The goal behind this module was to produce nice looking HTML pages from the 1152articles the French Perl Mongers are writing for the French magazine 1153GNU/Linux Magazine France (L<http://www.linuxmag-france.org/>). 1154 1155The resulting web pages can be seen at 1156L<http://articles.mongueurs.net/magazines/>. 1157 1158=head1 AUTHOR 1159 1160Philippe "BooK" Bruhat, C<< <book@cpan.org> >> 1161 1162=head1 THANKS 1163 1164Many thanks to S�bastien Aperghis-Tramoni (Maddingue), who helped 1165debugging the module and wrote C<Syntax::Highlight::HTML> and 1166C<Syntax::Highlight::Shell> so that I could ship PPVHF with more than 1167one filter. He also pointed me to C<Syntax::Highlight::Engine::Kate>, 1168which led me to clean up PPVHF before adding support for SHEK. 1169 1170Perl code examples where borrowed in Amelia, 1171aka I<Programming Perl, 3rd edition>. 1172 1173=head1 TODO 1174 1175There are a few other syntax highlighting modules on CPAN, which I should 1176try to add support for in C<Pod::POM::View::HTML::Filter>: 1177 1178=over 4 1179 1180=item * 1181 1182C<Syntax::Highlight::Universal> 1183 1184=item * 1185 1186C<Syntax::Highlight::Mason> 1187 1188=item * 1189 1190C<Syntax::Highlight::Perl> (seems old) 1191 1192=item * 1193 1194C<Syntax::Highlight::Perl::Improved> 1195 1196=back 1197 1198=head1 BUGS 1199 1200Please report any bugs or feature requests to 1201C<bug-pod-pom-view-html-filter@rt.cpan.org>, or through the web interface at 1202L<http://rt.cpan.org>. I will be notified, and then you'll automatically 1203be notified of progress on your bug as I make changes. 1204 1205=head1 COPYRIGHT & LICENSE 1206 1207Copyright 2004 Philippe "BooK" Bruhat, All Rights Reserved. 1208 1209This program is free software; you can redistribute it and/or modify it 1210under the same terms as Perl itself. 1211 1212=cut 1213 1214