File Coverage

lib/Pod/PseudoPod/DocBook.pm
Criterion Covered Total %
statement 170 250 68.0
branch 28 58 48.2
condition 7 8 87.5
subroutine 69 93 74.1
pod 1 87 1.1
total 275 496 55.4


line stmt bran cond sub pod time code
1             package Pod::PseudoPod::DocBook;
2 1     1   1378 use strict;
  1         3  
  1         47  
3 1     1   6 use vars qw( $VERSION );
  1         2  
  1         62  
4             $VERSION = '0.18';
5 1     1   5 use Carp ();
  1         2  
  1         21  
6 1     1   5 use base qw( Pod::PseudoPod );
  1         2  
  1         382  
7              
8 1     1   919 use HTML::Entities 'encode_entities';
  1         6512  
  1         3751  
9              
10             #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
11              
12             sub new {
13 23     23 1 15804 my $self = shift;
14 23         93 my $new = $self->SUPER::new(@_);
15 23   50     129 $new->{'output_fh'} ||= *STDOUT{IO};
16 23         83 $new->accept_targets( 'docbook', 'DocBook' );
17 23         464 $new->accept_targets_as_text( qw(blockquote caution
18             epigraph example figure important literal note
19             production screen sidebar table tip warning) );
20              
21 23         1364 $new->nbsp_for_S(1);
22             # $new->nix_Z_codes(1);
23 23         197 $new->codes_in_verbatim(1);
24 23         153 $new->chapter_type('chapter'); # default chapter type
25 23         37 $new->{'scratch'} = '';
26 23         35 $new->{'sections'} = (); # a stack for tracking section nesting
27 23         37 $new->{'sectionnum'} = (); # a list for tracking section number
28 23         66 $new->{'sectionname'} = ['chapter','sect1', 'sect2', 'sect3', 'sect4'];
29 23         67 return $new;
30             }
31              
32             #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
33              
34             sub handle_text {
35 60     60 0 79 my ($self) = @_;
36             # escape special characters in DocBook (<, >, &, etc)
37 60         156 my $text = encode_entities( $_[1] );
38 60 50       800 if ($self->{'in_filename'}) {
39 0         0 $self->{'figure_file'} = $text;
40             } else {
41 60         241 $self->{'scratch'} .= $text;
42             }
43             }
44              
45 17 50   17 0 81 sub start_Para { $_[0]{'scratch'} = '<para>' unless $_[0]{'in_figure'} }
46             sub start_Verbatim {
47 2 50   2 0 10 $_[0]{'scratch'} = "<programlisting>\n" unless ($_[0]{'in_screen'});
48 2         8 $_[0]{'in_verbatim'} = 1;
49             }
50              
51 2     2 0 7 sub start_head0 { $_[0]->set_section(0); }
52 3     3 0 10 sub start_head1 { $_[0]->set_section(1); }
53 1     1 0 4 sub start_head2 { $_[0]->set_section(2); }
54 2     2 0 7 sub start_head3 { $_[0]->set_section(3); }
55 1     1 0 4 sub start_head4 { $_[0]->set_section(4); }
56              
57             sub set_section {
58 9     9 0 12 my ($self, $level) = @_;
59 9         20 $self->{'scratch'} = $self->close_sections($level);
60 9 100       24 $self->{'sectionnum'}[$level]++ if ($level > 0);
61 9         20 my $id = $self->chapter_id();
62 9 100       22 if ($level > 0) {
63 7         7 my @sectionnum = @{$self->{'sectionnum'}};
  7         17  
64 7         11 $id .= '-SECT-';
65 7         26 $id .= join '.', @sectionnum[1 .. $#sectionnum];
66             }
67 9         21 $self->{'scratch'} .= '<' . $self->{'sectionname'}[$level];
68 9         16 $self->{'scratch'} .= ' id="' . $id . '">';
69 9         17 $self->{'scratch'} .= "\n<title>";
70 9         9 push @{$self->{'sections'}}, $level;
  9         29  
71             }
72              
73             sub close_sections {
74 31     31 0 34 my ($self, $level) = @_;
75 31         45 my $scratch = '';
76 31         42 my $sections = $self->{'sections'};
77             # Are we starting a new section that isn't a subsection?
78 31   100     117 while (defined $sections
      100        
79             && @$sections > 0
80             && $level <= $sections->[-1]) {
81 9         13 my $closing = pop @$sections;
82 9         16 delete $self->{'sectionnum'}[$closing + 1];
83 9         61 $scratch .= "</" . $self->{'sectionname'}[$closing] . ">\n";
84             }
85 31         68 return $scratch;
86             }
87              
88 1     1 0 3 sub start_over_bullet { $_[0]{'scratch'} = '<itemizedlist>'; $_[0]->emit() }
  1         4  
89 0     0 0 0 sub start_over_block { $_[0]{'scratch'} = '<itemizedlist>'; $_[0]->emit() }
  0         0  
90 1     1 0 3 sub start_over_number { $_[0]{'scratch'} = '<orderedlist>'; $_[0]->emit() }
  1         5  
91 1     1 0 4 sub start_over_text { $_[0]{'scratch'} = '<variablelist>'; $_[0]->emit() }
  1         7  
92              
93 1     1 0 3 sub end_over_bullet { $_[0]{'scratch'} .= '</itemizedlist>'; $_[0]->emit() }
  1         55  
94 0     0 0 0 sub end_over_block { $_[0]{'scratch'} .= '</itemizedlist>'; $_[0]->emit() }
  0         0  
95             sub end_over_number {
96 1 50   1 0 7 $_[0]{'scratch'} .= "</para></listitem>\n" if ($_[0]{'in_numlist'});
97 1         2 $_[0]{'scratch'} .= '</orderedlist>';
98 1         2 $_[0]{'in_numlist'} = 0;
99 1         4 $_[0]->emit();
100             }
101             sub end_over_text {
102 1 50   1 0 6 $_[0]{'scratch'} .= "</listitem>\n</varlistentry>\n" if ($_[0]{'in_varlist'});
103 1         3 $_[0]{'scratch'} .= '</variablelist>';
104 1         3 $_[0]{'in_varlist'} = 0;
105 1         4 $_[0]->emit();
106             }
107              
108 2     2 0 8 sub start_item_bullet { $_[0]{'scratch'} = '<listitem><para>' }
109 2     2 0 6 sub end_item_bullet { $_[0]{'scratch'} .= '</para></listitem>'; $_[0]->emit() }
  2         6  
110              
111             sub start_item_number {
112 2 100   2 0 7 $_[0]{'scratch'} .= "</para></listitem>\n" if ($_[0]{'in_numlist'});
113 2         6 $_[0]{'scratch'} .= "<listitem><para>";
114 2         6 $_[0]{'in_numlist'} = 1;
115             }
116             sub end_item_number {
117 2     2 0 6 $_[0]->emit()
118             }
119              
120             sub start_item_text {
121 2 100   2 0 9 $_[0]{'scratch'} .= "</listitem>\n</varlistentry>\n" if ($_[0]{'in_varlist'});
122 2         5 $_[0]{'scratch'} .= "<varlistentry>\n<term>";
123 2         6 $_[0]{'in_varlist'} = 1;
124             }
125              
126             sub end_item_text {
127 2     2 0 6 $_[0]{'scratch'} .= "</term>\n<listitem>";
128 2         5 $_[0]->emit()
129             }
130              
131              
132             # . . . . . Now the actual formatters:
133              
134             sub end_Para {
135 17 50   17 0 47 unless ($_[0]{'in_figure'}) {
136 17         33 $_[0]{'scratch'} .= '</para>';
137 17         36 $_[0]->emit();
138             }
139             }
140             sub end_Verbatim {
141 2 50   2 0 11 $_[0]{'scratch'} .= "\n</programlisting>" unless ($_[0]{'in_screen'});
142 2         5 $_[0]{'in_verbatim'} = 0;
143 2         6 $_[0]->emit();
144             }
145              
146 2     2 0 4 sub end_head0 { $_[0]{'scratch'} .= '</title>'; $_[0]->emit() }
  2         7  
147 3     3 0 6 sub end_head1 { $_[0]{'scratch'} .= '</title>'; $_[0]->emit() }
  3         7  
148 1     1 0 3 sub end_head2 { $_[0]{'scratch'} .= '</title>'; $_[0]->emit() }
  1         3  
149 2     2 0 4 sub end_head3 { $_[0]{'scratch'} .= '</title>'; $_[0]->emit() }
  2         13  
150 1     1 0 3 sub end_head4 { $_[0]{'scratch'} .= '</title>'; $_[0]->emit() }
  1         3  
151              
152              
153             sub start_sidebar {
154 0     0 0 0 my ($self, $flags) = @_;
155 0         0 $self->{'scratch'} = '<sidebar>';
156 0 0       0 if ($flags->{'title'}) {
157             # small hack for encoded entities in sidebar titles
158 0         0 $flags->{'title'} =~ s/E<(\w+)>/&$1;/g;
159 0         0 $self->{'scratch'} .= "\n<title>" . $flags->{'title'} . "</title>";
160             }
161 0         0 $self->emit();
162             }
163              
164 0     0 0 0 sub end_sidebar { $_[0]{'scratch'} .= '</sidebar>'; $_[0]->emit() }
  0         0  
165              
166             sub start_figure {
167 0     0 0 0 my ($self, $flags) = @_;
168 0         0 $self->{'in_figure'} = 1;
169 0         0 $self->{'figure_file'} = '';
170             # $self->{'scratch'} .= '<figure>';
171             # $self->{'scratch'} .= '<title>' . $flags->{'title'} . '</title>' if $flags->{'title'};
172             }
173              
174             sub end_figure {
175 0     0 0 0 my ($self, $flags) = @_;
176              
177 0 0       0 if ($self->{'figure_file'}) {
178 0         0 my $filepath = $self->{'figure_file'};
179 0         0 my $fileformat = '';
180 0 0       0 if ($filepath =~ m/\.(\w+$)/) {
181 0         0 $fileformat = uc($1);
182             }
183              
184 0         0 $self->{'scratch'} .= <<"XMLBLOCK";
185             <mediaobject>
186             <imageobject role="print">
187             <imagedata fileref="$filepath" format="$fileformat"/>
188             </imageobject>
189             <imageobject role="web">
190             <imagedata fileref="$filepath" format="$fileformat"/>
191             </imageobject>
192             </mediaobject>
193             XMLBLOCK
194              
195             # $self->{'scratch'} .= "</figure>";
196 0         0 $self->emit();
197             }
198              
199 0         0 $self->{'in_figure'} = 0;
200 0         0 $self->{'figure_file'} = '';
201             }
202              
203             # This handles =begin and =for blocks of all kinds.
204             sub start_for {
205 0     0 0 0 my ($self, $flags) = @_;
206 0         0 my $target = $flags->{'target'};
207 0 0       0 if ($target eq "production") {
208 0         0 $self->{'scratch'} .= "<important><para>Note for Production:</para>";
209             } else {
210 0         0 $self->{'scratch'} .= "<$target>";
211             }
212 0         0 $self->{"in_$target"} = 1;
213 0         0 $self->emit();
214              
215             }
216             sub end_for {
217 0     0 0 0 my ($self, $flags) = @_;
218 0         0 my $target = $flags->{'target'};
219 0 0       0 if ($target eq "production") {
220 0         0 $self->{'scratch'} .= "</important>";
221             } else {
222 0         0 $self->{'scratch'} .= "</$target>";
223             }
224 0         0 $self->{"in_$target"} = 0;
225 0         0 $self->emit();
226             }
227              
228             sub start_table {
229 0     0 0 0 my ($self, $flags) = @_;
230 0         0 my $id = $self->chapter_id() . '-TABLE-'. $self->table_next();
231 0         0 $self->{'scratch'} .= '<table id="'.$id.'" label="" frame="topbot" ';
232 0         0 $self->{'scratch'} .= 'colsep="0" rowsep="0">';
233 0 0       0 if ($flags->{'title'}) {
234 0         0 $self->{'scratch'} .= "\n<title>" . $flags->{'title'} . '</title>';
235             }
236 0         0 $self->{'scratch'} .= "\n" . '<tgroup cols="">';
237 0         0 $self->emit();
238             }
239 0     0 0 0 sub table_next { ++$_[0]{'table_count'} }
240              
241 0     0 0 0 sub end_table { $_[0]{'scratch'} .= '</tbody></tgroup></table>'; $_[0]->emit() }
  0         0  
242              
243 0     0 0 0 sub start_headrow { $_[0]{'scratch'} .= "<thead>\n"; $_[0]{'headrow'} = 1 }
  0         0  
244             sub start_bodyrows {
245 0     0 0 0 my ($self, $flags) = @_;
246 0 0       0 $self->{'scratch'} .= "</thead>\n" if ($self->{'headrow'});
247 0         0 $self->{'headrow'} = 0;
248 0         0 $self->{'scratch'} .= "<tbody>\n";
249             }
250              
251 0     0 0 0 sub start_row {$_[0]{'scratch'} .= "<row>\n" }
252 0     0 0 0 sub end_row { $_[0]{'scratch'} .= '</row>'; $_[0]->emit() }
  0         0  
253              
254             sub start_cell {
255 0     0 0 0 $_[0]{'scratch'} .= '<entry align="left"><para>';
256             }
257             sub end_cell {
258 0     0 0 0 my $self = shift;
259 0         0 $self->{'scratch'} .= '</para></entry>';
260 0         0 $self->emit();
261             }
262              
263             sub start_Document {
264 22     22 0 57 my ($self) = @_;
265             }
266             sub end_Document {
267 22     22 0 31 my ($self) = @_;
268 22         60 $self->{'scratch'} .= $self->close_sections(-1);
269 22         46 $self->emit();
270             }
271              
272             # Handling entity tags
273 1     1 0 4 sub start_L { $_[0]{'scratch'} .= '<xref linkend="' }
274 1     1 0 4 sub end_L { $_[0]{'scratch'} .= '"/>' }
275              
276 1     1 0 2 sub start_A { my $self = shift @_; $self->start_L(@_) }
  1         5  
277 1     1 0 2 sub end_A { my $self = shift @_; $self->end_L(@_) }
  1         4  
278              
279 2     2 0 8 sub start_B { $_[0]{'scratch'} .= '<emphasis role="strong">' }
280 2     2 0 7 sub end_B { $_[0]{'scratch'} .= '</emphasis>' }
281              
282 1     1 0 5 sub start_C { $_[0]{'scratch'} .= '<literal>' }
283 1     1 0 4 sub end_C { $_[0]{'scratch'} .= '</literal>' }
284              
285 1     1 0 3 sub start_E { $_[0]{'scratch'} .= '&' }
286 1     1 0 4 sub end_E { $_[0]{'scratch'} .= ';' }
287              
288             sub start_F {
289 1     1 0 2 my ($self) = @_;
290 1 50       4 if ($self->{'in_figure'}) {
291 0         0 $self->{'in_filename'} = 1;
292             } else {
293 1         5 $self->{'scratch'} .= '<filename>';
294             }
295             }
296             sub end_F {
297 1     1 0 4 my ($self) = @_;
298 1 50       5 if ($self->{'in_figure'}) {
299 0         0 $self->{'in_filename'} = 0;
300             } else {
301 1         4 $self->{'scratch'} .= '</filename>';
302             }
303             }
304              
305 1     1 0 5 sub start_G { $_[0]{'scratch'} .= '<superscript>' }
306 1     1 0 4 sub end_G { $_[0]{'scratch'} .= '</superscript>' }
307              
308 1     1 0 5 sub start_H { $_[0]{'scratch'} .= '<subscript>' }
309 1     1 0 5 sub end_H { $_[0]{'scratch'} .= '</subscript>' }
310              
311 1     1 0 4 sub start_I { $_[0]{'scratch'} .= '<emphasis>' }
312 1     1 0 3 sub end_I { $_[0]{'scratch'} .= '</emphasis>' }
313              
314             sub start_N {
315 2     2 0 3 my ($self) = @_;
316 2         5 my $id = $self->chapter_id() . '-FNOTE-'. $self->footnote_next();
317 2         8 $self->{'scratch'} .= '<footnote id="'.$id.'" label="*"><para>';
318             }
319             sub end_N {
320 2     2 0 3 my ($self) = @_;
321 2         6 $self->{'scratch'} .= '</para></footnote>';
322             }
323 2     2 0 7 sub footnote_next { ++$_[0]{'footnote_count'} }
324              
325 0     0 0 0 sub start_M { $_[0]{'scratch'} .= '<firstterm>' }
326 0     0 0 0 sub end_M { $_[0]{'scratch'} .= '</firstterm>' }
327              
328 1     1 0 5 sub start_R { $_[0]{'scratch'} .= '<replaceable>' }
329 1     1 0 4 sub end_R { $_[0]{'scratch'} .= '</replaceable>' }
330              
331 1     1 0 4 sub start_U { $_[0]{'scratch'} .= '<ulink url="' }
332 1     1 0 4 sub end_U { $_[0]{'scratch'} .= '"/>' }
333              
334             sub start_X {
335 0     0 0 0 my ($self) = @_;
336 0         0 my $id = $self->chapter_id() . '-IDX-' . $self->index_next();
337 0         0 $self->{'scratch'} .= '<indexterm id="'.$id.'"><primary>';
338             }
339 0     0 0 0 sub end_X { $_[0]{'scratch'} .= '</primary></indexterm>' }
340             sub index_next {
341 0     0 0 0 my ($self) = @_;
342 0         0 my $idx = ++$self->{'index_count'};
343 0         0 return sprintf("%04d", $idx);
344             }
345              
346 1     1 0 5 sub start_Z { $_[0]{'scratch'} .= '<anchor id="' }
347 1     1 0 5 sub end_Z { $_[0]{'scratch'} .= '"/>' }
348              
349             sub emit {
350 62     62 0 82 my($self) = @_;
351 62 100       154 if ($self->{'scratch'}) {
352 42         73 my $out = $self->{'scratch'} . "\n";
353 42         51 print {$self->{'output_fh'}} $out;
  42         143  
354 42         340 $self->{'scratch'} = '';
355             }
356 62         308 return;
357             }
358              
359 0     0 0 0 sub book_id { $_[0]{'book_id'} = $_[1] }
360             sub index_count {
361 0 0   0 0 0 $_[0]{'index_count'} = $_[1] if ($_[1]);
362 0         0 return $_[0]{'index_count'};
363             }
364             sub chapter_num {
365 2     2 0 3275 my ($self, $number) = @_;
366 2         4 $self->{'chapter_num'} = $number;
367 2         27 $self->{'sectionnum'}[0] = $number;
368             }
369             sub chapter_type {
370 25     25 0 170 my ($self, $type) = @_;
371 25         42 $self->{'chapter_type'} = $type;
372 25         85 $self->{'sectionname'}[0] = $type;
373             }
374             sub chapter_id {
375 11     11 0 15 my ($self) = @_;
376 11 100       25 unless ($self->{'chapter_id'}) {
377 4         5 my $id;
378 4 50       10 $id = $self->{'book_id'} . '-' if ($self->{'book_id'});
379 4 100       16 if ($self->{'chapter_type'} eq 'preface') {
    50          
    50          
    50          
380 2         5 $id .= 'PREFACE';
381 2 50       7 $id .= '-' . $self->{'chapter_num'} if ($self->{'chapter_num'});
382             } elsif ($self->{'chapter_type'} eq 'colophon') {
383 0         0 $id .= 'COLOPHON';
384             } elsif ($self->{'chapter_type'} eq 'appendix') {
385 0         0 $id .= 'APP-' . $self->{'chapter_num'};
386             } elsif ($self->{'chapter_type'} eq 'chapter') {
387 2         5 $id .= 'CHP-' . $self->{'chapter_num'};
388             }
389 4         9 $self->{'chapter_id'} = $id;
390             }
391 11         28 return $self->{'chapter_id'};
392             }
393              
394             # bypass built-in E<> handling to preserve entity encoding
395 13     13   333 sub _treat_Es {}
396              
397             1;
398              
399             __END__
400              
401             =head1 NAME
402              
403             Pod::PseudoPod::DocBook -- format PseudoPod as DocBook
404              
405             =head1 SYNOPSIS
406              
407             use Pod::PseudoPod::DocBook;
408              
409             my $parser = Pod::PseudoPod::DocBook->new();
410              
411             ...
412              
413             $parser->parse_file('path/to/file.pod');
414              
415             Before sending in your manuscript, check that the formatter produced a
416             well-formed DocBook file with I<xmllint>:
417              
418             $ xmllint --noout --valid book.xml
419              
420             =head1 DESCRIPTION
421              
422             This class is a formatter that takes PseudoPod and renders it as
423             DocBook 4.4.
424              
425             This is a subclass of L<Pod::PseudoPod> and inherits all its methods.
426              
427             =head1 SEE ALSO
428              
429             L<Pod::PseudoPod>, L<Pod::Simple>
430              
431             =head1 COPYRIGHT
432              
433             Copyright (c) 2003-2006 Allison Randal. All rights reserved.
434              
435             This library is free software; you can redistribute it and/or modify
436             it under the same terms as Perl itself. The full text of the license
437             can be found in the LICENSE file included with this module.
438              
439             This library is distributed in the hope that it will be useful, but
440             without any warranty; without even the implied warranty of
441             merchantability or fitness for a particular purpose.
442              
443             =head1 AUTHOR
444              
445             Allison Randal <allison@perl.org>
446              
447             =cut
448