File Coverage

blib/lib/Pod/PseudoPod/LaTeX.pm
Criterion Covered Total %
statement 216 318 67.9
branch 33 56 58.9
condition 8 14 57.1
subroutine 49 65 75.3
pod 1 40 2.5
total 307 493 62.2


line stmt bran cond sub pod time code
1             package Pod::PseudoPod::LaTeX;
2             BEGIN {
3 6     6   34422 $Pod::PseudoPod::LaTeX::VERSION = '1.20110710';
4             }
5              
6 6     6   5610 use Pod::PseudoPod 0.16;
  6         251188  
  6         210  
7              
8 6     6   76 use base 'Pod::PseudoPod';
  6         14  
  6         637  
9 6     6   107 use 5.008006;
  6         19  
  6         215  
10              
11 6     6   31 use strict;
  6         15  
  6         175  
12 6     6   32 use warnings;
  6         12  
  6         2139  
13              
14              
15              
16             sub new
17             {
18 6     6 1 8310 my ( $class, %args ) = @_;
19 6         54 my $self = $class->SUPER::new(%args);
20              
21 6 50       541 $self->{keep_ligatures} = exists($args{keep_ligatures}) ? $args{keep_ligatures} : 0;
22              
23             # These have their contents parsed
24 6         62 $self->accept_targets_as_text(
25             qw( sidebar blockquote programlisting screen figure table
26             PASM PIR PIR_FRAGMENT PASM_FRAGMENT PIR_FRAGMENT_INVALID )
27             );
28              
29             # These do not. Content is not touched.
30 6         364 $self->accept_target('latex');
31              
32 6   50     136 $self->{scratch} ||= '';
33 6         16 $self->{stack} = [];
34 6         19 $self->{labels} = { screen => 'Program output' };
35              
36 6         20 return $self;
37             }
38              
39             sub emit_environments
40             {
41 2     2 0 65 my ( $self, %env ) = @_;
42 2         7 for ( keys %env )
43             {
44 2         13 $self->{emit_environment}->{$_} = $env{$_};
45             }
46             }
47              
48             sub end_Document
49             {
50 6     6 0 239 my $self = shift;
51 6         20 $self->emit();
52             }
53              
54             sub emit
55             {
56 337     337 0 393 my $self = shift;
57 337 100       782 return unless defined $self->{scratch};
58 331         347 print { $self->{output_fh} } delete $self->{scratch};
  331         1359  
59             }
60              
61             sub handle_text
62             {
63 553     553 0 30467 my ( $self, $text ) = @_;
64 553         1520 $self->{scratch} .= $self->encode_text($text);
65             }
66              
67             sub encode_text
68             {
69 553     553 0 721 my ( $self, $text ) = @_;
70              
71 553         717 my $resolve = 1;
72 553         811 eval {
73 6     6   40 no warnings 'uninitialized';
  6         12  
  6         4026  
74 553 100 100     3037 if (exists($self->{curr_open}[-1][-1]{'~resolve'}) &&
75             $self->{curr_open}[-1][-1]{'~resolve'} == 0)
76             {
77 7         129 $resolve = 0;
78             }
79             };
80 553 100       1308 return $text unless $resolve;
81              
82 546 100       1441 return $self->encode_verbatim_text($text) if $self->{flags}{in_verbatim};
83 528 50       27428 return $text if $self->{flags}{in_xref};
84 528 50       1069 return $text if $self->{flags}{in_figure};
85              
86             # Escape LaTeX-specific characters
87 528         730 $text =~ s/\\/\\backslash/g; # backslashes are special
88 528         1046 $text =~ s/([#\$&%_{}])/\\$1/g;
89 528         692 $text =~ s/(\^)/\\char94{}/g; # carets are special
90 528         573 $text =~ s/
91 528         571 $text =~ s/>/\\textgreater{}/g;
92              
93 528         608 $text =~ s/(\\backslash)/\$$1\$/g; # add unescaped dollars
94              
95             # use the right beginning quotes
96 528         669 $text =~ s/(^|\s)"/$1``/g;
97              
98             # and the right ending quotes
99 528         608 $text =~ s/"(\W|$)/''$1/g;
100              
101             # fix the ellipses
102 528         638 $text =~ s/\.{3}\s*/\\ldots /g;
103              
104             # fix the ligatures
105 528 50       1428 $text =~ s/f([fil])/f\\mbox{}$1/g unless $self->{keep_ligatures};
106              
107             # fix emdashes
108 528         626 $text =~ s/\s--\s/---/g;
109              
110             # fix tildes
111 528         619 $text =~ s/~/\$\\sim\$/g;
112              
113             # suggest hyphenation points for module names
114 528         562 $text =~ s/::/::\\-/g;
115              
116 528         2259 return $text;
117             }
118              
119             # in verbatim mode, some things still need escaping - otherwise markup
120             # wouldn't work when the codes_in_verbatim option is enabled.
121             sub encode_verbatim_text {
122 18     18 0 25 my ($self, $text) = @_;
123              
124 18         34 $text =~ s/([{}])/\\$1/g;
125 18         36 $text =~ s/\\(?![{}])/\\textbackslash{}/g;
126              
127 18         76 return $text;
128             }
129              
130             sub start_head0
131             {
132 6     6 0 3997 my $self = shift;
133 6         25 $self->{scratch} .= '\\chapter{';
134             }
135              
136             sub end_head0
137             {
138 6     6 0 98 my $self = shift;
139 6         15 $self->{scratch} .= "}\n\n";
140 6         19 $self->emit();
141             }
142              
143             sub end_Para
144             {
145 162     162 0 1697 my $self = shift;
146 162         248 $self->{scratch} .= "\n\n";
147 162         303 $self->emit();
148             }
149              
150             BEGIN
151             {
152 6     6   26 for my $level ( 1 .. 5 )
153             {
154 30         74 my $prefix = '\\' . ( 'sub' x ( $level - 1 ) ) . 'section*{';
155             my $start_sub = sub {
156 24     24   8053 my $self = shift;
157 24         97 $self->{scratch} .= $prefix;
158 30         102 };
159              
160             my $end_sub = sub {
161 24     24   361 my $self = shift;
162 24         42 $self->{scratch} .= "}\n\n";
163 24         51 $self->emit();
164 30         91 };
165              
166 6     6   37 no strict 'refs';
  6         18  
  6         304  
167 30         36 *{ 'start_head' . $level } = $start_sub;
  30         130  
168 30         36 *{ 'end_head' . $level } = $end_sub;
  30         3269  
169             }
170             }
171              
172             sub start_E
173             {
174 36     36 0 516 my $self = shift;
175 36         43 push @{ $self->{stack} }, delete $self->{scratch};
  36         114  
176 36         112 $self->{scratch} = '';
177             }
178              
179             my %characters = (
180             acute => sub { qq|\\'| . shift },
181             grave => sub { qq|\\`| . shift },
182             uml => sub { qq|\\"| . shift },
183             cedilla => sub { '\c' }, # ccedilla
184             opy => sub { '\copyright' }, # copy
185             dash => sub { '---' }, # mdash
186             lusmn => sub { '\pm' }, # plusmn
187             mp => sub { '\&' }, # amp
188             );
189              
190             sub end_E
191             {
192 36     36 0 345 my $self = shift;
193 36         42 my $clean_entity;
194              
195             # XXX - error checking here
196 36         63 my $entity = delete $self->{scratch};
197 36         102 $entity =~ /(\w)(\w+)/;
198              
199 36 50       104 if ( exists $characters{$2} )
    0          
200             {
201 36         113 $clean_entity = $characters{$2}->($1);
202             }
203             elsif ( $clean_entity = Pod::Escapes::e2char($entity) )
204             {
205             }
206             else
207             {
208 0         0 die "Unrecognized character '$entity'\n";
209             }
210              
211 36         56 $self->{scratch} = pop @{ $self->{stack} };
  36         93  
212 36         113 $self->{scratch} .= $clean_entity;
213             }
214              
215 30     30   21765 sub _treat_Es { }
216              
217             sub start_X
218             {
219 36     36 0 811 my $self = shift;
220 36         47 push @{ $self->{stack} }, delete $self->{scratch};
  36         97  
221 36         114 $self->{scratch} = '';
222             }
223              
224             sub end_X
225             {
226 36     36 0 360 my $self = shift;
227 36         70 my $terms_text = delete $self->{scratch};
228 36         45 my @terms;
229 36         129 for my $t (split ',', $terms_text) {
230 42         317 $t =~ s/^\s+|\s+$//g;
231 42         81 $t =~ s/"/""/g;
232 42         149 $t =~ s/([!|@])/"$1/g;
233 42         115 push @terms, $t;
234             }
235             {
236 6     6   34 no warnings 'uninitialized';
  6         12  
  6         1181  
  36         56  
237 36         42 $self->{scratch} = pop(@{ $self->{stack} })
  36         269  
238             . '\\index{' . join('!', @terms) . '}';
239             }
240             }
241              
242             sub start_Z
243             {
244 0     0 0 0 my $self = shift;
245 0         0 push @{ $self->{stack} }, delete $self->{scratch};
  0         0  
246 0         0 $self->{scratch} = '';
247 0         0 $self->{flags}{in_xref}++;
248             }
249              
250             sub end_Z
251             {
252 0     0 0 0 my $self = shift;
253 0         0 my $clean_xref = delete $self->{scratch};
254              
255             # sanitize crossreference names
256 0         0 $clean_xref =~ s/[^\w:]/-/g;
257              
258             {
259 6     6   29 no warnings 'uninitialized';
  6         12  
  6         5463  
  0         0  
260 0         0 $self->{scratch} = pop( @{ $self->{stack} } )
  0         0  
261             . '\\label{' . $clean_xref . '}';
262             }
263 0         0 $self->{flags}{in_xref}--;
264             }
265              
266             sub start_A
267             {
268 0     0 0 0 my $self = shift;
269 0         0 push @{ $self->{stack} }, delete $self->{scratch};
  0         0  
270              
271 0         0 $self->{scratch} = '';
272 0         0 $self->{flags}{in_xref}++;
273             }
274              
275             sub end_A
276             {
277 0     0 0 0 my $self = shift;
278 0         0 my $clean_xref = delete $self->{scratch};
279              
280             # sanitize crossreference names
281 0         0 $clean_xref =~ s/[^\w:]/-/g;
282 0         0 $self->{scratch} = pop @{ $self->{stack} };
  0         0  
283              
284             # Figures have a different xref format
285 0 0       0 if ( $clean_xref =~ /^fig:/ )
    0          
286             {
287 0         0 $self->{scratch} .= 'Figure \\ref{' . $clean_xref . '} ';
288             }
289             # Tables have a different xref format
290             elsif ( $clean_xref =~ /^table:/ )
291             {
292 0         0 $self->{scratch} .= 'Table \\ref{' . $clean_xref . '} ';
293             }
294             else
295             {
296 0         0 $self->{scratch} .= '\\emph{\\titleref{' . $clean_xref . '}}';
297             }
298              
299 0         0 $self->{scratch} .= ' on page~'
300             . '\\pageref{' . $clean_xref . '}';
301              
302 0         0 $self->{flags}{in_xref}--;
303             }
304              
305             sub start_F
306             {
307 6     6 0 90 my $self = shift;
308              
309 6 50       41 if ( $self->{flags}{in_figure} )
310             {
311 0         0 push @{ $self->{stack} }, delete $self->{scratch};
  0         0  
312 0         0 $self->{scratch} = '';
313             }
314             else
315             {
316 6         22 $self->{scratch} .= '\\emph{';
317             }
318             }
319              
320             sub end_F
321             {
322 6     6 0 63 my $self = shift;
323              
324 6 50       25 if ( $self->{flags}{in_figure} )
325             {
326 0         0 my $raw_filename = delete $self->{scratch};
327 0         0 $self->{scratch} = pop @{ $self->{stack} };
  0         0  
328              
329             # extract bare image filename
330 0         0 $raw_filename =~ /(\w+)\.\w+$/;
331 0         0 $self->{scratch} .= "\n\\includegraphics{" . $1 . '}';
332             }
333             else
334             {
335 6         21 $self->{scratch} .= '}';
336             }
337             }
338              
339             sub start_for
340             {
341 25     25 0 3208 my ( $self, $flags ) = @_;
342              
343 25 100 33     283 if ($flags->{target} =~ /^latex$/i) { # support latex, LaTeX, et al
    100 66        
344 6         31 $self->{scratch} .= "\n\n";
345             } elsif (exists($flags->{'~really'}) &&
346             $flags->{'~really'} eq "=begin" &&
347             exists($self->{emit_environment}{$flags->{target}})) {
348 1         2 my $title = "";
349 1 50       11 $title = "{".$flags->{title}."}" if exists $flags->{title};
350 1         10 $self->{scratch} .= sprintf("\n\\begin{%s}%s\n",
351             $self->{emit_environment}{$flags->{target}},
352             $title);
353             }
354             }
355              
356             sub end_for
357             {
358 25     25 0 1816 my ( $self, $flags ) = @_;
359              
360 25 100       158 if ($flags->{target} =~ /^latex$/i) { # support latex, LaTeX, et al
    100          
361 6         18 $self->{scratch} .= "\n\n";
362 6         20 $self->emit;
363             } elsif (exists($self->{emit_environment}{$flags->{target}})) {
364 1         4 $self->{scratch} .= sprintf("\\end{%s}\n\n",
365             $self->{emit_environment}{$flags->{target}});
366 1         3 $self->emit;
367             }
368             }
369              
370             sub start_Verbatim
371             {
372 18     18 0 4803 my $self = shift;
373              
374 18         29 my $verb_options = "commandchars=\\\\\\{\\}";
375 18         23 eval {
376 6     6   46 no warnings 'uninitialized';
  6         14  
  6         6796  
377 18 100       84 if ($self->{curr_open}[-1][-1]{target} eq 'screen') {
378 6   33     45 my $label = $self->{curr_open}[-1][-1]{title} || $self->{labels}{screen};
379 6         22 $verb_options .= ",frame=single,label=$label";
380             }
381             };
382              
383 18         62 $self->{scratch} .= "\\vspace{-6pt}\n"
384             . "\\scriptsize\n"
385             . "\\begin{Verbatim}[$verb_options]\n";
386 18         56 $self->{flags}{in_verbatim}++;
387             }
388              
389             sub end_Verbatim
390             {
391 18     18 0 166 my $self = shift;
392              
393 18         40 $self->{scratch} .= "\n\\end{Verbatim}\n"
394             . "\\vspace{-6pt}\n";
395              
396             # $self->{scratch} .= "\\addtolength{\\parskip}{5pt}\n";
397 18         25 $self->{scratch} .= "\\normalsize\n";
398 18         26 $self->{flags}{in_verbatim}--;
399 18         33 $self->emit();
400             }
401              
402             sub end_screen
403             {
404 0     0 0 0 my $self = shift;
405 0         0 $self->{scratch} .= "\n\\end{Verbatim}\n"
406             . "\\vspace{-6pt}\n";
407              
408             # $self->{scratch} .= "\\addtolength{\\parskip}{5pt}\n";
409 0         0 $self->{scratch} .= "\\normalsize\n";
410 0         0 $self->{flags}{in_verbatim}--;
411 0         0 $self->emit();
412             }
413              
414             sub start_figure
415             {
416 0     0 0 0 my ( $self, $flags ) = @_;
417              
418 0         0 $self->{scratch} .= "\\begin{figure}[!h]\n";
419              
420 0 0       0 if ( $flags->{title} )
421             {
422 0         0 my $title = $self->encode_text( $flags->{title} );
423 0         0 $title =~ s/^graphic\s*//;
424 0         0 $self->{scratch} .= "\\caption{" . $title . "}\n";
425             }
426              
427 0         0 $self->{scratch} .= "\\begin{center}\n";
428 0         0 $self->{flags}{in_figure}++;
429             }
430              
431             sub end_figure
432             {
433 0     0 0 0 my $self = shift;
434 0         0 $self->{scratch} .= "\\end{center}\n";
435 0         0 $self->{scratch} .= "\\end{figure}\n";
436 0         0 $self->{flags}{in_figure}--;
437 0         0 $self->emit();
438             }
439              
440             sub start_table
441             {
442 0     0 0 0 my ( $self, $flags) = @_;
443              
444             # Open the table
445 0         0 $self->{scratch} .= "\\begin{table}[!h]\n";
446              
447 0 0       0 if ( $flags->{title} )
448             {
449 0         0 my $title = $self->encode_text( $flags->{title} );
450 0         0 $title =~ s/^graphic\s*//;
451 0         0 $self->{scratch} .= "\\caption{" . $title . "}\n";
452             }
453 0         0 $self->{scratch} .= "\\begin{center}\n";
454              
455 0         0 $self->{flags}{in_table}++;
456 0         0 delete $self->{table_rows};
457             }
458              
459             sub end_table
460             {
461 0     0 0 0 my $self = shift;
462              
463             # Format the table body
464 0         0 my $column_count = @{ $self->{table_rows}[0] };
  0         0  
465 0         0 my $format_spec = '|' . ( 'l|' x $column_count );
466              
467             # first row is gray
468 0         0 $self->{scratch} .= "\\begin{tabular}{$format_spec}\n"
469             . "\\hline\n"
470             . "\\rowcolor[gray]{.9}\n";
471              
472             # Format each row
473 0         0 my $row;
474 0         0 for $row ( @{ $self->{table_rows} } )
  0         0  
475             {
476 0         0 $self->{scratch} .= join( ' & ', @$row )
477             . "\\\\ \\hline\n";
478             }
479              
480             # Close the table
481 0         0 $self->{scratch} .= "\\end{tabular}\n"
482             . "\\end{center}\n"
483             . "\\end{table}\n";
484              
485 0         0 $self->{flags}{in_table}--;
486 0         0 delete $self->{table_rows};
487              
488 0         0 $self->emit();
489             }
490              
491             sub start_headrow
492             {
493 0     0 0 0 my $self = shift;
494 0         0 $self->{in_headrow}++;
495             }
496              
497             sub start_bodyrows
498             {
499 0     0 0 0 my $self = shift;
500 0         0 $self->{in_headrow}--;
501             }
502              
503             sub start_row
504             {
505 0     0 0 0 my $self = shift;
506 0         0 delete $self->{table_current_row};
507             }
508              
509             sub end_row
510             {
511 0     0 0 0 my $self = shift;
512 0         0 push @{ $self->{table_rows} }, $self->{table_current_row};
  0         0  
513 0         0 delete $self->{table_current_row};
514             }
515              
516             sub start_cell
517             {
518 0     0 0 0 my $self = shift;
519 0         0 push @{ $self->{stack} }, delete $self->{scratch};
  0         0  
520 0         0 $self->{scratch} = '';
521             }
522              
523             sub end_cell
524             {
525 0     0 0 0 my $self = shift;
526 0         0 my $cell_contents = delete $self->{scratch};
527              
528 0 0       0 if ( $self->{in_headrow} )
529             {
530 0         0 $cell_contents = '\\textbf{\\textsf{' . $cell_contents . '}}';
531             }
532              
533 0         0 push @{ $self->{table_current_row} }, $cell_contents;
  0         0  
534 0         0 $self->{scratch} = pop @{ $self->{stack} };
  0         0  
535             }
536              
537             BEGIN
538             {
539 6     6   73 for my $listtype (
540             [qw( bullet itemize )], [qw( number enumerate )],
541             [qw( text description )], [qw( block description )],
542             )
543             {
544              
545             my $start_sub = sub {
546 30     30   5311 my $self = shift;
547 30         178 $self->{scratch} .= "\\vspace{-5pt}\n"
548             . "\n\\begin{$listtype->[1]}\n\n"
549             . "\\setlength{\\topsep}{0pt}\n"
550             . "\\setlength{\\itemsep}{0pt}\n";
551              
552             # $self->{scratch} .= "\\setlength{\\parskip}{0pt}\n";
553             # $self->{scratch} .= "\\setlength{\\parsep}{0pt}\n";
554 24         101 };
555              
556             my $end_sub = sub {
557 30     30   2889 my $self = shift;
558 30         110 $self->{scratch} .= "\\end{$listtype->[1]}\n\n"
559             . "\\vspace{-5pt}\n";
560 30         67 $self->emit();
561 24         119 };
562              
563 6     6   46 no strict 'refs';
  6         10  
  6         512  
564 24         29 *{ 'start_over_' . $listtype->[0] } = $start_sub;
  24         104  
565 24         30 *{ 'end_over_' . $listtype->[0] } = $end_sub;
  24         2953  
566             }
567             }
568              
569             sub start_item_bullet
570             {
571 18     18 0 5453 my $self = shift;
572 18         238 $self->{scratch} .= '\item ';
573             }
574              
575             sub start_item_number
576             {
577 0     0 0 0 my ( $self, $flags ) = @_;
578              
579             # $self->{scratch} .= "\\item[$flags->{number}] ";
580 0         0 $self->{scratch} .= "\\item "; # LaTeX will auto-number
581             }
582              
583             sub start_item_text
584             {
585 66     66 0 14424 my $self = shift;
586 66         218 $self->{scratch} .= '\item[] ';
587             }
588              
589             sub start_sidebar
590             {
591 6     6 0 943 my ( $self, $flags ) = @_;
592              
593 6         28 my $title;
594 6 50       33 $title = $self->encode_text( $flags->{title} ) if $flags->{title};
595              
596 6 100       27 if ( $self->{emit_environment}->{sidebar} )
597             {
598 1         3 $self->{scratch} .= "\\begin{" . $self->{emit_environment}->{sidebar} . "}";
599 1 50       7 $self->{scratch} .= "[$title]" if $title;
600 1         4 $self->{scratch} .= "\n";
601             }
602             else
603             {
604 5         17 $self->{scratch} .= "\\begin{figure}[!h]\n"
605             . "\\begin{center}\n"
606             . "\\framebox{\n"
607             . "\\begin{minipage}{3.5in}\n"
608             . "\\vspace{3pt}\n\n";
609              
610 5 50       27 if ( $title )
611             {
612 0         0 $self->{scratch} .= "\\begin{center}\n"
613             . "\\large{\\bfseries{" . $title . "}}\n"
614             . "\\end{center}\n\n";
615             }
616             }
617             }
618              
619             sub end_sidebar
620             {
621 6     6 0 632 my $self = shift;
622 6 100       26 if ( $self->{emit_environment}->{sidebar} )
623             {
624 1         4 $self->{scratch} .= "\\end{"
625             . $self->{emit_environment}->{sidebar} . "}\n\n";
626             }
627             else
628             {
629 5         25 $self->{scratch} .= "\\vspace{3pt}\n"
630             . "\\end{minipage}\n"
631             # end framebox
632             . "}\n"
633             . "\\end{center}\n"
634             . "\\end{figure}\n";
635             }
636             }
637              
638             BEGIN
639             {
640 6     6   14 for my $end (qw( bullet number text))
641             {
642             my $end_sub = sub {
643 84     84   879 my $self = shift;
644 84         133 $self->{scratch} .= "\n\n";
645 84         201 $self->emit();
646 18         79 };
647              
648 6     6   34 no strict 'refs';
  6         11  
  6         1060  
649 18         25 *{ 'end_item_' . $end } = $end_sub;
  18         199  
650             }
651              
652 6         110 my %formats = (
653             B => [ '\\textbf', '' ],
654             C => [ '\\texttt', '' ],
655             I => [ '\\emph', '' ],
656             U => [ '\\url', '' ],
657             R => [ '\\emph', '' ],
658             L => [ '\\url', '' ],
659             N => [ '\\footnote', '' ],
660             G => [ '$^', '$' ],
661             H => [ '$_', '$' ],
662             );
663              
664 6         46 while ( my ( $code, $fixes ) = each %formats )
665             {
666             my $start_sub = sub {
667 60     60   874 my $self = shift;
668 60         237 $self->{scratch} .= $fixes->[0] . '{';
669 54         229 };
670              
671             my $end_sub = sub {
672 60     60   775 my $self = shift;
673 60         190 $self->{scratch} .= '}' . $fixes->[1];
674 54         174 };
675              
676 6     6   50 no strict 'refs';
  6         12  
  6         362  
677 54         62 *{ 'start_' . $code } = $start_sub;
  54         201  
678 54         59 *{ 'end_' . $code } = $end_sub;
  54         507  
679             }
680              
681             }
682              
683             1;
684             __END__