File Coverage

blib/lib/Image/Size.pm
Criterion Covered Total %
statement 224 336 66.6
branch 43 128 33.5
condition 18 41 43.9
subroutine 22 28 78.5
pod 3 19 15.7
total 310 552 56.1


line stmt bran cond sub pod time code
1             ###############################################################################
2             #
3             # This file copyright (c) 2012 by Randy J. Ray, all rights reserved
4             #
5             # Copying and distribution are permitted under the terms of the Artistic
6             # License 2.0 (http://www.opensource.org/licenses/artistic-license-2.0.php) or
7             # the GNU LGPL (http://www.opensource.org/licenses/lgpl-2.1.php).
8             #
9             ###############################################################################
10             #
11             # Once upon a time, this code was lifted almost verbatim from wwwis by Alex
12             # Knowles, alex@ed.ac.uk. Since then, even I barely recognize it. It has
13             # contributions, fixes, additions and enhancements from all over the world.
14             #
15             # See the file ChangeLog for change history.
16             #
17             ###############################################################################
18              
19             package Image::Size;
20              
21             require 5.006001;
22              
23             # These are the Perl::Critic policies that are being turned off globally:
24             ## no critic(RequireBriefOpen)
25             ## no critic(ProhibitAutomaticExportation)
26             ## no critic(ProhibitExplicitISA)
27              
28 2     2   36205 use strict;
  2         4  
  2         76  
29 2     2   11 use warnings;
  2         3  
  2         61  
30 2     2   2090 use bytes;
  2         26  
  2         10  
31 2         326 use vars qw(
32             @ISA @EXPORT @EXPORT_OK %EXPORT_TAGS $VERSION $NO_CACHE %CACHE
33             $GIF_BEHAVIOR @TYPE_MAP %PCD_MAP $PCD_SCALE $READ_IN $LAST_POS
34 2     2   71 );
  2         3  
35              
36 2     2   13 use Exporter;
  2         2  
  2         210  
37              
38             BEGIN
39             {
40 2     2   31 @ISA = qw(Exporter);
41 2         4 @EXPORT = qw(imgsize);
42 2         6 @EXPORT_OK = qw(imgsize html_imgsize attr_imgsize
43             %CACHE $NO_CACHE $PCD_SCALE $GIF_BEHAVIOR);
44 2         12 %EXPORT_TAGS = ('all' => [ @EXPORT_OK ]);
45              
46 2         3 $VERSION = '3.232';
47 2         101 $VERSION = eval $VERSION; ## no critic(ProhibitStringyEval)
48              
49             # Default behavior for GIFs is to return the "screen" size
50 2         8862 $GIF_BEHAVIOR = 0;
51             }
52              
53             # This allows people to specifically request that the cache not be used
54             $NO_CACHE = 0;
55              
56             # Package lexicals - invisible to outside world, used only in imgsize
57             #
58             # Mapping of patterns to the sizing routines
59             @TYPE_MAP = (
60             qr{^GIF8[79]a} => \&gifsize,
61             qr{^\xFF\xD8} => \&jpegsize,
62             qr{^\x89PNG\x0d\x0a\x1a\x0a} => \&pngsize,
63             qr{^P[1-7]} => \&ppmsize, # also XVpics
64             qr{#define\s+\S+\s+\d+} => \&xbmsize,
65             qr{/[*] XPM [*]/} => \&xpmsize,
66             qr{^MM\x00\x2a} => \&tiffsize,
67             qr{^II\x2a\x00} => \&tiffsize,
68             qr{^BM} => \&bmpsize,
69             qr{^8BPS} => \&psdsize,
70             qr{^PCD_OPA} => \&pcdsize,
71             qr{^FWS} => \&swfsize,
72             qr{^CWS} => \&swfmxsize,
73             qr{^\x8aMNG\x0d\x0a\x1a\x0a} => \&mngsize,
74             qr{^\x01\x00\x00\x00} => \&emfsize,
75             );
76             # Kodak photo-CDs are weird. Don't ask me why, you really don't want details.
77             %PCD_MAP = ( 'base/16' => [ 192, 128 ],
78             'base/4' => [ 384, 256 ],
79             'base' => [ 768, 512 ],
80             'base4' => [ 1536, 1024 ],
81             'base16' => [ 3072, 2048 ],
82             'base64' => [ 6144, 4096 ], );
83             # Default scale for PCD images
84             $PCD_SCALE = 'base';
85              
86             # These are lexically-scoped anonymous subroutines for reading the three
87             # types of input streams. When the input to imgsize() is typed, then the
88             # lexical "read_in" is assigned one of these, thus allowing the individual
89             # routines to operate on these streams abstractly.
90              
91             my $read_io = sub {
92             my $handle = shift;
93             my ($length, $offset) = @_;
94              
95             if (defined($offset) && ($offset != $LAST_POS))
96             {
97             $LAST_POS = $offset;
98             return q{} if (! seek $handle, $offset, 0);
99             }
100              
101             my ($buffer, $rtn) = (q{}, 0);
102             $rtn = read $handle, $buffer, $length;
103             if (! $rtn)
104             {
105             $buffer = q{};
106             }
107             $LAST_POS = tell $handle;
108              
109             return $buffer;
110             };
111              
112             my $read_buf = sub {
113             my $buf = shift;
114             my ($length, $offset) = @_;
115              
116             if (defined($offset) && ($offset != $LAST_POS))
117             {
118             $LAST_POS = $offset;
119             return q{} if ($LAST_POS > length ${$buf});
120             }
121              
122             my $content = substr ${$buf}, $LAST_POS, $length;
123             $LAST_POS += length $content;
124              
125             return $content;
126             };
127              
128             sub imgsize ## no critic(ProhibitExcessComplexity)
129             {
130 19     19 1 111956 my $stream = shift;
131              
132 19         68 my ($handle, $header);
133 0         0 my ($x, $y, $id, $mtime, @list);
134             # These only used if $stream is an existant open FH
135 19         44 my ($save_pos, $need_restore) = (0, 0);
136             # This is for when $stream is a locally-opened file
137 19         30 my $need_close = 0;
138             # This will contain the file name, if we got one
139 19         84 my $file_name = undef;
140              
141 19         42 $header = q{};
142              
143 19 100       284 if (ref($stream) eq 'SCALAR')
    100          
144             {
145 1         6 $handle = $stream;
146 1         3 $READ_IN = $read_buf;
147 1   50     2 $header = substr ${$handle} || q{}, 0, 256;
148             }
149             elsif (ref $stream)
150             {
151             # I no longer require $stream to be in the IO::* space. So I'm assuming
152             # you don't hose yourself by passing a ref that can't do fileops. If
153             # you do, you fix it.
154 2         5 $handle = $stream;
155 2         3 $READ_IN = $read_io;
156 2         4 $save_pos = tell $handle;
157 2         4 $need_restore = 1;
158              
159             # First alteration (didn't wait long, did I?) to the existant handle:
160             #
161             # assist dain-bramaged operating systems -- SWD
162             # SWD: I'm a bit uncomfortable with changing the mode on a file
163             # that something else "owns" ... the change is global, and there
164             # is no way to reverse it.
165             # But image files ought to be handled as binary anyway.
166 2         3 binmode $handle;
167 2         13 seek $handle, 0, 0;
168 2         39 read $handle, $header, 256;
169 2         19 seek $handle, 0, 0;
170             }
171             else
172             {
173 16 50       75 if (! $NO_CACHE)
174             {
175 16         142 require Cwd;
176 16         82 require File::Spec;
177              
178 16 50       828 if (! File::Spec->file_name_is_absolute($stream))
179             {
180 16         114684 $stream = File::Spec->catfile(Cwd::cwd(), $stream);
181             }
182 16         1551 $mtime = (stat $stream)[9];
183 16 50 66     857 if (-e "$stream" and exists $CACHE{$stream})
184             {
185 0         0 @list = split /,/, $CACHE{$stream}, 4;
186              
187             # Don't return the cache if the file is newer.
188 0 0       0 if ($mtime <= $list[0])
189             {
190 0         0 return @list[1 .. 3];
191             }
192             # In fact, clear it
193 0         0 delete $CACHE{$stream};
194             }
195             }
196              
197             # first try to open the stream
198 16         1694 require Symbol;
199 16         544 $handle = Symbol::gensym();
200 16 100       3476 if (! open $handle, '<', $stream)
201             {
202 1         51 return (undef, undef, "Can't open image file $stream: $!");
203             }
204              
205 15         174 $need_close = 1;
206             # assist dain-bramaged operating systems -- SWD
207 15         101 binmode $handle;
208 15         540 read $handle, $header, 256;
209 15         227 seek $handle, 0, 0;
210 15         137 $READ_IN = $read_io;
211 15         59 $file_name = $stream;
212             }
213 18         57 $LAST_POS = 0;
214              
215             # Right now, $x, $y and $id are undef. If the while-loop below doesn't
216             # match the header to a file-type and call a subroutine, then the later
217             # block that tried Image::Magick will default to setting the id/error to
218             # "unknown file type".
219 18         857 my $tm_idx = 0;
220 18         95 while ($tm_idx < @TYPE_MAP)
221             {
222 101 100       1190 if ($header =~ $TYPE_MAP[$tm_idx])
223             {
224 18         289 ($x, $y, $id) = $TYPE_MAP[$tm_idx + 1]->($handle);
225 18         59 last;
226             }
227 83         261 $tm_idx += 2;
228             }
229              
230             # Added as an afterthought: I'm probably not the only one who uses the
231             # same shaded-sphere image for several items on a bulleted list:
232 18 100 66     603 if (! ($NO_CACHE or (ref $stream) or (! defined $x)))
      66        
233             {
234 15         180 $CACHE{$stream} = join q{,}, $mtime, $x, $y, $id;
235             }
236              
237             # If we were passed an existant file handle, we need to restore the
238             # old filepos:
239 18 100       55 if ($need_restore)
240             {
241 2         19 seek $handle, $save_pos, 0;
242             }
243             # ...and if we opened the file ourselves, we need to close it
244 18 100       49 if ($need_close)
245             {
246 15         269 close $handle; ## no critic(RequireCheckedClose)
247             }
248              
249 18 50       63 if (! defined $id)
250             {
251 0 0       0 if ($file_name)
252             {
253             # Image::Magick operates on file names.
254 0         0 ($x, $y, $id) = imagemagick_size($file_name);
255             }
256             else
257             {
258 0         0 $id = 'Data stream is not a known image file format';
259             }
260             }
261              
262             # results:
263 18 50       476 return (wantarray) ? ($x, $y, $id) : ();
264             }
265              
266             sub imagemagick_size
267             {
268 0     0 0 0 my $file_name = shift;
269              
270 0         0 my $module_name;
271             # First see if we have already loaded Graphics::Magick or Image::Magick
272             # If so, just use whichever one is already loaded.
273 0 0       0 if (exists $INC{'Graphics/Magick.pm'})
    0          
    0          
    0          
274             {
275 0         0 $module_name = 'Graphics::Magick';
276             }
277             elsif (exists $INC{'Image/Magick.pm'})
278             {
279 0         0 $module_name = 'Image::Magick';
280             }
281             # If neither are already loaded, try loading either one.
282             elsif (_load_magick_module('Graphics::Magick'))
283             {
284 0         0 $module_name = 'Graphics::Magick';
285             }
286             elsif (_load_magick_module('Image::Magick'))
287             {
288 0         0 $module_name = 'Image::Magick';
289             }
290              
291 0 0       0 if ($module_name)
292             {
293 0         0 my $img = $module_name->new();
294 0         0 my $x = $img->Read($file_name);
295             # Image::Magick error handling is a bit weird, see
296             #
297 0 0       0 if("$x") {
298 0         0 return (undef, undef, "$x");
299             } else {
300 0         0 return ($img->Get('width', 'height', 'format'));
301             }
302              
303             }
304             else {
305 0         0 return (undef, undef, 'Data stream is not a known image file format');
306             }
307             }
308              
309             # load Graphics::Magick or Image::Magick if one is not already loaded.
310             sub _load_magick_module {
311 0     0   0 my $module_name = shift;
312 0         0 my $retval = eval {
313 0         0 local $SIG{__DIE__} = q{};
314 0         0 require $module_name;
315 0         0 1;
316             };
317 0 0       0 return $retval ? 1 : 0;
318             }
319              
320              
321             sub html_imgsize
322             {
323 1     1 1 811 my @args = @_;
324 1         10 @args = imgsize(@args);
325              
326             # Use lowercase and quotes so that it works with xhtml.
327 1 50       19 return ((defined $args[0]) ?
328             sprintf('width="%d" height="%d"', @args) :
329             undef);
330             }
331              
332             sub attr_imgsize
333             {
334 1     1 1 1000 my @args = @_;
335 1         8 @args = imgsize(@args);
336              
337 1 50       28 return ((defined $args[0]) ?
338             (('-width', '-height', @args)[0, 2, 1, 3]) :
339             undef);
340             }
341              
342             # This used only in gifsize:
343             sub img_eof
344             {
345 0     0 0 0 my $stream = shift;
346              
347 0 0       0 if (ref($stream) eq 'SCALAR')
348             {
349 0         0 return ($LAST_POS >= length ${$stream});
  0         0  
350             }
351              
352 0         0 return eof $stream;
353             }
354              
355             # Simple converter-routine used by SWF and CWS code
356             sub _bin2int
357             {
358 8     8   35 my $val = shift;
359             # "no critic" because I want it clear which args are being used by
360             # substr() versus unpack().
361             ## no critic (ProhibitParensWithBuiltins)
362 8         72 return unpack 'N', pack 'B32', substr(('0' x 32) . $val, -32);
363             }
364              
365             ###########################################################################
366             # Subroutine gets the size of the specified GIF
367             ###########################################################################
368             sub gifsize
369             {
370 5     5 0 12 my $stream = shift;
371              
372             # We use bitwise-and here:
373             ## no critic(ProhibitBitwiseOperators)
374              
375 5         9 my ($cmapsize, $buf, $sh, $sw, $x, $y, $type);
376              
377             my $gif_blockskip = sub {
378 0     0   0 my ($skip, $blocktype) = @_;
379 0         0 my ($lbuf);
380              
381 0         0 $READ_IN->($stream, $skip); # Skip header (if any)
382 0         0 while (1)
383             {
384 0 0       0 if (img_eof($stream))
385             {
386             return (undef, undef,
387 0         0 "Invalid/Corrupted GIF (at EOF in GIF $blocktype)");
388             }
389 0         0 $lbuf = $READ_IN->($stream, 1); # Block size
390 0 0       0 last if ord($lbuf) == 0; # Block terminator
391 0         0 $READ_IN->($stream, ord $lbuf); # Skip data
392             }
393 5         66 };
394              
395 5 50       573 if ($GIF_BEHAVIOR > 2)
396             {
397             return (undef, undef,
398 0         0 "\$Image::Size::GIF_BEHAVIOR out of range: $GIF_BEHAVIOR");
399             }
400              
401             # Skip over the identifying string, since we already know this is a GIF
402 5         33 $type = $READ_IN->($stream, 6);
403 5 50       15 if (length($buf = $READ_IN->($stream, 7)) != 7 )
404             {
405 0         0 return (undef, undef, 'Invalid/Corrupted GIF (bad header)');
406             }
407 5         33 ($sw, $sh, $x) = unpack 'vv C', $buf;
408 5 50       16 if ($GIF_BEHAVIOR == 0)
409             {
410 5         57 return ($sw, $sh, 'GIF');
411             }
412              
413 0 0       0 if ($x & 0x80)
414             {
415 0         0 $cmapsize = 3 * (2**(($x & 0x07) + 1));
416 0 0       0 if (! $READ_IN->($stream, $cmapsize))
417             {
418             return (undef, undef,
419 0         0 'Invalid/Corrupted GIF (global color map too small?)');
420             }
421             }
422              
423             # Before we start this loop, set $sw and $sh to 0s and use them to track
424             # the largest sub-image in the overall GIF.
425 0         0 $sw = $sh = 0;
426              
427             FINDIMAGE:
428 0         0 while (1)
429             {
430 0 0       0 if (img_eof($stream))
431             {
432             # At this point, if we haven't returned then the user wants the
433             # largest of the sub-images. So, if $sh and $sw are still 0s, then
434             # we didn't see even one Image Descriptor block. Otherwise, return
435             # those two values.
436 0 0 0     0 if ($sw and $sh)
437             {
438 0         0 return ($sw, $sh, 'GIF');
439             }
440             else
441             {
442             return (undef, undef,
443 0         0 'Invalid/Corrupted GIF (no Image Descriptors)');
444             }
445             }
446 0         0 $buf = $READ_IN->($stream, 1);
447 0         0 ($x) = unpack 'C', $buf;
448 0 0       0 if ($x == 0x2c)
449             {
450             # Image Descriptor (GIF87a, GIF89a 20.c.i)
451 0 0       0 if (length($buf = $READ_IN->($stream, 8)) != 8)
452             {
453             return (undef, undef,
454 0         0 'Invalid/Corrupted GIF (missing image header?)');
455             }
456 0         0 ($x, $y) = unpack 'x4 vv', $buf;
457 0 0       0 return ($x, $y, 'GIF') if ($GIF_BEHAVIOR == 1);
458 0 0 0     0 if ($x > $sw and $y > $sh)
459             {
460 0         0 $sw = $x;
461 0         0 $sh = $y;
462             }
463             }
464 0 0       0 if ($x == 0x21)
465             {
466             # Extension Introducer (GIF89a 23.c.i, could also be in GIF87a)
467 0         0 $buf = $READ_IN->($stream, 1);
468 0         0 ($x) = unpack 'C', $buf;
469 0 0       0 if ($x == 0xF9)
    0          
    0          
    0          
470             {
471             # Graphic Control Extension (GIF89a 23.c.ii)
472 0         0 $READ_IN->($stream, 6); # Skip it
473 0         0 next FINDIMAGE; # Look again for Image Descriptor
474             }
475             elsif ($x == 0xFE)
476             {
477             # Comment Extension (GIF89a 24.c.ii)
478 0         0 $gif_blockskip->(0, 'Comment');
479 0         0 next FINDIMAGE; # Look again for Image Descriptor
480             }
481             elsif ($x == 0x01)
482             {
483             # Plain Text Label (GIF89a 25.c.ii)
484 0         0 $gif_blockskip->(13, 'text data');
485 0         0 next FINDIMAGE; # Look again for Image Descriptor
486             }
487             elsif ($x == 0xFF)
488             {
489             # Application Extension Label (GIF89a 26.c.ii)
490 0         0 $gif_blockskip->(12, 'application data');
491 0         0 next FINDIMAGE; # Look again for Image Descriptor
492             }
493             else
494             {
495             return (undef, undef,
496 0         0 sprintf 'Invalid/Corrupted GIF (Unknown ' .
497             'extension %#x)', $x);
498             }
499             }
500             else
501             {
502             return (undef, undef,
503 0         0 sprintf 'Invalid/Corrupted GIF (Unknown code %#x)', $x);
504             }
505             }
506              
507 0         0 return (undef, undef, 'gifsize fell through to the end, error');
508             }
509              
510             sub xbmsize
511             {
512 1     1 0 7 my $stream = shift;
513              
514 1         7 my $input;
515 1         6 my ($x, $y, $id) = (undef, undef, 'Could not determine XBM size');
516              
517 1         31 $input = $READ_IN->($stream, 1024);
518 1 50       20 if ($input =~ /^\#define\s*\S*\s*(\d+)\s*\n\#define\s*\S*\s*(\d+)/ix)
519             {
520 1         17 ($x, $y) = ($1, $2);
521 1         8 $id = 'XBM';
522             }
523              
524 1         8 return ($x, $y, $id);
525             }
526              
527             # Added by Randy J. Ray, 30 Jul 1996
528             # Size an XPM file by looking for the "X Y N W" line, where X and Y are
529             # dimensions, N is the total number of colors defined, and W is the width of
530             # a color in the ASCII representation, in characters. We only care about X & Y.
531             sub xpmsize
532             {
533 1     1 0 8 my $stream = shift;
534              
535 1         5 my $line;
536 1         8 my ($x, $y, $id) = (undef, undef, 'Could not determine XPM size');
537              
538 1         11 while ($line = $READ_IN->($stream, 1024))
539             {
540 1 50       50 if ($line =~ /"\s*(\d+)\s+(\d+)(\s+\d+\s+\d+){1,2}\s*"/)
541             {
542 1         19 ($x, $y) = ($1, $2);
543 1         5 $id = 'XPM';
544 1         13 last;
545             }
546             }
547              
548 1         13 return ($x, $y, $id);
549             }
550              
551             # pngsize : gets the width & height (in pixels) of a png file
552             # cor this program is on the cutting edge of technology! (pity it's blunt!)
553             #
554             # Re-written and tested by tmetro@vl.com
555             sub pngsize
556             {
557 1     1 0 15 my $stream = shift;
558              
559 1         10 my ($x, $y, $id) = (undef, undef, 'Could not determine PNG size');
560 1         5 my ($offset, $length);
561              
562             # Offset to first Chunk Type code = 8-byte ident + 4-byte chunk length + 1
563 1         6 $offset = 12; $length = 4;
  1         9  
564 1 50       15 if ($READ_IN->($stream, $length, $offset) eq 'IHDR')
565             {
566             # IHDR = Image Header
567 1         2 $length = 8;
568 1         8 ($x, $y) = unpack 'NN', $READ_IN->($stream, $length);
569 1         4 $id = 'PNG';
570             }
571              
572 1         68 return ($x, $y, $id);
573             }
574              
575             # mngsize: gets the width and height (in pixels) of an MNG file.
576             # See for the specification.
577             #
578             # Basically a copy of pngsize.
579             sub mngsize
580             {
581 0     0 0 0 my $stream = shift;
582              
583 0         0 my ($x, $y, $id) = (undef, undef, 'Could not determine MNG size');
584 0         0 my ($offset, $length);
585              
586             # Offset to first Chunk Type code = 8-byte ident + 4-byte chunk length + 1
587 0         0 $offset = 12; $length = 4;
  0         0  
588 0 0       0 if ($READ_IN->($stream, $length, $offset) eq 'MHDR')
589             {
590             # MHDR = Image Header
591 0         0 $length = 8;
592 0         0 ($x, $y) = unpack 'NN', $READ_IN->($stream, $length);
593 0         0 $id = 'MNG';
594             }
595              
596 0         0 return ($x, $y, $id);
597             }
598              
599             # jpegsize: gets the width and height (in pixels) of a jpeg file
600             # Andrew Tong, werdna@ugcs.caltech.edu February 14, 1995
601             # modified slightly by alex@ed.ac.uk
602             # and further still by rjray@blackperl.com
603             # optimization and general re-write from tmetro@vl.com
604             sub jpegsize
605             {
606 2     2 0 10 my $stream = shift;
607              
608 2         15 my $MARKER = chr 0xff; # Section marker
609              
610 2         6 my $SIZE_FIRST = 0xC0; # Range of segment identifier codes
611 2         5 my $SIZE_LAST = 0xC3; # that hold size info.
612              
613 2         12 my ($x, $y, $id) = (undef, undef, 'Could not determine JPEG size');
614              
615 2         8 my ($marker, $code, $length);
616 0         0 my $segheader;
617              
618             # Dummy read to skip header ID
619 2         21 $READ_IN->($stream, 2);
620 2         2 while (1)
621             {
622 8         20 $segheader = $READ_IN->($stream, 2);
623              
624             # Extract the segment header.
625 8         38 ($marker, $code) = unpack 'a a', $segheader;
626              
627 8   66     50 while ( $code eq $MARKER && ($marker = $code) ) {
628 36         142 $segheader = $READ_IN->($stream, 1);
629 36         179 ($code) = unpack 'a', $segheader;
630             }
631 8         18 $segheader = $READ_IN->($stream, 2);
632 8         18 $length = unpack 'n', $segheader;
633              
634             # Verify that it's a valid segment.
635 8 50 66     86 if ($marker ne $MARKER)
    100          
636             {
637             # Was it there?
638 0         0 $id = 'JPEG marker not found';
639 0         0 last;
640             }
641             elsif ((ord($code) >= $SIZE_FIRST) && (ord($code) <= $SIZE_LAST))
642             {
643             # Segments that contain size info
644 2         11 ($y, $x) = unpack 'xnn', $READ_IN->($stream, 5);
645 2         5 $id = 'JPG';
646 2         5 last;
647             }
648             else
649             {
650             # Dummy read to skip over data
651 6         15 $READ_IN->($stream, ($length - 2));
652             }
653             }
654              
655 2         8 return ($x, $y, $id);
656             }
657              
658             # ppmsize: gets data on the PPM/PGM/PBM family.
659             #
660             # Contributed by Carsten Dominik
661             sub ppmsize
662             {
663 1     1 0 13 my $stream = shift;
664              
665 1         19 my ($x, $y, $id) =
666             (undef, undef, 'Unable to determine size of PPM/PGM/PBM data');
667 1         11 my $n;
668 1         24 my @table = qw(nil PBM PGM PPM PBM PGM PPM);
669              
670 1         16 my $header = $READ_IN->($stream, 1024);
671              
672             # PPM file of some sort
673 1         16 $header =~ s/^\#.*//mg;
674 1 50       36 if ($header =~ /^(?:P([1-7]))\s+(\d+)\s+(\d+)/)
675             {
676 1         30 ($n, $x, $y) = ($1, $2, $3);
677              
678 1 50       21 if ($n == 7)
679             {
680             # John Bradley's XV thumbnail pics (from inwap@jomis.Tymnet.COM)
681 0         0 $id = 'XV';
682 0         0 ($x, $y) = ($header =~ /IMGINFO:(\d+)x(\d+)/s);
683             }
684             else
685             {
686 1         8 $id = $table[$n];
687             }
688             }
689              
690 1         14 return ($x, $y, $id);
691             }
692              
693             # tiffsize: size a TIFF image
694             #
695             # Contributed by Cloyce Spradling
696             sub tiffsize
697             {
698 2     2 0 16 my $stream = shift;
699              
700 2         11 my ($x, $y, $id) = (undef, undef, 'Unable to determine size of TIFF data');
701              
702 2         16 my $endian = 'n'; # Default to big-endian; I like it better
703 2         31 my $header = $READ_IN->($stream, 4);
704 2 100       26 if ($header =~ /II\x2a\x00/o)
705             {
706             # little-endian
707 1         6 $endian = 'v';
708             }
709              
710             # Set up an association between data types and their corresponding
711             # pack/unpack specification. Don't take any special pains to deal with
712             # signed numbers; treat them as unsigned because none of the image
713             # dimensions should ever be negative. (I hope.)
714 2         15 my @packspec = ( undef, # nothing (shouldn't happen)
715             'C', # BYTE (8-bit unsigned integer)
716             undef, # ASCII
717             $endian, # SHORT (16-bit unsigned integer)
718             uc $endian, # LONG (32-bit unsigned integer)
719             undef, # RATIONAL
720             'c', # SBYTE (8-bit signed integer)
721             undef, # UNDEFINED
722             $endian, # SSHORT (16-bit unsigned integer)
723             uc $endian, # SLONG (32-bit unsigned integer)
724             );
725              
726 2         9 my $offset = $READ_IN->($stream, 4, 4); # Get offset to IFD
727 2         16 $offset = unpack uc $endian, $offset; # Fix it so we can use it
728              
729 2         6 my $ifd = $READ_IN->($stream, 2, $offset); # Get num. of directory entries
730 2         6 my $num_dirent = unpack $endian, $ifd; # Make it useful
731 2         4 $offset += 2;
732 2         11 $num_dirent = $offset + ($num_dirent * 12); # Calc. maximum offset of IFD
733              
734             # Do all the work
735 2         6 $ifd = q{};
736 2         2 my $tag = 0;
737 2         4 my $type = 0;
738 2   66     11 while ((! defined $x) || (! defined$y)) {
739 4         15 $ifd = $READ_IN->($stream, 12, $offset); # Get first directory entry
740 4 50 33     33 last if (($ifd eq q{}) || ($offset > $num_dirent));
741 4         12 $offset += 12;
742 4         11 $tag = unpack $endian, $ifd; # ...and decode its tag
743 4         13 $type = unpack $endian, substr $ifd, 2, 2; # ...and the data type
744             # Check the type for sanity.
745 4 50 33     29 next if (($type > @packspec+0) || (! defined $packspec[$type]));
746 4 100       17 if ($tag == 0x0100) # ImageWidth (x)
    50          
747             {
748             # Decode the value
749 2         175 $x = unpack $packspec[$type], substr $ifd, 8, 4;
750             }
751             elsif ($tag == 0x0101) # ImageLength (y)
752             {
753             # Decode the value
754 2         13 $y = unpack $packspec[$type], substr $ifd, 8, 4;
755             }
756             }
757              
758             # Decide if we were successful or not
759 2 50 33     21 if (defined $x and defined $y)
760             {
761 2         13 $id = 'TIF';
762             }
763             else
764             {
765 0         0 $id = q{};
766 0 0       0 if (! defined $x)
767             {
768 0         0 $id = 'ImageWidth ';
769             }
770 0 0       0 if (! defined $y)
771             {
772 0 0       0 if ($id ne q{})
773             {
774 0         0 $id .= 'and ';
775             }
776 0         0 $id .= 'ImageLength ';
777             }
778 0         0 $id .= 'tag(s) could not be found';
779             }
780              
781 2         11 return ($x, $y, $id);
782             }
783              
784             # bmpsize: size a Windows-ish BitMaP image
785             #
786             # Adapted from code contributed by Aldo Calpini
787             sub bmpsize
788             {
789 1     1 0 14 my $stream = shift;
790              
791 1         10 my ($x, $y, $id) = (undef, undef, 'Unable to determine size of BMP data');
792 1         6 my $buffer;
793              
794 1         15 $buffer = $READ_IN->($stream, 26);
795 1         8 ($x, $y) = unpack 'x18VV', $buffer;
796 1 50 33     26 if (defined $x and defined $y)
797             {
798 1         6 $id = 'BMP';
799             }
800              
801 1         9 return ($x, $y, $id);
802             }
803              
804             # psdsize: determine the size of a PhotoShop save-file (*.PSD)
805             sub psdsize
806             {
807 1     1 0 12 my $stream = shift;
808              
809 1         11 my ($x, $y, $id) = (undef, undef, 'Unable to determine size of PSD data');
810 1         5 my $buffer;
811              
812 1         17 $buffer = $READ_IN->($stream, 26);
813 1         13 ($y, $x) = unpack 'x14NN', $buffer;
814 1 50 33     25 if (defined $x and defined $y)
815             {
816 1         6 $id = 'PSD';
817             }
818              
819 1         6 return ($x, $y, $id);
820             }
821              
822             # swfsize: determine size of ShockWave/Flash files. Adapted from code sent by
823             # Dmitry Dorofeev
824             sub swfsize
825             {
826 1     1 0 22 my $image = shift;
827 1         33 my $header = $READ_IN->($image, 33);
828              
829 1         40 my $ver = _bin2int(unpack 'B8', substr $header, 3, 1);
830 1         22 my $bs = unpack 'B133', substr $header, 8, 17;
831 1         17 my $bits = _bin2int(substr $bs, 0, 5);
832 1         7 my $x = int _bin2int(substr $bs, 5+$bits, $bits)/20;
833 1         19 my $y = int _bin2int(substr $bs, 5+$bits*3, $bits)/20;
834              
835 1         14 return ($x, $y, 'SWF');
836             }
837              
838             # Suggested by Matt Mueller , and based on a piece of
839             # sample Perl code by a currently-unknown author. Credit will be placed here
840             # once the name is determined.
841             sub pcdsize
842             {
843 0     0 0 0 my $stream = shift;
844              
845             # We use bitwise-and here:
846             ## no critic(ProhibitBitwiseOperators)
847              
848 0         0 my ($x, $y, $id) = (undef, undef, 'Unable to determine size of PCD data');
849 0         0 my $buffer = $READ_IN->($stream, 0xf00);
850              
851             # Second-tier sanity check
852 0 0       0 if (substr($buffer, 0x800, 3) ne 'PCD')
853             {
854 0         0 return ($x, $y, $id);
855             }
856              
857 0         0 my $orient = ord(substr $buffer, 0x0e02, 1) & 1; # Clear down to one bit
858 0 0       0 ($x, $y) = @{$Image::Size::PCD_MAP{lc $Image::Size::PCD_SCALE}}
  0         0  
859             [($orient ? (0, 1) : (1, 0))];
860              
861 0         0 return ($x, $y, 'PCD');
862             }
863              
864             # swfmxsize: determine size of compressed ShockWave/Flash MX files. Adapted
865             # from code sent by Victor Kuriashkin
866             sub swfmxsize
867             {
868 1     1 0 16 my $image = shift;
869              
870 1         8 my $retval = eval {
871 1         21 local $SIG{__DIE__} = q{};
872 1         13 require Compress::Zlib;
873 1         5 1;
874             };
875 1 50       16 if (! $retval)
876             {
877 0         0 return (undef, undef, "Error loading Compress::Zlib: $@");
878             }
879              
880 1         15 my $header = $READ_IN->($image, 1058);
881 1         22 my $ver = _bin2int(unpack 'B8', substr $header, 3, 1);
882              
883 1         24 my ($d, $status) = Compress::Zlib::inflateInit();
884 1         390 $header = substr $header, 8, 1024;
885 1         15 $header = $d->inflate($header);
886              
887 1         104 my $bs = unpack 'B133', substr $header, 0, 17;
888 1         12 my $bits = _bin2int(substr $bs, 0, 5);
889 1         7 my $x = int _bin2int(substr $bs, 5+$bits, $bits)/20;
890 1         5 my $y = int _bin2int(substr $bs, 5+$bits*3, $bits)/20;
891              
892 1         22 return ($x, $y, 'CWS');
893             }
894              
895             # Windows EMF files, requested by Jan v/d Zee
896             sub emfsize
897             {
898 1     1 0 122 my $image = shift;
899              
900 1         5 my ($x, $y);
901 1         12 my $buffer = $READ_IN->($image, 24);
902              
903 1         8 my ($topleft_x, $topleft_y, $bottomright_x, $bottomright_y) =
904             unpack 'x8V4', $buffer;
905              
906             # The four values describe a box *around* the image, not *of* the image.
907             # In other words, the dimensions are not inclusive.
908 1         6 $x = $bottomright_x - $topleft_x - 1;
909 1         5 $y = $bottomright_y - $topleft_y - 1;
910              
911 1         4 return ($x, $y, 'EMF');
912             }
913              
914             1;
915              
916             __END__