File Coverage

blib/lib/EBook/Ishmael/EBook/zTXT.pm
Criterion Covered Total %
statement 84 99 84.8
branch 13 26 50.0
condition 1 2 50.0
subroutine 19 19 100.0
pod 0 9 0.0
total 117 155 75.4


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