source: main/trunk/greenstone2/perllib/cpan/Image/ExifTool/WritePhotoshop.pl@ 34921

Last change on this file since 34921 was 34921, checked in by anupama, 3 years ago

Committing the improvements to EmbeddedMetaPlugin's processing of Keywords vs other metadata fields. Keywords were literally stored as arrays of words rather than phrases in PDFs (at least in Diego's sample PDF), whereas other meta fields like Subjects and Creators stored them as arrays of phrases. To get both to work, Kathy updated EXIF to a newer version, to retrieve the actual EXIF values stored in the PDF. And Kathy and Dr Bainbridge came up with a new option that I added called apply_join_before_split_to_metafields that's a regex which can list the metadata fields to apply the join_before_split to and whcih previously always got applied to all metadata fields. Now it's applied to any *Keywords metafields by default, as that's the metafield we have experience of that behaves differently to the others, as it stores by word instead of phrases. Tested on Diego's sample PDF. Diego has double-checked it to works on his sample PDF too, setting the split char to ; and turning on the join_before_split and leaving apply_join_before_split_to_metafields at its default of .*Keywords. File changes are strings.properties for the tooltip, the plugin introducing the option and working with it and Kathy's EXIF updates affecting cpan/File and cpan/Image.

File size: 10.9 KB
Line 
1#------------------------------------------------------------------------------
2# File: WritePhotoshop.pl
3#
4# Description: Write Photoshop IRB meta information
5#
6# Revisions: 12/17/2004 - P. Harvey Created
7#------------------------------------------------------------------------------
8
9package Image::ExifTool::Photoshop;
10
11use strict;
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)
17sub SetResourceName($$$)
18{
19 my ($tagInfo, $name, $valPt) = @_;
20 my $setName = $$tagInfo{SetResourceName};
21 if (defined $setName) {
22 # extract resource name from value
23 if ($$valPt =~ m{.*/#(.{0,255})#/$}s) {
24 $name = $1;
25 # strip name from value
26 $$valPt = substr($$valPt, 0, -4 - length($name));
27 } elsif ($setName eq '1') {
28 return; # use old name
29 } else {
30 $name = $setName;
31 }
32 # convert to padded pascal string
33 $name = chr(length $name) . $name;
34 $name .= "\0" if length($name) & 0x01;
35 $_[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
45sub WritePhotoshop($$$)
46{
47 my ($et, $dirInfo, $tagTablePtr) = @_;
48 $et or return 1; # allow dummy access to autoload this package
49 my $dataPt = $$dirInfo{DataPt};
50 unless ($dataPt) {
51 my $emptyData = '';
52 $dataPt = \$emptyData;
53 }
54 my $start = $$dirInfo{DirStart} || 0;
55 my $dirLen = $$dirInfo{DirLen} || (length($$dataPt) - $start);
56 my $dirEnd = $start + $dirLen;
57 my $newData = '';
58
59 # make a hash of new tag info, keyed on tagID
60 my $newTags = $et->GetNewTagInfoHash($tagTablePtr);
61
62 my ($addDirs, $editDirs) = $et->GetAddDirHash($tagTablePtr);
63
64 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 my ($pos, $value, $size, $tagInfo, $tagID);
75 for ($pos=$start; $pos+8<$dirEnd; $pos+=$size) {
76 # each entry must be on same even byte boundary as directory start
77 ++$pos if ($pos ^ $start) & 0x01;
78 my $type = substr($$dataPt, $pos, 4);
79 if ($type !~ /^(8BIM|PHUT|DCSR|AgHg|MeSa)$/) {
80 $et->Error("Bad Photoshop IRB resource");
81 undef $newData;
82 last;
83 }
84 $tagID = Get16u($dataPt, $pos + 4);
85 # get resource block name (pascal string padded to an even # of bytes)
86 my $namelen = 1 + Get8u($dataPt, $pos + 6);
87 ++$namelen if $namelen & 0x01;
88 if ($pos + $namelen + 10 > $dirEnd) {
89 $et->Error("Bad APP13 resource block");
90 undef $newData;
91 last;
92 }
93 my $name = substr($$dataPt, $pos + 6, $namelen);
94 $size = Get32u($dataPt, $pos + 6 + $namelen);
95 $pos += $namelen + 10;
96 if ($size + $pos > $dirEnd) {
97 $et->Error("Bad APP13 resource data size $size");
98 undef $newData;
99 last;
100 }
101 if ($$newTags{$tagID} and $type eq '8BIM') {
102 $tagInfo = $$newTags{$tagID};
103 delete $$newTags{$tagID};
104 my $nvHash = $et->GetNewValueHash($tagInfo);
105 # check to see if we are overwriting this tag
106 $value = substr($$dataPt, $pos, $size);
107 my $isOverwriting = $et->IsOverwriting($nvHash, $value);
108 # handle special 'new' and 'old' values for IPTCDigest
109 if (not $isOverwriting and $tagInfo eq $iptcDigestInfo) {
110 if (grep /^new$/, @{$$nvHash{DelValue}}) {
111 $isOverwriting = 1 if $$et{NewIPTCDigest} and
112 $$et{NewIPTCDigest} eq $value;
113 }
114 if (grep /^old$/, @{$$nvHash{DelValue}}) {
115 $isOverwriting = 1 if $$et{OldIPTCDigest} and
116 $$et{OldIPTCDigest} eq $value;
117 }
118 }
119 if ($isOverwriting) {
120 $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 if ($tagInfo eq $iptcDigestInfo) {
124 $$newTags{$tagID} = $tagInfo; # add later
125 $value = undef;
126 } else {
127 $value = $et->GetNewValue($nvHash);
128 }
129 ++$$et{CHANGED};
130 next unless defined $value; # next if tag is being deleted
131 # set resource name if necessary
132 SetResourceName($tagInfo, $name, \$value);
133 $et->VerboseValue("+ Photoshop:$$tagInfo{Name}", $value);
134 }
135 } else {
136 if ($type eq '8BIM') {
137 $tagInfo = $$editDirs{$tagID};
138 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 # (note that I have seen a case of XMP in PSD-EXIFInfo-IFD0, and the EXIF
142 # exclusion means that this won't be written unless an EXIF tag is
143 # specifically edited, see forum10768 -- maybe this should be changed
144 # if it happens again)
145 my $tmpInfo = $et->GetTagInfo($tagTablePtr, $tagID);
146 if ($tmpInfo and $$tmpInfo{SubDirectory} and
147 $tmpInfo->{SubDirectory}->{TagTable} ne 'Image::ExifTool::Exif::Main')
148 {
149 my $table = Image::ExifTool::GetTagTable($tmpInfo->{SubDirectory}->{TagTable});
150 $tagInfo = $tmpInfo if $$table{WRITE_PROC};
151 }
152 }
153 }
154 if ($tagInfo) {
155 $$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 );
163 my $subTable = Image::ExifTool::GetTagTable($tagInfo->{SubDirectory}->{TagTable});
164 my $writeProc = $tagInfo->{SubDirectory}->{WriteProc};
165 my $newValue = $et->WriteDirectory(\%subdirInfo, $subTable, $writeProc);
166 if (defined $newValue) {
167 next unless length $newValue; # remove subdirectory entry
168 $value = $newValue;
169 SetResourceName($tagInfo, $name, \$value);
170 } else {
171 $value = substr($$dataPt, $pos, $size); # rewrite old directory
172 }
173 } else {
174 $value = substr($$dataPt, $pos, $size);
175 }
176 }
177 my $newSize = length $value;
178 # write this directory entry
179 $newData .= $type . Set16u($tagID) . $name . Set32u($newSize) . $value;
180 $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 my @tagsLeft = sort { $a <=> $b } keys(%$newTags), keys(%$addDirs);
187 foreach $tagID (@tagsLeft) {
188 my $name = "\0\0";
189 if ($$newTags{$tagID}) {
190 $tagInfo = $$newTags{$tagID};
191 my $nvHash = $et->GetNewValueHash($tagInfo);
192 $value = $et->GetNewValue($nvHash);
193 # handle new IPTCDigest value specially
194 if ($tagInfo eq $iptcDigestInfo and defined $value) {
195 if ($value eq 'new') {
196 $value = $$et{NewIPTCDigest};
197 } elsif ($value eq 'old') {
198 $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 next unless $$nvHash{IsCreating};
204 }
205 next unless defined $value; # next if tag is being deleted
206 $et->VerboseValue("+ Photoshop:$$tagInfo{Name}", $value);
207 ++$$et{CHANGED};
208 } else {
209 $tagInfo = $$addDirs{$tagID};
210 # create new directory
211 my %subdirInfo = (
212 Parent => $$dirInfo{DirName},
213 );
214 my $subTable = Image::ExifTool::GetTagTable($tagInfo->{SubDirectory}->{TagTable});
215 my $writeProc = $tagInfo->{SubDirectory}->{WriteProc};
216 $value = $et->WriteDirectory(\%subdirInfo, $subTable, $writeProc);
217 next unless $value;
218 }
219 # set resource name if necessary
220 SetResourceName($tagInfo, $name, \$value);
221 $size = length($value);
222 # write the new directory entry
223 $newData .= '8BIM' . Set16u($tagID) . $name . Set32u($size) . $value;
224 $newData .= "\0" if $size & 0x01; # must null pad to even numbered byte
225 ++$$et{CHANGED};
226 }
227 return $newData;
228}
229
230
2311; # end
232
233__END__
234
235=head1 NAME
236
237Image::ExifTool::WritePhotoshop.pl - Write Photoshop IRB meta information
238
239=head1 SYNOPSIS
240
241This file is autoloaded by Image::ExifTool::Photoshop.
242
243=head1 DESCRIPTION
244
245This file contains routines to write Photoshop metadata.
246
247=head1 NOTES
248
249Photoshop IRB blocks may have an associated resource name. By default, the
250existing name is preserved when rewriting a resource, and an empty name is
251used when creating a new resource. However, a different resource name may
252be specified by defining a C<SetResourceName> entry in the tag information
253hash. With this defined, a new resource name may be appended to the value
254in the form "VALUE/#NAME#/" (the slashes and hashes are literal). If
255C<SetResourceName> is anything other than '1', the value is used as a
256default resource name, and applied if no appended name is provided.
257
258=head1 AUTHOR
259
260Copyright 2003-2021, Phil Harvey (philharvey66 at gmail.com)
261
262This library is free software; you can redistribute it and/or modify it
263under the same terms as Perl itself.
264
265=head1 SEE ALSO
266
267L<Image::ExifTool::Photoshop(3pm)|Image::ExifTool::Photoshop>,
268L<Image::ExifTool(3pm)|Image::ExifTool>
269
270=cut
Note: See TracBrowser for help on using the repository browser.