File Coverage

blib/lib/EBook/Ishmael/EBook/HTML.pm
Criterion Covered Total %
statement 83 111 74.7
branch 24 48 50.0
condition 6 14 42.8
subroutine 17 17 100.0
pod 0 9 0.0
total 130 199 65.3


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