File Coverage

blib/lib/EBook/EPUB/Lite/Container.pm
Criterion Covered Total %
statement 82 93 88.1
branch 10 18 55.5
condition n/a
subroutine 15 15 100.0
pod 3 8 37.5
total 110 134 82.0


line stmt bran cond sub pod time code
1             # Copyright (c) 2009, 2010 Oleksandr Tymoshenko
2             # All rights reserved.
3              
4             # Redistribution and use in source and binary forms, with or without
5             # modification, are permitted provided that the following conditions
6             # are met:
7             # 1. Redistributions of source code must retain the above copyright
8             # notice, this list of conditions and the following disclaimer.
9             # 2. Redistributions in binary form must reproduce the above copyright
10             # notice, this list of conditions and the following disclaimer in the
11             # documentation and/or other materials provided with the distribution.
12              
13             # THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
14             # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15             # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
16             # ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
17             # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
18             # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
19             # OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
20             # HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
21             # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
22             # OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
23             # SUCH DAMAGE.
24              
25             # OEPBS Container format implementation
26             # http://www.idpf.org/ocf/ocf1.0/download/ocf10.htm
27             package EBook::EPUB::Lite::Container;
28              
29 4     4   16 use strict;
  4         5  
  4         102  
30 4     4   16 use warnings;
  4         4  
  4         78  
31 4     4   2636 use XML::Writer;
  4         35755  
  4         116  
32 4     4   2222 use IO::File;
  4         4315  
  4         589  
33 4     4   27 use File::Find;
  4         7  
  4         241  
34              
35 4     4   21 use Carp;
  4         13  
  4         2949  
36              
37             sub new
38             {
39 2     2 1 4 my ($class, %params) = @_;
40 2         10 my $self = {
41             root_files => [],
42             files => [],
43             encrypted_files => [],
44             };
45 2         7 return bless $self, $class;
46             }
47              
48             #
49             # Add root file (item in element)
50             #
51             sub add_root_file
52             {
53 2     2 0 4 my ($self, $path, $mediatype) = @_;
54 2         2 push @{$self->{root_files}}, {
  2         12  
55             path => $path,
56             mediatype => $mediatype,
57             };
58             }
59              
60             #
61             # Add content. Recurse if it's directory
62             #
63             sub add_path
64             {
65 2     2 1 5 my ($self, $from_path, $container_path) = @_;
66              
67             # Closure to collect files recursively
68             my $file_cb = sub {
69 15     15   17 my $file = $File::Find::name;
70 15         9 my $dest = $file;
71 15         72 $dest =~ s/\Q$from_path\E/$container_path/;
72              
73             # XXX: UNIX only
74 15 100       164 if (-d $file) {
75 2         4 $dest .= "/";
76             }
77              
78 15 50       25 if (!is_valid_path($dest)) {
79 0         0 croak("Bad container path: $dest");
80 0         0 return;
81             }
82              
83 15         15 push @{$self->{files}}, {
  15         281  
84             frompath => $file,
85             containerpath => $dest,
86             }
87 2         10 };
88 2 50       34 if (-d $from_path) {
89             # XXX: UNIX only
90             # Strip unncessary slashes
91 2         9 $from_path =~ s/\/+$//g;
92 2         7 $container_path =~ s/\/+$//g;
93 2         224 find( { wanted => $file_cb, no_chdir => 1 }, $from_path);
94             }
95             else {
96 0 0       0 if (!is_valid_path($container_path)) {
97 0         0 croak("Bad container path: $container_path");
98 0         0 return;
99             }
100              
101 0         0 push @{$self->{files}}, {
  0         0  
102             frompath => $from_path,
103             containerpath => $container_path,
104             }
105             }
106            
107             }
108              
109             #
110             # Add encrypted file, at the moment it means font "encrypted" with
111             # Adobe content protection algorithm
112             #
113             sub add_encrypted_path
114             {
115 1     1 1 3 my ($self, $path) = @_;
116              
117 1 50       3 if (!is_valid_path($path)) {
118 0         0 croak("Bad container path: $path");
119 0         0 return;
120             }
121              
122 1         2 push @{$self->{encrypted_files}}, $path;
  1         4  
123             }
124              
125             #
126             # Check if file name conforms specs
127             # TODO: make conformant to spec
128             sub is_valid_path
129             {
130 16     16 0 15 my $path = shift;
131 16 50       31 return if($path =~ /META-INF/);
132              
133 16         27 return 1;
134             }
135              
136             #
137             # Generate container.xml for META-INF directory
138             #
139             sub write_container
140             {
141 2     2 0 5 my ($self, $outname) = @_;
142 2         98 my $container = IO::File->new(">$outname");
143              
144 2 50       236 if (!defined($container)) {
145 0         0 return;
146             }
147              
148 2         22 my $writer = XML::Writer->new(
149             OUTPUT => $container,
150             DATA_MODE => 1,
151             DATA_INDENT => 2,
152             );
153 2         374 $writer->xmlDecl("utf-8");
154 2         96 $writer->startTag( "container",
155             "xmlns" => "urn:oasis:names:tc:opendocument:xmlns:container",
156             "version" => "1.0",
157             );
158 2         178 $writer->startTag("rootfiles");
159 2         67 foreach my $rf (@{$self->{root_files}}) {
  2         7  
160             $writer->emptyTag("rootfile",
161             "full-path", $rf->{path},
162             "media-type", $rf->{mediatype},
163 2         10 );
164             }
165 2         153 $writer->endTag("rootfiles");
166 2         56 $writer->endTag("container");
167 2         45 $writer->end();
168 2         40 $container->close();
169              
170 2         275 return 1;
171             }
172              
173             sub has_encrypted_files
174             {
175 2     2 0 5 my ($self) = @_;
176 2 100       4 return 1 if (@{$self->{encrypted_files}});
  2         14  
177              
178             # No encrypted data
179 1         3 return;
180             }
181              
182             #
183             # Generate encryption.xml for META-INF directory
184             #
185             sub write_encryption
186             {
187 1     1 0 3 my ($self, $outname) = @_;
188 1         8 my $container = IO::File->new(">$outname");
189              
190 1 50       78 if (!defined($container)) {
191 0         0 return;
192             }
193              
194 1         9 my $writer = XML::Writer->new(
195             OUTPUT => $container,
196             DATA_MODE => 1,
197             DATA_INDENT => 2,
198             );
199 1         132 $writer->xmlDecl("utf-8");
200 1         37 $writer->startTag( "encryption",
201             "xmlns" => "urn:oasis:names:tc:opendocument:xmlns:container",
202             );
203 1         70 foreach my $rf (@{$self->{encrypted_files}}) {
  1         5  
204 1         4 $writer->startTag('EncryptedData',
205             'xmlns' => 'http://www.w3.org/2001/04/xmlenc#',
206             );
207 1         60 $writer->emptyTag('EncryptionMethod',
208             'Algorithm' => 'http://ns.adobe.com/pdf/enc#RC',
209             );
210 1         61 $writer->startTag('CipherData');
211 1         40 $writer->emptyTag('CipherReference',
212             'URI' => $rf,
213             );
214 1         57 $writer->endTag('CipherData');
215 1         63 $writer->endTag('EncryptedData');
216             }
217              
218 1         33 $writer->endTag("encryption");
219 1         29 $writer->end();
220 1         20 $container->close();
221              
222 1         121 return 1;
223             }
224              
225              
226              
227             1;
228              
229             __END__;