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   337 use 5.016;
  17         73  
3             our $VERSION = '2.03';
4 17     17   110 use strict;
  17         48  
  17         524  
5 17     17   83 use warnings;
  17         44  
  17         1056  
6              
7 17     17   121 use Encode qw(decode);
  17         452  
  17         1139  
8              
9 17     17   103 use EBook::Ishmael::CharDet;
  17         62  
  17         900  
10 17     17   131 use EBook::Ishmael::EBook::Metadata;
  17         32  
  17         645  
11 17     17   117 use EBook::Ishmael::PDB;
  17         43  
  17         423  
12 17     17   79 use EBook::Ishmael::TextToHtml;
  17         48  
  17         22811  
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 62 my $class = shift;
26 24         50 my $file = shift;
27 24         46 my $fh = shift;
28              
29 24 50       440 return 0 unless -s $file >= 68;
30              
31 24         147 seek $fh, 32, 0;
32 24         205 read $fh, my ($null), 1;
33              
34             # Last byte in title must be null
35 24 100       87 unless ($null eq "\0") {
36 12         87 return 0;
37             }
38              
39 12         83 seek $fh, 60, 0;
40 12         108 read $fh, my ($type), 4;
41              
42 12         103 return $type eq $TYPE;
43              
44             }
45              
46             # Should be called with record #1 first.
47             sub _decode_record {
48              
49 21     21   36 my $self = shift;
50 21         41 my $rec = shift;
51              
52 21         33 $rec++;
53              
54 21         88 my $out = $self->{_inflate}->inflate($self->{_pdb}->record($rec)->data);
55              
56 21 50       2098 unless (defined $out) {
57 0         0 die "Error decompressing zTXT stream in $self->{Source}\n";
58             }
59              
60 21         292 return $out;
61              
62             }
63              
64             sub _text {
65              
66 7     7   15137 my $self = shift;
67              
68 7         1775 require Compress::Zlib;
69              
70 7 50       110789 $self->{_inflate} = Compress::Zlib::inflateInit(-WindowBits => 15)
71             or die "Failed to initialize zlib inflator\n";
72              
73 7         1845 my $text = join('', map { $self->_decode_record($_) } 0 .. $self->{_recnum} - 1);
  21         67  
74              
75 7         55 $self->{_inflate} = undef;
76              
77 7         29 return $text;
78              
79             }
80              
81             sub new {
82              
83 11     11 0 48 my $class = shift;
84 11         26 my $file = shift;
85 11         27 my $enc = shift;
86              
87 11         128 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         32 bless $self, $class;
106              
107 11         453 $self->{Source} = File::Spec->rel2abs($file);
108              
109 11         108 $self->{_pdb} = EBook::Ishmael::PDB->new($file);
110              
111 11         77 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         118 ) = unpack "n n N n n n n n C C N C4", $hdr;
127              
128 11 50       61 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       46 unless ($self->{_flags} & $RANDOM_ACCESS) {
137 0         0 die "$self->{Source} zTXT uses unsupported compression method\n";
138             }
139              
140 11         56 $self->{Metadata}->set_title($self->{_pdb}->name);
141              
142 11 50       47 if ($self->{_pdb}->cdate) {
143 11         39 $self->{Metadata}->set_created($self->{_pdb}->cdate);
144             }
145 11 50       37 if ($self->{_pdb}->mdate) {
146 11         39 $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         82 $self->{_version} & 0xff
154             );
155              
156 11         47 return $self;
157              
158             }
159              
160             sub html {
161              
162 3     3 0 11 my $self = shift;
163 3         8 my $out = shift;
164              
165 3         91 my $html = text2html($self->raw);
166              
167 3 50       15 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         25 return $html;
176             }
177              
178             }
179              
180             sub raw {
181              
182 6     6 0 18 my $self = shift;
183 6         14 my $out = shift;
184              
185 6         29 my $raw = $self->_text;
186 6 100       26 if (not defined $self->{Encoding}) {
187 5   50     30 $self->{Encoding} = chardet($raw) // 'ASCII';
188             }
189 6         89 $raw = decode($self->{Encoding}, $raw);
190              
191 6 50       1936 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         51 return $raw;
200             }
201              
202             }
203              
204             sub metadata {
205              
206 4     4 0 12 my $self = shift;
207              
208 4         27 return $self->{Metadata};
209              
210             }
211              
212 2     2 0 12 sub has_cover { 0 }
213              
214 1     1 0 6 sub cover { (undef, undef) }
215              
216 2     2 0 29 sub image_num { 0 }
217              
218 1     1 0 7 sub image { (undef, undef) }
219              
220             1;