File Coverage

blib/lib/EBook/Ishmael/EBook/HTML.pm
Criterion Covered Total %
statement 88 116 75.8
branch 24 48 50.0
condition 6 14 42.8
subroutine 18 18 100.0
pod 0 9 0.0
total 136 205 66.3


line stmt bran cond sub pod time code
1             package EBook::Ishmael::EBook::HTML;
2 17     17   232 use 5.016;
  17         46  
3             our $VERSION = '2.05';
4 17     17   68 use strict;
  17         23  
  17         269  
5 17     17   44 use warnings;
  17         21  
  17         654  
6              
7 17     17   81 use File::Basename;
  17         21  
  17         1030  
8 17     17   63 use File::Spec;
  17         37  
  17         364  
9              
10 17     17   59 use XML::LibXML;
  17         20  
  17         79  
11              
12 17     17   2236 use EBook::Ishmael::HTML qw(prepare_html);
  17         43  
  17         585  
13 17     17   72 use EBook::Ishmael::EBook::Metadata;
  17         21  
  17         17033  
14              
15             my $XHTML_NS = 'http://www.w3.org/1999/xhtml';
16              
17             sub heuristic {
18              
19 93     93 0 190 my $class = shift;
20 93         164 my $file = shift;
21 93         116 my $fh = shift;
22              
23 93 100       317 return 1 if $file =~ /\.html?$/;
24 81 100       836 return 0 unless -T $fh;
25              
26 23         56 read $fh, my ($head), 1024;
27              
28 23 100       274 return 0 if $head =~ /<[^<>]+xmlns\s*=\s*"\Q$XHTML_NS\E"[^<>]*>/;
29              
30 12         68 return $head =~ /<\s*html[^<>]*>/;
31              
32             }
33              
34             sub _read_metadata {
35              
36 21     21   32 my $self = shift;
37              
38 21         82 my ($ns) = $self->{_dom}->findnodes('/html/@xmlns');
39              
40 21 100 66     599 if (defined $ns and $ns->value eq $XHTML_NS) {
41 10         40 $self->{Metadata}->set_format('XHTML');
42             } else {
43 11         40 $self->{Metadata}->set_format('HTML');
44             }
45 21         46 my ($head) = $self->{_dom}->findnodes('/html/head');
46              
47 21 50       294 unless (defined $head) {
48 0         0 return 1;
49             }
50              
51 21         66 my ($title) = $head->findnodes('./title');
52              
53 21 50       219 if (defined $title) {
54 21         308 my $str = $title->textContent =~ s/\s+/ /gr;
55 21         61 $self->{Metadata}->set_title($str);
56             }
57              
58 21         34 for my $n ($head->findnodes('./meta')) {
59 32         287 my $name = $n->getAttribute('name');
60 32 100       256 if (not defined $name) {
61 21         43 next;
62             }
63 11         18 my $content = $n->getAttribute('content');
64 11 50       71 if (not defined $content) {
65 0         0 next;
66             }
67 11 50       56 if ($name eq 'dc.title') {
    50          
    50          
    50          
    50          
    50          
    50          
    0          
68 0         0 $self->{Metadata}->set_title($content);
69             } elsif ($name eq 'dc.language') {
70 0         0 $self->{Metadata}->add_language($content);
71             } elsif ($name eq 'dcterms.modified') {
72 0         0 my $t = eval { guess_time($content) };
  0         0  
73 0 0       0 if (defined $t) {
74 0         0 $self->{Metadata}->set_modified($t);
75             }
76             } elsif ($name eq 'dc.creator') {
77 0         0 $self->{Metadata}->add_author($content);
78             } elsif ($name eq 'dc.subject') {
79 0         0 $self->{Metadata}->add_genre($content);
80             } elsif ($name eq 'dcterms.created') {
81 0         0 my $t = eval { guess_time($content) };
  0         0  
82 0 0       0 if (defined $t) {
83 0         0 $self->{Metadata}->set_created($t);
84             }
85             } elsif ($name eq 'generator') {
86 11         25 $self->{Metadata}->set_software($content);
87             } elsif ($name eq 'description') {
88 0         0 $self->{Metadata}->set_description($content);
89             }
90             }
91              
92 21         74 my ($lang) = $self->{_dom}->findnodes('/html/@lang');
93              
94 21 50 33     706 if (defined $lang and not defined $self->{Metadata}->language) {
95 21         94 $self->{Metadata}->add_language($lang->value);
96             }
97              
98              
99 21         69 return 1;
100              
101             }
102              
103             sub new {
104              
105 21     21 0 27 my $class = shift;
106 21         36 my $file = shift;
107 21         25 my $enc = shift;
108 21   50     44 my $net = shift // 1;
109              
110 21         129 my $self = {
111             Source => undef,
112             Metadata => EBook::Ishmael::EBook::Metadata->new,
113             Encode => $enc,
114             Network => $net,
115             _dom => undef,
116             };
117              
118 21         32 bless $self, $class;
119              
120 21         514 $self->{Source} = File::Spec->rel2abs($file);
121              
122             $self->{_dom} = XML::LibXML->load_html(
123             location => $file,
124             no_network => !$self->{Network},
125             recover => 2,
126             encoding => $self->{Encode},
127 21         153 );
128              
129 21         13071 $self->_read_metadata;
130              
131 21 50       279 if (not defined $self->{Metadata}->title) {
132 0         0 $self->{Metadata}->set_title((fileparse($file, qr/\.[^.]*/))[0]);
133             }
134              
135 21         242 return $self;
136              
137             }
138              
139             sub html {
140              
141 6     6 0 12 my $self = shift;
142 6         10 my $out = shift;
143              
144             # Extract body from HTML tree and serialize that, or just serialize the
145             # entire tree if there is no body.
146 6         55 my ($body) = $self->{_dom}->documentElement->findnodes('/html/body');
147 6   33     124 $body //= $self->{_dom}->documentElement;
148 6         85 prepare_html($body);
149              
150 6         29 my $html = join '', map { $_->toString } $body->childNodes;
  1311         4596  
151              
152 6 50       101 if (defined $out) {
153 0 0       0 open my $fh, '>', $out
154             or die "Failed to open $out for writing: $!\n";
155 0         0 binmode $fh, ':utf8';
156 0         0 print { $fh } $html;
  0         0  
157 0         0 close $fh;
158 0         0 return $out;
159             } else {
160 6         3248 return $html;
161             }
162              
163             }
164              
165             sub raw {
166              
167 5     5 0 10 my $self = shift;
168 5         7 my $out = shift;
169              
170 5         17 my ($body) = $self->{_dom}->findnodes('/html/body');
171 5   33     116 $body //= $self->{_dom}->documentElement;
172 5         17 prepare_html($body);
173              
174 5         222 my $raw = $body->textContent;
175              
176 5 50       14 if (defined $out) {
177 0 0       0 open my $fh, '>', $out
178             or die "Failed to open $out for writing: $!\n";
179 0         0 binmode $fh, ':utf8';
180 0         0 print { $fh } $raw;
  0         0  
181 0         0 close $fh;
182 0         0 return $out;
183             } else {
184 5         22 return $raw;
185             }
186              
187             }
188              
189             sub metadata {
190              
191 8     8 0 2237 my $self = shift;
192              
193 8         29 return $self->{Metadata};
194              
195             }
196              
197 4     4 0 588 sub has_cover { 0 }
198              
199 2     2 0 9 sub cover { (undef, undef) }
200              
201 4     4 0 89 sub image_num { 0 }
202              
203 2     2 0 8 sub image { (undef, undef) }
204              
205             1;