File Coverage

blib/lib/EBook/MOBI/Mhtml2Mobi.pm
Criterion Covered Total %
statement 92 136 67.6
branch 6 24 25.0
condition 1 6 16.6
subroutine 17 23 73.9
pod 1 4 25.0
total 117 193 60.6


line stmt bran cond sub pod time code
1             package EBook::MOBI::Mhtml2Mobi;
2              
3             our $VERSION = '0.71'; # TRIAL VERSION (hook for Dist::Zilla::Plugin::OurPkgVersion)
4              
5             # This file contains some example code, borrowed from MobiPerl.
6             # The code comes from the html2mobi file from MobiPerl.
7             # Thus this code has the same license than MobiPerl:
8              
9             # Copyright (C) 2011 Boris Daeppen
10             #
11             # ORIGINAL:
12             # MobiPerl/EXTH.pm, Copyright (C) 2007 Tommy Persson, tpe@ida.liu.se
13             #
14             # This program is free software: you can redistribute it and/or modify
15             # it under the terms of the GNU General Public License as published by
16             # the Free Software Foundation, either version 2 of the License, or
17             # (at your option) any later version.
18             #
19             # This program is distributed in the hope that it will be useful,
20             # but WITHOUT ANY WARRANTY; without even the implied warranty of
21             # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22             # GNU General Public License for more details.
23             #
24             # You should have received a copy of the GNU General Public License
25             # along with this program. If not, see .
26              
27             # This code creates a .mobi file for the Amazone Kindle eBook Reader
28 9     9   742 use strict;
  9         16  
  9         239  
29 9     9   41 use warnings;
  9         13  
  9         248  
30 9     9   44 use File::Basename;
  9         19  
  9         710  
31 9     9   44 use File::Spec;
  9         17  
  9         213  
32 9     9   38 use Carp;
  9         12  
  9         1650  
33              
34             # Use some project library
35             #use EBook::MOBI::Image; # this lib gets called from the fly
36             {
37             package MockImage;
38              
39             sub new {
40 4     4   15 return bless {}, shift
41             }
42             sub rescale_dimensions {
43 0     0   0 print "EBook::MOBI::Image not loaded, rescale_dimensions command ignored\n"
44             }
45             sub debug_on {
46 0     0   0 print "EBook::MOBI::Image not loaded, debug_on command ignored\n"
47             }
48             sub debug_off {
49 0     0   0 print "EBook::MOBI::Image not loaded, debug_off command ignored\n"
50             }
51             sub _debug {
52 0     0   0 print "EBook::MOBI::Image not loaded, _debug command ignored\n"
53             }
54             }
55              
56             # Use the library, downloaded from MobiPerl
57 9     9   6530 use EBook::MOBI::MobiPerl::Palm::PDB;
  9         23  
  9         274  
58 9     9   5421 use EBook::MOBI::MobiPerl::Palm::Doc;
  9         25  
  9         60  
59 9     9   5438 use EBook::MOBI::MobiPerl::MobiHeader;
  9         28  
  9         293  
60 9     9   5863 use EBook::MOBI::MobiPerl::Util;
  9         39  
  9         425  
61              
62             # This values are set according to MobiPerl
63 9     9   105 use constant DOC_UNCOMPRESSED => scalar 1;
  9         24  
  9         903  
64 9     9   54 use constant DOC_COMPRESSED => scalar 2;
  9         18  
  9         389  
65 9     9   47 use constant DOC_RECSIZE => scalar 4096;
  9         16  
  9         10038  
66              
67             # Constructor of this class
68             sub new {
69 4     4 0 838 my $self=shift;
70 4         8 my $ref={};
71              
72 4         13 $ref->{picture_paths} = []; # containing all the pictures path
73 4         21 $ref->{mobi_pic} = MockImage->new();
74              
75 4         7 bless($ref, $self);
76 4         12 return $ref;
77             }
78              
79             sub debug_on {
80 0     0 0 0 my ($self, $ref_to_debug_sub) = @_;
81              
82 0         0 $self->{ref_to_debug_sub} = $ref_to_debug_sub;
83            
84 0         0 &$ref_to_debug_sub('DEBUG mode on');
85             }
86              
87             sub debug_off {
88 0     0 0 0 my ($self) = @_;
89              
90 0 0       0 if ($self->{ref_to_debug_sub}) {
91 0         0 &{$self->{ref_to_debug_sub}}('DEBUG mode off');
  0         0  
92 0         0 $self->{ref_to_debug_sub} = 0;
93              
94 0         0 $self->{mobi_pic}->debug_off();
95             }
96             }
97              
98             # Internal debug method
99             sub _debug {
100 3     3   8 my ($self,$msg) = @_;
101              
102 3 50       21 if ($self->{ref_to_debug_sub}) {
103 0         0 &{$self->{ref_to_debug_sub}}($msg);
  0         0  
104             }
105             }
106              
107             # This method does the job!
108             # Give it some (mobi compatible) HTML and it creates a Mobi file for you
109             sub pack {
110 3     3 1 10 my ($self, # object
111             $html, # data to put in the mobi eBook
112             $filename, # filename (with path) of the desired eBook
113             $author, # author of the eBook
114             $title, # title of the eBook
115             $codepage, # codepage that eBook reader is to use when displaying text
116             $header_opts,
117             ) = @_;
118              
119             # un-comment if you need to see all the HTML
120             #print "\n--HTML--\n$html\n--HTML--\n";
121              
122             # Palm DOC Header
123             # According to MobiPerl (html2mobi)
124 3         29 my $mobi = EBook::MOBI::MobiPerl::Palm::Doc->new();
125 3         8 $mobi->{attributes}{"resource"} = 0;
126 3         8 $mobi->{attributes}{"ResDB"} = 0;
127 3         6 $mobi->{"name"} = $title;
128 3         6 $mobi->{"type"} = "BOOK";
129 3         7 $mobi->{"creator"} = "MOBI";
130 3         10 $mobi->{"version"} = 0;
131 3         9 $mobi->{"uniqueIDseed"} = 28;
132 3         6 $mobi->{'records'} = [];
133 3         7 $mobi->{'resources'} = [];
134              
135             # Inside Palm DOC Header is the MOBI Header
136             # According to MobiPerl (html2mobi)
137 3         22 my $header = $mobi->append_Record();
138 3         6 my $version = DOC_COMPRESSED;
139 3         6 $header->{'version'} = $version;
140 3         5 $header->{'length'} = 0;
141 3         7 $header->{'records'} = 0;
142 3         6 $header->{'recsize'} = DOC_RECSIZE;
143              
144             # Large HTML text must be devided into chunks...
145             # break the document into record-sized chunks.
146             # According to MobiPerl (html2mobi)
147 3         6 my $current_record_index = 1;
148 3         13 for( my $i = 0; $i < length($html); $i += DOC_RECSIZE ) {
149              
150             # DEBUG: print the current record index
151 3         17 $self->_debug(
152             'Storing HTML in the mobi format at record '
153             . $current_record_index
154             );
155 3         17 my $record = $mobi->append_Record;
156 3         10 my $chunk = substr($html,$i,DOC_RECSIZE);
157 3         15 $record->{'data'} =
158             EBook::MOBI::MobiPerl::Palm::Doc::_compress_record
159             ( $version, $chunk );
160 3         13 $record->{'id'} = $current_record_index++;
161 3         9 $header->{'records'} ++;
162             }
163 3         6 $header->{'length'} += length $html;
164             $header->{'recsize'} = $header->{'length'}
165 3 50       12 if $header->{'length'} < DOC_RECSIZE;
166              
167             # pack the Palm Doc header
168             # According to MobiPerl (html2mobi)
169             $header->{'data'} = pack( 'n xx N n n N' ,
170             $header->{'version'},
171             $header->{'length'} ,
172             $header->{'records'},
173 3         17 $header->{'recsize'},
174             0
175             );
176              
177             # Add MOBI header
178             # According to MobiPerl (html2mobi)
179 3         33 my $mh = new EBook::MOBI::MobiPerl::MobiHeader;
180 3         14 $mh->set_title ($title);
181 3         16 $mh->set_author ($author);
182 3         13 $mh->set_codepage ($codepage);
183            
184 3 50 33     15 if($header_opts and ref($header_opts) eq 'HASH'){
185 0 0       0 $mh->set_language($header_opts->{language}) if(exists $header_opts->{language});
186             }
187            
188 3         12 $mh->set_image_record_index ($current_record_index);
189              
190 3         23 $header->{'data'} .= $mh->get_data ();
191              
192             # Add pictures into the binary mobi format.
193             # Each picture gets its own record, so splitting into chunks.
194              
195             # Looking for pictures in the html data,
196             # storing the path of the pics in $self->{picture_paths}
197 3         13 $self->_gather_IMG_ref($html);
198              
199 3 50       5 if ( @{$self->{picture_paths}} ) {
  3         11  
200 0         0 eval {
201 0         0 require EBook::MOBI::Image;
202 0         0 EBook::MOBI::Image->import();
203 0         0 $self->{mobi_pic} = EBook::MOBI::Image->new();
204             };
205 0 0       0 die "MODULE MISSING! Ebook contains images. Can only proceed if you install EBook::MOBI::Image\n$@" if $@;
206              
207 0 0       0 if ($self->{ref_to_debug_sub}) {
208 0         0 $self->{mobi_pic}->debug_on($self->{ref_to_debug_sub});
209             }
210             }
211            
212             # add each pic to the mobi container
213 3         5 foreach my $img_path (@{$self->{picture_paths}}) {
  3         7  
214              
215             # We pass the picture to this object, to ensure that
216             # the picture size is fine for the mobi format.
217             # Return-value migth be a new path, in case of resizing!
218 0         0 $img_path = $self->{mobi_pic}->rescale_dimensions($img_path);
219            
220             # DEBUG: print info for each picture
221 0         0 $self->_debug(
222             'Storing picture in mobi format: '
223             . "record_index: $current_record_index, image: $img_path");
224              
225             # According to MobiPerl (html2mobi)
226 0         0 my $img = EBook::MOBI::MobiPerl::Palm::PDB->new_Record();
227 0         0 $img->{"categori"} = 0;
228 0         0 $img->{"attributes"}{"Dirty"} = 1;
229             # increase counter, for the next picture to be added...
230 0         0 $img->{"id"} = $current_record_index++;
231              
232             # read binary picture data
233 0         0 my $data;
234             my $buff;
235 0 0       0 open(my $IMG, $img_path) or die "can't open file: $!";
236 0         0 binmode($IMG);
237             # That's how MobiPerl reads the data so we do it the same way
238 0         0 while (read($IMG, $buff, 8 * 2**10)) {
239 0         0 $data .= $buff;
240             }
241 0         0 close($IMG);
242 0         0 $img->{"data"} = $data;
243              
244             # finally we append the image data to the record,
245             # and repeat the loop
246 0         0 $mobi->append_Record ($img);
247             }
248              
249             # FINISH! Write the Mobi file (and pray that it's fine)
250 3         15 $mobi->Write ($filename);
251             }
252              
253             # Internal sub.
254             # It fetches all the paths from the IMG tags of a HTML string
255             sub _gather_IMG_ref {
256 3     3   6 my ($self,$html) = @_;
257              
258 3         7 my @err_img = (); # var for images that can't be found
259              
260             # process line by line
261 3         29 my @lines = split /\n/, $html;
262 3         10 foreach my $line (@lines) {
263             #
264 18 50       41 if ($line =~ m/.*/g) {
265 0         0 my $img_path = $1;
266              
267             # Is the image existing and readable? If not, push on array
268 0 0 0     0 unless ( -e $img_path and -r $img_path ) {
269 0         0 push @err_img, $img_path;
270             }
271              
272             # if we found a path, we add it to a classwide array
273 0         0 push (@{$self->{picture_paths}}, $img_path);
  0         0  
274             }
275             }
276              
277             # after processing the images... if we found errors we croak!
278 3 50       13 if (@err_img >= 1) {
279 0           my $err_list = join ("\n ", @err_img);
280 0           croak "Could not find this images:\n $err_list\n"
281             . "Aborting! Please make sure that all images are accessible.\n";
282             }
283              
284             }
285              
286             1;
287              
288             __END__