CaptureOne.pm 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235
  1. #------------------------------------------------------------------------------
  2. # File: CaptureOne.pm
  3. #
  4. # Description: Read Capture One EIP and COS files
  5. #
  6. # Revisions: 2009/11/01 - P. Harvey Created
  7. #
  8. # Notes: The EIP format is a ZIP file containing an image (IIQ or TIFF)
  9. # and some settings files (COS). COS files are XML based.
  10. #------------------------------------------------------------------------------
  11. package Image::ExifTool::CaptureOne;
  12. use strict;
  13. use vars qw($VERSION);
  14. use Image::ExifTool qw(:DataAccess :Utils);
  15. use Image::ExifTool::XMP;
  16. use Image::ExifTool::ZIP;
  17. $VERSION = '1.04';
  18. # CaptureOne COS XML tags
  19. # - tags are added dynamically when encountered
  20. # - this table is not listed in tag name docs
  21. %Image::ExifTool::CaptureOne::Main = (
  22. GROUPS => { 0 => 'XML', 1 => 'XML', 2 => 'Image' },
  23. PROCESS_PROC => \&Image::ExifTool::XMP::ProcessXMP,
  24. VARS => { NO_ID => 1 },
  25. ColorCorrections => { ValueConv => '\$val' }, # (long list of floating point numbers)
  26. );
  27. #------------------------------------------------------------------------------
  28. # We found an XMP property name/value
  29. # Inputs: 0) attribute list ref, 1) attr hash ref,
  30. # 2) property name ref, 3) property value ref
  31. # Returns: true if value was changed
  32. sub HandleCOSAttrs($$$$)
  33. {
  34. my ($attrList, $attrs, $prop, $valPt) = @_;
  35. my $changed;
  36. if (not length $$valPt and defined $$attrs{K} and defined $$attrs{V}) {
  37. $$prop = $$attrs{K};
  38. $$valPt = $$attrs{V};
  39. # remove these attributes from the list
  40. my @attrs = @$attrList;
  41. @$attrList = ( );
  42. my $a;
  43. foreach $a (@attrs) {
  44. if ($a eq 'K' or $a eq 'V') {
  45. delete $$attrs{$a};
  46. } else {
  47. push @$attrList, $a;
  48. }
  49. }
  50. $changed = 1;
  51. }
  52. return $changed;
  53. }
  54. #------------------------------------------------------------------------------
  55. # We found a COS property name/value
  56. # Inputs: 0) ExifTool object ref, 1) tag table ref
  57. # 2) reference to array of XMP property names (last is current property)
  58. # 3) property value, 4) attribute hash ref (not used here)
  59. # Returns: 1 if valid tag was found
  60. sub FoundCOS($$$$;$)
  61. {
  62. my ($et, $tagTablePtr, $props, $val, $attrs) = @_;
  63. my $tag = $$props[-1];
  64. unless ($$tagTablePtr{$tag}) {
  65. $et->VPrint(0, " | [adding $tag]\n");
  66. my $name = ucfirst $tag;
  67. $name =~ tr/-_a-zA-Z0-9//dc;
  68. return 0 unless length $tag;
  69. my %tagInfo = ( Name => $tag );
  70. # try formatting any tag with "Date" in the name as a date
  71. # (shouldn't affect non-date tags)
  72. if ($name =~ /Date(?![a-z])/) {
  73. $tagInfo{Groups} = { 2 => 'Time' };
  74. $tagInfo{ValueConv} = 'Image::ExifTool::XMP::ConvertXMPDate($val,1)';
  75. $tagInfo{PrintConv} = '$self->ConvertDateTime($val)';
  76. }
  77. AddTagToTable($tagTablePtr, $tag, \%tagInfo);
  78. }
  79. # convert from UTF8 to ExifTool Charset
  80. $val = $et->Decode($val, "UTF8");
  81. # un-escape XML character entities
  82. $val = Image::ExifTool::XMP::UnescapeXML($val);
  83. $et->HandleTag($tagTablePtr, $tag, $val);
  84. return 0;
  85. }
  86. #------------------------------------------------------------------------------
  87. # Extract information from a COS file
  88. # Inputs: 0) ExifTool object reference, 1) dirInfo reference
  89. # Returns: 1 on success, 0 if this wasn't a valid XML file
  90. sub ProcessCOS($$)
  91. {
  92. my ($et, $dirInfo) = @_;
  93. # process using XMP module, but override handling of attributes and tags
  94. $$dirInfo{XMPParseOpts} = {
  95. AttrProc => \&HandleCOSAttrs,
  96. FoundProc => \&FoundCOS,
  97. };
  98. my $tagTablePtr = GetTagTable('Image::ExifTool::CaptureOne::Main');
  99. my $success = $et->ProcessDirectory($dirInfo, $tagTablePtr);
  100. delete $$dirInfo{XMLParseArgs};
  101. return $success;
  102. }
  103. #------------------------------------------------------------------------------
  104. # Extract information from a CaptureOne EIP file
  105. # Inputs: 0) ExifTool object reference, 1) dirInfo reference
  106. # Returns: 1
  107. # Notes: Upon entry to this routine, the file type has already been verified
  108. # and the dirInfo hash contains a ZIP element unique to this process proc:
  109. # ZIP - reference to Archive::Zip object for this file
  110. sub ProcessEIP($$)
  111. {
  112. my ($et, $dirInfo) = @_;
  113. my $zip = $$dirInfo{ZIP};
  114. my ($file, $buff, $status, $member, %parseFile);
  115. $et->SetFileType('EIP');
  116. # must catch all Archive::Zip warnings
  117. local $SIG{'__WARN__'} = \&Image::ExifTool::ZIP::WarnProc;
  118. # find all manifest files
  119. my @members = $zip->membersMatching('^manifest\d*.xml$');
  120. # and choose the one with the highest version number (any better ideas?)
  121. while (@members) {
  122. my $m = shift @members;
  123. my $f = $m->fileName();
  124. next if $file and $file gt $f;
  125. $member = $m;
  126. $file = $f;
  127. }
  128. # get file names from our chosen manifest file
  129. if ($member) {
  130. ($buff, $status) = $zip->contents($member);
  131. if (not $status) {
  132. my $foundImage;
  133. while ($buff =~ m{<(RawPath|SettingsPath)>(.*?)</\1>}sg) {
  134. $file = $2;
  135. next unless $file =~ /\.(cos|iiq|jpe?g|tiff?)$/i;
  136. $parseFile{$file} = 1; # set flag to parse this file
  137. $foundImage = 1 unless $file =~ /\.cos$/i;
  138. }
  139. # ignore manifest unless it contained a valid image
  140. undef %parseFile unless $foundImage;
  141. }
  142. }
  143. # extract meta information from embedded files
  144. my $docNum = 0;
  145. @members = $zip->members(); # get all members
  146. foreach $member (@members) {
  147. # get filename of this ZIP member
  148. $file = $member->fileName();
  149. next unless defined $file;
  150. $et->VPrint(0, "File: $file\n");
  151. # set the document number and extract ZIP tags
  152. $$et{DOC_NUM} = ++$docNum;
  153. Image::ExifTool::ZIP::HandleMember($et, $member);
  154. if (%parseFile) {
  155. next unless $parseFile{$file};
  156. } else {
  157. # reading the manifest didn't work, so look for image files in the
  158. # root directory and .cos files in the CaptureOne directory
  159. next unless $file =~ m{^([^/]+\.(iiq|jpe?g|tiff?)|CaptureOne/.*\.cos)$}i;
  160. }
  161. # extract the contents of the file
  162. # Note: this could use a LOT of memory here for RAW images...
  163. ($buff, $status) = $zip->contents($member);
  164. $status and $et->Warn("Error extracting $file"), next;
  165. if ($file =~ /\.cos$/i) {
  166. # process Capture One Settings files
  167. my %dirInfo = (
  168. DataPt => \$buff,
  169. DirLen => length $buff,
  170. DataLen => length $buff,
  171. );
  172. ProcessCOS($et, \%dirInfo);
  173. } else {
  174. # set HtmlDump error if necessary because it doesn't work with embedded files
  175. if ($$et{HTML_DUMP}) {
  176. $$et{HTML_DUMP}{Error} = "Sorry, can't dump images embedded in ZIP files";
  177. }
  178. # process IIQ, JPEG and TIFF images
  179. $et->ExtractInfo(\$buff, { ReEntry => 1 });
  180. }
  181. undef $buff; # (free memory now)
  182. }
  183. delete $$et{DOC_NUM};
  184. return 1;
  185. }
  186. 1; # end
  187. __END__
  188. =head1 NAME
  189. Image::ExifTool::CaptureOne - Read Capture One EIP and COS files
  190. =head1 SYNOPSIS
  191. This module is used by Image::ExifTool
  192. =head1 DESCRIPTION
  193. This module contains definitions required by Image::ExifTool to extract meta
  194. information from Capture One EIP (Enhanced Image Package) and COS (Capture
  195. One Settings) files.
  196. =head1 NOTES
  197. The EIP format is a ZIP file containing an image (IIQ or TIFF) and some
  198. settings files (COS).
  199. =head1 AUTHOR
  200. Copyright 2003-2016, Phil Harvey (phil at owl.phy.queensu.ca)
  201. This library is free software; you can redistribute it and/or modify it
  202. under the same terms as Perl itself.
  203. =head1 SEE ALSO
  204. L<Image::ExifTool::TagNames/ZIP Tags>,
  205. L<Image::ExifTool(3pm)|Image::ExifTool>
  206. =cut