#------------------------------------------------------------------------------ # File: BMP.pm # # Description: Read BMP meta information # # Revisions: 07/16/2005 - P. Harvey Created # # References: 1) http://www.fortunecity.com/skyscraper/windows/364/bmpffrmt.html # 2) http://www.fourcc.org/rgb.php # 3) https://msdn.microsoft.com/en-us/library/dd183381(v=vs.85).aspx #------------------------------------------------------------------------------ package Image::ExifTool::BMP; use strict; use vars qw($VERSION); use Image::ExifTool qw(:DataAccess :Utils); $VERSION = '1.09'; # conversions for fixed-point 2.30 format values my %fixed2_30 = ( ValueConv => q{ my @a = split ' ', $val; $_ /= 0x40000000 foreach @a; "@a"; }, PrintConv => q{ my @a = split ' ', $val; $_ = sprintf('%.6f', $_) foreach @a; "@a"; }, ); # BMP chunks %Image::ExifTool::BMP::Main = ( PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData, GROUPS => { 0 => 'File', 1 => 'File', 2 => 'Image' }, NOTES => q{ There really isn't much meta information in a BMP file as such, just a bit of image related information. }, 0 => { Name => 'BMPVersion', Format => 'int32u', Notes => q{ this is actually the size of the BMP header, but used to determine the BMP version }, RawConv => '$$self{BMPVersion} = $val', PrintConv => { 40 => 'Windows V3', 68 => 'AVI BMP structure?', #PH (seen in AVI movies from some Casio and Nikon cameras) 108 => 'Windows V4', 124 => 'Windows V5', }, }, 4 => { Name => 'ImageWidth', Format => 'int32u', }, 8 => { Name => 'ImageHeight', Format => 'int32s', # (negative when stored in top-to-bottom order) ValueConv => 'abs($val)', }, 12 => { Name => 'Planes', Format => 'int16u', # values: 0,1,4,8,16,24,32 }, 14 => { Name => 'BitDepth', Format => 'int16u', }, 16 => { Name => 'Compression', Format => 'int32u', RawConv => '$$self{BMPCompression} = $val', # (formatted as string[4] for some values in AVI images) ValueConv => '$val > 256 ? unpack("A4",pack("V",$val)) : $val', PrintConv => { 0 => 'None', 1 => '8-Bit RLE', 2 => '4-Bit RLE', 3 => 'Bitfields', 4 => 'JPEG', #2 5 => 'PNG', #2 # pass through ASCII video compression codec ID's OTHER => sub { my $val = shift; # convert non-ascii characters $val =~ s/([\0-\x1f\x7f-\xff])/sprintf('\\x%.2x',ord $1)/eg; return $val; }, }, }, 20 => { Name => 'ImageLength', Format => 'int32u', RawConv => '$$self{BMPImageLength} = $val', }, 24 => { Name => 'PixelsPerMeterX', Format => 'int32u', }, 28 => { Name => 'PixelsPerMeterY', Format => 'int32u', }, 32 => { Name => 'NumColors', Format => 'int32u', PrintConv => '$val ? $val : "Use BitDepth"', }, 36 => { Name => 'NumImportantColors', Format => 'int32u', Hook => '$varSize += $size if $$self{BMPVersion} == 68', # (the rest is invalid for AVI BMP's) PrintConv => '$val ? $val : "All"', }, 40 => { Name => 'RedMask', Format => 'int32u', PrintConv => 'sprintf("0x%.8x",$val)', }, 44 => { Name => 'GreenMask', Format => 'int32u', PrintConv => 'sprintf("0x%.8x",$val)', }, 48 => { Name => 'BlueMask', Format => 'int32u', PrintConv => 'sprintf("0x%.8x",$val)', }, 52 => { Name => 'AlphaMask', Format => 'int32u', PrintConv => 'sprintf("0x%.8x",$val)', }, 56 => { Name => 'ColorSpace', Format => 'undef[4]', RawConv => '$$self{BMPColorSpace} = $val =~ /\0/ ? Get32u(\$val, 0) : pack("N",unpack("V",$val))', PrintConv => { 0 => 'Calibrated RGB', 1 => 'Device RGB', 2 => 'Device CMYK', LINK => 'Linked Color Profile', MBED => 'Embedded Color Profile', sRGB => 'sRGB', 'Win ' => 'Windows Color Space', }, }, 60 => { Name => 'RedEndpoint', Condition => '$$self{BMPColorSpace} eq "0"', Format => 'int32u[3]', %fixed2_30, }, 72 => { Name => 'GreenEndpoint', Condition => '$$self{BMPColorSpace} eq "0"', Format => 'int32u[3]', %fixed2_30, }, 84 => { Name => 'BlueEndpoint', Condition => '$$self{BMPColorSpace} eq "0"', Format => 'int32u[3]', %fixed2_30, }, 96 => { Name => 'GammaRed', Condition => '$$self{BMPColorSpace} eq "0"', Format => 'fixed32u', }, 100 => { Name => 'GammaGreen', Condition => '$$self{BMPColorSpace} eq "0"', Format => 'fixed32u', }, 104 => { Name => 'GammaBlue', Condition => '$$self{BMPColorSpace} eq "0"', Format => 'fixed32u', }, 108 => { Name => 'RenderingIntent', Format => 'int32u', PrintConv => { 1 => 'Graphic (LCS_GM_BUSINESS)', 2 => 'Proof (LCS_GM_GRAPHICS)', 4 => 'Picture (LCS_GM_IMAGES)', 8 => 'Absolute Colorimetric (LCS_GM_ABS_COLORIMETRIC)', }, }, 112 => { Name => 'ProfileDataOffset', Condition => '$$self{BMPColorSpace} eq "LINK" or $$self{BMPColorSpace} eq "MBED"', Format => 'int32u', RawConv => '$$self{BMPProfileOffset} = $val', }, 116 => { Name => 'ProfileSize', Condition => '$$self{BMPColorSpace} eq "LINK" or $$self{BMPColorSpace} eq "MBED"', Format => 'int32u', RawConv => '$$self{BMPProfileSize} = $val', }, # 120 - reserved ); # OS/2 12-byte bitmap header (ref http://www.fileformat.info/format/bmp/egff.htm) %Image::ExifTool::BMP::OS2 = ( PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData, GROUPS => { 0 => 'File', 1 => 'File', 2 => 'Image' }, NOTES => 'Information extracted from OS/2-format BMP images.', 0 => { Name => 'BMPVersion', Format => 'int32u', Notes => 'again, the header size is used to determine the BMP version', PrintConv => { 12 => 'OS/2 V1', 64 => 'OS/2 V2', }, }, 4 => { Name => 'ImageWidth', Format => 'int16u' }, 6 => { Name => 'ImageHeight', Format => 'int16u' }, 8 => { Name => 'Planes', Format => 'int16u' }, 10 => { Name => 'BitDepth', Format => 'int16u' }, ); %Image::ExifTool::BMP::Extra = ( GROUPS => { 0 => 'File', 1 => 'File', 2 => 'Image' }, NOTES => 'Extra information extracted from some BMP images.', VARS => { NO_ID => 1 }, LinkedProfileName => { }, ICC_Profile => { SubDirectory => { TagTable => 'Image::ExifTool::ICC_Profile::Main' } }, EmbeddedJPG => { Groups => { 2 => 'Preview' }, Binary => 1, }, EmbeddedPNG => { Groups => { 2 => 'Preview' }, Binary => 1, }, ); #------------------------------------------------------------------------------ # Extract EXIF information from a BMP image # Inputs: 0) ExifTool object reference, 1) dirInfo reference # Returns: 1 on success, 0 if this wasn't a valid BMP file sub ProcessBMP($$) { my ($et, $dirInfo) = @_; my $raf = $$dirInfo{RAF}; my ($buff, $tagTablePtr); # verify this is a valid BMP file return 0 unless $raf->Read($buff, 18) == 18; return 0 unless $buff =~ /^BM/; SetByteOrder('II'); my $len = Get32u(\$buff, 14); # len = v1:12, v4:108, v5:124 return 0 unless $len == 12 or $len == 16 or ($len >= 40 and $len < 1000000); return 0 unless $raf->Seek(-4, 1) and $raf->Read($buff, $len) == $len; $et->SetFileType(); # set the FileType tag # # process the BMP header # my %dirInfo = ( DataPt => \$buff, DirStart => 0, DirLen => length($buff), ); if ($len == 12 or $len == 16 or $len == 64) { # old OS/2 format BMP $tagTablePtr = GetTagTable('Image::ExifTool::BMP::OS2'); } else { $tagTablePtr = GetTagTable('Image::ExifTool::BMP::Main'); } $et->ProcessDirectory(\%dirInfo, $tagTablePtr); # # extract any embedded images # my $extraTable = GetTagTable('Image::ExifTool::BMP::Extra'); if ($$et{BMPCompression} and $$et{BMPImageLength} and ($$et{BMPCompression} == 4 or $$et{BMPCompression} == 5)) { my $tag = $$et{BMPCompression} == 4 ? 'EmbeddedJPG' : 'EmbeddedPNG'; my $val = $et->ExtractBinary($raf->Tell(), $$et{BMPImageLength}, $tag); if ($val) { $et->HandleTag($extraTable, $tag, $val); } } # # process profile data if it exists (v5 header only) # if ($len == 124 and $$et{BMPProfileOffset}) { my $pos = $$et{BMPProfileOffset} + 14; # (note the 14-byte shift!) my $size = $$et{BMPProfileSize}; if ($raf->Seek($pos, 0) and $raf->Read($buff, $size) == $size) { my $tag; if ($$et{BMPColorSpace} eq 'LINK') { $buff =~ s/\0+$//; # remove null terminator(s) $buff = $et->Decode($buff, 'Latin'); # convert from Latin $tag = 'LinkedProfileName'; } else { $tag = 'ICC_Profile'; } $et->HandleTag($extraTable, $tag => $buff, Size => $size, DataPos => $pos); } else { $et->Warn('Error loading profile data', 1); } } return 1; } 1; # end __END__ =head1 NAME Image::ExifTool::BMP - Read BMP meta information =head1 SYNOPSIS This module is used by Image::ExifTool =head1 DESCRIPTION This module contains definitions required by Image::ExifTool to read BMP (Windows Bitmap) images. =head1 AUTHOR Copyright 2003-2018, Phil Harvey (phil at owl.phy.queensu.ca) This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself. =head1 REFERENCES =over 4 =item L =item L =item L =back =head1 SEE ALSO L, L =cut