| line | stmt | bran | cond | sub | pod | time | code | 
| 1 |  |  |  |  |  |  | #------------------------------------------------------------------------------ | 
| 2 |  |  |  |  |  |  | # File:         MPF.pm | 
| 3 |  |  |  |  |  |  | # | 
| 4 |  |  |  |  |  |  | # Description:  Read Multi-Picture Format information | 
| 5 |  |  |  |  |  |  | # | 
| 6 |  |  |  |  |  |  | # Revisions:    06/12/2009 - P. Harvey Created | 
| 7 |  |  |  |  |  |  | # | 
| 8 |  |  |  |  |  |  | # References:   1) http://www.cipa.jp/std/documents/e/DC-007_E.pdf | 
| 9 |  |  |  |  |  |  | #------------------------------------------------------------------------------ | 
| 10 |  |  |  |  |  |  |  | 
| 11 |  |  |  |  |  |  | package Image::ExifTool::MPF; | 
| 12 |  |  |  |  |  |  |  | 
| 13 | 4 |  |  | 4 |  | 35 | use strict; | 
|  | 4 |  |  |  |  | 9 |  | 
|  | 4 |  |  |  |  | 167 |  | 
| 14 | 4 |  |  | 4 |  | 25 | use vars qw($VERSION); | 
|  | 4 |  |  |  |  | 10 |  | 
|  | 4 |  |  |  |  | 203 |  | 
| 15 | 4 |  |  | 4 |  | 29 | use Image::ExifTool qw(:DataAccess :Utils); | 
|  | 4 |  |  |  |  | 9 |  | 
|  | 4 |  |  |  |  | 976 |  | 
| 16 | 4 |  |  | 4 |  | 38 | use Image::ExifTool::Exif; | 
|  | 4 |  |  |  |  | 10 |  | 
|  | 4 |  |  |  |  | 3431 |  | 
| 17 |  |  |  |  |  |  |  | 
| 18 |  |  |  |  |  |  | $VERSION = '1.14'; | 
| 19 |  |  |  |  |  |  |  | 
| 20 |  |  |  |  |  |  | sub ProcessMPImageList($$$); | 
| 21 |  |  |  |  |  |  |  | 
| 22 |  |  |  |  |  |  | # Tags found in APP2 MPF segment in JPEG images | 
| 23 |  |  |  |  |  |  | %Image::ExifTool::MPF::Main = ( | 
| 24 |  |  |  |  |  |  | GROUPS => { 0 => 'MPF', 1 => 'MPF0', 2 => 'Image'}, | 
| 25 |  |  |  |  |  |  | NOTES => q{ | 
| 26 |  |  |  |  |  |  | These tags are part of the CIPA Multi-Picture Format specification, and are | 
| 27 |  |  |  |  |  |  | found in the APP2 "MPF" segment of JPEG images.  MPImage data referenced | 
| 28 |  |  |  |  |  |  | from this segment is stored as a JPEG trailer.  The MPF tags are not | 
| 29 |  |  |  |  |  |  | writable, however the MPF segment may be deleted as a group (with "MPF:All") | 
| 30 |  |  |  |  |  |  | but then the JPEG trailer should also be deleted (with "Trailer:All").  See | 
| 31 |  |  |  |  |  |  | L | 
| 32 |  |  |  |  |  |  | for the official specification. | 
| 33 |  |  |  |  |  |  | }, | 
| 34 |  |  |  |  |  |  | 0xb000 => 'MPFVersion', | 
| 35 |  |  |  |  |  |  | 0xb001 => 'NumberOfImages', | 
| 36 |  |  |  |  |  |  | 0xb002 => { | 
| 37 |  |  |  |  |  |  | Name => 'MPImageList', | 
| 38 |  |  |  |  |  |  | SubDirectory => { | 
| 39 |  |  |  |  |  |  | TagTable => 'Image::ExifTool::MPF::MPImage', | 
| 40 |  |  |  |  |  |  | ProcessProc => \&ProcessMPImageList, | 
| 41 |  |  |  |  |  |  | }, | 
| 42 |  |  |  |  |  |  | }, | 
| 43 |  |  |  |  |  |  | 0xb003 => { | 
| 44 |  |  |  |  |  |  | Name => 'ImageUIDList', | 
| 45 |  |  |  |  |  |  | Binary => 1, | 
| 46 |  |  |  |  |  |  | }, | 
| 47 |  |  |  |  |  |  | 0xb004 => 'TotalFrames', | 
| 48 |  |  |  |  |  |  | 0xb101 => 'MPIndividualNum', | 
| 49 |  |  |  |  |  |  | 0xb201 => { | 
| 50 |  |  |  |  |  |  | Name => 'PanOrientation', | 
| 51 |  |  |  |  |  |  | PrintHex => 1, | 
| 52 |  |  |  |  |  |  | Notes => 'long integer is split into 4 bytes', | 
| 53 |  |  |  |  |  |  | ValueConv => 'join(" ",unpack("C*",pack("N",$val)))', | 
| 54 |  |  |  |  |  |  | PrintConv => [ | 
| 55 |  |  |  |  |  |  | '"$val rows"', | 
| 56 |  |  |  |  |  |  | '"$val columns"', | 
| 57 |  |  |  |  |  |  | { | 
| 58 |  |  |  |  |  |  | 0 => '[unused]', | 
| 59 |  |  |  |  |  |  | 1 => 'Start at top right', | 
| 60 |  |  |  |  |  |  | 2 => 'Start at top left', | 
| 61 |  |  |  |  |  |  | 3 => 'Start at bottom left', | 
| 62 |  |  |  |  |  |  | 4 => 'Start at bottom right', | 
| 63 |  |  |  |  |  |  | }, | 
| 64 |  |  |  |  |  |  | { | 
| 65 |  |  |  |  |  |  | 0x01 => 'Left to right', | 
| 66 |  |  |  |  |  |  | 0x02 => 'Right to left', | 
| 67 |  |  |  |  |  |  | 0x03 => 'Top to bottom', | 
| 68 |  |  |  |  |  |  | 0x04 => 'Bottom to top', | 
| 69 |  |  |  |  |  |  | 0x10 => 'Clockwise', | 
| 70 |  |  |  |  |  |  | 0x20 => 'Counter clockwise', | 
| 71 |  |  |  |  |  |  | 0x30 => 'Zigzag (row start)', | 
| 72 |  |  |  |  |  |  | 0x40 => 'Zigzag (column start)', | 
| 73 |  |  |  |  |  |  | }, | 
| 74 |  |  |  |  |  |  | ], | 
| 75 |  |  |  |  |  |  | }, | 
| 76 |  |  |  |  |  |  | 0xb202 => 'PanOverlapH', | 
| 77 |  |  |  |  |  |  | 0xb203 => 'PanOverlapV', | 
| 78 |  |  |  |  |  |  | 0xb204 => 'BaseViewpointNum', | 
| 79 |  |  |  |  |  |  | 0xb205 => 'ConvergenceAngle', | 
| 80 |  |  |  |  |  |  | 0xb206 => 'BaselineLength', | 
| 81 |  |  |  |  |  |  | 0xb207 => 'VerticalDivergence', | 
| 82 |  |  |  |  |  |  | 0xb208 => 'AxisDistanceX', | 
| 83 |  |  |  |  |  |  | 0xb209 => 'AxisDistanceY', | 
| 84 |  |  |  |  |  |  | 0xb20a => 'AxisDistanceZ', | 
| 85 |  |  |  |  |  |  | 0xb20b => 'YawAngle', | 
| 86 |  |  |  |  |  |  | 0xb20c => 'PitchAngle', | 
| 87 |  |  |  |  |  |  | 0xb20d => 'RollAngle', | 
| 88 |  |  |  |  |  |  | ); | 
| 89 |  |  |  |  |  |  |  | 
| 90 |  |  |  |  |  |  | # Tags found in MPImage structure | 
| 91 |  |  |  |  |  |  | %Image::ExifTool::MPF::MPImage = ( | 
| 92 |  |  |  |  |  |  | PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData, | 
| 93 |  |  |  |  |  |  | #WRITE_PROC => \&Image::ExifTool::WriteBinaryData, | 
| 94 |  |  |  |  |  |  | #CHECK_PROC => \&Image::ExifTool::CheckBinaryData, | 
| 95 |  |  |  |  |  |  | #WRITABLE => 1, | 
| 96 |  |  |  |  |  |  | GROUPS => { 0 => 'MPF', 1 => 'MPImage', 2 => 'Image'}, | 
| 97 |  |  |  |  |  |  | NOTES => q{ | 
| 98 |  |  |  |  |  |  | The first MPF "Large Thumbnail" image is extracted as PreviewImage, and the | 
| 99 |  |  |  |  |  |  | rest of the embedded MPF images are extracted as MPImage#.  The | 
| 100 |  |  |  |  |  |  | L (-ee) option may be used to extract information from these | 
| 101 |  |  |  |  |  |  | embedded images. | 
| 102 |  |  |  |  |  |  | }, | 
| 103 |  |  |  |  |  |  | 0.1 => { | 
| 104 |  |  |  |  |  |  | Name => 'MPImageFlags', | 
| 105 |  |  |  |  |  |  | Format => 'int32u', | 
| 106 |  |  |  |  |  |  | Mask => 0xf8000000, | 
| 107 |  |  |  |  |  |  | PrintConv => { BITMASK => { | 
| 108 |  |  |  |  |  |  | 2 => 'Representative image', | 
| 109 |  |  |  |  |  |  | 3 => 'Dependent child image', | 
| 110 |  |  |  |  |  |  | 4 => 'Dependent parent image', | 
| 111 |  |  |  |  |  |  | }}, | 
| 112 |  |  |  |  |  |  | }, | 
| 113 |  |  |  |  |  |  | 0.2 => { | 
| 114 |  |  |  |  |  |  | Name => 'MPImageFormat', | 
| 115 |  |  |  |  |  |  | Format => 'int32u', | 
| 116 |  |  |  |  |  |  | Mask => 0x07000000, | 
| 117 |  |  |  |  |  |  | PrintConv => { | 
| 118 |  |  |  |  |  |  | 0 => 'JPEG', | 
| 119 |  |  |  |  |  |  | }, | 
| 120 |  |  |  |  |  |  | }, | 
| 121 |  |  |  |  |  |  | 0.3 => { | 
| 122 |  |  |  |  |  |  | Name => 'MPImageType', | 
| 123 |  |  |  |  |  |  | Format => 'int32u', | 
| 124 |  |  |  |  |  |  | Mask => 0x00ffffff, | 
| 125 |  |  |  |  |  |  | PrintHex => 1, | 
| 126 |  |  |  |  |  |  | PrintConv => { | 
| 127 |  |  |  |  |  |  | 0x000000 => 'Undefined', | 
| 128 |  |  |  |  |  |  | 0x010001 => 'Large Thumbnail (VGA equivalent)', | 
| 129 |  |  |  |  |  |  | 0x010002 => 'Large Thumbnail (full HD equivalent)', | 
| 130 |  |  |  |  |  |  | 0x020001 => 'Multi-frame Panorama', | 
| 131 |  |  |  |  |  |  | 0x020002 => 'Multi-frame Disparity', | 
| 132 |  |  |  |  |  |  | 0x020003 => 'Multi-angle', | 
| 133 |  |  |  |  |  |  | 0x030000 => 'Baseline MP Primary Image', | 
| 134 |  |  |  |  |  |  | }, | 
| 135 |  |  |  |  |  |  | }, | 
| 136 |  |  |  |  |  |  | 4 => { | 
| 137 |  |  |  |  |  |  | Name => 'MPImageLength', | 
| 138 |  |  |  |  |  |  | Format => 'int32u', | 
| 139 |  |  |  |  |  |  | }, | 
| 140 |  |  |  |  |  |  | 8 => { | 
| 141 |  |  |  |  |  |  | Name => 'MPImageStart', | 
| 142 |  |  |  |  |  |  | Format => 'int32u', | 
| 143 |  |  |  |  |  |  | IsOffset => '$val', | 
| 144 |  |  |  |  |  |  | }, | 
| 145 |  |  |  |  |  |  | 12 => { | 
| 146 |  |  |  |  |  |  | Name => 'DependentImage1EntryNumber', | 
| 147 |  |  |  |  |  |  | Format => 'int16u', | 
| 148 |  |  |  |  |  |  | }, | 
| 149 |  |  |  |  |  |  | 14 => { | 
| 150 |  |  |  |  |  |  | Name => 'DependentImage2EntryNumber', | 
| 151 |  |  |  |  |  |  | Format => 'int16u', | 
| 152 |  |  |  |  |  |  | }, | 
| 153 |  |  |  |  |  |  | ); | 
| 154 |  |  |  |  |  |  |  | 
| 155 |  |  |  |  |  |  | # extract MP Images as composite tags | 
| 156 |  |  |  |  |  |  | %Image::ExifTool::MPF::Composite = ( | 
| 157 |  |  |  |  |  |  | GROUPS => { 2 => 'Preview' }, | 
| 158 |  |  |  |  |  |  | MPImage => { | 
| 159 |  |  |  |  |  |  | Require => { | 
| 160 |  |  |  |  |  |  | 0 => 'MPImageStart', | 
| 161 |  |  |  |  |  |  | 1 => 'MPImageLength', | 
| 162 |  |  |  |  |  |  | 2 => 'MPImageType', | 
| 163 |  |  |  |  |  |  | }, | 
| 164 |  |  |  |  |  |  | Notes => q{ | 
| 165 |  |  |  |  |  |  | the first MPF "Large Thumbnail" is extracted as PreviewImage, and the rest | 
| 166 |  |  |  |  |  |  | of the embedded MPF images are extracted as MPImage#.  The L | 
| 167 |  |  |  |  |  |  | option may be used to extract information from these embedded images. | 
| 168 |  |  |  |  |  |  | }, | 
| 169 |  |  |  |  |  |  | # extract all MPF images (not just one) | 
| 170 |  |  |  |  |  |  | RawConv => q{ | 
| 171 |  |  |  |  |  |  | require Image::ExifTool::MPF; | 
| 172 |  |  |  |  |  |  | @grps = $self->GetGroup($$val{0});  # set groups from input tag | 
| 173 |  |  |  |  |  |  | Image::ExifTool::MPF::ExtractMPImages($self); | 
| 174 |  |  |  |  |  |  | }, | 
| 175 |  |  |  |  |  |  | }, | 
| 176 |  |  |  |  |  |  | ); | 
| 177 |  |  |  |  |  |  |  | 
| 178 |  |  |  |  |  |  | # add our composite tags | 
| 179 |  |  |  |  |  |  | Image::ExifTool::AddCompositeTags('Image::ExifTool::MPF'); | 
| 180 |  |  |  |  |  |  |  | 
| 181 |  |  |  |  |  |  | #------------------------------------------------------------------------------ | 
| 182 |  |  |  |  |  |  | # Extract all MP images | 
| 183 |  |  |  |  |  |  | # Inputs: 0) ExifTool object ref | 
| 184 |  |  |  |  |  |  | # Returns: undef | 
| 185 |  |  |  |  |  |  | sub ExtractMPImages($) | 
| 186 |  |  |  |  |  |  | { | 
| 187 | 19 |  |  | 19 | 0 | 70 | my $et = shift; | 
| 188 | 19 |  |  |  |  | 87 | my $ee = $et->Options('ExtractEmbedded'); | 
| 189 | 19 |  |  |  |  | 159 | my $saveBinary = $et->Options('Binary'); | 
| 190 | 19 |  |  |  |  | 106 | my ($i, $didPreview, $xtra); | 
| 191 |  |  |  |  |  |  |  | 
| 192 | 19 |  | 100 |  |  | 146 | for ($i=1; $xtra or not defined $xtra; ++$i) { | 
| 193 |  |  |  |  |  |  | # run through MP images in the same order they were extracted | 
| 194 | 38 | 100 |  |  |  | 211 | $xtra = defined $$et{VALUE}{"MPImageStart ($i)"} ? " ($i)" : ''; | 
| 195 | 38 |  |  |  |  | 178 | my $off = $et->GetValue("MPImageStart$xtra", 'ValueConv'); | 
| 196 | 38 |  |  |  |  | 171 | my $len = $et->GetValue("MPImageLength$xtra", 'ValueConv'); | 
| 197 | 38 | 100 | 66 |  |  | 274 | if ($off and $len) { | 
| 198 | 19 |  |  |  |  | 138 | my $type = $et->GetValue("MPImageType$xtra", 'ValueConv'); | 
| 199 | 19 |  |  |  |  | 119 | my $tag = "MPImage$i"; | 
| 200 |  |  |  |  |  |  | # store first "Large Thumbnail" as a PreviewImage | 
| 201 | 19 | 50 | 33 |  |  | 207 | if (not $didPreview and $type and ($type & 0x0f0000) == 0x010000) { | 
|  |  |  | 33 |  |  |  |  | 
| 202 | 19 |  |  |  |  | 51 | $tag = 'PreviewImage'; | 
| 203 | 19 |  |  |  |  | 39 | $didPreview = 1; | 
| 204 |  |  |  |  |  |  | } | 
| 205 | 19 | 50 |  |  |  | 90 | $et->Options('Binary', 1) if $ee; | 
| 206 | 19 |  |  |  |  | 93 | my $val = Image::ExifTool::Exif::ExtractImage($et, $off, $len, $tag); | 
| 207 | 19 | 50 |  |  |  | 97 | $et->Options('Binary', $saveBinary) if $ee; | 
| 208 | 19 | 100 |  |  |  | 108 | next unless defined $val; | 
| 209 | 14 | 50 |  |  |  | 75 | unless ($Image::ExifTool::Extra{$tag}) { | 
| 210 | 0 |  |  |  |  | 0 | AddTagToTable(\%Image::ExifTool::Extra, $tag, { | 
| 211 |  |  |  |  |  |  | Name => $tag, | 
| 212 |  |  |  |  |  |  | Groups => { 0 => 'Composite', 1 => 'Composite', 2 => 'Preview'}, | 
| 213 |  |  |  |  |  |  | }); | 
| 214 |  |  |  |  |  |  | } | 
| 215 | 14 |  |  |  |  | 85 | my $key = $et->FoundTag($tag, $val, $et->GetGroup("MPImageStart$xtra")); | 
| 216 |  |  |  |  |  |  | # extract information from MP images if ExtractEmbedded option used | 
| 217 | 14 | 50 |  |  |  | 145 | if ($ee) { | 
| 218 | 0 |  |  |  |  | 0 | my $oldBase = $$et{BASE}; | 
| 219 | 0 |  |  |  |  | 0 | $$et{BASE} = $off; | 
| 220 | 0 |  |  |  |  | 0 | $$et{DOC_NUM} = $i; | 
| 221 | 0 |  |  |  |  | 0 | $et->ExtractInfo($val, { ReEntry => 1 }); | 
| 222 | 0 |  |  |  |  | 0 | delete $$et{DOC_NUM}; | 
| 223 | 0 |  |  |  |  | 0 | $$et{BASE} = $oldBase; | 
| 224 |  |  |  |  |  |  | } | 
| 225 |  |  |  |  |  |  | } | 
| 226 |  |  |  |  |  |  | } | 
| 227 | 19 |  |  |  |  | 240 | return undef; | 
| 228 |  |  |  |  |  |  | } | 
| 229 |  |  |  |  |  |  |  | 
| 230 |  |  |  |  |  |  | #------------------------------------------------------------------------------ | 
| 231 |  |  |  |  |  |  | # Process MP Entry list | 
| 232 |  |  |  |  |  |  | # Inputs: 0) ExifTool object ref, 1) dirInfo ref, 2) tag table ref | 
| 233 |  |  |  |  |  |  | # Returns: 1 on success | 
| 234 |  |  |  |  |  |  | sub ProcessMPImageList($$$) | 
| 235 |  |  |  |  |  |  | { | 
| 236 | 19 |  |  | 19 | 0 | 103 | my ($et, $dirInfo, $tagTablePtr) = @_; | 
| 237 | 19 |  |  |  |  | 83 | my $num = int($$dirInfo{DirLen} / 16); # (16 bytes per MP Entry) | 
| 238 | 19 |  |  |  |  | 53 | $$dirInfo{DirLen} = 16; | 
| 239 | 19 |  |  |  |  | 46 | my ($i, $success); | 
| 240 | 19 |  |  |  |  | 64 | my $oldG1 = $$et{SET_GROUP1}; | 
| 241 | 19 |  |  |  |  | 83 | for ($i=0; $i<$num; ++$i) { | 
| 242 | 38 |  |  |  |  | 114 | $$et{SET_GROUP1} = '+' . ($i + 1); | 
| 243 | 38 |  |  |  |  | 157 | $success = $et->ProcessBinaryData($dirInfo, $tagTablePtr); | 
| 244 | 38 |  |  |  |  | 246 | $$dirInfo{DirStart} += 16; | 
| 245 |  |  |  |  |  |  | } | 
| 246 | 19 |  |  |  |  | 125 | $$et{SET_GROUP1} = $oldG1; | 
| 247 | 19 |  |  |  |  | 70 | return $success; | 
| 248 |  |  |  |  |  |  | } | 
| 249 |  |  |  |  |  |  |  | 
| 250 |  |  |  |  |  |  | 1;  # end | 
| 251 |  |  |  |  |  |  |  | 
| 252 |  |  |  |  |  |  | __END__ |