ZIP.pm 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667
  1. #------------------------------------------------------------------------------
  2. # File: ZIP.pm
  3. #
  4. # Description: Read ZIP archive meta information
  5. #
  6. # Revisions: 10/28/2007 - P. Harvey Created
  7. #
  8. # References: 1) http://www.pkware.com/documents/casestudies/APPNOTE.TXT
  9. # 2) http://www.cpanforum.com/threads/9046
  10. # 3) http://www.gzip.org/zlib/rfc-gzip.html
  11. # 4) http://DataCompression.info/ArchiveFormats/RAR202.txt
  12. # 5) https://jira.atlassian.com/browse/CONF-21706
  13. # 6) http://wwwimages.adobe.com/www.adobe.com/content/dam/Adobe/en/devnet/indesign/cs55-docs/IDML/idml-specification.pdf
  14. #------------------------------------------------------------------------------
  15. package Image::ExifTool::ZIP;
  16. use strict;
  17. use vars qw($VERSION $warnString);
  18. use Image::ExifTool qw(:DataAccess :Utils);
  19. $VERSION = '1.18';
  20. sub WarnProc($) { $warnString = $_[0]; }
  21. # file types for recognized Open Document "mimetype" values
  22. my %openDocType = (
  23. 'application/vnd.oasis.opendocument.database' => 'ODB', #5
  24. 'application/vnd.oasis.opendocument.chart' => 'ODC', #5
  25. 'application/vnd.oasis.opendocument.formula' => 'ODF', #5
  26. 'application/vnd.oasis.opendocument.graphics' => 'ODG', #5
  27. 'application/vnd.oasis.opendocument.image' => 'ODI', #5
  28. 'application/vnd.oasis.opendocument.presentation' => 'ODP',
  29. 'application/vnd.oasis.opendocument.spreadsheet' => 'ODS',
  30. 'application/vnd.oasis.opendocument.text' => 'ODT',
  31. 'application/vnd.adobe.indesign-idml-package' => 'IDML', #6 (not open doc)
  32. 'application/epub+zip' => 'EPUB', #PH (not open doc)
  33. );
  34. # ZIP metadata blocks
  35. %Image::ExifTool::ZIP::Main = (
  36. PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
  37. GROUPS => { 2 => 'Other' },
  38. FORMAT => 'int16u',
  39. NOTES => q{
  40. The following tags are extracted from ZIP archives. ExifTool also extracts
  41. additional meta information from compressed documents inside some ZIP-based
  42. files such Office Open XML (DOCX, PPTX and XLSX), Open Document (ODB, ODC,
  43. ODF, ODG, ODI, ODP, ODS and ODT), iWork (KEY, PAGES, NUMBERS), Capture One
  44. Enhanced Image Package (EIP), Adobe InDesign Markup Language (IDML), and
  45. Electronic Publication (EPUB). The ExifTool family 3 groups may be used to
  46. organize ZIP tags by embedded document number (ie. the exiftool C<-g3>
  47. option).
  48. },
  49. 2 => 'ZipRequiredVersion',
  50. 3 => {
  51. Name => 'ZipBitFlag',
  52. PrintConv => '$val ? sprintf("0x%.4x",$val) : $val',
  53. },
  54. 4 => {
  55. Name => 'ZipCompression',
  56. PrintConv => {
  57. 0 => 'None',
  58. 1 => 'Shrunk',
  59. 2 => 'Reduced with compression factor 1',
  60. 3 => 'Reduced with compression factor 2',
  61. 4 => 'Reduced with compression factor 3',
  62. 5 => 'Reduced with compression factor 4',
  63. 6 => 'Imploded',
  64. 7 => 'Tokenized',
  65. 8 => 'Deflated',
  66. 9 => 'Enhanced Deflate using Deflate64(tm)',
  67. 10 => 'Imploded (old IBM TERSE)',
  68. 12 => 'BZIP2',
  69. 14 => 'LZMA (EFS)',
  70. 18 => 'IBM TERSE (new)',
  71. 19 => 'IBM LZ77 z Architecture (PFS)',
  72. 96 => 'JPEG recompressed', #2
  73. 97 => 'WavPack compressed', #2
  74. 98 => 'PPMd version I, Rev 1',
  75. },
  76. },
  77. 5 => {
  78. Name => 'ZipModifyDate',
  79. Format => 'int32u',
  80. Groups => { 2 => 'Time' },
  81. ValueConv => sub {
  82. my $val = shift;
  83. return sprintf('%.4d:%.2d:%.2d %.2d:%.2d:%.2d',
  84. ($val >> 25) + 1980, # year
  85. ($val >> 21) & 0x0f, # month
  86. ($val >> 16) & 0x1f, # day
  87. ($val >> 11) & 0x1f, # hour
  88. ($val >> 5) & 0x3f, # minute
  89. $val & 0x1f # second
  90. );
  91. },
  92. PrintConv => '$self->ConvertDateTime($val)',
  93. },
  94. 7 => { Name => 'ZipCRC', Format => 'int32u', PrintConv => 'sprintf("0x%.8x",$val)' },
  95. 9 => { Name => 'ZipCompressedSize', Format => 'int32u' },
  96. 11 => { Name => 'ZipUncompressedSize', Format => 'int32u' },
  97. 13 => {
  98. Name => 'ZipFileNameLength',
  99. # don't store a tag -- just extract the value for use with ZipFileName
  100. Hidden => 1,
  101. RawConv => '$$self{ZipFileNameLength} = $val; undef',
  102. },
  103. # 14 => 'ZipExtraFieldLength',
  104. 15 => {
  105. Name => 'ZipFileName',
  106. Format => 'string[$$self{ZipFileNameLength}]',
  107. },
  108. );
  109. # GNU ZIP tags (ref 3)
  110. %Image::ExifTool::ZIP::GZIP = (
  111. PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
  112. GROUPS => { 2 => 'Other' },
  113. NOTES => q{
  114. These tags are extracted from GZIP (GNU ZIP) archives, but currently only
  115. for the first file in the archive.
  116. },
  117. 2 => {
  118. Name => 'Compression',
  119. PrintConv => {
  120. 8 => 'Deflated',
  121. },
  122. },
  123. 3 => {
  124. Name => 'Flags',
  125. PrintConv => { BITMASK => {
  126. 0 => 'Text',
  127. 1 => 'CRC16',
  128. 2 => 'ExtraFields',
  129. 3 => 'FileName',
  130. 4 => 'Comment',
  131. }},
  132. },
  133. 4 => {
  134. Name => 'ModifyDate',
  135. Format => 'int32u',
  136. Groups => { 2 => 'Time' },
  137. ValueConv => 'ConvertUnixTime($val,1)',
  138. PrintConv => '$self->ConvertDateTime($val)',
  139. },
  140. 8 => {
  141. Name => 'ExtraFlags',
  142. PrintConv => {
  143. 0 => '(none)',
  144. 2 => 'Maximum Compression',
  145. 4 => 'Fastest Algorithm',
  146. },
  147. },
  148. 9 => {
  149. Name => 'OperatingSystem',
  150. PrintConv => {
  151. 0 => 'FAT filesystem (MS-DOS, OS/2, NT/Win32)',
  152. 1 => 'Amiga',
  153. 2 => 'VMS (or OpenVMS)',
  154. 3 => 'Unix',
  155. 4 => 'VM/CMS',
  156. 5 => 'Atari TOS',
  157. 6 => 'HPFS filesystem (OS/2, NT)',
  158. 7 => 'Macintosh',
  159. 8 => 'Z-System',
  160. 9 => 'CP/M',
  161. 10 => 'TOPS-20',
  162. 11 => 'NTFS filesystem (NT)',
  163. 12 => 'QDOS',
  164. 13 => 'Acorn RISCOS',
  165. 255 => 'unknown',
  166. },
  167. },
  168. 10 => 'ArchivedFileName',
  169. 11 => 'Comment',
  170. );
  171. # RAR tags (ref 4)
  172. %Image::ExifTool::ZIP::RAR = (
  173. PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
  174. GROUPS => { 2 => 'Other' },
  175. NOTES => 'These tags are extracted from RAR archive files.',
  176. 0 => {
  177. Name => 'CompressedSize',
  178. Format => 'int32u',
  179. },
  180. 4 => {
  181. Name => 'UncompressedSize',
  182. Format => 'int32u',
  183. },
  184. 8 => {
  185. Name => 'OperatingSystem',
  186. PrintConv => {
  187. 0 => 'MS-DOS',
  188. 1 => 'OS/2',
  189. 2 => 'Win32',
  190. 3 => 'Unix',
  191. },
  192. },
  193. 13 => {
  194. Name => 'ModifyDate',
  195. Format => 'int32u',
  196. Groups => { 2 => 'Time' },
  197. ValueConv => sub {
  198. my $val = shift;
  199. return sprintf('%.4d:%.2d:%.2d %.2d:%.2d:%.2d',
  200. ($val >> 25) + 1980, # year
  201. ($val >> 21) & 0x0f, # month
  202. ($val >> 16) & 0x1f, # day
  203. ($val >> 11) & 0x1f, # hour
  204. ($val >> 5) & 0x3f, # minute
  205. $val & 0x1f # second
  206. );
  207. },
  208. PrintConv => '$self->ConvertDateTime($val)',
  209. },
  210. 18 => {
  211. Name => 'PackingMethod',
  212. PrintHex => 1,
  213. PrintConv => {
  214. 0x30 => 'Stored',
  215. 0x31 => 'Fastest',
  216. 0x32 => 'Fast',
  217. 0x33 => 'Normal',
  218. 0x34 => 'Good Compression',
  219. 0x35 => 'Best Compression',
  220. },
  221. },
  222. 19 => {
  223. Name => 'FileNameLength',
  224. Format => 'int16u',
  225. Hidden => 1,
  226. RawConv => '$$self{FileNameLength} = $val; undef',
  227. },
  228. 25 => {
  229. Name => 'ArchivedFileName',
  230. Format => 'string[$$self{FileNameLength}]',
  231. },
  232. );
  233. #------------------------------------------------------------------------------
  234. # Extract information from a RAR file (ref 4)
  235. # Inputs: 0) ExifTool object reference, 1) dirInfo reference
  236. # Returns: 1 on success, 0 if this wasn't a valid RAR file
  237. sub ProcessRAR($$)
  238. {
  239. my ($et, $dirInfo) = @_;
  240. my $raf = $$dirInfo{RAF};
  241. my ($flags, $buff);
  242. return 0 unless $raf->Read($buff, 7) and $buff eq "Rar!\x1a\x07\0";
  243. $et->SetFileType();
  244. SetByteOrder('II');
  245. my $tagTablePtr = GetTagTable('Image::ExifTool::ZIP::RAR');
  246. my $docNum = 0;
  247. for (;;) {
  248. # read block header
  249. $raf->Read($buff, 7) == 7 or last;
  250. my ($type, $flags, $size) = unpack('xxCvv', $buff);
  251. $size -= 7;
  252. if ($flags & 0x8000) {
  253. $raf->Read($buff, 4) == 4 or last;
  254. $size += unpack('V',$buff) - 4;
  255. }
  256. last if $size < 0;
  257. next unless $size; # ignore blocks with no data
  258. # don't try to read very large blocks unless LargeFileSupport is enabled
  259. if ($size > 0x80000000 and not $et->Options('LargeFileSupport')) {
  260. $et->Warn('Large block encountered. Aborting.');
  261. last;
  262. }
  263. # process the block
  264. if ($type == 0x74) { # file block
  265. # read maximum 4 KB from a file block
  266. my $n = $size > 4096 ? 4096 : $size;
  267. $raf->Read($buff, $n) == $n or last;
  268. # add compressed size to start of data so we can extract it with the other tags
  269. $buff = pack('V',$size) . $buff;
  270. $$et{DOC_NUM} = ++$docNum;
  271. $et->ProcessDirectory({ DataPt => \$buff }, $tagTablePtr);
  272. $size -= $n;
  273. } elsif ($type == 0x75 and $size > 6) { # comment block
  274. $raf->Read($buff, $size) == $size or last;
  275. # save comment, only if "Stored" (this is untested)
  276. if (Get8u(\$buff, 3) == 0x30) {
  277. $et->FoundTag('Comment', substr($buff, 6));
  278. }
  279. next;
  280. }
  281. # seek to the start of the next block
  282. $raf->Seek($size, 1) or last if $size;
  283. }
  284. $$et{DOC_NUM} = 0;
  285. return 1;
  286. }
  287. #------------------------------------------------------------------------------
  288. # Extract information from a GNU ZIP file (ref 3)
  289. # Inputs: 0) ExifTool object reference, 1) dirInfo reference
  290. # Returns: 1 on success, 0 if this wasn't a valid GZIP file
  291. sub ProcessGZIP($$)
  292. {
  293. my ($et, $dirInfo) = @_;
  294. my $raf = $$dirInfo{RAF};
  295. my ($flags, $buff);
  296. return 0 unless $raf->Read($buff, 10) and $buff =~ /^\x1f\x8b\x08/;
  297. $et->SetFileType();
  298. SetByteOrder('II');
  299. my $tagTablePtr = GetTagTable('Image::ExifTool::ZIP::GZIP');
  300. $et->HandleTag($tagTablePtr, 2, Get8u(\$buff, 2));
  301. $et->HandleTag($tagTablePtr, 3, $flags = Get8u(\$buff, 3));
  302. $et->HandleTag($tagTablePtr, 4, Get32u(\$buff, 4));
  303. $et->HandleTag($tagTablePtr, 8, Get8u(\$buff, 8));
  304. $et->HandleTag($tagTablePtr, 9, Get8u(\$buff, 9));
  305. # extract file name and comment if they exist
  306. if ($flags & 0x18) {
  307. if ($flags & 0x04) {
  308. # skip extra field
  309. $raf->Read($buff, 2) == 2 or return 1;
  310. my $len = Get16u(\$buff, 0);
  311. $raf->Read($buff, $len) == $len or return 1;
  312. }
  313. $raf->Read($buff, 4096) or return 1;
  314. my $pos = 0;
  315. my $tagID;
  316. # loop for ArchivedFileName (10) and Comment (11) tags
  317. foreach $tagID (10, 11) {
  318. my $mask = $tagID == 10 ? 0x08 : 0x10;
  319. next unless $flags & $mask;
  320. my $end = $buff =~ /\0/g ? pos($buff) - 1 : length($buff);
  321. # (the doc specifies the string should be ISO 8859-1,
  322. # but in OS X it seems to be UTF-8, so don't translate
  323. # it because I could just as easily screw it up)
  324. my $str = substr($buff, $pos, $end - $pos);
  325. $et->HandleTag($tagTablePtr, $tagID, $str);
  326. last if $end >= length $buff;
  327. $pos = $end + 1;
  328. }
  329. }
  330. return 1;
  331. }
  332. #------------------------------------------------------------------------------
  333. # Call HandleTags for attributes of an Archive::Zip member
  334. # Inputs: 0) ExifTool object ref, 1) member ref, 2) optional tag table ref
  335. sub HandleMember($$;$)
  336. {
  337. my ($et, $member, $tagTablePtr) = @_;
  338. $tagTablePtr or $tagTablePtr = GetTagTable('Image::ExifTool::ZIP::Main');
  339. $et->HandleTag($tagTablePtr, 2, $member->versionNeededToExtract());
  340. $et->HandleTag($tagTablePtr, 3, $member->bitFlag());
  341. $et->HandleTag($tagTablePtr, 4, $member->compressionMethod());
  342. $et->HandleTag($tagTablePtr, 5, $member->lastModFileDateTime());
  343. $et->HandleTag($tagTablePtr, 7, $member->crc32());
  344. $et->HandleTag($tagTablePtr, 9, $member->compressedSize());
  345. $et->HandleTag($tagTablePtr, 11, $member->uncompressedSize());
  346. $et->HandleTag($tagTablePtr, 15, $member->fileName());
  347. }
  348. #------------------------------------------------------------------------------
  349. # Extract information from a ZIP file
  350. # Inputs: 0) ExifTool object reference, 1) dirInfo reference
  351. # Returns: 1 on success, 0 if this wasn't a valid ZIP file
  352. sub ProcessZIP($$)
  353. {
  354. my ($et, $dirInfo) = @_;
  355. my $raf = $$dirInfo{RAF};
  356. my ($buff, $buf2, $zip, $docNum);
  357. return 0 unless $raf->Read($buff, 30) and $buff =~ /^PK\x03\x04/;
  358. my $tagTablePtr = GetTagTable('Image::ExifTool::ZIP::Main');
  359. # use Archive::Zip if avilable
  360. for (;;) {
  361. unless (eval { require Archive::Zip } and eval { require IO::File }) {
  362. if ($$et{FILE_EXT} and $$et{FILE_EXT} ne 'ZIP') {
  363. $et->Warn("Install Archive::Zip to decode compressed ZIP information");
  364. }
  365. last;
  366. }
  367. # Archive::Zip requires a seekable IO::File object
  368. my $fh;
  369. if ($raf->{TESTED} >= 0) {
  370. unless (eval { require IO::File }) {
  371. # (this shouldn't happen because IO::File is a prerequisite of Archive::Zip)
  372. $et->Warn("Install IO::File to decode compressed ZIP information");
  373. last;
  374. }
  375. $raf->Seek(0,0);
  376. $fh = $raf->{FILE_PT};
  377. bless $fh, 'IO::File'; # Archive::Zip expects an IO::File object
  378. } elsif (eval { require IO::String }) {
  379. # read the whole file into memory (what else can I do?)
  380. $raf->Slurp();
  381. $fh = new IO::String ${$raf->{BUFF_PT}};
  382. } else {
  383. my $type = $raf->{FILE_PT} ? 'pipe or socket' : 'scalar reference';
  384. $et->Warn("Install IO::String to decode compressed ZIP information from a $type");
  385. last;
  386. }
  387. $et->VPrint(1, " --- using Archive::Zip ---\n");
  388. $zip = new Archive::Zip;
  389. # catch all warnings! (Archive::Zip is bad for this)
  390. local $SIG{'__WARN__'} = \&WarnProc;
  391. my $status = $zip->readFromFileHandle($fh);
  392. if ($status eq '4' and $raf->{TESTED} >= 0 and eval { require IO::String } and
  393. $raf->Seek(0,2) and $raf->Tell() < 100000000)
  394. {
  395. # try again, reading it ourself this time in an attempt to avoid
  396. # a failed test with Perl 5.6.2 GNU/Linux 2.6.32-5-686 i686-linux-64int-ld
  397. $raf->Seek(0,0);
  398. $raf->Slurp();
  399. $fh = new IO::String ${$raf->{BUFF_PT}};
  400. $zip = new Archive::Zip;
  401. $status = $zip->readFromFileHandle($fh);
  402. }
  403. if ($status) {
  404. undef $zip;
  405. my %err = ( 1=>'Stream end error', 3=>'Format error', 4=>'IO error' );
  406. my $err = $err{$status} || "Error $status";
  407. $et->Warn("$err reading ZIP file");
  408. last;
  409. }
  410. $$dirInfo{ZIP} = $zip;
  411. # check for an Office Open file (DOCX, etc)
  412. # --> read '[Content_Types].xml' to determine the file type
  413. my ($mime, @members, $epub);
  414. my $cType = $zip->memberNamed('[Content_Types].xml');
  415. if ($cType) {
  416. ($buff, $status) = $zip->contents($cType);
  417. if (not $status and $buff =~ /ContentType\s*=\s*(['"])([^"']+)\.main(\+xml)?\1/) {
  418. $mime = $2;
  419. }
  420. }
  421. # check for docProps if we couldn't find a MIME type
  422. $mime or @members = $zip->membersMatching('^docProps/.*\.(xml|XML)$');
  423. if ($mime or @members) {
  424. $$dirInfo{MIME} = $mime;
  425. require Image::ExifTool::OOXML;
  426. Image::ExifTool::OOXML::ProcessDOCX($et, $dirInfo);
  427. delete $$dirInfo{MIME};
  428. last;
  429. }
  430. # check for an EIP file
  431. @members = $zip->membersMatching('^CaptureOne/.*\.(cos|COS)$');
  432. if (@members) {
  433. require Image::ExifTool::CaptureOne;
  434. Image::ExifTool::CaptureOne::ProcessEIP($et, $dirInfo);
  435. last;
  436. }
  437. # check for an iWork file
  438. @members = $zip->membersMatching('^(index\.(xml|apxl)|QuickLook/Thumbnail\.jpg)$');
  439. if (@members) {
  440. require Image::ExifTool::iWork;
  441. Image::ExifTool::iWork::Process_iWork($et, $dirInfo);
  442. last;
  443. }
  444. # check for an Open Document, IDML or EPUB file
  445. my $mType = $zip->memberNamed('mimetype');
  446. if ($mType) {
  447. ($mime, $status) = $zip->contents($mType);
  448. if (not $status and $mime =~ /([\x21-\xfe]+)/s) {
  449. # clean up MIME type just in case (note that MIME is case insensitive)
  450. $mime = lc $1;
  451. $et->SetFileType($openDocType{$mime} || 'ZIP', $mime);
  452. $et->Warn("Unrecognized MIMEType $mime") unless $openDocType{$mime};
  453. # extract Open Document metadata from "meta.xml"
  454. my $meta = $zip->memberNamed('meta.xml');
  455. # IDML files have metadata in a different place (ref 6)
  456. $meta or $meta = $zip->memberNamed('META-INF/metadata.xml');
  457. if ($meta) {
  458. ($buff, $status) = $zip->contents($meta);
  459. unless ($status) {
  460. my %dirInfo = (
  461. DataPt => \$buff,
  462. DirLen => length $buff,
  463. DataLen => length $buff,
  464. );
  465. # (avoid structure warnings when copying from XML)
  466. my $oldWarn = $$et{NO_STRUCT_WARN};
  467. $$et{NO_STRUCT_WARN} = 1;
  468. $et->ProcessDirectory(\%dirInfo, GetTagTable('Image::ExifTool::XMP::Main'));
  469. $$et{NO_STRUCT_WARN} = $oldWarn;
  470. }
  471. }
  472. # process rootfile of EPUB container if applicable
  473. for (;;) {
  474. last if $meta and $mime ne 'application/epub+zip';
  475. my $container = $zip->memberNamed('META-INF/container.xml');
  476. ($buff, $status) = $zip->contents($container);
  477. last if $status;
  478. $buff =~ /<rootfile\s+[^>]*?\bfull-path=(['"])(.*?)\1/s or last;
  479. # load the rootfile data (OPF extension; contains XML metadata)
  480. my $meta2 = $zip->memberNamed($2) or last;
  481. $meta = $meta2;
  482. ($buff, $status) = $zip->contents($meta);
  483. last if $status;
  484. # use opf:event to generate more meaningful tag names for dc:date
  485. while ($buff =~ s{<dc:date opf:event="(\w+)">([^<]+)</dc:date>}{<dc:${1}Date>$2</dc:${1}Date>}s) {
  486. my $dcTable = GetTagTable('Image::ExifTool::XMP::dc');
  487. my $tag = "${1}Date";
  488. AddTagToTable($dcTable, $tag, {
  489. Name => ucfirst $tag,
  490. Groups => { 2 => 'Time' },
  491. List => 'Seq',
  492. %Image::ExifTool::XMP::dateTimeInfo
  493. }) unless $$dcTable{$tag};
  494. }
  495. my %dirInfo = (
  496. DataPt => \$buff,
  497. DirLen => length $buff,
  498. DataLen => length $buff,
  499. IgnoreProp => { 'package' => 1, metadata => 1 },
  500. );
  501. # (avoid structure warnings when copying from XML)
  502. my $oldWarn = $$et{NO_STRUCT_WARN};
  503. $$et{NO_STRUCT_WARN} = 1;
  504. $et->ProcessDirectory(\%dirInfo, GetTagTable('Image::ExifTool::XMP::XML'));
  505. $$et{NO_STRUCT_WARN} = $oldWarn;
  506. last;
  507. }
  508. if ($openDocType{$mime} or $meta) {
  509. # extract preview image(s) from "Thumbnails" directory if they exist
  510. my $type;
  511. my %tag = ( jpg => 'PreviewImage', png => 'PreviewPNG' );
  512. foreach $type ('jpg', 'png') {
  513. my $thumb = $zip->memberNamed("Thumbnails/thumbnail.$type");
  514. next unless $thumb;
  515. ($buff, $status) = $zip->contents($thumb);
  516. $et->FoundTag($tag{$type}, $buff) unless $status;
  517. }
  518. last; # all done since we recognized the MIME type or found metadata
  519. }
  520. # continue on to list ZIP contents...
  521. }
  522. }
  523. # otherwise just extract general ZIP information
  524. $et->SetFileType();
  525. @members = $zip->members();
  526. $docNum = 0;
  527. my $member;
  528. foreach $member (@members) {
  529. $$et{DOC_NUM} = ++$docNum;
  530. HandleMember($et, $member, $tagTablePtr);
  531. }
  532. last;
  533. }
  534. # all done if we processed this using Archive::Zip
  535. if ($zip) {
  536. delete $$dirInfo{ZIP};
  537. delete $$et{DOC_NUM};
  538. return 1;
  539. }
  540. #
  541. # process the ZIP file by hand (funny, but this seems easier than using Archive::Zip)
  542. #
  543. $docNum = 0;
  544. $et->VPrint(1, " -- processing as binary data --\n");
  545. $raf->Seek(30, 0);
  546. $et->SetFileType();
  547. SetByteOrder('II');
  548. # A. Local file header:
  549. # local file header signature 0) 4 bytes (0x04034b50)
  550. # version needed to extract 4) 2 bytes
  551. # general purpose bit flag 6) 2 bytes
  552. # compression method 8) 2 bytes
  553. # last mod file time 10) 2 bytes
  554. # last mod file date 12) 2 bytes
  555. # crc-32 14) 4 bytes
  556. # compressed size 18) 4 bytes
  557. # uncompressed size 22) 4 bytes
  558. # file name length 26) 2 bytes
  559. # extra field length 28) 2 bytes
  560. for (;;) {
  561. my $len = Get16u(\$buff, 26) + Get16u(\$buff, 28);
  562. $raf->Read($buf2, $len) == $len or last;
  563. $$et{DOC_NUM} = ++$docNum;
  564. $buff .= $buf2;
  565. my %dirInfo = (
  566. DataPt => \$buff,
  567. DataPos => $raf->Tell() - 30 - $len,
  568. DataLen => 30 + $len,
  569. DirStart => 0,
  570. DirLen => 30 + $len,
  571. );
  572. $et->ProcessDirectory(\%dirInfo, $tagTablePtr);
  573. my $flags = Get16u(\$buff, 6);
  574. if ($flags & 0x08) {
  575. # we don't yet support skipping stream mode data
  576. # (when this happens, the CRC, compressed size and uncompressed
  577. # sizes are set to 0 in the header. Instead, they are stored
  578. # after the compressed data with an optional header of 0x08074b50)
  579. $et->Warn('Stream mode data encountered, file list may be incomplete');
  580. last;
  581. }
  582. $len = Get32u(\$buff, 18); # file data length
  583. $raf->Seek($len, 1) or last; # skip file data
  584. $raf->Read($buff, 30) == 30 and $buff =~ /^PK\x03\x04/ or last;
  585. }
  586. delete $$et{DOC_NUM};
  587. return 1;
  588. }
  589. 1; # end
  590. __END__
  591. =head1 NAME
  592. Image::ExifTool::ZIP - Read ZIP archive meta information
  593. =head1 SYNOPSIS
  594. This module is used by Image::ExifTool
  595. =head1 DESCRIPTION
  596. This module contains definitions required by Image::ExifTool to extract meta
  597. information from ZIP, GZIP and RAR archives. This includes ZIP-based file
  598. types like Office Open XML (DOCX, PPTX and XLSX), Open Document (ODB, ODC,
  599. ODF, ODG, ODI, ODP, ODS and ODT), iWork (KEY, PAGES, NUMBERS), Capture One
  600. Enhanced Image Package (EIP), Adobe InDesign Markup Language (IDML), and
  601. Electronic Publication (EPUB).
  602. =head1 AUTHOR
  603. Copyright 2003-2016, Phil Harvey (phil at owl.phy.queensu.ca)
  604. This library is free software; you can redistribute it and/or modify it
  605. under the same terms as Perl itself.
  606. =head1 REFERENCES
  607. =over 4
  608. =item L<http://www.pkware.com/documents/casestudies/APPNOTE.TXT>
  609. =item L<http://www.gzip.org/zlib/rfc-gzip.html>
  610. =item L<http://DataCompression.info/ArchiveFormats/RAR202.txt>
  611. =back
  612. =head1 SEE ALSO
  613. L<Image::ExifTool::TagNames/ZIP Tags>,
  614. L<Image::ExifTool::TagNames/OOXML Tags>,
  615. L<Image::ExifTool(3pm)|Image::ExifTool>
  616. =cut