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   372 use 5.016;
  17         73  
3             our $VERSION = '2.01';
4 17     17   116 use strict;
  17         34  
  17         474  
5 17     17   79 use warnings;
  17         47  
  17         1162  
6              
7 17     17   113 use File::Basename;
  17         38  
  17         1533  
8 17     17   125 use File::Spec;
  17         61  
  17         491  
9              
10 17     17   89 use XML::LibXML;
  17         40  
  17         178  
11              
12 17     17   3680 use EBook::Ishmael::EBook::Metadata;
  17         49  
  17         27404  
13              
14             my $XHTML_NS = 'http://www.w3.org/1999/xhtml';
15              
16             sub heuristic {
17              
18 93     93 0 229 my $class = shift;
19 93         189 my $file = shift;
20 93         224 my $fh = shift;
21              
22 93 100       492 return 1 if $file =~ /\.html?$/;
23 81 100       1578 return 0 unless -T $fh;
24              
25 23         122 read $fh, my ($head), 1024;
26              
27 23 100       430 return 0 if $head =~ /<[^<>]+xmlns\s*=\s*"\Q$XHTML_NS\E"[^<>]*>/;
28              
29 12         90 return $head =~ /<\s*html[^<>]*>/;
30              
31             }
32              
33             sub _read_metadata {
34              
35 21     21   51 my $self = shift;
36              
37 21         118 my ($ns) = $self->{_dom}->findnodes('/html/@xmlns');
38              
39 21 100 66     852 if (defined $ns and $ns->value eq $XHTML_NS) {
40 10         56 $self->{Metadata}->set_format('XHTML');
41             } else {
42 11         60 $self->{Metadata}->set_format('HTML');
43             }
44 21         70 my ($head) = $self->{_dom}->findnodes('/html/head');
45              
46 21 50       506 unless (defined $head) {
47 0         0 return 1;
48             }
49              
50 21         75 my ($title) = $head->findnodes('./title');
51              
52 21 50       352 if (defined $title) {
53 21         439 my $str = $title->textContent =~ s/\s+/ /gr;
54 21         97 $self->{Metadata}->set_title($str);
55             }
56              
57 21         90 for my $n ($head->findnodes('./meta')) {
58 32         456 my $name = $n->getAttribute('name');
59 32 100       418 if (not defined $name) {
60 21         57 next;
61             }
62 11         24 my $content = $n->getAttribute('content');
63 11 50       118 if (not defined $content) {
64 0         0 next;
65             }
66 11 50       74 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         37 $self->{Metadata}->set_software($content);
86             } elsif ($name eq 'description') {
87 0         0 $self->{Metadata}->set_description($content);
88             }
89             }
90              
91 21         77 my ($lang) = $self->{_dom}->findnodes('/html/@lang');
92              
93 21 50 33     1074 if (defined $lang and not defined $self->{Metadata}->language) {
94 21         148 $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 47 my $class = shift;
105 21         44 my $file = shift;
106 21         45 my $enc = shift;
107 21   50     58 my $net = shift // 1;
108              
109 21         159 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         51 bless $self, $class;
118              
119 21         708 $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         201 );
127              
128 21         20010 $self->_read_metadata;
129              
130 21 50       460 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 14 my $self = shift;
141 6         12 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         72 my ($body) = $self->{_dom}->documentElement->findnodes('/html/body');
146 6   33     1535 $body //= $self->{_dom}->documentElement;
147              
148 6         116 my $html = join '', map { $_->toString } $body->childNodes;
  1311         7022  
149              
150 6 50       135 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         4611 return $html;
159             }
160              
161             }
162              
163             sub raw {
164              
165 5     5 0 12 my $self = shift;
166 5         10 my $out = shift;
167              
168 5         26 my ($body) = $self->{_dom}->findnodes('/html/body');
169 5   33     210 $body //= $self->{_dom}->documentElement;
170              
171 5         439 my $raw = $body->textContent;
172              
173 5 50       24 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         30 return $raw;
182             }
183              
184             }
185              
186             sub metadata {
187              
188 8     8 0 2866 my $self = shift;
189              
190 8         36 return $self->{Metadata};
191              
192             }
193              
194 4     4 0 729 sub has_cover { 0 }
195              
196 2     2 0 11 sub cover { undef }
197              
198 4     4 0 17 sub image_num { 0 }
199              
200 2     2 0 10 sub image { undef }
201              
202             1;