File Coverage

blib/lib/Image/ExifTool/ICO.pm
Criterion Covered Total %
statement 25 25 100.0
branch 5 10 50.0
condition n/a
subroutine 4 4 100.0
pod 0 1 0.0
total 34 40 85.0


line stmt bran cond sub pod time code
1             #------------------------------------------------------------------------------
2             # File: ICO.pm
3             #
4             # Description: Read Windows ICO and CUR files
5             #
6             # Revisions: 2020-10-18 - P. Harvey Created
7             #
8             # References: 1) https://docs.fileformat.com/image/ico/
9             #------------------------------------------------------------------------------
10              
11             package Image::ExifTool::ICO;
12              
13 1     1   4607 use strict;
  1         3  
  1         34  
14 1     1   8 use vars qw($VERSION);
  1         2  
  1         40  
15 1     1   7 use Image::ExifTool qw(:DataAccess :Utils);
  1         2  
  1         609  
16              
17             $VERSION = '1.00';
18              
19             %Image::ExifTool::ICO::Main = (
20             PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
21             GROUPS => { 0 => 'File', 1 => 'File', 2 => 'Image' },
22             NOTES => 'Information extracted from Windows ICO (icon) and CUR (cursor) files.',
23             2 => {
24             Name => 'ImageType',
25             Format => 'int16u',
26             PrintConv => { 1 => 'Icon', 2 => 'Cursor' },
27             },
28             4 => {
29             Name => 'ImageCount',
30             Format => 'int16u',
31             RawConv => '$$self{ImageCount} = $val',
32             },
33             6 => {
34             Name => 'IconDir',
35             SubDirectory => { TagTable => 'Image::ExifTool::ICO::IconDir' },
36             },
37             );
38              
39             %Image::ExifTool::ICO::IconDir = (
40             PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
41             GROUPS => { 0 => 'File', 1 => 'File', 2 => 'Image' },
42             0 => {
43             Name => 'ImageWidth',
44             ValueConv => '$val or $val + 256',
45             },
46             1 => {
47             Name => 'ImageHeight',
48             ValueConv => '$val or $val + 256',
49             },
50             2 => 'NumColors',
51             4 => [{
52             Name => 'ColorPlanes',
53             Condition => '$$self{FileType} eq "ICO"',
54             Format => 'int16u',
55             Notes => 'ICO files',
56             },{
57             Name => 'HotspotX',
58             Format => 'int16u',
59             Notes => 'CUR files',
60             }],
61             6 => [{
62             Name => 'BitsPerPixel',
63             Condition => '$$self{FileType} eq "ICO"',
64             Format => 'int16u',
65             Notes => 'ICO files',
66             },{
67             Name => 'HotspotY',
68             Format => 'int16u',
69             Notes => 'CUR files',
70             }],
71             8 => {
72             Name => 'ImageLength',
73             Format => 'int32u',
74             },
75             );
76              
77             #------------------------------------------------------------------------------
78             # Process ICO/CUR file
79             # Inputs: 0) ExifTool ref, 1) dirInfo ref
80             # Returns: 1 on success, 0 if this wasn't a valid ICO/CUR file
81             sub ProcessICO($$$)
82             {
83 1     1 0 4 my ($et, $dirInfo) = @_;
84 1         3 my $raf = $$dirInfo{RAF};
85 1         2 my ($i, $buff);
86             # verify this is a valid ICO/CUR file
87 1 50       4 return 0 unless $raf->Read($buff, 6) == 6;
88 1 50       6 return 0 unless $buff =~ /^\0\0([\x01\x02])\0[^0]\0/s;
89             # (note: have seen cursor files in the wild with an 0x01 here,
90             # but SetFileType will use the .cur extension to identify these)
91 1 50       10 $et->SetFileType($1 eq "\x01" ? 'ICO' : 'CUR');
92 1         10 SetByteOrder('II');
93 1         5 my $tagTbl = GetTagTable('Image::ExifTool::ICO::Main');
94 1         7 my $num = Get16u(\$buff, 4);
95 1         6 $et->HandleTag($tagTbl, 4, $num);
96 1         4 for ($i=0; $i<$num; ++$i) {
97 1 50       5 $raf->Read($buff, 16) == 16 or $et->Warn('Truncated file'), last;
98 1 50       5 $$et{DOC_NUM} = ++$$et{DOC_COUNT} if $i;
99 1         5 $et->HandleTag($tagTbl, 6, $buff);
100             }
101 1         3 delete $$et{DOC_NUM};
102 1         4 return 1;
103             }
104              
105             1; # end
106              
107             __END__