File Coverage

blib/lib/EBook/Ishmael/EBook/zTXT.pm
Criterion Covered Total %
statement 78 93 83.8
branch 11 24 45.8
condition 2 2 100.0
subroutine 18 18 100.0
pod 0 9 0.0
total 109 146 74.6


line stmt bran cond sub pod time code
1             package EBook::Ishmael::EBook::zTXT;
2 17     17   366 use 5.016;
  17         68  
3             our $VERSION = '2.01';
4 17     17   115 use strict;
  17         33  
  17         598  
5 17     17   103 use warnings;
  17         57  
  17         1222  
6              
7 17     17   136 use Encode qw(decode);
  17         83  
  17         1086  
8              
9 17     17   122 use EBook::Ishmael::EBook::Metadata;
  17         47  
  17         559  
10 17     17   96 use EBook::Ishmael::PDB;
  17         33  
  17         502  
11 17     17   101 use EBook::Ishmael::TextToHtml;
  17         30  
  17         23943  
12              
13             my $TYPE = 'zTXT';
14              
15             # Minimum version we support is 1.40. Pre-1.40 is de-standardized.
16             my $MINVER = 0x0128;
17              
18             my $RANDOM_ACCESS = 0x1;
19              
20             # Checks to see if the PDB type is 'zTXT'. The creator is ignored, as that can
21             # differ between zTXT creators.
22             sub heuristic {
23              
24 24     24 0 56 my $class = shift;
25 24         36 my $file = shift;
26 24         40 my $fh = shift;
27              
28 24 50       402 return 0 unless -s $file >= 68;
29              
30 24         154 seek $fh, 32, 0;
31 24         176 read $fh, my ($null), 1;
32              
33             # Last byte in title must be null
34 24 100       77 unless ($null eq "\0") {
35 12         64 return 0;
36             }
37              
38 12         73 seek $fh, 60, 0;
39 12         73 read $fh, my ($type), 4;
40              
41 12         41 return $type eq $TYPE;
42              
43             }
44              
45             # Should be called with record #1 first.
46             sub _decode_record {
47              
48 21     21   28 my $self = shift;
49 21         31 my $rec = shift;
50              
51 21         29 $rec++;
52              
53 21         72 my $out = $self->{_inflate}->inflate($self->{_pdb}->record($rec)->data);
54              
55 21 50       1852 unless (defined $out) {
56 0         0 die "Error decompressing zTXT stream in $self->{Source}\n";
57             }
58              
59 21         212 return $out;
60              
61             }
62              
63             sub _text {
64              
65 7     7   26037 my $self = shift;
66              
67 7         1923 require Compress::Zlib;
68              
69 7 50       137382 $self->{_inflate} = Compress::Zlib::inflateInit(-WindowBits => 15)
70             or die "Failed to initialize zlib inflator\n";
71              
72 7         1492 my $text = join('', map { $self->_decode_record($_) } 0 .. $self->{_recnum} - 1);
  21         53  
73              
74 7         51 $self->{_inflate} = undef;
75              
76 7         507 return $text;
77              
78             }
79              
80             sub new {
81              
82 11     11 0 25 my $class = shift;
83 11         18 my $file = shift;
84 11   100     44 my $enc = shift // 'UTF-8';
85              
86 11         82 my $self = {
87             Source => undef,
88             Metadata => EBook::Ishmael::EBook::Metadata->new,
89             Encoding => $enc,
90             _pdb => undef,
91             _version => undef,
92             _recnum => undef,
93             _size => undef,
94             _recsize => undef,
95             _bookmarknum => undef,
96             _bookmarkrec => undef,
97             _annotnum => undef,
98             _annotrec => undef,
99             _flags => undef,
100             _reserved => undef,
101             _crc32 => undef,
102             };
103              
104 11         22 bless $self, $class;
105              
106 11         385 $self->{Source} = File::Spec->rel2abs($file);
107              
108 11         100 $self->{_pdb} = EBook::Ishmael::PDB->new($file);
109              
110 11         46 my $hdr = $self->{_pdb}->record(0)->data;
111              
112             (
113             $self->{_version},
114             $self->{_recnum},
115             $self->{_size},
116             $self->{_recsize},
117             $self->{_bookmarknum},
118             $self->{_bookmarkrec},
119             $self->{_annotnum},
120             $self->{_annotrec},
121             $self->{_flags},
122             $self->{_reserved},
123             $self->{_crc32},
124             undef
125 11         97 ) = unpack "n n N n n n n n C C N C4", $hdr;
126              
127 11 50       48 if ($self->{_version} < $MINVER) {
128             die sprintf
129             "%s zTXT is of an unsupported version, %d.%d (%d.%d and above are supported).\n",
130             $self->{Source},
131 0         0 ($self->{_version} >> 8) & 0xff, $self->{_version} & 0xff,
132             ($MINVER >> 8) & 0xff, $MINVER & 0xff;
133             }
134              
135 11 50       32 unless ($self->{_flags} & $RANDOM_ACCESS) {
136 0         0 die "$self->{Source} zTXT uses unsupported compression method\n";
137             }
138              
139 11         38 $self->{Metadata}->set_title($self->{_pdb}->name);
140              
141 11 50       38 if ($self->{_pdb}->cdate) {
142 11         30 $self->{Metadata}->set_created($self->{_pdb}->cdate);
143             }
144 11 50       31 if ($self->{_pdb}->mdate) {
145 11         27 $self->{Metadata}->set_modified($self->{_pdb}->mdate);
146             }
147              
148             $self->{Metadata}->set_format(
149             sprintf
150             "zTXT %s.%s",
151             ($self->{_version} >> 8) & 0xff,
152 11         64 $self->{_version} & 0xff
153             );
154              
155 11         32 return $self;
156              
157             }
158              
159             sub html {
160              
161 3     3 0 9 my $self = shift;
162 3         7 my $out = shift;
163              
164 3         35 my $html = decode($self->{Encoding}, text2html($self->_text));
165              
166 3 50       333 if (defined $out) {
167 0 0       0 open my $fh, '>', $out
168             or die "Failed to open $out for writing: $!\n";
169 0         0 binmode $fh, ':utf8';
170 0         0 print { $fh } $html;
  0         0  
171 0         0 close $fh;
172 0         0 return $out;
173             } else {
174 3         22 return $html;
175             }
176              
177             }
178              
179             sub raw {
180              
181 3     3 0 4 my $self = shift;
182 3         3 my $out = shift;
183              
184 3         9 my $raw = decode($self->{Encoding}, $self->_text);
185              
186 3 50       135 if (defined $out) {
187 0 0       0 open my $fh, '>', $out
188             or die "Failed to open $out for writing: $!\n";
189 0         0 binmode $fh, ':utf8';
190 0         0 print { $fh } $raw;
  0         0  
191 0         0 close $fh;
192 0         0 return $out;
193             } else {
194 3         9 return $raw;
195             }
196              
197             }
198              
199             sub metadata {
200              
201 4     4 0 10 my $self = shift;
202              
203 4         20 return $self->{Metadata};
204              
205             }
206              
207 2     2 0 7 sub has_cover { 0 }
208              
209 1     1 0 5 sub cover { undef }
210              
211 2     2 0 8 sub image_num { 0 }
212              
213 1     1 0 4 sub image { undef }
214              
215             1;