File Coverage

blib/lib/Image/ExifTool/PhotoMechanic.pm
Criterion Covered Total %
statement 52 58 89.6
branch 12 22 54.5
condition 6 14 42.8
subroutine 7 7 100.0
pod 0 1 0.0
total 77 102 75.4


line stmt bran cond sub pod time code
1             #------------------------------------------------------------------------------
2             # File: PhotoMechanic.pm
3             #
4             # Description: Read/write Camera Bits Photo Mechanic information
5             #
6             # Revisions: 10/28/2006 - P. Harvey Created
7             #------------------------------------------------------------------------------
8              
9             package Image::ExifTool::PhotoMechanic;
10              
11 12     12   4413 use strict;
  12         42  
  12         483  
12 12     12   102 use vars qw($VERSION);
  12         40  
  12         640  
13 12     12   82 use Image::ExifTool qw(:DataAccess :Utils);
  12         31  
  12         2731  
14 12     12   1475 use Image::ExifTool::Exif;
  12         84  
  12         337  
15 12     12   1931 use Image::ExifTool::IPTC;
  12         59  
  12         459  
16 12     12   1207 use Image::ExifTool::XMP;
  12         84  
  12         10649  
17              
18             $VERSION = '1.06';
19              
20             sub ProcessPhotoMechanic($$);
21              
22             # color class names
23             my %colorClasses = (
24             0 => '0 (None)',
25             1 => '1 (Winner)',
26             2 => '2 (Winner alt)',
27             3 => '3 (Superior)',
28             4 => '4 (Superior alt)',
29             5 => '5 (Typical)',
30             6 => '6 (Typical alt)',
31             7 => '7 (Extras)',
32             8 => '8 (Trash)',
33             );
34              
35             # main tag table IPTC-format records in PhotoMechanic trailer
36             %Image::ExifTool::PhotoMechanic::Main = (
37             GROUPS => { 2 => 'Image' },
38             PROCESS_PROC => \&Image::ExifTool::IPTC::ProcessIPTC,
39             WRITE_PROC => \&Image::ExifTool::IPTC::WriteIPTC,
40             NOTES => q{
41             The Photo Mechanic trailer contains data in an IPTC-format structure, with
42             soft edit information stored under record number 2.
43             },
44             2 => {
45             Name => 'SoftEdit',
46             SubDirectory => {
47             TagTable => 'Image::ExifTool::PhotoMechanic::SoftEdit',
48             },
49             },
50             );
51              
52             # raw/preview crop coordinate conversions
53             my %rawCropConv = (
54             ValueConv => '$val / 655.36',
55             ValueConvInv => 'int($val * 655.36 + 0.5)',
56             PrintConv => 'sprintf("%.3f%%",$val)',
57             PrintConvInv => '$val=~tr/ %//d; $val',
58             );
59              
60             # Record 2 -- PhotoMechanic soft edit information
61             %Image::ExifTool::PhotoMechanic::SoftEdit = (
62             GROUPS => { 2 => 'Image' },
63             WRITE_PROC => \&Image::ExifTool::IPTC::WriteIPTC,
64             CHECK_PROC => \&Image::ExifTool::IPTC::CheckIPTC,
65             WRITABLE => 1,
66             FORMAT => 'int32s',
67             209 => { Name => 'RawCropLeft', %rawCropConv },
68             210 => { Name => 'RawCropTop', %rawCropConv },
69             211 => { Name => 'RawCropRight', %rawCropConv },
70             212 => { Name => 'RawCropBottom', %rawCropConv },
71             213 => 'ConstrainedCropWidth',
72             214 => 'ConstrainedCropHeight',
73             215 => 'FrameNum',
74             216 => {
75             Name => 'Rotation',
76             PrintConv => {
77             0 => '0',
78             1 => '90',
79             2 => '180',
80             3 => '270',
81             },
82             },
83             217 => 'CropLeft',
84             218 => 'CropTop',
85             219 => 'CropRight',
86             220 => 'CropBottom',
87             221 => {
88             Name => 'Tagged',
89             PrintConv => { 0 => 'No', 1 => 'Yes' },
90             },
91             222 => {
92             Name => 'ColorClass',
93             PrintConv => \%colorClasses,
94             },
95             223 => 'Rating',
96             236 => { Name => 'PreviewCropLeft', %rawCropConv },
97             237 => { Name => 'PreviewCropTop', %rawCropConv },
98             238 => { Name => 'PreviewCropRight', %rawCropConv },
99             239 => { Name => 'PreviewCropBottom', %rawCropConv },
100             );
101              
102             # PhotoMechanic XMP properties
103             %Image::ExifTool::PhotoMechanic::XMP = (
104             GROUPS => { 0 => 'XMP', 1 => 'XMP-photomech', 2 => 'Image' },
105             NAMESPACE => { photomechanic => 'http://ns.camerabits.com/photomechanic/1.0/' },
106             WRITE_PROC => \&Image::ExifTool::XMP::WriteXMP,
107             WRITABLE => 'string',
108             NOTES => q{
109             Below is a list of the observed PhotoMechanic XMP tags. The actual
110             namespace prefix is "photomechanic" but ExifTool shortens this in
111             the family 1 group name.
112             },
113             ColorClass => {
114             Writable => 'integer',
115             PrintConv => \%colorClasses,
116             },
117             CountryCode => { Avoid => 1, Groups => { 2 => 'Location' } },
118             EditStatus => { },
119             PMVersion => { },
120             Prefs => {
121             Notes => 'format is "Tagged:0, ColorClass:1, Rating:2, FrameNum:3"',
122             PrintConv => q{
123             $val =~ s[\s*(\d+):\s*(\d+):\s*(\d+):\s*(\S*)]
124             [Tagged:$1, ColorClass:$2, Rating:$3, FrameNum:$4];
125             return $val;
126             },
127             PrintConvInv => q{
128             $val =~ s[Tagged:\s*(\d+).*ColorClass:\s*(\d+).*Rating:\s*(\d+).*FrameNum:\s*(\S*)]
129             [$1:$2:$3:$4]is;
130             return $val;
131             },
132             },
133             Tagged => { Writable => 'boolean', PrintConv => { False => 'No', True => 'Yes' } },
134             TimeCreated => {
135             Avoid => 1,
136             Groups => { 2 => 'Time' },
137             Shift => 'Time',
138             ValueConv => 'Image::ExifTool::Exif::ExifTime($val)',
139             ValueConvInv => 'Image::ExifTool::IPTC::IptcTime($val)',
140             },
141             );
142              
143             #------------------------------------------------------------------------------
144             # Read/write PhotoMechanic information in a file
145             # Inputs: 0) ExifTool object reference, 1) dirInfo reference
146             # Returns: 1 on success, 0 if this file didn't contain PhotoMechanic information
147             # - updates DataPos to point to start of PhotoMechanic information
148             # - updates DirLen to trailer length
149             sub ProcessPhotoMechanic($$)
150             {
151 34     34 0 129 my ($et, $dirInfo) = @_;
152 34         105 my $raf = $$dirInfo{RAF};
153 34   100     172 my $offset = $$dirInfo{Offset} || 0;
154 34         85 my $outfile = $$dirInfo{OutFile};
155 34         165 my $verbose = $et->Options('Verbose');
156 34         171 my $out = $et->Options('TextOut');
157 34         145 my $rtnVal = 0;
158 34         107 my ($buff, $footer);
159              
160 34         125 for (;;) {
161             # read and validate trailer footer (last 12 bytes)
162 34 50 33     155 last unless $raf->Seek(-12-$offset, 2) and $raf->Read($footer, 12) == 12;
163 34 50       317 last unless $footer =~ /cbipcbbl$/;
164 34         148 my $size = unpack('N', $footer);
165              
166 34 50 33     267 if ($size & 0x80000000 or not $raf->Seek(-$size-12, 1)) {
167 0         0 $et->Warn('Bad PhotoMechanic trailer');
168 0         0 last;
169             }
170 34 50       368 unless ($raf->Read($buff, $size) == $size) {
171 0         0 $et->Warn('Error reading PhotoMechanic trailer');
172 0         0 last;
173             }
174 34         145 $rtnVal = 1; # we read the trailer successfully
175              
176             # set variables returned in dirInfo hash
177 34         171 $$dirInfo{DataPos} = $raf->Tell() - $size;
178 34         156 $$dirInfo{DirLen} = $size + 12;
179              
180             my %dirInfo = (
181             DataPt => \$buff,
182             DataPos => $$dirInfo{DataPos},
183 34         309 DirStart => 0,
184             DirLen => $size,
185             Parent => 'PhotoMechanic',
186             );
187 34         152 my $tagTablePtr = GetTagTable('Image::ExifTool::PhotoMechanic::Main');
188 34 100       249 if (not $outfile) {
    50          
189             # extract trailer information
190 25 50 33     209 $et->DumpTrailer($dirInfo) if $verbose or $$et{HTML_DUMP};
191 25         111 $et->ProcessDirectory(\%dirInfo, $tagTablePtr);
192             } elsif ($$et{DEL_GROUP}{PhotoMechanic}) {
193             # delete the trailer
194 0 0       0 $verbose and print $out " Deleting PhotoMechanic trailer\n";
195 0         0 ++$$et{CHANGED};
196             } else {
197             # rewrite the trailer
198 9         22 my $newPt;
199 9         52 my $newBuff = $et->WriteDirectory(\%dirInfo, $tagTablePtr);
200 9 100       66 if (defined $newBuff) {
201 2         7 $newPt = \$newBuff; # write out the modified trailer
202 2         7 my $pad = 0x800 - length($newBuff);
203 2 50 33     229 if ($pad > 0 and not $$et{OPTIONS}{Compact}{NoPadding}) {
204             # pad out to 2kB like PhotoMechanic does
205 2         23 $newBuff .= "\0" x $pad;
206             }
207             # generate new footer
208 2         14 $footer = pack('N', length($$newPt)) . 'cbipcbbl';
209             } else {
210 7         32 $newPt = \$buff; # just copy existing trailer
211             }
212             # write out the trailer
213 9 50       54 Write($outfile, $$newPt, $footer) or $rtnVal = -1;
214             }
215 34         265 last;
216             }
217 34         143 return $rtnVal;
218             }
219              
220             1; # end
221              
222             __END__