File Coverage

blib/lib/Image/ExifTool/WritePhotoshop.pl
Criterion Covered Total %
statement 96 122 78.6
branch 44 74 59.4
condition 14 29 48.2
subroutine 3 3 100.0
pod 0 2 0.0
total 157 230 68.2


line stmt bran cond sub pod time code
1             #------------------------------------------------------------------------------
2             # File: WritePhotoshop.pl
3             #
4             # Description: Write Photoshop IRB meta information
5             #
6             # Revisions: 12/17/2004 - P. Harvey Created
7             #------------------------------------------------------------------------------
8              
9             package Image::ExifTool::Photoshop;
10              
11 16     16   88 use strict;
  16         31  
  16         18972  
12              
13             #------------------------------------------------------------------------------
14             # Strip resource name from value prepare resource name for writing into IRB
15             # Inputs: 0) tagInfo ref, 1) resource name (padded pascal string), 2) new value ref
16             # Returns: none (updates name and value if necessary)
17             sub SetResourceName($$$)
18             {
19 68     68 0 176 my ($tagInfo, $name, $valPt) = @_;
20 68         131 my $setName = $$tagInfo{SetResourceName};
21 68 50       198 if (defined $setName) {
22             # extract resource name from value
23 0 0       0 if ($$valPt =~ m{.*/#(.{0,255})#/$}s) {
    0          
24 0         0 $name = $1;
25             # strip name from value
26 0         0 $$valPt = substr($$valPt, 0, -4 - length($name));
27             } elsif ($setName eq '1') {
28 0         0 return; # use old name
29             } else {
30 0         0 $name = $setName;
31             }
32             # convert to padded pascal string
33 0         0 $name = chr(length $name) . $name;
34 0 0       0 $name .= "\0" if length($name) & 0x01;
35 0         0 $_[1] = $name; # return new name
36             }
37             }
38              
39             #------------------------------------------------------------------------------
40             # Write Photoshop IRB resource
41             # Inputs: 0) ExifTool object reference, 1) source dirInfo reference,
42             # 2) tag table reference
43             # Returns: IRB resource data (may be empty if no Photoshop data)
44             # Notes: Increments ExifTool CHANGED flag for each tag changed
45             sub WritePhotoshop($$$)
46             {
47 77     77 0 184 my ($et, $dirInfo, $tagTablePtr) = @_;
48 77 100       315 $et or return 1; # allow dummy access to autoload this package
49 32         72 my $dataPt = $$dirInfo{DataPt};
50 32 100       91 unless ($dataPt) {
51 21         44 my $emptyData = '';
52 21         46 $dataPt = \$emptyData;
53             }
54 32   100     163 my $start = $$dirInfo{DirStart} || 0;
55 32   66     147 my $dirLen = $$dirInfo{DirLen} || (length($$dataPt) - $start);
56 32         57 my $dirEnd = $start + $dirLen;
57 32         90 my $newData = '';
58              
59             # make a hash of new tag info, keyed on tagID
60 32         193 my $newTags = $et->GetNewTagInfoHash($tagTablePtr);
61              
62 32         128 my ($addDirs, $editDirs) = $et->GetAddDirHash($tagTablePtr);
63              
64 32         125 SetByteOrder('MM'); # Photoshop is always big-endian
65             #
66             # rewrite existing tags in the old directory, deleting ones as necessary
67             # (the Photoshop directory entries aren't in any particular order)
68             #
69             # Format: 0) Type, 4 bytes - '8BIM' (or the rare 'PHUT', 'DCSR', 'AgHg' or 'MeSa')
70             # 1) TagID,2 bytes
71             # 2) Name, pascal string padded to even no. bytes
72             # 3) Size, 4 bytes - N
73             # 4) Data, N bytes
74 32         56 my ($pos, $value, $size, $tagInfo, $tagID);
75 32         151 for ($pos=$start; $pos+8<$dirEnd; $pos+=$size) {
76             # each entry must be on same even byte boundary as directory start
77 90 100       159 ++$pos if ($pos ^ $start) & 0x01;
78 90         134 my $type = substr($$dataPt, $pos, 4);
79 90 50       275 if ($type !~ /^(8BIM|PHUT|DCSR|AgHg|MeSa)$/) {
80 0         0 $et->Error("Bad Photoshop IRB resource");
81 0         0 undef $newData;
82 0         0 last;
83             }
84 90         168 $tagID = Get16u($dataPt, $pos + 4);
85             # get resource block name (pascal string padded to an even # of bytes)
86 90         168 my $namelen = 1 + Get8u($dataPt, $pos + 6);
87 90 50       152 ++$namelen if $namelen & 0x01;
88 90 50       153 if ($pos + $namelen + 10 > $dirEnd) {
89 0         0 $et->Error("Bad APP13 resource block");
90 0         0 undef $newData;
91 0         0 last;
92             }
93 90         121 my $name = substr($$dataPt, $pos + 6, $namelen);
94 90         139 $size = Get32u($dataPt, $pos + 6 + $namelen);
95 90         108 $pos += $namelen + 10;
96 90 50       137 if ($size + $pos > $dirEnd) {
97 0         0 $et->Error("Bad APP13 resource data size $size");
98 0         0 undef $newData;
99 0         0 last;
100             }
101 90 100 66     197 if ($$newTags{$tagID} and $type eq '8BIM') {
102 3         5 $tagInfo = $$newTags{$tagID};
103 3         4 delete $$newTags{$tagID};
104 3         8 my $nvHash = $et->GetNewValueHash($tagInfo);
105             # check to see if we are overwriting this tag
106 3         4 $value = substr($$dataPt, $pos, $size);
107 3         7 my $isOverwriting = $et->IsOverwriting($nvHash, $value);
108             # handle special 'new' and 'old' values for IPTCDigest
109 3 50 33     14 if (not $isOverwriting and $tagInfo eq $iptcDigestInfo) {
110 0 0       0 if (grep /^new$/, @{$$nvHash{DelValue}}) {
  0         0  
111             $isOverwriting = 1 if $$et{NewIPTCDigest} and
112 0 0 0     0 $$et{NewIPTCDigest} eq $value;
113             }
114 0 0       0 if (grep /^old$/, @{$$nvHash{DelValue}}) {
  0         0  
115             $isOverwriting = 1 if $$et{OldIPTCDigest} and
116 0 0 0     0 $$et{OldIPTCDigest} eq $value;
117             }
118             }
119 3 50       7 if ($isOverwriting) {
120 3         11 $et->VerboseValue("- Photoshop:$$tagInfo{Name}", $value);
121             # handle IPTCDigest specially because we want to write it last
122             # so the new IPTC digest will be known
123 3 50       6 if ($tagInfo eq $iptcDigestInfo) {
124 0         0 $$newTags{$tagID} = $tagInfo; # add later
125 0         0 $value = undef;
126             } else {
127 3         7 $value = $et->GetNewValue($nvHash);
128             }
129 3         6 ++$$et{CHANGED};
130 3 50       6 next unless defined $value; # next if tag is being deleted
131             # set resource name if necessary
132 3         7 SetResourceName($tagInfo, $name, \$value);
133 3         6 $et->VerboseValue("+ Photoshop:$$tagInfo{Name}", $value);
134             }
135             } else {
136 87 50       169 if ($type eq '8BIM') {
137 87         125 $tagInfo = $$editDirs{$tagID};
138 87 100       134 unless ($tagInfo) {
139             # process subdirectory anyway if writable (except EXIF to avoid recursion)
140             # --> this allows IPTC to be processed if found here in TIFF images
141             # (but allow EXIF to be written in PSD files if XMP or IPTC tags are
142             # being written because I have seen cases of XMP in PSD-EXIFInfo-IFD0
143             # and IPTC in PSD-EXIFInfo-IFD0-IPTC, see forum10768 and forum13198)
144 76         161 my $tmpInfo = $et->GetTagInfo($tagTablePtr, $tagID);
145 76 50 66     284 if ($tmpInfo and $$tmpInfo{SubDirectory} and
      33        
      66        
146             ($tmpInfo->{SubDirectory}->{TagTable} ne 'Image::ExifTool::Exif::Main' or
147             ($$et{FILE_TYPE} eq 'PSD' and ($$editDirs{0x0404} or $$editDirs{0x0424}))))
148             {
149 22         56 my $table = Image::ExifTool::GetTagTable($tmpInfo->{SubDirectory}->{TagTable});
150 22 100       73 $tagInfo = $tmpInfo if $$table{WRITE_PROC};
151             }
152             }
153             }
154 87 100       130 if ($tagInfo) {
155 29 100       89 $$addDirs{$tagID} and delete $$addDirs{$tagID};
156             my %subdirInfo = (
157             DataPt => $dataPt,
158             DirStart => $pos,
159             DataLen => $dirLen,
160             DirLen => $size,
161             Parent => $$dirInfo{DirName},
162 29         149 );
163 29         73 my $subTable = Image::ExifTool::GetTagTable($tagInfo->{SubDirectory}->{TagTable});
164 29         70 my $writeProc = $tagInfo->{SubDirectory}->{WriteProc};
165 29         129 my $newValue = $et->WriteDirectory(\%subdirInfo, $subTable, $writeProc);
166 29 100       56 if (defined $newValue) {
167 27 100       102 next unless length $newValue; # remove subdirectory entry
168 25         37 $value = $newValue;
169 25         61 SetResourceName($tagInfo, $name, \$value);
170             } else {
171 2         11 $value = substr($$dataPt, $pos, $size); # rewrite old directory
172             }
173             } else {
174 58         135 $value = substr($$dataPt, $pos, $size);
175             }
176             }
177 88         118 my $newSize = length $value;
178             # write this directory entry
179 88         147 $newData .= $type . Set16u($tagID) . $name . Set32u($newSize) . $value;
180 88 100       237 $newData .= "\0" if $newSize & 0x01; # must null pad to even byte
181             }
182             #
183             # write any remaining entries we didn't find in the old directory
184             # (might as well write them in numerical tag order)
185             #
186 32         139 my @tagsLeft = sort { $a <=> $b } keys(%$newTags), keys(%$addDirs);
  37         56  
187 32         66 foreach $tagID (@tagsLeft) {
188 44         71 my $name = "\0\0";
189 44 100       116 if ($$newTags{$tagID}) {
190 21         33 $tagInfo = $$newTags{$tagID};
191 21         43 my $nvHash = $et->GetNewValueHash($tagInfo);
192 21         49 $value = $et->GetNewValue($nvHash);
193             # handle new IPTCDigest value specially
194 21 100 66     58 if ($tagInfo eq $iptcDigestInfo and defined $value) {
195 1 50       2 if ($value eq 'new') {
    0          
196 1         8 $value = $$et{NewIPTCDigest};
197             } elsif ($value eq 'old') {
198 0         0 $value = $$et{OldIPTCDigest};
199             }
200             # (we already know we want to create this tag)
201             } else {
202             # don't add this tag unless specified
203 20 100       50 next unless $$nvHash{IsCreating};
204             }
205 17 50       31 next unless defined $value; # next if tag is being deleted
206 17         78 $et->VerboseValue("+ Photoshop:$$tagInfo{Name}", $value);
207 17         44 ++$$et{CHANGED};
208             } else {
209 23         44 $tagInfo = $$addDirs{$tagID};
210             # create new directory
211             my %subdirInfo = (
212             Parent => $$dirInfo{DirName},
213 23         85 );
214 23         104 my $subTable = Image::ExifTool::GetTagTable($tagInfo->{SubDirectory}->{TagTable});
215 23         57 my $writeProc = $tagInfo->{SubDirectory}->{WriteProc};
216 23         139 $value = $et->WriteDirectory(\%subdirInfo, $subTable, $writeProc);
217 23 50       93 next unless $value;
218             }
219             # set resource name if necessary
220 40         139 SetResourceName($tagInfo, $name, \$value);
221 40         58 $size = length($value);
222             # write the new directory entry
223 40         112 $newData .= '8BIM' . Set16u($tagID) . $name . Set32u($size) . $value;
224 40 100       125 $newData .= "\0" if $size & 0x01; # must null pad to even numbered byte
225 40         96 ++$$et{CHANGED};
226             }
227 32         247 return $newData;
228             }
229              
230              
231             1; # end
232              
233             __END__