File Coverage

blib/lib/Pod/Parser/Groffmom.pm
Criterion Covered Total %
statement 187 194 96.3
branch 77 92 83.7
condition 5 7 71.4
subroutine 34 34 100.0
pod 0 21 0.0
total 303 348 87.0


line stmt bran cond sub pod time code
1             package Pod::Parser::Groffmom;
2              
3 7     7   366081 use warnings;
  7         29  
  7         268  
4 7     7   39 use strict;
  7         11  
  7         242  
5 7     7   53 use Carp qw(carp croak);
  7         18  
  7         410  
6 7     7   5790 use Perl6::Junction 'any';
  7         76017  
  7         587  
7 7     7   4536 use Pod::Parser::Groffmom::Color ':all';
  7         22  
  7         1394  
8 7     7   5580 use Pod::Parser::Groffmom::Entities 'entity_to_num';
  7         18  
  7         494  
9              
10 7     7   6977 use Moose;
  7         3858450  
  7         71  
11 7     7   74520 use MooseX::NonMoose;
  7         7571  
  7         42  
12 7     7   447413 use Moose::Util::TypeConstraints 'enum';
  7         21  
  7         79  
13             extends qw(Pod::Parser);
14              
15             # order of MOM_METHODS is very important. See '.COPYRIGHT' in mom docs.
16             my @MOM_METHODS = qw(title subtitle author copyright);
17             my @MOM_BOOLEANS = qw(cover newpage toc);
18             my %IS_MOM = map { uc($_) => 1 } ( @MOM_METHODS, @MOM_BOOLEANS );
19              
20             foreach my $method ( __PACKAGE__->mom_methods, __PACKAGE__->mom_booleans ) {
21              
22             # did we find this thing in the POD?
23             has $method => ( is => 'rw', isa => 'Bool' );
24              
25             # let's set the actual value to what we found
26             has "mom_$method" => ( is => 'rw', isa => 'Str' );
27              
28             # it was set in the contructor
29             has "$method\_set_in_constructor" => ( is => 'rw', isa => 'Bool' );
30              
31             # Also store them in plain text in case people need 'em
32             has "original_$method" => ( is => 'rw', isa => 'Str' );
33             }
34              
35             # This is an embarrasing, nasty hack.
36             sub get_mom_and_ctr_methods {
37 160     160 0 212 my ( $class, $method ) = @_;
38 160         400 return ( "mom_$method", "$method\_set_in_constructor" );
39             }
40              
41             sub BUILD {
42 8     8 0 77312 my $self = shift;
43 8         43 foreach my $method ( __PACKAGE__->mom_methods, __PACKAGE__->mom_booleans )
44             {
45 56         123 my ( $mom, $set_in_contructor )
46             = $self->get_mom_and_ctr_methods($method);
47 56 50       2013 if ( my $value = $self->$mom ) {
48 0         0 my $orig = "original_$method";
49 0         0 $self->$orig($value);
50 0         0 $self->$set_in_contructor(1);
51             }
52             }
53             }
54              
55             has head => ( is => 'rw' );
56             has subhead => ( is => 'rw' );
57             has mom => ( is => 'rw', isa => 'Str', default => '' );
58             has toc => ( is => 'rw', => isa => 'Bool' );
59             has highlight => ( is => 'rw' );
60             has last_title => ( is => 'rw', isa => 'Str' );
61             has in_file_mode => ( is => 'ro', isa => 'Bool', default => 0, writer => 'set_in_file_mode' );
62              
63             # list helpers
64             has in_list_mode => ( is => 'rw', isa => 'Int', default => 0 );
65             has list_data => ( is => 'rw', isa => 'Str', default => '' );
66             has list_type => ( is => 'rw', isa => enum( [ '', qw/BULLET DIGIT/ ] ) );
67              
68             has fh => ( is => 'rw' );
69             has file_name => ( is => 'rw', isa => 'Str' );
70              
71 101     101 0 1824 sub mom_methods {@MOM_METHODS}
72 15     15 0 60 sub mom_booleans {@MOM_BOOLEANS}
73              
74             sub is_mom {
75 46     46 0 72 my ( $self, $command, $paragraph ) = @_;
76 46 100       186 if ( $command =~ /^head[123]/ ) {
    100          
77 15 100       61 return 1 if $paragraph eq 'NAME'; # special alias for 'TITLE'
78 12         35 return $IS_MOM{$paragraph};
79             }
80             elsif ( $command eq 'for' ) {
81 4 100       46 if ( $paragraph =~ /^mom\s+(?:newpage|toc|cover)/i ) {
82 3         8 return 1;
83             }
84             }
85             }
86              
87             =head1 NAME
88              
89             Pod::Parser::Groffmom - Convert POD to a format groff_mom can handle.
90              
91             =head1 VERSION
92              
93             Version 0.042
94              
95             =cut
96              
97             our $VERSION = '0.042';
98             $VERSION = eval $VERSION;
99              
100             sub _trim {
101 74     74   2547 local $_ = $_[1];
102 74         366 s/^\s+|\s+$//gs;
103 74         162 return $_;
104             }
105              
106             sub _escape {
107 62     62   2127 my ( $self, $text ) = @_;
108 62         90 $text =~ s/"/\\[dq]/g;
109              
110             # This is a quick and nasty hack, but we assume that we escape all
111             # backslashes unless they look like they're followed by a mom escape
112 62         91 $text =~ s/\\(?!\[\w+\])/\\\\/g;
113              
114             # We need to do this list dots appear at the beginning of a line an look
115             # like mom macros. This can happen if you have a some inline sequences
116             # terminating a sentence (e.g. "See the module S<C<Foo::Module>>.").
117 62         71 $text =~ s/^\./\\N'46'/;
118 62         74 $text =~ s/\n\./\n\\N'46'/g;
119              
120             # cheap attempt to combat issue with some inline sequences ending a
121             # sentence and forcing a linebreak with the final period on a line by
122             # itself.
123 62         80 $text =~ s/([[:upper:]]<+[^>]*>+)\./$1\\N'46'/g;
124              
125 62         115 return $text;
126             }
127              
128             sub command {
129 46     46 0 2620 my ( $self, $command, $paragraph, $line_num ) = @_;
130              
131             # reset mom_methods
132 46         97 $self->$_(0) foreach $self->mom_methods;
133 46         133 $paragraph = $self->_trim($paragraph);
134              
135 46         111 my $is_mom = $self->is_mom( $command, $paragraph );
136 46 100       134 $paragraph = lc $paragraph if $is_mom;
137 46 100       77 if ($is_mom) {
138 9         27 $self->parse_mom( $command, $paragraph, $line_num );
139             }
140             else {
141 37         88 $self->build_mom( $command, $paragraph, $line_num );
142             }
143             }
144              
145             sub parse_mom {
146 9     9 0 22 my ( $self, $command, $paragraph, $line_num ) = @_;
147 9 100       27 $paragraph = 'title' if $paragraph eq 'name';
148 9         21 foreach my $method ( $self->mom_methods ) {
149 24         45 my ( $mom, $set_in_contructor )
150             = $self->get_mom_and_ctr_methods($method);
151 24 100       60 if ( $paragraph eq $method ) {
152 6 50       244 if ( my $item = $self->$mom ) {
153 0 0       0 croak("Tried to reset $method ($item) at line $line_num")
154             unless $self->$set_in_contructor;
155             }
156 6         206 $self->$method(1);
157 6         273 return;
158             }
159             }
160 3 100       46 $self->mom_cover(1) if $paragraph =~ /^mom\s+cover/i;
161 3 100       44 $self->mom_toc(1) if $paragraph =~ /^mom\s+toc/i;
162 3 100       105 $self->add_to_mom(".NEWPAGE\n") if $paragraph =~ /^mom\s+newpage/i;
163             }
164              
165             {
166             my %command_handler = (
167             head1 => sub {
168             my ( $self, $paragraph ) = @_;
169             $self->last_title($paragraph);
170             $paragraph = $self->interpolate($paragraph);
171             $self->add_to_mom(qq{.HEAD "$paragraph"\n\n});
172             },
173             head2 => sub {
174             my ( $self, $paragraph ) = @_;
175             $self->last_title($paragraph);
176             $paragraph = $self->interpolate($paragraph);
177             $self->add_to_mom(qq{.SUBHEAD "$paragraph"\n\n});
178             },
179             head3 => sub {
180             my ( $self, $paragraph ) = @_;
181             $paragraph = $self->interpolate($paragraph);
182             $self->last_title($paragraph);
183             $self->add_to_mom(qq{\\f[B]$paragraph\\f[P]\n\n});
184             },
185             for => sub {
186             my ( $self, $paragraph ) = @_;
187             if ( $paragraph =~ /^mom\s+tofile\s+(.*?)\s*$/i ) {
188             if ( my $file = $1 ) {
189             if ( -f $file ) {
190             unlink $file or croak("Could not unlink($file): $!");
191             }
192             open my $fh, '>>', $file
193             or croak("Could not open ($file) for appending: $!");
194             close $self->fh if $self->fh;
195             $self->fh($fh);
196             $self->file_name($file);
197             }
198             elsif ( not $self->file_name ) {
199             croak("'=for mom tofile' found but filename not set");
200             }
201             }
202             },
203             begin => sub {
204             my ( $self, $paragraph ) = @_;
205             $paragraph = $self->_trim($paragraph);
206             if ( $paragraph =~ /^(highlight)(?:\s+(.*))?$/i ) {
207             my ( $target, $language ) = ( $1, $2 );
208             if ( $target && !$language ) {
209             $language = 'Perl';
210             }
211             $self->highlight( get_highlighter($language) );
212             }
213             elsif ( $paragraph =~ /^mom\s+tofile\s*/i ) {
214             $self->set_in_file_mode(1);
215             }
216             },
217             end => sub {
218             my ( $self, $paragraph ) = @_;
219             $paragraph = $self->_trim($paragraph);
220             if ( $paragraph eq 'highlight' ) {
221             $self->highlight('');
222             }
223             elsif ( $paragraph =~ /^mom\s+tofile\s*/i ) {
224             $self->set_in_file_mode(0);
225             }
226             },
227             pod => sub { }, # noop
228             );
229              
230             sub handler_for {
231 14     14 0 1097 my ( $self, $command ) = @_;
232 14         69 return $command_handler{$command};
233             }
234             }
235              
236             sub build_mom {
237 37     37 0 63 my ( $self, $command, $paragraph, $line_num ) = @_;
238 37         74 $paragraph = $self->_escape($paragraph);
239 37 100       119 if ( any(qw/over item back/) eq $command ) {
    50          
240 23         1351 $self->build_list( $command, $paragraph, $line_num );
241             }
242             elsif ( my $handler = $self->handler_for($command) ) {
243 14         56 $self->$handler($paragraph);
244             }
245             else {
246 0         0 carp("Unknown command (=$command $paragraph) at line $line_num");
247             }
248             }
249              
250             sub build_list {
251 23     23 0 50 my ( $self, $command, $paragraph, $line_num ) = @_;
252 23 100       76 if ( 'over' eq $command ) {
    100          
    50          
253 5         185 $self->in_list_mode( $self->in_list_mode + 1 );
254 5         170 $self->list_type('');
255             }
256             elsif ( 'back' eq $command ) {
257 5 50       196 if ( $self->in_list_mode == 0 ) {
258 0         0 warn "=back without =over at line $line_num";
259 0         0 return;
260             }
261             else {
262 5         188 $self->in_list_mode( $self->in_list_mode - 1 );
263 5         241 $self->list_data( $self->list_data . ".LIST END\n" );
264             }
265 5 100       188 if ( !$self->in_list_mode ) {
266 4         144 my $list
267             = ".L_MARGIN 1.25i\n" . $self->list_data . "\n.L_MARGIN 1i\n";
268 4         16 $self->add_to_mom($list);
269 4         154 $self->list_data('');
270             }
271             }
272             elsif ( 'item' eq $command ) {
273 13 100       461 if ( not $self->in_list_mode ) {
274 1         52 carp(
275             "Found (=item $paragraph) outside of list at line $line_num. Discarding"
276             );
277 1         1404 return;
278             }
279 12         41 $paragraph =~ /^(\*|\d+)?\s*(.*)/;
280 12         29 my ( $list_type, $item ) = ( $1, $2 );
281 12   50     25 $list_type ||= '';
282 12 100       33 $list_type = '*' ne $list_type ? 'DIGIT' : 'BULLET';
283              
284             # default to BULLET if we cannot identify the list type
285 12 100       390 if ( not $self->list_type ) {
286 5         163 $self->list_type($list_type);
287 5         168 $self->list_data( $self->list_data . ".LIST $list_type\n" );
288             }
289 12         751 $item = $self->interpolate($item);
290 12         47 $self->add_to_list(".ITEM\n$item\n");
291             }
292             }
293              
294             sub add_to_mom {
295 32     32 0 53 my ( $self, $text ) = @_;
296 32 50       77 return unless defined $text;
297 32         1098 $self->mom( ( $self->mom ) . $text );
298             }
299              
300             sub add_to_list {
301 14     14 0 20 my ( $self, $text ) = @_;
302 14 50       37 return unless defined $text;
303 14         605 $text = $self->interpolate($text);
304              
305             # This horror of horrors is because we want simple lists to be single
306             # spaced, unless there are further details after the =item line, in which
307             # case an extra newline is needed for pleasant formatting.
308 14         21 my $break = '';
309 14 100 100     486 if ( $self->list_data =~ /\n.ITEM\n.*\n$/ && $text !~ /^.ITEM/ ) {
310 2         4 $break = "\n";
311             }
312 14         478 $self->list_data( ( $self->list_data ) . "$break$text" );
313             }
314              
315             sub end_input {
316 7     7 0 17 my $self = shift;
317 7         15 my $mom = '';
318              
319 7 100       251 if ( $self->fh ) {
320 1         30 my $filename = $self->file_name;
321 1 50       30 close $self->fh or croak("Could not close ($filename): $!");
322             }
323              
324 7         25 foreach my $method ( $self->mom_methods ) {
325 28         49 my $mom_method = "mom_$method";
326 28         57 my $macro = ".\U$method";
327 28 100       1007 if ( my $item = $self->$mom_method ) {
328              
329             # handle double quotes later
330 6         24 $mom .= qq{$macro "$item"\n};
331             }
332             }
333 7 100       353 if ( $self->mom_cover ) {
334 1         3 my $cover = ".COVER";
335 1         3 foreach my $method ( $self->mom_methods ) {
336 4         8 my $mom_method = "mom_$method";
337 4 50       152 next unless $self->$mom_method;
338 4         13 $cover .= " \U$method";
339             }
340 1         5 $mom .= "$cover\n";
341             }
342 7         110 my $color_definitions = Pod::Parser::Groffmom::Color->color_definitions;
343 7         42 $mom .= <<"END";
344             .PRINTSTYLE TYPESET
345             \\#
346             .FAM H
347             .PT_SIZE 12
348             \\#
349             $color_definitions
350             .START
351             END
352 7         279 $self->mom( $mom .= $self->mom );
353 7 100       243 $self->mom( $self->mom . ".TOC\n" ) if $self->mom_toc;
354             }
355              
356             sub add_to_file {
357 2     2 0 4 my ( $self, $data ) = @_;
358 2 50       70 croak("Not in file mode!") unless $self->in_file_mode;
359 2 50       56 my $fh = $self->fh or croak("No filehandle found");
360 2         15 print $fh $data;
361             }
362              
363             sub verbatim {
364 4     4 0 12 my ( $self, $verbatim, $paragraph, $line_num ) = @_;
365 4 100       148 if ( $self->in_file_mode ) {
366 1         2 $self->add_to_file($verbatim);
367 1         44 return;
368             }
369 3         31 $verbatim =~ s/\s+$//s;
370 3 100       212 if ( $self->highlight ) {
371 2         59 $verbatim = $self->highlight->highlightText($verbatim);
372             }
373             else {
374 1         4 $verbatim = $self->_escape($verbatim);
375             }
376 3         263149 $self->add( sprintf <<'END' => $verbatim );
377             .FAM C
378             .PT_SIZE 10
379             .LEFT
380             .L_MARGIN 1.25i
381             %s
382             .QUAD
383             .L_MARGIN 1i
384             .FAM H
385             .PT_SIZE 12
386              
387             END
388             }
389              
390             sub textblock {
391 24     24 0 1973 my ( $self, $textblock, $paragraph, $line_num ) = @_;
392              
393 24 100       887 if ( $self->in_file_mode ) {
394 1         5 $self->add_to_file($textblock);
395 1         38 return;
396             }
397              
398 23         33 my $original_textblock = $textblock;
399              
400             # newlines can lead to strange things happening the new text on the next
401             # line is interpreted as a command to mom.
402 23         92 $textblock =~ s/\n/ /g;
403 23         177 $textblock = $self->_escape( $self->_trim($textblock) );
404 23         2044 $textblock = $self->interpolate( $textblock, $line_num );
405 23         68 foreach my $method ( $self->mom_methods ) {
406 80         166 my ( $mom, $set_in_contructor )
407             = $self->get_mom_and_ctr_methods($method);
408              
409 80 100       2888 if ( $self->$method ) {
410              
411             # Don't override these values if set in the contructor
412 6 50       228 if ( not $self->$set_in_contructor ) {
413              
414 6         13 my $orig = "original_$method";
415 6         237 $self->$orig($original_textblock);
416              
417             # This was set in command()
418 6         197 $self->$mom($textblock);
419             }
420 6         344 return;
421             }
422             }
423 17         67 $self->add("$textblock\n\n");
424             }
425              
426             sub add {
427 20     20 0 38 my ( $self, $data ) = @_;
428 20 100       758 my $add = $self->in_list_mode ? 'add_to_list' : 'add_to_mom';
429 20         62 $self->$add($data);
430             }
431              
432             {
433              
434             sub get_open_close_formats {
435 8     8 0 14 my ( $self, $pod_seq, $format_code ) = @_;
436 8         20 my $parent_format = $self->get_parent_format_code($pod_seq);
437 8 100       28 my $open
438             = $parent_format
439             ? "\\f[P]\\f[${parent_format}$format_code]"
440             : "\\f[$format_code]";
441 8 100       26 my $close
442             = $parent_format
443             ? "\\f[P]\\f[$parent_format]"
444             : "\\f[P]";
445 8         23 return ( $open, $close );
446             }
447             my %handler_for = (
448             I => {
449             format_code => 'I',
450             handler => sub { # italics
451             my ( $self, $paragraph, $pod_seq ) = @_;
452             my ( $open, $close )
453             = $self->get_open_close_formats( $pod_seq, 'I' );
454             return "$open$paragraph$close";
455             },
456             },
457             C => {
458             format_code => 'C',
459             handler => sub { # code
460             my ( $self, $paragraph, $pod_seq ) = @_;
461             my ( $open, $close )
462             = $self->get_open_close_formats( $pod_seq, 'C' );
463             return "$open$paragraph$close";
464             }
465             },
466             B => {
467             format_code => 'B',
468             handler => sub { # bold
469             my ( $self, $paragraph, $pod_seq ) = @_;
470             my ( $open, $close )
471             = $self->get_open_close_formats( $pod_seq, 'B' );
472             return "$open$paragraph$close";
473             }
474             },
475             E => {
476             format_code => '',
477             handler => sub { # entity
478             my ( $self, $paragraph, $pod_seq ) = @_;
479             my $num = entity_to_num($paragraph);
480             return "\\N'$num'";
481             }
482             },
483             L => {
484             format_code => '',
485             handler => sub { # link
486             my ( $self, $paragraph, $pod_seq ) = @_;
487              
488             # This is legal because the POD docs are quite specific about this.
489             my $strip_quotes = sub { $_[0] =~ s/^"|"$//g };
490              
491             if ( $paragraph !~ m{(?:/|\|)} ) { # no / or |
492             # L<Net::Ping>
493             return "\\f[C]$paragraph\\f[P]";
494             }
495             elsif ( $paragraph =~ m{^([^/]*)\|(.+)$} ) {
496              
497             # L<the Net::Ping module|Net::Ping>
498             # L<support section|PPI/SUPPORT>
499             my ( $text, $name ) = ( $1, $2 );
500              
501             $strip_quotes->($_) foreach $text, $name;
502             if ( $name eq $text ) {
503             return "\\f[C]$text\\f[P]";
504             }
505             else {
506             return qq{$text (\\f[C]$name\\f[P])};
507             }
508             }
509             elsif ( $paragraph =~ m{^(.*)/(.*)} ) {
510             my ( $name, $text ) = ( $1, $2 );
511             $strip_quotes->($_) foreach $text, $name;
512             return "$text (\\f[C]$name\\f[P])";
513             }
514             else {
515              
516             # XXX eventually we'll need better handling of this
517             warn "Unknown sequence format for L<$paragraph>";
518             return qq{"$paragraph"};
519             }
520             }
521             },
522             F => {
523             format_code => 'I',
524             handler => sub { # filename
525             my ( $self, $paragraph, $pod_seq ) = @_;
526             my ( $open, $close )
527             = $self->get_open_close_formats( $pod_seq, 'I' );
528             return "$open$paragraph$close";
529             }
530             },
531             S => {
532             format_code => '',
533             handler => sub { # non-breaking spaces
534             my ( $self, $paragraph, $pod_seq ) = @_;
535             $paragraph =~ s/\s/\\~/g; # non-breaking space
536             return " \\c\n.HYPHENATE OFF\n$paragraph\\c\n.HYPHENATE\n";
537             }
538             },
539             Z => {
540             format_code => '',
541             handler => sub { # null-effect sequence
542             my ( $self, $paragraph, $pod_seq ) = @_;
543             return '';
544             }
545             },
546             X => {
547             format_code => '',
548             handler => sub { # indexes
549             my ( $self, $paragraph, $pod_seq ) = @_;
550             return $paragraph;
551              
552             # XXX Rats. Didn't work. mom doesn't do indexing
553             # return "$paragraph\\c\n.IQ $paragraph\\c\n";
554             }
555             },
556             );
557              
558             sub get_parent_format_code {
559 8     8 0 12 my ( $self, $pod_seq ) = @_;
560              
561             # note that we only handle one level of nesting. This should change
562             # in the future.
563 8 50       24 return '' unless $pod_seq;
564 8 100       45 my $parent = $pod_seq->nested or return '';
565 7     7   39990 no warnings 'uninitialized';
  7         18  
  7         2118  
566 2   50     12 return $handler_for{ $parent->name }{format_code} || '';
567             }
568              
569             sub sequence_handler {
570 21     21 0 29 my ( $self, $sequence ) = @_;
571 21         73 return $handler_for{$sequence};
572             }
573             }
574              
575             sub interior_sequence {
576 21     21 0 1936 my ( $self, $sequence, $paragraph, $pod_seq ) = @_;
577 21 100       50 if ( my $handler = $self->sequence_handler($sequence) ) {
578 20         37 $handler = $handler->{handler};
579 20         50 return $self->$handler( $paragraph, $pod_seq );
580             }
581             else {
582 1         27 carp(
583             "Unknown sequence ($sequence<$paragraph>). Stripping sequence code."
584             );
585 1         710 return $paragraph;
586             }
587             }
588              
589             =head1 SYNOPSIS
590              
591             use Pod::Parser::Groffmom;
592             my $foo = Pod::Parser::Groffmom->new();
593             my $file = 't/test_pod.pod';
594             open my $fh, '<', $file
595             or die "Cannot open ($file) for reading: $!";
596             $parser->parse_from_filehandle($fh);
597             print $parser->mom;
598              
599             If you have printed the "mom" output to file named 'my.mom', you can then do this:
600              
601             groff -mom my.mom > my.ps
602              
603             And you will have a postscript file suitable for opening in C<gv>, Apple's
604             C<Preview.app> or anything else which can read postscript files.
605              
606             If you prefer, read C<perldoc pod2mom> for an easier interface.
607              
608             =head1 DESCRIPTION
609              
610             This subclass of C<Pod::Parser> will take a POD file and produce "mom"
611             output. See L<http://linuxgazette.net/107/schaffter.html> for a gentle
612             introduction.
613              
614             If you have C<groff> on your system, it I<should> have docs for "momdoc".
615             Otherwise, you can read them at
616             L<http://www.opensource.apple.com/source/groff/groff-28/groff/contrib/mom/momdoc/toc.html?f=text>.
617              
618             The "mom" documentation is not needed to use this module, but it would be
619             needed if you wish to hack on it.
620              
621             =head1 CONSTRUCTOR
622              
623             The following arguments may be supplied to the constructor and override any
624             values found in the POD document.
625              
626             =over 4
627              
628             =item * C<mom_title>
629              
630             =item * C<mom_subtitle>
631              
632             =item * C<mom_author>
633              
634             =item * C<mom_copyright>
635              
636             =item * C<mom_cover> (creates a cover page)
637              
638             =item * C<mom_toc> (creates a table of contents)
639              
640             =back
641              
642             =head1 ALPHA CODE
643              
644             This is alpha code. There's not much control over it yet and there are plenty
645             of POD corner cases it doesn't handle.
646              
647             =head1 MOM COMMANDS
648              
649             Most POD files will convert directly to "mom" output. However, when you view
650             the result, you might want more control over it. The following is how
651             MOM directives are handled. They may begin with either '=head1' or =head2'.
652             It doesn't matter (this might change later).
653              
654             Some commands which should alter mom behavior but not show up in the POD begin
655             with C<=for>.
656              
657             =over 4
658              
659             =item * NAME
660              
661             =head1 NAME
662              
663             This is the title of the pod.
664              
665             Whatever follows "NAME" will be the title of your document.
666              
667             =item * TITLE
668              
669             =head1 TITLE
670              
671             This is the title of the pod.
672              
673             Synonymous with 'NAME'. You may only have one title for a document.
674              
675             =item * SUBTITLE
676              
677             =head1 SUBTITLE
678              
679             This is the subtitle of the pod.
680              
681             =item * AUTHOR
682              
683             =head1 AUTHOR
684              
685             Curtis "Ovid" Poe
686              
687             This is the author of your document.
688              
689             =item * COPYRIGHT
690              
691             =head1 COPYRIGHT
692              
693             2009, Curtis "Ovid" Poe
694              
695             This will be the copyright statement on your pod. Will only appear in the
696             document if the C<=head1 COVER> command is given.
697              
698             =item * cover
699              
700             =for mom cover
701              
702             Does not require any text after it. This is merely a boolean command telling
703             C<Pod::Parser::Groffmom> to create a cover page.
704              
705             =item * newpage
706              
707             =for mom newpage
708              
709             Does not require any text after it. This is merely a boolean command telling
710             C<Pod::Parser::Groffmom> to create page break here.
711              
712             =item * toc
713              
714             =for mom toc
715              
716             Does not require any text after it. This is merely a boolean command telling
717             C<Pod::Parser::Groffmom> to create a table of contents. Due to limitations in
718             with C<groff -mom>, the table of contents will be the final page of the
719             document.
720              
721             =item * begin highlight
722              
723             =begin highlight Perl
724            
725             sub add {
726             my ( $self, $data ) = @_;
727             my $add = $self->in_list_mode ? 'add_to_list' : 'add_to_mom';
728             $self->$add($data);
729             }
730              
731             =end highlight
732              
733             This turns on syntax highlighting. Allowable highlight types are the types
734             allowed for L<Syntax::Highlight::Engine::Kate>. We default to Perl, so the
735             above can be written as:
736              
737             =begin highlight
738            
739             sub add {
740             my ( $self, $data ) = @_;
741             my $add = $self->in_list_mode ? 'add_to_list' : 'add_to_mom';
742             $self->$add($data);
743             }
744              
745             =end highlight
746              
747             For a list of allowable names for syntax highlighting, see
748             L<Pod::Parser::Groffmom::Color>.
749              
750             =item * C<for mom tofile $filename>
751              
752             This command tells L<Pod::Parser::Groffmom> that you're going to send some
753             output to a file. Must have a filename with it.
754              
755             =item * C<begin mom tofile>
756              
757             Sometimes you want to include data in your pod and have it written out to a
758             separate file. This is useful, for example, if you're writing the POD for
759             beautiful documentation for a talk, but you want to embed slides for your
760             talk.
761              
762             =begin mom tofile
763              
764             <h1>Some Header</h1>
765             <h2>Another header</h2>
766              
767             =end mom tofile
768              
769             Any paragraph or verbatim text included between these tokens are automatically
770             sent to the C<$filename> specified in C<=for mom tofile $filename>. The file
771             is opened in I<append> mode, so any text found will be added to what is
772             already there. The text is passed "as is".
773              
774             If you must pass pod, the leading '=' sign will cause this text to be handled
775             by L<Pod::Parser::Groffmom> instead of being passed to your file. Thus, any
776             leading '=' must be escaped in the format you need it in:
777              
778             =for mom tofile tmp/some.html
779              
780             ... perhaps lots of pod ...
781              
782             =begin mom tofile
783              
784             <p>To specify a header in POD:</p>
785              
786             <pre><tt>
787             &#61;head1 SOME HEADER
788             </tt></pre>
789              
790             =end mom tofile
791              
792             Of course, if the line is indented, it's considered "verbatim" text and will
793             be not be processed as a POD command:
794              
795             =begin mom tofile
796              
797             <p>To specify a header in POD:</p>
798              
799             <pre><tt>
800             =head1 SOME HEADER
801             </tt></pre>
802              
803             =end mom tofile
804              
805             =back
806              
807             =head1 SPECIAL CHARACTERS
808              
809             Special characters are often encountered in POD:
810              
811             Salvador FandiE<ntilde>o
812              
813             To see the list of named characters we support, check
814             L<Pod::Parser::Groffmom::Entities>. If the character you need is not on that
815             list, you may still enter its numeric value. The above name could also be
816             written as:
817              
818             Salvador FandiE<241>o
819              
820             And should be rendered as "Salvador FandiE<241>o".
821              
822             =head1 LIMITATIONS
823              
824             Probably plenty.
825              
826             =over 4
827              
828             =item * We don't yet handle numbered lists well (they always start with '1')
829              
830             =item * List indent level (C<=over 4>) ignored.
831              
832             =item * Syntax highlighting is experimental.
833              
834             =item * No support for hyperlinks.
835              
836             =item * No C<=head4> or below are supported.
837              
838             =item * Table of contents are generated at the end. This is a limitation of mom.
839              
840             The L<bin/pod2mom> script in this distribution will attempt to correct that
841             for you.
842              
843             =item * C<=for> is ignored except for C<=for mom tofile $filename>.
844              
845             =item * C<SE<lt>E<gt>> sequences try to work but they're finicky.
846              
847             =back
848              
849             =head1 AUTHOR
850              
851             Curtis "Ovid" Poe, C<< <ovid at cpan.org> >>
852              
853             =head1 BUGS
854              
855             Please report any bugs or feature requests to C<bug-pod-parser-groffmom at
856             rt.cpan.org>, or through the web interface at
857             L<http://rt.cpan.org/NoAuth/ReportBug.html?Queue=Pod-Parser-Groffmom>. I will
858             be notified, and then you'll automatically be notified of progress on your bug
859             as I make changes.
860              
861             =head1 SUPPORT
862              
863             You can find documentation for this module with the perldoc command.
864              
865             perldoc Pod::Parser::Groffmom
866              
867             You can also look for information at:
868              
869             =over 4
870              
871             =item * RT: CPAN's request tracker
872              
873             L<http://rt.cpan.org/NoAuth/Bugs.html?Dist=Pod-Parser-Groffmom>
874              
875             =item * AnnoCPAN: Annotated CPAN documentation
876              
877             L<http://annocpan.org/dist/Pod-Parser-Groffmom>
878              
879             =item * CPAN Ratings
880              
881             L<http://cpanratings.perl.org/d/Pod-Parser-Groffmom>
882              
883             =item * Search CPAN
884              
885             L<http://search.cpan.org/dist/Pod-Parser-Groffmom/>
886              
887             =back
888              
889             =head1 REPOSITORY
890              
891             The latest version of this module can be found at
892             L<http://github.com/Ovid/Pod-Parser-GroffMom>.
893              
894             =head1 ACKNOWLEDGEMENTS
895              
896             =head1 COPYRIGHT & LICENSE
897              
898             Copyright 2009 Curtis "Ovid" Poe, all rights reserved.
899              
900             This program is free software; you can redistribute it and/or modify it under
901             the same terms as Perl itself.
902              
903             =cut
904              
905 7     7   47 no Moose;
  7         13  
  7         95  
906             __PACKAGE__->meta->make_immutable( inline_constructor => 0 );
907              
908             1; # End of Pod::Parser::Groffmom