File Coverage

blib/lib/Image/PNG/FileConvert.pm
Criterion Covered Total %
statement 10 12 83.3
branch n/a
condition n/a
subroutine 4 4 100.0
pod n/a
total 14 16 87.5


line stmt bran cond sub pod time code
1             package Image::PNG::FileConvert;
2             require Exporter;
3             @ISA = qw(Exporter);
4             @EXPORT_OK = qw/file2png png2file/;
5 1     1   26476 use warnings;
  1         2  
  1         31  
6 1     1   5 use strict;
  1         1  
  1         45  
7             our $VERSION = 0.08;
8 1     1   5 use Carp;
  1         5  
  1         102  
9 1     1   545 use Image::PNG::Libpng ':all';
  0            
  0            
10             use Image::PNG::Const ':all';
11              
12             use constant {
13             default_row_length => 0x800,
14             default_max_rows => 0x800,
15             };
16              
17             sub file2png
18             {
19             my ($file, $png_file, $options) = @_;
20             if (! -f $file) {
21             carp "I can't find '$file'";
22             return;
23             }
24             if (! $png_file) {
25             carp "I need a name for the PNG output";
26             return;
27             }
28             if (-f $png_file) {
29             carp "Output PNG file '$png_file' already exists";
30             return;
31             }
32             if (! $options) {
33             $options = {};
34             }
35             if (! $options->{row_length}) {
36             $options->{row_length} = default_row_length;
37             }
38             if (! $options->{max_rows}) {
39             $options->{max_rows} = default_max_rows;
40             }
41             my @rows;
42             my $bytes = -s $file;
43             open my $input, "<:raw", $file;
44             my $i = 0;
45             my $total_red = 0;
46             while (! eof ($input)) {
47             my $red = read ($input, $rows[$i], $options->{row_length});
48             if ($red != $options->{row_length}) {
49             if ($total_red + $red != $bytes) {
50             warn "Short read of $red bytes at row $i.\n"
51             }
52             }
53             $total_red += $red;
54             $i++;
55             }
56             close $input;
57             if ($options->{verbose}) {
58             printf "Read 0x%X rows.\n", $i;
59             }
60              
61             # Fill the final row up with useless bytes so that we are not
62             # reading from unallocated memory.
63              
64             # The number of bytes in the last row.
65             my $end_bytes = $bytes % $options->{row_length};
66             if ($end_bytes > 0) {
67             $rows[-1] .= "X" x ($options->{row_length} - $end_bytes);
68             }
69              
70             # Create the PNG data in a Perl structure.
71              
72             my $png = create_write_struct ();
73             my %IHDR = (
74             width => $options->{row_length},
75             height => scalar @rows,
76             color_type => PNG_COLOR_TYPE_GRAY,
77             bit_depth => 8,
78             );
79             set_IHDR ($png, \%IHDR);
80             set_rows ($png, \@rows);
81              
82             # Write the PNG data to a file.
83              
84             open my $output, ">:raw", "$png_file";
85             init_io ($png, $output);
86              
87             # Set the timestamp of the PNG file to the current time.
88              
89             set_tIME ($png);
90             my $name;
91             if ($options->{name}) {
92             $name = $options->{name};
93             }
94             else {
95             $name = $file;
96             }
97             # Put the name and size of the file into the file as text
98             # segments.
99             set_text ($png, [{key => 'bytes',
100             text => $bytes,
101             compression => PNG_TEXT_COMPRESSION_NONE},
102             {key => 'name',
103             text => $name,
104             compression => PNG_TEXT_COMPRESSION_NONE},
105             ]);
106             write_png ($png);
107             close $output;
108             }
109              
110             sub png2file
111             {
112             my ($png_file, $options) = @_;
113             my $me = __PACKAGE__ . "::png2file";
114             if (! $png_file) {
115             croak "$me: please specify a file";
116             }
117             if (! -f $png_file) {
118             croak "$me: can't find the PNG file '$png_file'";
119             }
120             if (! defined $options) {
121             $options = {};
122             }
123             open my $input, "<:raw", $png_file;
124             my $png = create_read_struct ();
125             init_io ($png, $input);
126             if ($options->{verbose}) {
127             print "Reading file\n";
128             }
129             read_png ($png);
130             my $IHDR = get_IHDR ($png);
131             if ($options->{verbose}) {
132             print "Getting rows\n";
133             }
134             my $rows = get_rows ($png);
135             if ($options->{verbose}) {
136             print "Finished reading file\n";
137             }
138             close $input;
139             my $text_segments = get_text ($png);
140             if (! defined $text_segments) {
141             croak "$me: the PNG file '$png_file' does not have any text segments, so either it was not created by " . __PACKAGE__ . "::file2png, or it has had its text segments removed";
142             return;
143             }
144             my $name;
145             my $bytes;
146             for my $text_segment (@$text_segments) {
147             if ($text_segment->{key} eq 'name') {
148             $name = $text_segment->{text};
149             }
150             elsif ($text_segment->{key} eq 'bytes') {
151             $bytes = $text_segment->{text};
152             }
153             else {
154             carp "Unknown text segment with key '$text_segment->{key}' in '$png_file'";
155             }
156             }
157             if (! $name || ! $bytes) {
158             croak "$me: the PNG file '$png_file' does not have information about the file name or the number of bytes of data, so either it was not created by " . __PACKAGE__ . "::file2png, or it has had its text segments removed";
159             }
160             if ($bytes <= 0) {
161             croak "$me: the byte file size $bytes in '$png_file' is impossible";
162             }
163             my $row_bytes = get_rowbytes ($png);
164             if (-f $name) {
165             croak "$me: a file with the name '$name' already exists";
166             }
167             open my $output, ">:raw", $name;
168             for my $i (0..$#$rows - 1) {
169             print $output $rows->[$i];
170             }
171             my $final_row = substr ($rows->[-1], 0, $bytes % $row_bytes);
172             print $output $final_row;
173             close $output;
174             return;
175             }
176              
177             1;
178              
179             =head1 NAME
180              
181             Image::PNG::FileConvert - convert a file to or from a PNG image
182              
183             =head1 SYNOPSIS
184              
185             use Image::PNG::FileConvert qw/file2png png2file/;
186             # Convert a data file into a PNG image
187             file2png ('myfile.txt', 'myfile.png');
188             # Extract a data file from a PNG image
189             png2file ('myfile.png');
190              
191             =head1 FUNCTIONS
192              
193             =head2 file2png
194              
195             file2png ('myfile.txt', 'myfile.png');
196              
197             Convert C into a PNG graphic. The function uses the data
198             from myfile.txt to write a greyscale (black and white) image. The
199             bytes of the file correspond to the pixels of the image.
200              
201             When this PNG is unwrapped using L, it will be called
202             C again. If you want to specify a different name,
203              
204             file2png ('myfile.txt', 'myfile.png',
205             { name => 'not-the-same-name.txt' });
206              
207             and the file will be unwrapped into C.
208              
209             If you want your PNG to have a different width than the default, there
210             is another option, C, specified in the same way:
211              
212             file2png ('myfile.txt', 'myfile.png', { row_length => 0x100 });
213              
214             The number you specify for C will be the width of the
215             image in pixels.
216              
217             =head2 png2file
218              
219             png2file ('myfile.png');
220              
221             Convert C into a data file. C must be a PNG
222             created using L. The file is stored in whatever the name of
223             the file given to L was.
224              
225             Please note that this only converts PNG files output by L,
226             not general PNG files.
227              
228             =head1 BUGS
229              
230             =over
231              
232             =item Holds file in memory
233              
234             Both the routines here hold the entire file in memory, limiting the
235             data size which can be converted to or from a PNG.
236              
237             =item There should be a way to specify the output name in png2file
238              
239             There should be some option to specify the name of the output file in
240             L.
241              
242             =back
243              
244             =head1 AUTHOR
245              
246             Ben Bullock,
247              
248             =head1 LICENCE
249              
250             You can use, modify and distribute this software under the Perl
251             Artistic Licence or the GNU General Public Licence.
252              
253             =head1 SEE ALSO
254              
255             =over
256              
257             =item Acme::Steganography::Image::Png
258              
259             L I'm not sure what this does, but
260             maybe it does something similar to Image::PNG::FileConvert.
261              
262             =back
263              
264             =head1 UTILITIES
265              
266             The distribution also includes two utility scripts, file2png and
267             png2file, which convert a file to a PNG image and back again.
268              
269             =cut
270