File Coverage

lib/Pod/PseudoPod/Book/Command/buildepub.pm
Criterion Covered Total %
statement 27 133 20.3
branch 0 20 0.0
condition 0 12 0.0
subroutine 9 17 52.9
pod 1 8 12.5
total 37 190 19.4


line stmt bran cond sub pod time code
1             package Pod::PseudoPod::Book::Command::buildepub;
2             # ABSTRACT: command module for C<ppbook buildepub>
3              
4 2     2   2488 use strict;
  2         7  
  2         82  
5 2     2   13 use warnings;
  2         5  
  2         82  
6              
7 2     2   12 use parent 'Pod::PseudoPod::Book::Command';
  2         6  
  2         18  
8              
9 2     2   146 use autodie;
  2         5  
  2         15  
10 2     2   13350 use EBook::EPUB;
  2         2371550  
  2         90  
11 2     2   20 use File::Slurp;
  2         5  
  2         187  
12 2     2   17 use File::Basename;
  2         5  
  2         146  
13 2     2   1373 use HTML::Entities;
  2         12547  
  2         229  
14 2     2   23 use File::Spec::Functions qw( catfile catdir splitpath );
  2         4  
  2         3418  
15              
16             sub execute
17             {
18 0     0 1   my ($self, $opt, $args) = @_;
19 0           my $conf = $self->config;
20 0           my @chapters = $self->get_built_html( 'xhtml' );
21 0           my $toc = $self->get_toc( @chapters );
22              
23 0           generate_ebook( $conf, $toc, @chapters );
24             }
25              
26             sub get_toc
27             {
28 0     0 0   my $self = shift;
29 0           my @toc;
30              
31 0           for my $chapter (@_)
32             {
33 0           my $contents = File::Slurp::read_file( $chapter );
34 0           while ($contents =~ /<h(\d) id="([^"]+)">(.+)<\/h\1>/g)
35             {
36 0           my ($level, $identifier, $label) = ($1, $2, $3);
37 0           $label =~ s/<[^>]+>//g;
38 0           $label =~ s/&amp;/&/g;
39 0 0         $identifier = '' if $level == 1;
40              
41 0           push @toc,
42             [ $level, $identifier, decode_entities($label), $chapter ];
43             }
44             }
45              
46 0           return \@toc;
47             }
48              
49             ##############################################################################
50             #
51             # generate_ebook()
52             #
53             # Assemble the XHTML pages into an ePub eBook.
54             #
55             sub generate_ebook
56             {
57 0     0 0   my ($conf, $table_of_contents, @chapters) = @_;
58              
59             # Create EPUB object
60 0           my $epub = EBook::EPUB->new;
61 0           my $metadata = $conf->{book};
62              
63             # Set the ePub metadata.
64 0           $epub->add_title( $metadata->{title} );
65 0           $epub->add_author( $metadata->{author_name} );
66 0           $epub->add_language( $metadata->{language} );
67 0 0         $epub->add_publisher( $metadata->{publisher} ) if $metadata->{publisher};
68              
69             $epub->add_identifier( $metadata->{ISBN13}, 'ISBN' )
70 0 0         if $metadata->{ISBN13};
71              
72             # Add the book cover.
73 0           my $cover = $conf->{book}{cover_image};
74 0 0         add_cover($conf, $epub, $cover) if -e $cover;
75              
76             # Add some other metadata to the OPF file.
77 0           $epub->add_meta_item('EBook::EPUB version', $EBook::EPUB::VERSION);
78              
79             # Add package content: stylesheet, font, html
80 0           $epub->copy_stylesheet('./build/html/style.css', 'css/style.css');
81              
82 0           my $chapter_ids = add_chapters( $epub, @chapters );
83 0           add_images( $epub );
84              
85             # Add Pod headings to table of contents.
86 0           set_table_of_contents( $epub, $table_of_contents, $chapter_ids );
87 0           write_toc( $epub, $table_of_contents, $chapter_ids );
88 0           (my $filename_title = lc $conf->{book}{title} . '.epub') =~ s/\s+/_/g;
89              
90             # Generate the ePub eBook.
91 0           my $filename = catfile( qw( build epub ), $filename_title );
92 0           $epub->pack_zip($filename);
93             }
94              
95             sub add_images
96             {
97 0     0 0   my $epub = shift;
98 0           my %mime_types =
99             (
100             jpg => 'image/jpeg',
101             gif => 'image/gif',
102             png => 'image/png',
103             );
104              
105 0           for my $image (glob( './build/images/*' ))
106             {
107 0           my ($name, $path, $suffix) = fileparse( $image, qw( jpg gif png ) );
108 0           my $mime_type = $mime_types{$suffix};
109 0           my $dest = "text/images/$name$suffix";
110              
111 0 0         die "Unknown image '$image'" unless $mime_type;
112 0           $epub->add_image_entry( $dest, $mime_type );
113 0           $epub->copy_file( $image, $dest, $mime_type );
114             }
115             }
116              
117             sub add_chapters
118             {
119 0     0 0   my $epub = shift;
120 0           my %ids;
121              
122 0           for my $chapter (@_)
123             {
124 0           my $file = (splitpath $chapter )[-1];
125 0           (my $dest = 'text/' . $file) =~ s/\.html/\.xhtml/;
126              
127 0           $ids{ $dest } = $epub->copy_xhtml( $chapter, $dest );
128             }
129              
130 0           return \%ids;
131             }
132              
133              
134             ##############################################################################
135             #
136             # set_table_of_contents()
137             #
138             # Add the Pod headings to the NCX <navMap> table of contents.
139             #
140             sub set_table_of_contents
141             {
142 0     0 0   my ($epub, $pod_headings, $ids) = @_;
143 0           my $play_order = 1;
144 0           my @navpoints = ($epub) x 5;
145 0           my @navpoint_obj;
146             my %labels;
147              
148 0           for my $heading (@$pod_headings)
149             {
150 0           my $heading_level = $heading->[0];
151 0           my $section = $heading->[1];
152 0           my $label = $heading->[2];
153 0           (my $filename = $heading->[3]) =~ s!.*/([^/]+.xhtml)$!$1!;
154 0           my $content = 'text/' . $filename;
155 0           my $count = ++$labels{$label};
156 0 0         $label .= " ($count)" if $count > 1;
157              
158             # Add the pod section to the NCX data, Except for root headings.
159 0 0 0       $content .= '#' . $section if $section && $section ne 'heading_id_2';
160 0   0       my $id = $ids->{$content} || 'navPoint-' . $play_order;
161              
162 0           my %options = (
163             content => $content,
164             id => $id,
165             play_order => $play_order,
166             label => $label,
167             );
168              
169 0           $play_order++;
170              
171             # Add the navpoints at the correct nested level.
172 0           my $navpoint_obj = $navpoints[$heading_level - 1];
173              
174 0           $navpoint_obj = $navpoint_obj->add_navpoint(%options);
175              
176             # The returned navpoint object is used for the next nested level.
177 0           $navpoints[$heading_level] = $navpoint_obj;
178              
179             # This is a workaround for non-contiguous heading levels.
180 0           $navpoints[$heading_level + 1] = $navpoint_obj;
181             }
182             }
183              
184             ##############################################################################
185             #
186             # write_toc()
187             #
188             # Writes the toc index.html file
189             #
190             sub write_toc
191             {
192 0     0 0   my ($epub, $pod_headings, $ids) = @_;
193 0           my $play_order = 1;
194 0           my %labels;
195             my $html;
196              
197 0           for my $heading (@$pod_headings)
198             {
199 0           my $heading_level = $heading->[0];
200 0           my $section = $heading->[1];
201 0           my $label = $heading->[2];
202 0           (my $filename = $heading->[3]) =~ s!.*/([^/]+.xhtml)$!$1!;
203 0           my $content = 'text/' . $filename;
204 0           my $count = ++$labels{$label};
205 0 0         $label .= " ($count)" if $count > 1;
206              
207             # Add the pod section to the NCX data, Except for root headings.
208 0 0 0       $content .= '#' . $section if $section && $section ne 'heading_id_2';
209 0   0       my $id = $ids->{$content} || 'navPoint-' . $play_order;
210 0           my $indent = '&nbsp;&nbsp;&nbsp;' x ( $heading_level - 1 );
211              
212 0           $html .= $indent . qq|<a href="$id">$label</a><br />\n|;
213             }
214              
215 0           open my $fh, '>:utf8', 'index.html';
216 0           print {$fh} $html;
  0            
217 0           close $fh;
218             }
219              
220             ###############################################################################
221             #
222             # add_cover()
223             #
224             # Add a cover image to the eBook. Add cover metadata for iBooks and add an
225             # additional cover page for other eBook readers.
226             #
227             sub add_cover
228             {
229 0     0 0   my ($conf, $epub, $cover_image) = @_;
230              
231             # Check if the cover image exists.
232 0 0         if (!-e $cover_image)
233             {
234 0           warn "Cover image $cover_image not found.\n";
235 0           return;
236             }
237              
238             # Add cover metadata for iBooks.
239 0           my $cover_id = $epub->copy_image($cover_image, 'images/cover.png');
240 0           $epub->add_meta_item('cover', $cover_id);
241              
242             # Add an additional cover page for other eBook readers.
243 0           my $cover_xhtml = <<END_XHTML;
244             <?xml version="1.0" encoding="UTF-8"?>
245             <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
246             "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
247             <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
248             <head>
249             <title></title>
250             <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
251             <style type="text/css">img { max-width: 100%; }</style>
252             </head>
253             <body>
254             <p><img alt="$conf->{book}{title}" src="../images/cover.png" /></p>
255             </body>
256             </html>
257              
258             END_XHTML
259              
260             # Create the cover xhtml file.
261 0           my $cover_filename = './build/html/cover.xhtml';
262 0           open my $cover_fh, '>:utf8', $cover_filename;
263              
264 0           print $cover_fh $cover_xhtml;
265 0           close $cover_fh;
266              
267             # Add the cover page to the ePub doc.
268 0           $epub->copy_xhtml($cover_filename, 'text/cover.xhtml' );
269 0           unlink $cover_filename;
270              
271             # Add the cover to the OPF guide.
272 0           my $guide_options =
273             {
274             type => 'cover',
275             href => 'text/cover.xhtml',
276             title => 'Cover',
277             };
278              
279 0           $epub->guide->add_reference($guide_options);
280              
281 0           return $cover_id;
282             }
283              
284             1;
285              
286             __END__
287              
288             =pod
289              
290             =encoding UTF-8
291              
292             =head1 NAME
293              
294             Pod::PseudoPod::Book::Command::buildepub - command module for C<ppbook buildepub>
295              
296             =head1 VERSION
297              
298             version 1.20210620.2051
299              
300             =head1 AUTHOR
301              
302             chromatic <chromatic@wgz.org>
303              
304             =head1 COPYRIGHT AND LICENSE
305              
306             This software is copyright (c) 2011 by chromatic.
307              
308             This is free software; you can redistribute it and/or modify it under
309             the same terms as the Perl 5 programming language system itself.
310              
311             =cut