- Timestamp:
- 2021-02-26T19:39:51+13:00 (3 years ago)
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
main/trunk/greenstone2/perllib/cpan/Image/ExifTool/FlashPix.pm
r24107 r34921 10 10 # 3) http://search.cpan.org/~jdb/libwin32/ 11 11 # 4) http://msdn.microsoft.com/en-us/library/aa380374.aspx 12 # 5) http://www.cpan.org/modules/by-authors/id/H/HC/HCARVEY/File-MSWord-0.1.zip 13 # 6) https://msdn.microsoft.com/en-us/library/cc313153(v=office.12).aspx 12 14 #------------------------------------------------------------------------------ 13 15 … … 20 22 use Image::ExifTool::ASF; # for GetGUID() 21 23 22 $VERSION = '1. 19';24 $VERSION = '1.38'; 23 25 24 26 sub ProcessFPX($$); … … 28 30 sub ProcessHyperlinks($$); 29 31 sub ProcessContents($$$); 32 sub ProcessWordDocument($$$); 33 sub ProcessDocumentTable($); 34 sub ProcessCommentBy($$$); 35 sub ProcessLastSavedBy($$$); 30 36 sub SetDocNum($$;$$$); 37 sub ConvertDTTM($); 31 38 32 39 # sector type constants … … 80 87 30 => 'VT_LPSTR', # VT_LPSTR (int32u count, followed by string) 81 88 31 => 'VT_LPWSTR', # VT_LPWSTR (int32u word count, followed by Unicode string) 82 64 => 'VT_FILETIME',# VT_FILETIME (int64u, number of nanoseconds since Jan 1, 1601)89 64 => 'VT_FILETIME',# VT_FILETIME (int64u, 100 ns increments since Jan 1, 1601) 83 90 65 => 'VT_BLOB', # VT_BLOB 84 91 # 66 => 'VT_STREAM', … … 119 126 # (ref http://msdn.microsoft.com/en-us/library/dd317756(VS.85).aspx) 120 127 my %codePage = ( 121 037 => 'IBM EBCDIC US-Canada',128 37 => 'IBM EBCDIC US-Canada', 122 129 437 => 'DOS United States', 123 130 500 => 'IBM EBCDIC International', … … 299 306 popular. However, some of the structures used in FlashPix streams are part 300 307 of the EXIF specification, and are still being used in the APP2 FPXR segment 301 of JPEG images by some Kodak and Hewlett-Packard digital cameras. 308 of JPEG images by some digital cameras from manufacturers such as FujiFilm, 309 Hewlett-Packard, Kodak and Sanyo. 302 310 303 311 ExifTool extracts FlashPix information from both FPX images and the APP2 … … 306 314 (Microsoft Visio) drawings, and FLA (Macromedia/Adobe Flash project) files 307 315 since these are based on the same file format as FlashPix (the Windows 308 Compound Binary File format). See 316 Compound Binary File format). Note that ExifTool identifies any 317 unrecognized Windows Compound Binary file as a FlashPix (FPX) file. See 309 318 L<http://graphcomp.com/info/specs/livepicture/fpx.pdf> for the FlashPix 310 319 specification. … … 371 380 }, 372 381 # 'Subimage 0000 Data' 373 "\x05Data Object" => { # plus instance number ( ie. " 000000")382 "\x05Data Object" => { # plus instance number (eg. " 000000") 374 383 Name => 'DataObject', 375 384 SubDirectory => { … … 377 386 }, 378 387 }, 379 # "\x05Data Object Store" => { # plus instance number ( ie. " 000000")380 "\x05Transform" => { # plus instance number ( ie. " 000000")388 # "\x05Data Object Store" => { # plus instance number (eg. " 000000") 389 "\x05Transform" => { # plus instance number (eg. " 000000") 381 390 Name => 'Transform', 382 391 SubDirectory => { … … 384 393 }, 385 394 }, 386 "\x05Operation" => { # plus instance number ( ie. " 000000")395 "\x05Operation" => { # plus instance number (eg. " 000000") 387 396 Name => 'Operation', 388 397 SubDirectory => { … … 396 405 }, 397 406 }, 398 "\x05Screen Nail" => { # plus class ID ( ie. "_bd0100609719a180")407 "\x05Screen Nail" => { # plus class ID (eg. "_bd0100609719a180") 399 408 Name => 'ScreenNail', 400 409 Groups => { 2 => 'Other' }, … … 408 417 }, 409 418 }, 410 'Audio Stream' => { # plus instance number ( ie. " 000000")419 'Audio Stream' => { # plus instance number (eg. " 000000") 411 420 Name => 'AudioStream', 412 421 Groups => { 2 => 'Audio' }, … … 423 432 return undef if $len < 0 or length $val < $size + 8; 424 433 return substr($val, 8 + $pos, $len); 434 }, 435 }, 436 'WordDocument' => { 437 Name => 'WordDocument', 438 SubDirectory => { TagTable => 'Image::ExifTool::FlashPix::WordDocument' }, 439 }, 440 # save these tables until after the WordDocument was processed 441 '0Table' => { 442 Name => 'Table0', 443 Hidden => 1, # (used only as temporary storage until table is processed) 444 Binary => 1, 445 }, 446 '1Table' => { 447 Name => 'Table1', 448 Hidden => 1, # (used only as temporary storage until table is processed) 449 Binary => 1, 450 }, 451 Preview => { 452 Name => 'PreviewImage', 453 Groups => { 2 => 'Preview' }, 454 Binary => 1, 455 Notes => 'written by some FujiFilm models', 456 # skip 47-byte Fuji header 457 RawConv => q{ 458 return undef unless length $val > 47; 459 $val = substr($val, 47); 460 return $val =~ /^\xff\xd8\xff/ ? $val : undef; 461 }, 462 }, 463 Property => { 464 Name => 'PreviewInfo', 465 SubDirectory => { 466 TagTable => 'Image::ExifTool::FlashPix::PreviewInfo', 467 ByteOrder => 'BigEndian', 425 468 }, 426 469 }, … … 464 507 0x0f => 'Words', 465 508 0x10 => 'Characters', 466 0x11 => { Name => 'ThumbnailClip', Binary => 1 }, 509 0x11 => { 510 Name => 'ThumbnailClip', 511 # (not a displayable format, so not in the "Preview" group) 512 Binary => 1, 513 }, 467 514 0x12 => { 468 515 Name => 'Software', … … 474 521 PrintConv => { 475 522 0 => 'None', 476 1 => 'Password protected', 477 2 => 'Read-only recommended', 478 4 => 'Read-only enforced', 479 8 => 'Locked for annotations', 480 }, 481 }, 523 BITMASK => { 524 0 => 'Password protected', 525 1 => 'Read-only recommended', 526 2 => 'Read-only enforced', 527 3 => 'Locked for annotations', 528 }, 529 }, 530 }, 531 0x22 => { Name => 'CreatedBy', Groups => { 2 => 'Author' } }, #PH (guess) (MAX files) 532 0x23 => 'DocumentID', # PH (guess) (MAX files) 533 # 0x25 ? seen values 1.0-1.97 (MAX files) 482 534 0x80000000 => { Name => 'LocaleIndicator', Groups => { 2 => 'Other' } }, 483 535 ); … … 507 559 }, 508 560 0x0c => 'HeadingPairs', 509 0x0d => 'TitleOfParts', 561 0x0d => { 562 Name => 'TitleOfParts', 563 # look for "3ds Max" software name at beginning of TitleOfParts 564 RawConv => q{ 565 (ref $val eq 'ARRAY' ? $$val[0] : $val) =~ /^(3ds Max)/ and $$self{Software} = $1; 566 return $val; 567 } 568 }, 510 569 0x0e => 'Manager', 511 570 0x0f => 'Company', … … 515 574 }, 516 575 0x11 => 'CharCountWithSpaces', 517 # 0x12 ? 576 # 0x12 ? seen -32.1850395202637,-386.220672607422,-9.8100004196167,-9810,... 518 577 0x13 => { #PH (unconfirmed) 519 578 Name => 'SharedDoc', 520 579 PrintConv => { 0 => 'No', 1 => 'Yes' }, 521 580 }, 522 # 0x14 ? 523 # 0x15 ? 581 # 0x14 ? seen -1 582 # 0x15 ? seen 1 524 583 0x16 => { 525 584 Name => 'HyperlinksChanged', 526 585 PrintConv => { 0 => 'No', 1 => 'Yes' }, 527 586 }, 528 0x17 => { #PH (unconfirmed handling of lower 16 bits )587 0x17 => { #PH (unconfirmed handling of lower 16 bits, not valid for MAX files) 529 588 Name => 'AppVersion', 530 589 ValueConv => 'sprintf("%d.%.4d",$val >> 16, $val & 0xffff)', 531 590 }, 591 # 0x18 ? seen -1 592 # 0x19 ? seen 0 593 # 0x1a ? seen 0 594 # 0x1b ? seen 0 595 # 0x1c ? seen 0,1 596 # 0x1d ? seen 1 597 # 0x1e ? seen 1 598 # 0x1f ? seen 1,5 599 # 0x20 ? seen 0,5 600 # 0x21 ? seen -1 601 # 0x22 ? seen 0 532 602 '_PID_LINKBASE' => { 533 603 Name => 'HyperlinkBase', … … 985 1055 ); 986 1056 1057 # decode Word document FIB header (ref [MS-DOC].pdf) 1058 %Image::ExifTool::FlashPix::WordDocument = ( 1059 PROCESS_PROC => \&ProcessWordDocument, 1060 GROUPS => { 2 => 'Other' }, 1061 FORMAT => 'int16u', 1062 NOTES => 'Tags extracted from the Microsoft Word document stream.', 1063 0 => { 1064 Name => 'Identification', 1065 PrintHex => 1, 1066 PrintConv => { 1067 0x6a62 => 'MS Word 97', 1068 0x626a => 'Word 98 Mac', 1069 0xa5dc => 'Word 6.0/7.0', 1070 0xa5ec => 'Word 8.0', 1071 }, 1072 }, 1073 3 => { 1074 Name => 'LanguageCode', 1075 PrintHex => 1, 1076 PrintConv => { 1077 0x0400 => 'None', 1078 0x0401 => 'Arabic', 1079 0x0402 => 'Bulgarian', 1080 0x0403 => 'Catalan', 1081 0x0404 => 'Traditional Chinese', 1082 0x0804 => 'Simplified Chinese', 1083 0x0405 => 'Czech', 1084 0x0406 => 'Danish', 1085 0x0407 => 'German', 1086 0x0807 => 'German (Swiss)', 1087 0x0408 => 'Greek', 1088 0x0409 => 'English (US)', 1089 0x0809 => 'English (British)', 1090 0x0c09 => 'English (Australian)', 1091 0x040a => 'Spanish (Castilian)', 1092 0x080a => 'Spanish (Mexican)', 1093 0x040b => 'Finnish', 1094 0x040c => 'French', 1095 0x080c => 'French (Belgian)', 1096 0x0c0c => 'French (Canadian)', 1097 0x100c => 'French (Swiss)', 1098 0x040d => 'Hebrew', 1099 0x040e => 'Hungarian', 1100 0x040f => 'Icelandic', 1101 0x0410 => 'Italian', 1102 0x0810 => 'Italian (Swiss)', 1103 0x0411 => 'Japanese', 1104 0x0412 => 'Korean', 1105 0x0413 => 'Dutch', 1106 0x0813 => 'Dutch (Belgian)', 1107 0x0414 => 'Norwegian (Bokmal)', 1108 0x0814 => 'Norwegian (Nynorsk)', 1109 0x0415 => 'Polish', 1110 0x0416 => 'Portuguese (Brazilian)', 1111 0x0816 => 'Portuguese', 1112 0x0417 => 'Rhaeto-Romanic', 1113 0x0418 => 'Romanian', 1114 0x0419 => 'Russian', 1115 0x041a => 'Croato-Serbian (Latin)', 1116 0x081a => 'Serbo-Croatian (Cyrillic)', 1117 0x041b => 'Slovak', 1118 0x041c => 'Albanian', 1119 0x041d => 'Swedish', 1120 0x041e => 'Thai', 1121 0x041f => 'Turkish', 1122 0x0420 => 'Urdu', 1123 0x0421 => 'Bahasa', 1124 0x0422 => 'Ukrainian', 1125 0x0423 => 'Byelorussian', 1126 0x0424 => 'Slovenian', 1127 0x0425 => 'Estonian', 1128 0x0426 => 'Latvian', 1129 0x0427 => 'Lithuanian', 1130 0x0429 => 'Farsi', 1131 0x042d => 'Basque', 1132 0x042f => 'Macedonian', 1133 0x0436 => 'Afrikaans', 1134 0x043e => 'Malaysian', 1135 }, 1136 }, 1137 5 => { 1138 Name => 'DocFlags', 1139 Mask => 0xff0f, # ignore save count 1140 RawConv => '$$self{DocFlags} = $val', 1141 PrintConv => { BITMASK => { 1142 0 => 'Template', 1143 1 => 'AutoText only', 1144 2 => 'Complex', 1145 3 => 'Has picture', 1146 # 4-7 = number of incremental saves 1147 8 => 'Encrypted', 1148 9 => '1Table', 1149 10 => 'Read only', 1150 11 => 'Passworded', 1151 12 => 'ExtChar', 1152 13 => 'Load override', 1153 14 => 'Far east', 1154 15 => 'Obfuscated', 1155 }}, 1156 }, 1157 9.1 => { 1158 Name => 'System', 1159 Mask => 0x0001, 1160 PrintConv => { 1161 0x0000 => 'Windows', 1162 0x0001 => 'Macintosh', 1163 }, 1164 }, 1165 9.2 => { 1166 Name => 'Word97', 1167 Mask => 0x0010, 1168 PrintConv => { 0 => 'No', 1 => 'Yes' }, 1169 }, 1170 ); 1171 1172 # tags decoded from Word document table 1173 %Image::ExifTool::FlashPix::DocTable = ( 1174 GROUPS => { 1 => 'MS-DOC', 2 => 'Document' }, 1175 NOTES => 'Tags extracted from the Microsoft Word document table.', 1176 VARS => { NO_ID => 1 }, 1177 CommentBy => { 1178 Groups => { 2 => 'Author' }, 1179 Notes => 'enable L<Duplicates|../ExifTool.html#Duplicates> option to extract all entries', 1180 }, 1181 LastSavedBy => { 1182 Groups => { 2 => 'Author' }, 1183 Notes => 'enable L<Duplicates|../ExifTool.html#Duplicates> option to extract history of up to 10 entries', 1184 }, 1185 DOP => { SubDirectory => { TagTable => 'Image::ExifTool::FlashPix::DOP' } }, 1186 ModifyDate => { 1187 Groups => { 2 => 'Time' }, 1188 Format => 'int64u', 1189 Priority => 0, 1190 RawConv => q{ 1191 $val = $val * 1e-7 - 11644473600; # convert to seconds since 1970 1192 return $val > 0 ? $val : undef; 1193 }, 1194 ValueConv => 'ConvertUnixTime($val)', 1195 PrintConv => '$self->ConvertDateTime($val)', 1196 }, 1197 # 1198 # tags below are used internally in intermediate steps to extract the tags above 1199 # 1200 TableOffsets => { Hidden => 1 }, # stores offsets to extract data from document table 1201 CommentByBlock => { # entire block of CommentBy entries 1202 SubDirectory => { 1203 TagTable => 'Image::ExifTool::FlashPix::DocTable', 1204 ProcessProc => \&ProcessCommentBy, 1205 }, 1206 Hidden => 1, 1207 }, 1208 LastSavedByBlock => { # entire block of LastSavedBy entries 1209 SubDirectory => { 1210 TagTable => 'Image::ExifTool::FlashPix::DocTable', 1211 ProcessProc => \&ProcessLastSavedBy, 1212 }, 1213 Hidden => 1, 1214 }, 1215 ); 1216 1217 # Microsoft Office Document Properties (ref [MS-DOC].pdf) 1218 %Image::ExifTool::FlashPix::DOP = ( 1219 PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData, 1220 GROUPS => { 1 => 'MS-DOC', 2 => 'Document' }, 1221 NOTES => 'Microsoft office document properties.', 1222 20 => { 1223 Name => 'CreateDate', 1224 Format => 'int32u', 1225 Groups => { 2 => 'Time' }, 1226 Priority => 0, 1227 RawConv => \&ConvertDTTM, 1228 PrintConv => '$self->ConvertDateTime($val)', 1229 }, 1230 24 => { 1231 Name => 'ModifyDate', 1232 Format => 'int32u', 1233 Groups => { 2 => 'Time' }, 1234 Priority => 0, 1235 RawConv => \&ConvertDTTM, 1236 PrintConv => '$self->ConvertDateTime($val)', 1237 }, 1238 28 => { 1239 Name => 'LastPrinted', 1240 Format => 'int32u', 1241 Groups => { 2 => 'Time' }, 1242 RawConv => \&ConvertDTTM, 1243 PrintConv => '$self->ConvertDateTime($val)', 1244 }, 1245 32 => { Name => 'RevisionNumber', Format => 'int16u' }, 1246 34 => { 1247 Name => 'TotalEditTime', 1248 Format => 'int32u', 1249 PrintConv => 'ConvertTimeSpan($val,60)', 1250 }, 1251 # (according to the MS-DOC specification, the following are accurate only if 1252 # flag 'X' is set, and flag 'u' specifies whether the main or subdoc tags are 1253 # used, but in my tests it seems that both are filled in with reasonable values, 1254 # so just extract the main counts and ignore the subdoc counts for now - PH) 1255 38 => { Name => 'Words', Format => 'int32u' }, 1256 42 => { Name => 'Characters', Format => 'int32u' }, 1257 46 => { Name => 'Pages', Format => 'int16u' }, 1258 48 => { Name => 'Paragraphs', Format => 'int32u' }, 1259 56 => { Name => 'Lines', Format => 'int32u' }, 1260 #60 => { Name => 'WordsWithSubdocs', Format => 'int32u' }, 1261 #64 => { Name => 'CharactersWithSubdocs', Format => 'int32u' }, 1262 #68 => { Name => 'PagesWithSubdocs', Format => 'int16u' }, 1263 #70 => { Name => 'ParagraphsWithSubdocs', Format => 'int32u' }, 1264 #74 => { Name => 'LinesWithSubdocs', Format => 'int32u' }, 1265 ); 1266 1267 # FujiFilm "Property" information (ref PH) 1268 %Image::ExifTool::FlashPix::PreviewInfo = ( 1269 PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData, 1270 GROUPS => { 2 => 'Image' }, 1271 NOTES => 'Preview information written by some FujiFilm models.', 1272 FIRST_ENTRY => 0, 1273 # values are all constant for for my samples except the two decoded tags 1274 # 0x0000: 01 01 00 00 02 01 00 00 00 00 00 00 00 xx xx 01 1275 # 0x0010: 01 00 00 00 00 00 00 xx xx 00 00 00 00 00 00 00 1276 # 0x0020: 00 00 00 00 00 1277 0x0d => { 1278 Name => 'PreviewImageWidth', 1279 Format => 'int16u', 1280 }, 1281 0x17 => { 1282 Name => 'PreviewImageHeight', 1283 Format => 'int16u', 1284 }, 1285 ); 1286 987 1287 # composite FlashPix tags 988 1288 %Image::ExifTool::FlashPix::Composite = ( 989 1289 GROUPS => { 2 => 'Image' }, 990 1290 PreviewImage => { 1291 Groups => { 2 => 'Preview' }, 991 1292 # extract JPEG preview from ScreenNail if possible 992 1293 Require => { … … 996 1297 RawConv => q{ 997 1298 return undef unless $val[0] =~ /\xff\xd8\xff/g; 1299 @grps = $self->GetGroup($$val{0}); # set groups from ScreenNail 998 1300 return substr($val[0], pos($val[0])-3); 999 1301 }, … … 1003 1305 # add our composite tags 1004 1306 Image::ExifTool::AddCompositeTags('Image::ExifTool::FlashPix'); 1307 1308 #------------------------------------------------------------------------------ 1309 # Convert Microsoft DTTM structure to date/time 1310 # Inputs: 0) DTTM value 1311 # Returns: EXIF-format date/time string ("0000:00:00 00:00:00" for zero date/time) 1312 sub ConvertDTTM($) 1313 { 1314 my $val = shift; 1315 my $yr = ($val >> 20) & 0x1ff; 1316 my $mon = ($val >> 16) & 0x0f; 1317 my $day = ($val >> 11) & 0x1f; 1318 my $hr = ($val >> 6) & 0x1f; 1319 my $min = ($val & 0x3f); 1320 $yr += 1900 if $val; 1321 return sprintf("%.4d:%.2d:%.2d %.2d:%.2d:00%s",$yr,$mon,$day,$hr,$min,$val ? 'Z' : ''); 1322 } 1005 1323 1006 1324 #------------------------------------------------------------------------------ … … 1011 1329 sub ProcessHyperlinks($$) 1012 1330 { 1013 my ($val, $e xifTool) = @_;1331 my ($val, $et) = @_; 1014 1332 1015 1333 # process as an array of VT_VARIANT's … … 1021 1339 for ($i=0; $i<$num; ++$i) { 1022 1340 # read VT_BLOB entries as an array of VT_VARIANT's 1023 my $value = ReadFPXValue($e xifTool, \$val, $valPos, VT_VARIANT, $dirEnd);1341 my $value = ReadFPXValue($et, \$val, $valPos, VT_VARIANT, $dirEnd); 1024 1342 last unless defined $value; 1025 1343 push @vals, $value; … … 1042 1360 sub ReadFPXValue($$$$$;$$) 1043 1361 { 1044 my ($e xifTool, $dataPt, $valPos, $type, $dirEnd, $noPad, $codePage) = @_;1362 my ($et, $dataPt, $valPos, $type, $dirEnd, $noPad, $codePage) = @_; 1045 1363 my @vals; 1046 1364 … … 1078 1396 my $subType = Get32u($dataPt, $valPos); 1079 1397 $valPos += $size; 1080 $val = ReadFPXValue($e xifTool, $dataPt, $valPos, $subType, $dirEnd, $noPad, $codePage);1398 $val = ReadFPXValue($et, $dataPt, $valPos, $subType, $dirEnd, $noPad, $codePage); 1081 1399 last unless defined $val; 1082 1400 push @vals, $val; 1083 1401 next; # avoid adding $size to $valPos again 1084 1402 } elsif ($format eq 'VT_FILETIME') { 1085 # gettime in seconds1403 # convert from time in 100 ns increments to time in seconds 1086 1404 $val = 1e-7 * Image::ExifTool::Get64u($dataPt, $valPos); 1087 1405 # print as date/time if value is greater than one year (PH hack) 1088 if ($val > 365 * 24 * 3600) { 1406 my $secDay = 24 * 3600; 1407 if ($val > 365 * $secDay) { 1089 1408 # shift from Jan 1, 1601 to Jan 1, 1970 1090 $val -= 134774 * 24 * 3600 if $val != 0; 1409 my $unixTimeZero = 134774 * $secDay; 1410 $val -= $unixTimeZero; 1411 # there are a lot of bad programmers out there... 1412 my $sec100yr = 100 * 365 * $secDay; 1413 if ($val < 0 || $val > $sec100yr) { 1414 # some software writes the wrong byte order (but the proper word order) 1415 my @w = unpack("x${valPos}NN", $$dataPt); 1416 my $v2 = ($w[0] + $w[1] * 4294967296) * 1e-7 - $unixTimeZero; 1417 if ($v2 > 0 && $v2 < $sec100yr) { 1418 $val = $v2; 1419 # also check for wrong time base 1420 } elsif ($val < 0 && $val + $unixTimeZero > 0) { 1421 $val += $unixTimeZero; 1422 } 1423 } 1091 1424 $val = Image::ExifTool::ConvertUnixTime($val); 1092 1425 } … … 1103 1436 if ($format eq 'VT_LPWSTR') { 1104 1437 # convert wide string from Unicode 1105 $val = $e xifTool->Decode($val, 'UCS2');1438 $val = $et->Decode($val, 'UCS2'); 1106 1439 } elsif ($codePage) { 1107 1440 my $charset = $Image::ExifTool::charsetName{"cp$codePage"}; 1108 1441 if ($charset) { 1109 $val = $e xifTool->Decode($val, $charset);1110 } elsif ($codePage eq1200) { # UTF-16, little endian1111 $val = $e xifTool->Decode(undef, 'UCS2', 'II');1442 $val = $et->Decode($val, $charset); 1443 } elsif ($codePage == 1200) { # UTF-16, little endian 1444 $val = $et->Decode($val, 'UCS2', 'II'); 1112 1445 } 1113 1446 } … … 1131 1464 } 1132 1465 # join VT_ values with commas unless we want an array 1133 @vals = ( join $e xifTool->Options('ListSep'), @vals ) if @vals > 1 and not wantarray;1466 @vals = ( join $et->Options('ListSep'), @vals ) if @vals > 1 and not wantarray; 1134 1467 last; # didn't really want to loop 1135 1468 } … … 1153 1486 sub ProcessContents($$$) 1154 1487 { 1155 my ($e xifTool, $dirInfo, $tagTablePtr) = @_;1488 my ($et, $dirInfo, $tagTablePtr) = @_; 1156 1489 my $dataPt = $$dirInfo{DataPt}; 1157 1490 my $isFLA; 1158 1491 1159 # all of my FLA samples contain "Contents" data, an no other FPX-like samples have1492 # all of my FLA samples contain "Contents" data, and no other FPX-like samples have 1160 1493 # this, but check the data for a familiar pattern to be sure this is FLA: the 1161 1494 # Contents of all of my FLA samples start with two bytes (0x29,0x38,0x3f,0x43 or 0x47, 1162 1495 # then 0x01) followed by a number of zero bytes (from 0x18 to 0x26 of them, related 1163 1496 # somehow to the value of the first byte), followed by the string "DocumentPage" 1164 $isFLA = 1 if $$dataPt =~ /^..\0+\xff\xff\x01\0\x0d\0CDocumentPage/ ;1165 1497 $isFLA = 1 if $$dataPt =~ /^..\0+\xff\xff\x01\0\x0d\0CDocumentPage/s; 1498 1166 1499 # do a brute-force scan of the "Contents" for UTF-16 XMP 1167 1500 # (this may always be little-endian, but allow for either endianness) … … 1170 1503 if ($$dataPt =~ /<\0\?\0x\0p\0a\0c\0k\0e\0t\0 \0e\0n\0d\0=\0['"]\0[wr]\0['"]\0\?\0>\0?/g) { 1171 1504 $$dirInfo{DirLen} = pos($$dataPt) - $$dirInfo{DirStart}; 1172 Image::ExifTool::XMP::ProcessXMP($e xifTool, $dirInfo, $tagTablePtr);1505 Image::ExifTool::XMP::ProcessXMP($et, $dirInfo, $tagTablePtr); 1173 1506 # override format if not already FLA but XMP-dc:Format indicates it is 1174 $isFLA = 1 if $$exifTool{FILE_TYPE} ne 'FLA' and $$exifTool{VALUE}{Format} and 1175 $$exifTool{VALUE}{Format} eq 'application/vnd.adobe.fla'; 1176 } 1177 } 1178 $exifTool->OverrideFileType('FLA') if $isFLA; 1507 $isFLA = 1 if $$et{FILE_TYPE} ne 'FLA' and $$et{VALUE}{Format} and 1508 $$et{VALUE}{Format} eq 'application/vnd.adobe.fla'; 1509 } 1510 } 1511 $et->OverrideFileType('FLA') if $isFLA; 1512 return 1; 1513 } 1514 1515 #------------------------------------------------------------------------------ 1516 # Process WordDocument stream of MSWord doc file (ref 6) 1517 # Inputs: 0) ExifTool object ref, 1) dirInfo ref, 2) tag table ref 1518 # Returns: 1 on success 1519 sub ProcessWordDocument($$$) 1520 { 1521 my ($et, $dirInfo, $tagTablePtr) = @_; 1522 my $dataPt = $$dirInfo{DataPt} or return 0; 1523 my $dirLen = length $$dataPt; 1524 # validate the FIB signature 1525 unless ($dirLen > 2 and Get16u($dataPt,0) == 0xa5ec) { 1526 $et->WarnOnce('Invalid FIB signature', 1); 1527 return 0; 1528 } 1529 $et->ProcessBinaryData($dirInfo, $tagTablePtr); # process FIB 1530 # continue parsing the WordDocument stream until we find the FibRgFcLcb 1531 my $pos = 32; 1532 return 0 if $pos + 2 > $dirLen; 1533 my $n = Get16u($dataPt, $pos); # read csw 1534 $pos += 2 + $n * 2; # skip fibRgW 1535 return 0 if $pos + 2 > $dirLen; 1536 $n = Get16u($dataPt, $pos); # read cslw 1537 $pos += 2 + $n * 4; # skip fibRgLw 1538 return 0 if $pos + 2 > $dirLen; 1539 $n = Get16u($dataPt, $pos); # read cbRgFcLcb 1540 $pos += 2; # point to start of fibRgFcLcbBlob 1541 return 0 if $pos + $n * 8 > $dirLen; 1542 my ($off, @tableOffsets); 1543 # save necessary entries for later processing of document table 1544 # (DOP, CommentBy, LastSavedBy) 1545 foreach $off (0xf8, 0x120, 0x238) { 1546 last if $off + 8 > $n * 8; 1547 push @tableOffsets, Get32u($dataPt, $pos + $off); 1548 push @tableOffsets, Get32u($dataPt, $pos + $off + 4); 1549 } 1550 my $tbl = GetTagTable('Image::ExifTool::FlashPix::DocTable'); 1551 # extract ModifyDate if it exists 1552 $et->HandleTag($tbl, 'ModifyDate', undef, 1553 DataPt => $dataPt, 1554 Start => $pos + 0x2b8, 1555 Size => 8, 1556 ); 1557 $et->HandleTag($tbl, TableOffsets => \@tableOffsets); # save for later 1558 # $pos += $n * 8; # skip fibRgFcLcbBlob 1559 # return 0 if $pos + 2 > $dirLen; 1560 # $n = Get16u($dataPt, $pos); # read cswNew 1561 # return 0 if $pos + 2 + $n * 2 > $dirLen; 1562 # my $nFib = Get16u($dataPt, 2 + ($n ? $pos : 0)); 1563 # $pos += 2 + $n * 2; # skip fibRgCswNew 1564 return 1; 1565 } 1566 1567 #------------------------------------------------------------------------------ 1568 # Process Microsoft Word Document Table 1569 # Inputs: 0) ExifTool object ref 1570 sub ProcessDocumentTable($) 1571 { 1572 my $et = shift; 1573 my $value = $$et{VALUE}; 1574 my $extra = $$et{TAG_EXTRA}; 1575 my ($i, $j, $tag); 1576 # loop through TableOffsets for each sub-document 1577 for ($i=0; ; ++$i) { 1578 my $key = 'TableOffsets' . ($i ? " ($i)" : ''); 1579 my $offsets = $$value{$key}; 1580 last unless defined $offsets; 1581 my $doc; 1582 $doc = $$extra{$key}{G3} if $$extra{$key}; 1583 $doc = '' unless $doc; 1584 # get DocFlags for this sub-document 1585 my ($docFlags, $docTable); 1586 for ($j=0; ; ++$j) { 1587 my $key = 'DocFlags' . ($j ? " ($j)" : ''); 1588 last unless defined $$value{$key}; 1589 my $tmp; 1590 $tmp = $$extra{$key}{G3} if $$extra{$key}; 1591 $tmp = '' unless $tmp; 1592 if ($tmp eq $doc) { 1593 $docFlags = $$value{$key}; 1594 last; 1595 } 1596 } 1597 next unless defined $docFlags; 1598 $tag = $docFlags & 0x200 ? 'Table1' : 'Table0'; 1599 # get table for this sub-document 1600 for ($j=0; ; ++$j) { 1601 my $key = $tag . ($j ? " ($j)" : ''); 1602 last unless defined $$value{$key}; 1603 my $tmp; 1604 $tmp = $$extra{$key}{G3} if $$extra{$key}; 1605 $tmp = '' unless $tmp; 1606 if ($tmp eq $doc) { 1607 $docTable = \$$value{$key}; 1608 last; 1609 } 1610 } 1611 next unless defined $docTable; 1612 # extract DOP and LastSavedBy information from document table 1613 $$et{DOC_NUM} = $doc; # use same document number 1614 my $tagTablePtr = GetTagTable('Image::ExifTool::FlashPix::DocTable'); 1615 foreach $tag (qw(DOP CommentByBlock LastSavedByBlock)) { 1616 last unless @$offsets; 1617 my $off = shift @$offsets; 1618 my $len = shift @$offsets; 1619 next unless $len and $off + $len <= length $$docTable; 1620 $et->HandleTag($tagTablePtr, $tag, undef, 1621 DataPt => $docTable, 1622 Start => $off, 1623 Size => $len, 1624 ); 1625 } 1626 delete $$et{DOC_NUM}; 1627 } 1628 # delete intermediate tags 1629 foreach $tag (qw(TableOffsets Table0 Table1)) { 1630 for ($i=0; ; ++$i) { 1631 my $key = $tag . ($i ? " ($i)" : ''); 1632 last unless defined $$value{$key}; 1633 $et->DeleteTag($key); 1634 } 1635 } 1636 } 1637 1638 #------------------------------------------------------------------------------ 1639 # Extract names of comment authors (ref 6) 1640 # Inputs: 0) ExifTool object ref, 1) dirInfo ref, 2) tag table ref 1641 # Returns: 1 on success 1642 sub ProcessCommentBy($$$) 1643 { 1644 my ($et, $dirInfo, $tagTablePtr) = @_; 1645 my $dataPt = $$dirInfo{DataPt}; 1646 my $pos = $$dirInfo{DirStart}; 1647 my $end = $$dirInfo{DirLen} + $pos; 1648 $et->VerboseDir($$dirInfo{DirName}); 1649 while ($pos + 2 < $end) { 1650 my $len = Get16u($dataPt, $pos); 1651 $pos += 2; 1652 last if $pos + $len * 2 > $end; 1653 my $author = $et->Decode(substr($$dataPt, $pos, $len*2), 'UCS2'); 1654 $pos += $len * 2; 1655 $et->HandleTag($tagTablePtr, CommentBy => $author); 1656 } 1657 return 1; 1658 } 1659 1660 #------------------------------------------------------------------------------ 1661 # Extract last-saved-by names (ref 5) 1662 # Inputs: 0) ExifTool object ref, 1) dirInfo ref, 2) tag table ref 1663 # Returns: 1 on success 1664 sub ProcessLastSavedBy($$$) 1665 { 1666 my ($et, $dirInfo, $tagTablePtr) = @_; 1667 my $dataPt = $$dirInfo{DataPt}; 1668 my $pos = $$dirInfo{DirStart}; 1669 my $end = $$dirInfo{DirLen} + $pos; 1670 return 0 if $pos + 6 > $end; 1671 $et->VerboseDir($$dirInfo{DirName}); 1672 my $num = Get16u($dataPt, $pos+2); 1673 $pos += 6; 1674 while ($num >= 2) { 1675 last if $pos + 2 > $end; 1676 my $len = Get16u($dataPt, $pos); 1677 $pos += 2; 1678 last if $pos + $len * 2 > $end; 1679 my $author = $et->Decode(substr($$dataPt, $pos, $len*2), 'UCS2'); 1680 $pos += $len * 2; 1681 last if $pos + 2 > $end; 1682 $len = Get16u($dataPt, $pos); 1683 $pos += 2; 1684 last if $pos + $len * 2 > $end; 1685 my $path = $et->Decode(substr($$dataPt, $pos, $len*2), 'UCS2'); 1686 $pos += $len * 2; 1687 $et->HandleTag($tagTablePtr, LastSavedBy => "$author ($path)"); 1688 $num -= 2; 1689 } 1179 1690 return 1; 1180 1691 } … … 1200 1711 sub ProcessProperties($$$) 1201 1712 { 1202 my ($e xifTool, $dirInfo, $tagTablePtr) = @_;1713 my ($et, $dirInfo, $tagTablePtr) = @_; 1203 1714 my $dataPt = $$dirInfo{DataPt}; 1204 1715 my $pos = $$dirInfo{DirStart} || 0; 1205 1716 my $dirLen = $$dirInfo{DirLen} || length($$dataPt) - $pos; 1206 1717 my $dirEnd = $pos + $dirLen; 1207 my $verbose = $e xifTool->Options('Verbose');1718 my $verbose = $et->Options('Verbose'); 1208 1719 my $n; 1209 1720 1210 1721 if ($dirLen < 48) { 1211 $e xifTool->Warn('Truncated FPX properties');1722 $et->Warn('Truncated FPX properties'); 1212 1723 return 0; 1213 1724 } 1214 1725 # check and set our byte order if necessary 1215 1726 unless (CheckBOM($dataPt, $pos)) { 1216 $e xifTool->Warn('Bad FPX property byte order mark');1727 $et->Warn('Bad FPX property byte order mark'); 1217 1728 return 0; 1218 1729 } … … 1220 1731 $pos = Get32u($dataPt, $pos + 44); 1221 1732 if ($pos < 48) { 1222 $e xifTool->Warn('Bad FPX property section offset');1733 $et->Warn('Bad FPX property section offset'); 1223 1734 return 0; 1224 1735 } … … 1231 1742 last unless $size; 1232 1743 my $numEntries = Get32u($dataPt, $pos + 4); 1233 $verbose and $e xifTool->VerboseDir('Property Info', $numEntries, $size);1744 $verbose and $et->VerboseDir('Property Info', $numEntries, $size); 1234 1745 if ($pos + 8 + 8 * $numEntries > $dirEnd) { 1235 $e xifTool->Warn('Truncated property list');1746 $et->Warn('Truncated property list'); 1236 1747 last; 1237 1748 } … … 1263 1774 $name =~ tr/-_a-zA-Z0-9//dc; # remove illegal characters 1264 1775 next unless length $name; 1265 $e xifTool->VPrint(0, "$$exifTool{INDENT}\[adding $name]\n") if $verbose;1266 Image::ExifTool::AddTagToTable($tagTablePtr, $tag, { Name => $name });1776 $et->VPrint(0, "$$et{INDENT}\[adding $name]\n") if $verbose; 1777 AddTagToTable($tagTablePtr, $tag, { Name => $name }); 1267 1778 } 1268 1779 next; … … 1274 1785 $custom = 1; 1275 1786 } 1276 my @vals = ReadFPXValue($e xifTool, $dataPt, $valPos, $type, $dirEnd, undef, $codePage);1277 @vals or $e xifTool->Warn('Error reading property value');1787 my @vals = ReadFPXValue($et, $dataPt, $valPos, $type, $dirEnd, undef, $codePage); 1788 @vals or $et->Warn('Error reading property value'); 1278 1789 $val = @vals > 1 ? \@vals : $vals[0]; 1279 1790 my $format = $type & 0x0fff; … … 1287 1798 # get tagInfo from SummaryInfo table 1288 1799 my $summaryTable = GetTagTable('Image::ExifTool::FlashPix::SummaryInfo'); 1289 $tagInfo = $e xifTool->GetTagInfo($summaryTable, $tag);1800 $tagInfo = $et->GetTagInfo($summaryTable, $tag); 1290 1801 if ($tag == 1) { 1291 1802 $val += 0x10000 if $val < 0; # (may be incorrectly stored as int16s) … … 1293 1804 } 1294 1805 } elsif ($$tagTablePtr{$tag}) { 1295 $tagInfo = $e xifTool->GetTagInfo($tagTablePtr, $tag);1806 $tagInfo = $et->GetTagInfo($tagTablePtr, $tag); 1296 1807 } elsif ($$tagTablePtr{VARS} and not $custom) { 1297 1808 # mask off insignificant bits of tag ID if necessary … … 1300 1811 foreach $mask (keys %$masked) { 1301 1812 if ($masked->{$mask}->{$tag & $mask}) { 1302 $tagInfo = $e xifTool->GetTagInfo($tagTablePtr, $tag & $mask);1813 $tagInfo = $et->GetTagInfo($tagTablePtr, $tag & $mask); 1303 1814 last; 1304 1815 } 1305 1816 } 1306 1817 } 1307 $e xifTool->HandleTag($tagTablePtr, $tag, $val,1818 $et->HandleTag($tagTablePtr, $tag, $val, 1308 1819 DataPt => $dataPt, 1309 1820 Start => $valStart, … … 1316 1827 } 1317 1828 # issue warning if we hit end of property section prematurely 1318 $e xifTool->Warn('Truncated property data') if $index < $numEntries;1829 $et->Warn('Truncated property data') if $index < $numEntries; 1319 1830 last unless $$dirInfo{Multi}; 1320 1831 $pos += $size; … … 1355 1866 sub ProcessFPXR($$$) 1356 1867 { 1357 my ($e xifTool, $dirInfo, $tagTablePtr) = @_;1868 my ($et, $dirInfo, $tagTablePtr) = @_; 1358 1869 my $dataPt = $$dirInfo{DataPt}; 1359 1870 my $dirStart = $$dirInfo{DirStart}; 1360 1871 my $dirLen = $$dirInfo{DirLen}; 1361 my $verbose = $e xifTool->Options('Verbose');1872 my $verbose = $et->Options('Verbose'); 1362 1873 1363 1874 if ($dirLen < 13) { 1364 $e xifTool->Warn('FPXR segment to small');1875 $et->Warn('FPXR segment too small'); 1365 1876 return 0; 1366 1877 } … … 1371 1882 if ($type == 1) { # a "Contents List" segment 1372 1883 1373 $vers != 0 and $e xifTool->Warn("Untested FPXR version $vers");1374 if ($$e xifTool{FPXR}) {1375 $e xifTool->Warn('Multiple FPXR contents lists');1376 delete $$e xifTool{FPXR};1884 $vers != 0 and $et->Warn("Untested FPXR version $vers"); 1885 if ($$et{FPXR}) { 1886 $et->Warn('Multiple FPXR contents lists'); 1887 delete $$et{FPXR}; 1377 1888 } 1378 1889 my $numEntries = unpack('x7n', $$dataPt); 1379 1890 my @contents; 1380 $verbose and $e xifTool->VerboseDir('Contents List', $numEntries);1891 $verbose and $et->VerboseDir('Contents List', $numEntries); 1381 1892 my $pos = 9; 1382 1893 my $entry; 1383 1894 for ($entry = 0; $entry < $numEntries; ++$entry) { 1384 1895 if ($pos + 4 > $dirLen) { 1385 $e xifTool->Warn('Truncated FPXR contents');1896 $et->Warn('Truncated FPXR contents'); 1386 1897 return 0; 1387 1898 } … … 1392 1903 # and the first char must be '/' 1393 1904 unless ($$dataPt =~ m{\G(/\0(..)*?)\0\0}sg) { 1394 $e xifTool->Warn('Invalid FPXR stream name');1905 $et->Warn('Invalid FPXR stream name'); 1395 1906 return 0; 1396 1907 } … … 1399 1910 if ($verbose) { 1400 1911 my $psize = ($size == 0xffffffff) ? 'storage' : "$size bytes"; 1401 $e xifTool->VPrint(0," | $entry) Name: '$name' [$psize]\n");1912 $et->VPrint(0," | $entry) Name: '${name}' [$psize]\n"); 1402 1913 } 1403 1914 # remove directory specification … … 1407 1918 if ($size == 0xffffffff) { 1408 1919 unless ($$dataPt =~ m{(.{16})}sg) { 1409 $e xifTool->Warn('Truncated FPXR storage class ID');1920 $et->Warn('Truncated FPXR storage class ID'); 1410 1921 return 0; 1411 1922 } 1412 1923 # unpack class ID in case we want to use it sometime 1413 1924 $classID = Image::ExifTool::ASF::GetGUID($1); 1925 } 1926 # find the tagInfo if available 1927 my $tagInfo; 1928 unless ($$tagTablePtr{$name}) { 1929 # remove instance number or class ID from tag if necessary 1930 $tagInfo = $et->GetTagInfo($tagTablePtr, $1) if 1931 ($name =~ /(.*) \d{6}$/s and $$tagTablePtr{$1}) or 1932 ($name =~ /(.*)_[0-9a-f]{16}$/s and $$tagTablePtr{$1}); 1414 1933 } 1415 1934 # update position in list … … 1421 1940 Default => $default, 1422 1941 ClassID => $classID, 1942 TagInfo => $tagInfo, 1423 1943 }; 1424 1944 } 1425 # save contents list as $e xifToolmember variable1945 # save contents list as $et member variable 1426 1946 # (must do this last so we don't save list on error) 1427 $$e xifTool{FPXR} = \@contents;1947 $$et{FPXR} = \@contents; 1428 1948 1429 1949 } elsif ($type == 2) { # a "Stream Data" segment … … 1431 1951 # get the contents list index and stream data offset 1432 1952 my ($index, $offset) = unpack('x7nN', $$dataPt); 1433 my $fpxr = $$e xifTool{FPXR};1953 my $fpxr = $$et{FPXR}; 1434 1954 if ($fpxr and $$fpxr[$index]) { 1435 1955 my $obj = $$fpxr[$index]; … … 1440 1960 $$obj{Stream} = substr($$dataPt, $dirStart+13); 1441 1961 } else { 1442 # add data to the stream at the proper offset 1443 my $pad = $offset - length($$obj{Stream}); 1444 if ($pad >= 0) { 1445 if ($pad) { 1446 if ($pad > 0x10000) { 1447 $exifTool->Warn("Bad FPXR stream offset ($offset)"); 1448 } else { 1449 # pad with default value to specified offset 1450 $exifTool->Warn("Padding FPXR stream with $pad default bytes",1); 1451 $$obj{Stream} .= ($$obj{Default} x $pad); 1452 } 1453 } 1454 # concatenate data with this stream 1455 $$obj{Stream} .= substr($$dataPt, $dirStart+13); 1962 # add data at the proper offset to the stream 1963 my $overlap = length($$obj{Stream}) - $offset; 1964 my $start = $dirStart + 13; 1965 if ($overlap < 0 or $dirLen - $overlap < 13) { 1966 $et->WarnOnce("Bad FPXR stream $index offset",1); 1456 1967 } else { 1457 $exifTool->Warn("Duplicate FPXR stream data at offset $offset"); 1458 substr($$obj{Stream}, $offset, -$pad) = substr($$dataPt, $dirStart+13); 1968 # ignore any overlapping data in this segment 1969 # (this seems to be the convention) 1970 $start += $overlap; 1459 1971 } 1972 # concatenate data with this stream 1973 $$obj{Stream} .= substr($$dataPt, $start); 1460 1974 } 1461 1975 # save value for this tag if stream is complete 1462 1976 my $len = length $$obj{Stream}; 1463 1977 if ($len >= $$obj{Size}) { 1464 if ($verbose) { 1465 $exifTool->VPrint(0, " + [FPXR stream, Contents index $index, $len bytes]\n"); 1466 } 1978 $et->VPrint(0, " + [FPXR stream $index, $len bytes]\n") if $verbose; 1467 1979 if ($len > $$obj{Size}) { 1468 $e xifTool->Warn('Extra data in FPXR segment (truncated)');1980 $et->Warn('Extra data in FPXR segment (truncated)'); 1469 1981 $$obj{Stream} = substr($$obj{Stream}, 0, $$obj{Size}); 1470 1982 } 1471 my $tag = $$obj{Name}; 1472 my $tagInfo; 1473 unless ($$tagTablePtr{$tag}) { 1474 # remove instance number or class ID from tag if necessary 1475 $tagInfo = $exifTool->GetTagInfo($tagTablePtr, $1) if 1476 ($tag =~ /(.*) \d{6}$/s and $$tagTablePtr{$1}) or 1477 ($tag =~ /(.*)_[0-9a-f]{16}$/s and $$tagTablePtr{$1}); 1478 } 1479 # save the data for this tag 1480 $exifTool->HandleTag($tagTablePtr, $tag, $$obj{Stream}, 1983 # handle this tag 1984 $et->HandleTag($tagTablePtr, $$obj{Name}, $$obj{Stream}, 1481 1985 DataPt => \$$obj{Stream}, 1482 TagInfo => $ tagInfo,1986 TagInfo => $$obj{TagInfo}, 1483 1987 ); 1484 1988 delete $$obj{Stream}; # done with this stream 1485 1989 } 1486 1990 # hack for improperly stored FujiFilm PreviewImage (stored with no contents list) 1487 } elsif ($index == 512 and $dirLen > 60 and ($$e xifTool{FujiPreview} or1991 } elsif ($index == 512 and $dirLen > 60 and ($$et{FujiPreview} or 1488 1992 ($dirLen > 64 and substr($$dataPt, $dirStart+60, 4) eq "\xff\xd8\xff\xdb"))) 1489 1993 { 1490 # recombine PreviewImage, skipping unknown 60 byte header 1491 if ($$exifTool{FujiPreview}) { 1492 $$exifTool{FujiPreview} .= substr($$dataPt, $dirStart+60); 1493 } else { 1494 $$exifTool{FujiPreview} = substr($$dataPt, $dirStart+60); 1495 } 1994 $$et{FujiPreview} = '' unless defined $$et{FujiPreview}; 1995 # recombine PreviewImage, skipping 13-byte FPXR header + 47-byte Fuji header 1996 $$et{FujiPreview} .= substr($$dataPt, $dirStart+60); 1496 1997 } else { 1497 1998 # (Kodak uses index 255 for a free segment in images from some cameras) 1498 $e xifTool->Warn("Unlisted FPXR segment (index $index)") if $index != 255;1499 } 1500 1501 } elsif ($type ne3) { # not a "Reserved" segment1502 1503 $e xifTool->Warn("Unknown FPXR segment (type $type)");1999 $et->Warn("Unlisted FPXR segment (index $index)") if $index != 255; 2000 } 2001 2002 } elsif ($type != 3) { # not a "Reserved" segment 2003 2004 $et->Warn("Unknown FPXR segment (type $type)"); 1504 2005 1505 2006 } … … 1507 2008 # clean up if this was the last FPXR segment 1508 2009 if ($$dirInfo{LastFPXR}) { 1509 if ($$e xifTool{FPXR}) {2010 if ($$et{FPXR}) { 1510 2011 my $obj; 1511 my $i = 0; 1512 foreach $obj (@{$$exifTool{FPXR}}) { 1513 $exifTool->Warn("Missing stream for FPXR object $i") if defined $$obj{Stream}; 1514 ++$i; 2012 foreach $obj (@{$$et{FPXR}}) { 2013 next unless defined $$obj{Stream} and length $$obj{Stream}; 2014 # parse it even though it isn't the proper length 2015 $et->HandleTag($tagTablePtr, $$obj{Name}, $$obj{Stream}, 2016 DataPt => \$$obj{Stream}, 2017 TagInfo => $$obj{TagInfo}, 2018 ); 1515 2019 } 1516 delete $$e xifTool{FPXR}; # delete our temporary variables1517 } 1518 if ($$e xifTool{FujiPreview}) {1519 $e xifTool->FoundTag('PreviewImage', $$exifTool{FujiPreview});1520 delete $$e xifTool{FujiPreview};2020 delete $$et{FPXR}; # delete our temporary variables 2021 } 2022 if ($$et{FujiPreview}) { 2023 $et->FoundTag('PreviewImage', $$et{FujiPreview}); 2024 delete $$et{FujiPreview}; 1521 2025 } 1522 2026 } … … 1549 2053 $subDoc[-1] = ++$$used[$#subDoc]; 1550 2054 } 1551 SetDocNum($hier, $$obj{Child}, \@subDoc, $used, not $meta) 2055 SetDocNum($hier, $$obj{Child}, \@subDoc, $used, not $meta); 1552 2056 } 1553 2057 } … … 1559 2063 sub ProcessFPX($$) 1560 2064 { 1561 my ($e xifTool, $dirInfo) = @_;2065 my ($et, $dirInfo) = @_; 1562 2066 my $raf = $$dirInfo{RAF}; 1563 my ($buff, $out, %dumpParms,$oldIndent, $miniStreamBuff);1564 my ($tag, %hier, %objIndex );2067 my ($buff, $out, $oldIndent, $miniStreamBuff); 2068 my ($tag, %hier, %objIndex, %loadedDifSect); 1565 2069 1566 2070 # read header … … 1570 2074 1571 2075 # set FileType initially based on file extension (we may override this later) 1572 my $fileType = $ exifTool->{FILE_EXT};2076 my $fileType = $$et{FILE_EXT}; 1573 2077 $fileType = 'FPX' unless $fileType and $fpxFileType{$fileType}; 1574 $e xifTool->SetFileType($fileType);2078 $et->SetFileType($fileType); 1575 2079 SetByteOrder(substr($buff, 0x1c, 2) eq "\xff\xfe" ? 'MM' : 'II'); 1576 2080 my $tagTablePtr = GetTagTable('Image::ExifTool::FlashPix::Main'); 1577 my $verbose = $e xifTool->Options('Verbose');2081 my $verbose = $et->Options('Verbose'); 1578 2082 # copy LargeFileSupport option to RAF for use in LoadChain 1579 $$raf{LargeFileSupport} = $e xifTool->Options('LargeFileSupport');2083 $$raf{LargeFileSupport} = $et->Options('LargeFileSupport'); 1580 2084 1581 2085 my $sectSize = 1 << Get16u(\$buff, 0x1e); … … 1590 2094 1591 2095 if ($verbose) { 1592 $out = $exifTool->Options('TextOut'); 1593 $dumpParms{Out} = $out; 1594 $dumpParms{MaxLen} = 96 if $verbose == 3; 2096 $out = $et->Options('TextOut'); 1595 2097 print $out " Sector size=$sectSize\n FAT: Count=$fatCount\n"; 1596 2098 print $out " DIR: Start=$dirStart\n"; … … 1605 2107 my $fat = ''; 1606 2108 my $fatCountCheck = 0; 2109 my $difCountCheck = 0; 2110 my $hdrSize = $sectSize > HDR_SIZE ? $sectSize : HDR_SIZE; 2111 1607 2112 for (;;) { 1608 2113 while ($pos <= $endPos - 4) { … … 1610 2115 $pos += 4; 1611 2116 next if $sect == FREE_SECT; 1612 my $offset = $sect * $sectSize + HDR_SIZE;2117 my $offset = $sect * $sectSize + $hdrSize; 1613 2118 my $fatSect; 1614 2119 unless ($raf->Seek($offset, 0) and 1615 2120 $raf->Read($fatSect, $sectSize) == $sectSize) 1616 2121 { 1617 $e xifTool->Error("Error reading FAT from sector $sect");2122 $et->Error("Error reading FAT from sector $sect"); 1618 2123 return 1; 1619 2124 } … … 1623 2128 last if $difStart >= END_OF_CHAIN; 1624 2129 # read next DIF (Dual Indirect FAT) sector 1625 my $offset = $difStart * $sectSize + HDR_SIZE; 2130 if (++$difCountCheck > $difCount) { 2131 $et->Warn('Unterminated DIF FAT'); 2132 last; 2133 } 2134 if ($loadedDifSect{$difStart}) { 2135 $et->Warn('Cyclical reference in DIF FAT'); 2136 last; 2137 } 2138 my $offset = $difStart * $sectSize + $hdrSize; 1626 2139 unless ($raf->Seek($offset, 0) and $raf->Read($buff, $sectSize) == $sectSize) { 1627 $e xifTool->Error("Error reading DIF sector $difStart");2140 $et->Error("Error reading DIF sector $difStart"); 1628 2141 return 1; 1629 2142 } 2143 $loadedDifSect{$difStart} = 1; 1630 2144 # set end of sector information in this DIF 1631 2145 $pos = 0; … … 1635 2149 } 1636 2150 if ($fatCountCheck != $fatCount) { 1637 $e xifTool->Warn("Bad number of FAT sectors (expected $fatCount but found $fatCountCheck)");2151 $et->Warn("Bad number of FAT sectors (expected $fatCount but found $fatCountCheck)"); 1638 2152 } 1639 2153 # 1640 2154 # load the mini-FAT and the directory 1641 2155 # 1642 my $miniFat = LoadChain($raf, $miniStart, \$fat, $sectSize, HDR_SIZE);1643 my $dir = LoadChain($raf, $dirStart, \$fat, $sectSize, HDR_SIZE);2156 my $miniFat = LoadChain($raf, $miniStart, \$fat, $sectSize, $hdrSize); 2157 my $dir = LoadChain($raf, $dirStart, \$fat, $sectSize, $hdrSize); 1644 2158 unless (defined $miniFat and defined $dir) { 1645 $e xifTool->Error('Error reading mini-FAT or directory stream');2159 $et->Error('Error reading mini-FAT or directory stream'); 1646 2160 return 1; 1647 2161 } 1648 2162 if ($verbose) { 1649 2163 print $out " FAT [",length($fat)," bytes]:\n"; 1650 Image::ExifTool::HexDump(\$fat, undef, %dumpParms) if $verbose > 2;2164 $et->VerboseDump(\$fat); 1651 2165 print $out " Mini-FAT [",length($miniFat)," bytes]:\n"; 1652 Image::ExifTool::HexDump(\$miniFat, undef, %dumpParms) if $verbose > 2;2166 $et->VerboseDump(\$miniFat); 1653 2167 print $out " Directory [",length($dir)," bytes]:\n"; 1654 Image::ExifTool::HexDump(\$dir, undef, %dumpParms) if $verbose > 2;2168 $et->VerboseDump(\$dir); 1655 2169 } 1656 2170 # … … 1658 2172 # 1659 2173 if ($verbose) { 1660 $oldIndent = $ exifTool->{INDENT};1661 $ exifTool->{INDENT} .= '| ';1662 $e xifTool->VerboseDir('FPX', undef, length $dir);2174 $oldIndent = $$et{INDENT}; 2175 $$et{INDENT} .= '| '; 2176 $et->VerboseDir('FPX', undef, length $dir); 1663 2177 } 1664 2178 my $miniStream; … … 1673 2187 next if $type == 0; # skip invalid entries 1674 2188 if ($type > 5) { 1675 $e xifTool->Warn("Invalid directory entry type $type");2189 $et->Warn("Invalid directory entry type $type"); 1676 2190 last; # rest of directory is probably garbage 1677 2191 } … … 1689 2203 # load Ministream (referenced from first directory entry) 1690 2204 unless ($miniStream) { 1691 $miniStreamBuff = LoadChain($raf, $sect, \$fat, $sectSize, HDR_SIZE);2205 $miniStreamBuff = LoadChain($raf, $sect, \$fat, $sectSize, $hdrSize); 1692 2206 unless (defined $miniStreamBuff) { 1693 $e xifTool->Warn('Error loading Mini-FAT stream');2207 $et->Warn('Error loading Mini-FAT stream'); 1694 2208 last; 1695 2209 } … … 1699 2213 my $tagInfo; 1700 2214 if ($$tagTablePtr{$tag}) { 1701 $tagInfo = $e xifTool->GetTagInfo($tagTablePtr, $tag);2215 $tagInfo = $et->GetTagInfo($tagTablePtr, $tag); 1702 2216 } else { 1703 2217 # remove instance number or class ID from tag if necessary 1704 $tagInfo = $e xifTool->GetTagInfo($tagTablePtr, $1) if2218 $tagInfo = $et->GetTagInfo($tagTablePtr, $1) if 1705 2219 ($tag =~ /(.*) \d{6}$/s and $$tagTablePtr{$1}) or 1706 2220 ($tag =~ /(.*)_[0-9a-f]{16}$/s and $$tagTablePtr{$1}); … … 1711 2225 my $chld = Get32u(\$dir, $pos + 0x4c); # child directory 1712 2226 1713 # save information about object hiera chy2227 # save information about object hierarchy 1714 2228 my ($obj, $sub); 1715 2229 $obj = $hier{$index} or $obj = $hier{$index} = { }; … … 1730 2244 if ($size >= $miniCutoff) { 1731 2245 # stream is in the main FAT 1732 $buff = LoadChain($raf, $sect, \$fat, $sectSize, HDR_SIZE);2246 $buff = LoadChain($raf, $sect, \$fat, $sectSize, $hdrSize); 1733 2247 } elsif ($size) { 1734 2248 # stream is in the mini-FAT … … 1739 2253 unless (defined $buff) { 1740 2254 my $name = $tagInfo ? $$tagInfo{Name} : 'unknown'; 1741 $e xifTool->Warn("Error reading $name stream");2255 $et->Warn("Error reading $name stream"); 1742 2256 $buff = ''; 1743 2257 } … … 1756 2270 $extra .= " Right=$rSib" unless $rSib == FREE_SECT; 1757 2271 $extra .= " Child=$chld" unless $chld == FREE_SECT; 1758 $e xifTool->VerboseInfo($tag, $tagInfo,2272 $et->VerboseInfo($tag, $tagInfo, 1759 2273 Index => $index, 1760 2274 Value => $buff, … … 1765 2279 } 1766 2280 if ($tagInfo and $buff) { 1767 my $num = $$e xifTool{NUM_FOUND};2281 my $num = $$et{NUM_FOUND}; 1768 2282 my $subdir = $$tagInfo{SubDirectory}; 1769 2283 if ($subdir) { … … 1775 2289 ); 1776 2290 my $subTablePtr = GetTagTable($$subdir{TagTable}); 1777 $e xifTool->ProcessDirectory(\%dirInfo, $subTablePtr, $$subdir{ProcessProc});2291 $et->ProcessDirectory(\%dirInfo, $subTablePtr, $$subdir{ProcessProc}); 1778 2292 } else { 1779 $e xifTool->FoundTag($tagInfo, $buff);2293 $et->FoundTag($tagInfo, $buff); 1780 2294 } 1781 2295 # save object index number for all found tags 1782 my $num2 = $$e xifTool{NUM_FOUND};2296 my $num2 = $$et{NUM_FOUND}; 1783 2297 $objIndex{++$num} = $index while $num < $num2; 1784 2298 } 1785 2299 } 1786 2300 # set document numbers for tags extracted from embedded documents 1787 unless ($$e xifTool{DOC_NUM}) {2301 unless ($$et{DOC_NUM}) { 1788 2302 # initialize document number for all objects, beginning at root (index 0) 1789 2303 SetDocNum(\%hier, 0); 1790 2304 # set family 3 group name for all tags in embedded documents 1791 my $order = $$e xifTool{FILE_ORDER};2305 my $order = $$et{FILE_ORDER}; 1792 2306 my (@pri, $copy, $member); 1793 2307 foreach $tag (keys %$order) { … … 1797 2311 my $docNums = $$obj{DocNum}; 1798 2312 next unless $docNums and @$docNums; 1799 $$e xifTool{TAG_EXTRA}{$tag}{G3} = join '-', @$docNums;2313 $$et{TAG_EXTRA}{$tag}{G3} = join '-', @$docNums; 1800 2314 push @pri, $tag unless $tag =~ / /; # save keys for priority sub-doc tags 1801 2315 } … … 1804 2318 for ($copy=1; ;++$copy) { 1805 2319 my $key = "$tag ($copy)"; 1806 last unless defined $$e xifTool{VALUE}{$key};1807 my $extra = $$e xifTool{TAG_EXTRA}{$key};2320 last unless defined $$et{VALUE}{$key}; 2321 my $extra = $$et{TAG_EXTRA}{$key}; 1808 2322 next if $extra and $$extra{G3}; # not Main if family 3 group is set 1809 2323 foreach $member ('PRIORITY','VALUE','FILE_ORDER','TAG_INFO','TAG_EXTRA') { 1810 my $pHash = $$e xifTool{$member};2324 my $pHash = $$et{$member}; 1811 2325 my $t = $$pHash{$tag}; 1812 2326 $$pHash{$tag} = $$pHash{$key}; … … 1817 2331 } 1818 2332 } 1819 $ exifTool->{INDENT} = $oldIndent if $verbose;2333 $$et{INDENT} = $oldIndent if $verbose; 1820 2334 # try to better identify the file type 1821 if ($$e xifTool{VALUE}{FileType} eq 'FPX') {1822 my $val = $$e xifTool{CompObjUserType} || $$exifTool{Software};2335 if ($$et{VALUE}{FileType} eq 'FPX') { 2336 my $val = $$et{CompObjUserType} || $$et{Software}; 1823 2337 if ($val) { 1824 my %type = ( Word => 'DOC', PowerPoint => 'PPT', Excel => 'XLS' );2338 my %type = ( '^3ds Max' => 'MAX', Word => 'DOC', PowerPoint => 'PPT', Excel => 'XLS' ); 1825 2339 my $pat; 1826 2340 foreach $pat (sort keys %type) { 1827 2341 next unless $val =~ /$pat/; 1828 $e xifTool->OverrideFileType($type{$pat});2342 $et->OverrideFileType($type{$pat}); 1829 2343 last; 1830 2344 } 1831 2345 } 1832 2346 } 2347 # process Word document table 2348 ProcessDocumentTable($et); 2349 1833 2350 return 1; 1834 2351 } … … 1854 2371 =head1 AUTHOR 1855 2372 1856 Copyright 2003-20 11, Phil Harvey (phil at owl.phy.queensu.ca)2373 Copyright 2003-2021, Phil Harvey (philharvey66 at gmail.com) 1857 2374 1858 2375 This library is free software; you can redistribute it and/or modify it
Note:
See TracChangeset
for help on using the changeset viewer.