source: main/trunk/greenstone2/perllib/cpan/Image/ExifTool/Photoshop.pm@ 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: 43.1 KB
Line 
1#------------------------------------------------------------------------------
2# File: Photoshop.pm
3#
4# Description: Read/write Photoshop IRB meta information
5#
6# Revisions: 02/06/2004 - P. Harvey Created
7# 02/25/2004 - P. Harvey Added hack for problem with old photoshops
8# 10/04/2004 - P. Harvey Added a bunch of tags (ref Image::MetaData::JPEG)
9# but left most of them commented out until I have enough
10# information to write PrintConv routines for them to
11# display something useful
12# 07/08/2005 - P. Harvey Added support for reading PSD files
13# 01/07/2006 - P. Harvey Added PSD write support
14# 11/04/2006 - P. Harvey Added handling of resource name
15#
16# References: 1) http://www.fine-view.com/jp/lab/doc/ps6ffspecsv2.pdf
17# 2) http://www.ozhiker.com/electronics/pjmt/jpeg_info/irb_jpeg_qual.html
18# 3) Matt Mueller private communication (tests with PS CS2)
19# 4) http://www.fileformat.info/format/psd/egff.htm
20# 5) http://www.telegraphics.com.au/svn/psdparse/trunk/resources.c
21# 6) http://libpsd.graphest.com/files/Photoshop%20File%20Formats.pdf
22# 7) http://www.adobe.com/devnet-apps/photoshop/fileformatashtml/
23#------------------------------------------------------------------------------
24
25package Image::ExifTool::Photoshop;
26
27use strict;
28use vars qw($VERSION $AUTOLOAD $iptcDigestInfo);
29use Image::ExifTool qw(:DataAccess :Utils);
30
31$VERSION = '1.65';
32
33sub ProcessPhotoshop($$$);
34sub WritePhotoshop($$$);
35sub ProcessLayers($$$);
36
37# map of where information is stored in PSD image
38my %psdMap = (
39 IPTC => 'Photoshop',
40 XMP => 'Photoshop',
41 EXIFInfo => 'Photoshop',
42 IFD0 => 'EXIFInfo',
43 IFD1 => 'IFD0',
44 ICC_Profile => 'Photoshop',
45 ExifIFD => 'IFD0',
46 GPS => 'IFD0',
47 SubIFD => 'IFD0',
48 GlobParamIFD => 'IFD0',
49 PrintIM => 'IFD0',
50 InteropIFD => 'ExifIFD',
51 MakerNotes => 'ExifIFD',
52);
53
54# tag information for PhotoshopThumbnail and PhotoshopBGRThumbnail
55my %thumbnailInfo = (
56 Writable => 'undef',
57 Protected => 1,
58 RawConv => 'my $img=substr($val,0x1c); $self->ValidateImage(\$img,$tag)',
59 ValueConvInv => q{
60 my $et = new Image::ExifTool;
61 my @tags = qw{ImageWidth ImageHeight FileType};
62 my $info = $et->ImageInfo(\$val, @tags);
63 my ($w, $h, $type) = @$info{@tags};
64 $w and $h and $type eq 'JPEG' or warn("Not a valid JPEG image\n"), return undef;
65 my $wbytes = int(($w * 24 + 31) / 32) * 4;
66 return pack('N6n2', 1, $w, $h, $wbytes, $wbytes * $h, length($val), 24, 1) . $val;
67 },
68);
69
70# tag info to decode Photoshop Unicode string
71my %unicodeString = (
72 ValueConv => sub {
73 my ($val, $et) = @_;
74 return '<err>' if length($val) < 4;
75 my $len = unpack('N', $val) * 2;
76 return '<err>' if length($val) < 4 + $len;
77 return $et->Decode(substr($val, 4, $len), 'UCS2', 'MM');
78 },
79 ValueConvInv => sub {
80 my ($val, $et) = @_;
81 return pack('N', length $val) . $et->Encode($val, 'UCS2', 'MM');
82 },
83);
84
85# Photoshop APP13 tag table
86# (set Unknown flag for information we don't want to display normally)
87%Image::ExifTool::Photoshop::Main = (
88 GROUPS => { 2 => 'Image' },
89 PROCESS_PROC => \&ProcessPhotoshop,
90 WRITE_PROC => \&WritePhotoshop,
91 0x03e8 => { Unknown => 1, Name => 'Photoshop2Info' },
92 0x03e9 => { Unknown => 1, Name => 'MacintoshPrintInfo' },
93 0x03ea => { Unknown => 1, Name => 'XMLData', Binary => 1 }, #PH
94 0x03eb => { Unknown => 1, Name => 'Photoshop2ColorTable' },
95 0x03ed => {
96 Name => 'ResolutionInfo',
97 SubDirectory => {
98 TagTable => 'Image::ExifTool::Photoshop::Resolution',
99 },
100 },
101 0x03ee => {
102 Name => 'AlphaChannelsNames',
103 ValueConv => 'Image::ExifTool::Photoshop::ConvertPascalString($self,$val)',
104 },
105 0x03ef => { Unknown => 1, Name => 'DisplayInfo' },
106 0x03f0 => { Unknown => 1, Name => 'PStringCaption' },
107 0x03f1 => { Unknown => 1, Name => 'BorderInformation' },
108 0x03f2 => { Unknown => 1, Name => 'BackgroundColor' },
109 0x03f3 => { Unknown => 1, Name => 'PrintFlags', Format => 'int8u' },
110 0x03f4 => { Unknown => 1, Name => 'BW_HalftoningInfo' },
111 0x03f5 => { Unknown => 1, Name => 'ColorHalftoningInfo' },
112 0x03f6 => { Unknown => 1, Name => 'DuotoneHalftoningInfo' },
113 0x03f7 => { Unknown => 1, Name => 'BW_TransferFunc' },
114 0x03f8 => { Unknown => 1, Name => 'ColorTransferFuncs' },
115 0x03f9 => { Unknown => 1, Name => 'DuotoneTransferFuncs' },
116 0x03fa => { Unknown => 1, Name => 'DuotoneImageInfo' },
117 0x03fb => { Unknown => 1, Name => 'EffectiveBW', Format => 'int8u' },
118 0x03fc => { Unknown => 1, Name => 'ObsoletePhotoshopTag1' },
119 0x03fd => { Unknown => 1, Name => 'EPSOptions' },
120 0x03fe => { Unknown => 1, Name => 'QuickMaskInfo' },
121 0x03ff => { Unknown => 1, Name => 'ObsoletePhotoshopTag2' },
122 0x0400 => { Unknown => 1, Name => 'TargetLayerID', Format => 'int16u' }, # (LayerStateInfo)
123 0x0401 => { Unknown => 1, Name => 'WorkingPath' },
124 0x0402 => { Unknown => 1, Name => 'LayersGroupInfo', Format => 'int16u' },
125 0x0403 => { Unknown => 1, Name => 'ObsoletePhotoshopTag3' },
126 0x0404 => {
127 Name => 'IPTCData',
128 SubDirectory => {
129 DirName => 'IPTC',
130 TagTable => 'Image::ExifTool::IPTC::Main',
131 },
132 },
133 0x0405 => { Unknown => 1, Name => 'RawImageMode' },
134 0x0406 => { #2
135 Name => 'JPEG_Quality',
136 SubDirectory => {
137 TagTable => 'Image::ExifTool::Photoshop::JPEG_Quality',
138 },
139 },
140 0x0408 => { Unknown => 1, Name => 'GridGuidesInfo' },
141 0x0409 => {
142 Name => 'PhotoshopBGRThumbnail',
143 Notes => 'this is a JPEG image, but in BGR format instead of RGB',
144 %thumbnailInfo,
145 Groups => { 2 => 'Preview' },
146 },
147 0x040a => {
148 Name => 'CopyrightFlag',
149 Writable => 'int8u',
150 Groups => { 2 => 'Author' },
151 ValueConv => 'join(" ",unpack("C*", $val))',
152 ValueConvInv => 'pack("C*",split(" ",$val))',
153 PrintConv => { #3
154 0 => 'False',
155 1 => 'True',
156 },
157 },
158 0x040b => {
159 Name => 'URL',
160 Writable => 'string',
161 Groups => { 2 => 'Author' },
162 },
163 0x040c => {
164 Name => 'PhotoshopThumbnail',
165 %thumbnailInfo,
166 Groups => { 2 => 'Preview' },
167 },
168 0x040d => {
169 Name => 'GlobalAngle',
170 Writable => 'int32u',
171 ValueConv => 'unpack("N",$val)',
172 ValueConvInv => 'pack("N",$val)',
173 },
174 0x040e => { Unknown => 1, Name => 'ColorSamplersResource' },
175 0x040f => {
176 Name => 'ICC_Profile',
177 SubDirectory => {
178 TagTable => 'Image::ExifTool::ICC_Profile::Main',
179 },
180 },
181 0x0410 => { Unknown => 1, Name => 'Watermark', Format => 'int8u' },
182 0x0411 => { Unknown => 1, Name => 'ICC_Untagged', Format => 'int8u' },
183 0x0412 => { Unknown => 1, Name => 'EffectsVisible', Format => 'int8u' },
184 0x0413 => { Unknown => 1, Name => 'SpotHalftone' },
185 0x0414 => { Unknown => 1, Name => 'IDsBaseValue', Description => 'IDs Base Value', Format => 'int32u' },
186 0x0415 => { Unknown => 1, Name => 'UnicodeAlphaNames' },
187 0x0416 => { Unknown => 1, Name => 'IndexedColorTableCount', Format => 'int16u' },
188 0x0417 => { Unknown => 1, Name => 'TransparentIndex', Format => 'int16u' },
189 0x0419 => {
190 Name => 'GlobalAltitude',
191 Writable => 'int32u',
192 ValueConv => 'unpack("N",$val)',
193 ValueConvInv => 'pack("N",$val)',
194 },
195 0x041a => {
196 Name => 'SliceInfo',
197 SubDirectory => { TagTable => 'Image::ExifTool::Photoshop::SliceInfo' },
198 },
199 0x041b => { Name => 'WorkflowURL', %unicodeString },
200 0x041c => { Unknown => 1, Name => 'JumpToXPEP' },
201 0x041d => { Unknown => 1, Name => 'AlphaIdentifiers' },
202 0x041e => {
203 Name => 'URL_List',
204 List => 1,
205 Writable => 1,
206 ValueConv => sub {
207 my ($val, $et) = @_;
208 return '<err>' if length($val) < 4;
209 my $num = unpack('N', $val);
210 my ($i, @vals);
211 my $pos = 4;
212 for ($i=0; $i<$num; ++$i) {
213 $pos += 8; # (skip word and ID)
214 last if length($val) < $pos + 4;
215 my $len = unpack("x${pos}N", $val) * 2;
216 last if length($val) < $pos + 4 + $len;
217 push @vals, $et->Decode(substr($val,$pos+4,$len), 'UCS2', 'MM');
218 $pos += 4 + $len;
219 }
220 return \@vals;
221 },
222 # (this is tricky to make writable)
223 },
224 0x0421 => {
225 Name => 'VersionInfo',
226 SubDirectory => {
227 TagTable => 'Image::ExifTool::Photoshop::VersionInfo',
228 },
229 },
230 0x0422 => {
231 Name => 'EXIFInfo', #PH (Found in EPS and PSD files)
232 SubDirectory => {
233 TagTable=> 'Image::ExifTool::Exif::Main',
234 ProcessProc => \&Image::ExifTool::ProcessTIFF,
235 WriteProc => \&Image::ExifTool::WriteTIFF,
236 },
237 },
238 0x0423 => { Unknown => 1, Name => 'ExifInfo2', Binary => 1 }, #5
239 0x0424 => {
240 Name => 'XMP',
241 SubDirectory => {
242 TagTable => 'Image::ExifTool::XMP::Main',
243 },
244 },
245 0x0425 => {
246 Name => 'IPTCDigest',
247 Writable => 'string',
248 Protected => 1,
249 Notes => q{
250 this tag indicates provides a way for XMP-aware applications to indicate
251 that the XMP is synchronized with the IPTC. When writing, special values of
252 "new" and "old" represent the digests of the IPTC from the edited and
253 original files respectively, and are undefined if the IPTC does not exist in
254 the respective file. Set this to "new" as an indication that the XMP is
255 synchronized with the IPTC
256 },
257 # also note the 'new' feature requires that the IPTC comes before this tag is written
258 ValueConv => 'unpack("H*", $val)',
259 ValueConvInv => q{
260 if (lc($val) eq 'new' or lc($val) eq 'old') {
261 {
262 local $SIG{'__WARN__'} = sub { };
263 return lc($val) if eval { require Digest::MD5 };
264 }
265 warn "Digest::MD5 must be installed\n";
266 return undef;
267 }
268 return pack('H*', $val) if $val =~ /^[0-9a-f]{32}$/i;
269 warn "Value must be 'new', 'old' or 32 hexadecimal digits\n";
270 return undef;
271 }
272 },
273 0x0426 => {
274 Name => 'PrintScaleInfo',
275 SubDirectory => { TagTable => 'Image::ExifTool::Photoshop::PrintScaleInfo' },
276 },
277 0x0428 => {
278 Name => 'PixelInfo',
279 SubDirectory => { TagTable => 'Image::ExifTool::Photoshop::PixelInfo' },
280 },
281 0x0429 => { Unknown => 1, Name => 'LayerComps' }, #5
282 0x042a => { Unknown => 1, Name => 'AlternateDuotoneColors' }, #5
283 0x042b => { Unknown => 1, Name => 'AlternateSpotColors' }, #5
284 0x042d => { #7
285 Name => 'LayerSelectionIDs',
286 Description => 'Layer Selection IDs',
287 Unknown => 1,
288 ValueConv => q{
289 my ($n, @a) = unpack("nN*",$val);
290 $#a = $n - 1 if $n > @a;
291 return join(' ', @a);
292 },
293 },
294 0x042e => { Unknown => 1, Name => 'HDRToningInfo' }, #7
295 0x042f => { Unknown => 1, Name => 'PrintInfo' }, #7
296 0x0430 => { Unknown => 1, Name => 'LayerGroupsEnabledID', Format => 'int8u' }, #7
297 0x0431 => { Unknown => 1, Name => 'ColorSamplersResource2' }, #7
298 0x0432 => { Unknown => 1, Name => 'MeasurementScale' }, #7
299 0x0433 => { Unknown => 1, Name => 'TimelineInfo' }, #7
300 0x0434 => { Unknown => 1, Name => 'SheetDisclosure' }, #7
301 0x0435 => { Unknown => 1, Name => 'DisplayInfo' }, #7
302 0x0436 => { Unknown => 1, Name => 'OnionSkins' }, #7
303 0x0438 => { Unknown => 1, Name => 'CountInfo' }, #7
304 0x043a => { Unknown => 1, Name => 'PrintInfo2' }, #7
305 0x043b => { Unknown => 1, Name => 'PrintStyle' }, #7
306 0x043c => { Unknown => 1, Name => 'MacintoshNSPrintInfo' }, #7
307 0x043d => { Unknown => 1, Name => 'WindowsDEVMODE' }, #7
308 0x043e => { Unknown => 1, Name => 'AutoSaveFilePath' }, #7
309 0x043f => { Unknown => 1, Name => 'AutoSaveFormat' }, #7
310 0x0440 => { Unknown => 1, Name => 'PathSelectionState' }, #7
311 # 0x07d0-0x0bb6 Path information
312 0x0bb7 => {
313 Name => 'ClippingPathName',
314 # convert from a Pascal string (ignoring 6 bytes of unknown data after string)
315 ValueConv => q{
316 my $len = ord($val);
317 $val = substr($val, 0, $len+1) if $len < length($val);
318 return Image::ExifTool::Photoshop::ConvertPascalString($self,$val);
319 },
320 },
321 0x0bb8 => { Unknown => 1, Name => 'OriginPathInfo' }, #7
322 # 0x0fa0-0x1387 - plug-in resources (ref 7)
323 0x1b58 => { Unknown => 1, Name => 'ImageReadyVariables' }, #7
324 0x1b59 => { Unknown => 1, Name => 'ImageReadyDataSets' }, #7
325 0x1f40 => { Unknown => 1, Name => 'LightroomWorkflow' }, #7
326 0x2710 => { Unknown => 1, Name => 'PrintFlagsInfo' },
327);
328
329# Photoshop JPEG quality record (ref 2)
330%Image::ExifTool::Photoshop::JPEG_Quality = (
331 PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
332 WRITE_PROC => \&Image::ExifTool::WriteBinaryData,
333 CHECK_PROC => \&Image::ExifTool::CheckBinaryData,
334 FORMAT => 'int16s',
335 GROUPS => { 2 => 'Image' },
336 0 => {
337 Name => 'PhotoshopQuality',
338 Writable => 1,
339 PrintConv => '$val + 4',
340 PrintConvInv => '$val - 4',
341 },
342 1 => {
343 Name => 'PhotoshopFormat',
344 PrintConv => {
345 0x0000 => 'Standard',
346 0x0001 => 'Optimized',
347 0x0101 => 'Progressive',
348 },
349 },
350 2 => {
351 Name => 'ProgressiveScans',
352 PrintConv => {
353 1 => '3 Scans',
354 2 => '4 Scans',
355 3 => '5 Scans',
356 },
357 },
358);
359
360# Photoshop Slices
361%Image::ExifTool::Photoshop::SliceInfo = (
362 PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
363 20 => { Name => 'SlicesGroupName', Format => 'var_ustr32' },
364 24 => { Name => 'NumSlices', Format => 'int32u' },
365);
366
367# Photoshop resolution information #PH
368%Image::ExifTool::Photoshop::Resolution = (
369 PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
370 WRITE_PROC => \&Image::ExifTool::WriteBinaryData,
371 CHECK_PROC => \&Image::ExifTool::CheckBinaryData,
372 FORMAT => 'int16u',
373 FIRST_ENTRY => 0,
374 WRITABLE => 1,
375 GROUPS => { 2 => 'Image' },
376 0 => {
377 Name => 'XResolution',
378 Format => 'int32u',
379 Priority => 0,
380 ValueConv => '$val / 0x10000',
381 ValueConvInv => 'int($val * 0x10000 + 0.5)',
382 PrintConv => 'int($val * 100 + 0.5) / 100',
383 PrintConvInv => '$val',
384 },
385 2 => {
386 Name => 'DisplayedUnitsX',
387 PrintConv => {
388 1 => 'inches',
389 2 => 'cm',
390 },
391 },
392 4 => {
393 Name => 'YResolution',
394 Format => 'int32u',
395 Priority => 0,
396 ValueConv => '$val / 0x10000',
397 ValueConvInv => 'int($val * 0x10000 + 0.5)',
398 PrintConv => 'int($val * 100 + 0.5) / 100',
399 PrintConvInv => '$val',
400 },
401 6 => {
402 Name => 'DisplayedUnitsY',
403 PrintConv => {
404 1 => 'inches',
405 2 => 'cm',
406 },
407 },
408);
409
410# Photoshop version information
411%Image::ExifTool::Photoshop::VersionInfo = (
412 PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
413 WRITE_PROC => \&Image::ExifTool::WriteBinaryData,
414 CHECK_PROC => \&Image::ExifTool::CheckBinaryData,
415 FIRST_ENTRY => 0,
416 GROUPS => { 2 => 'Image' },
417 # (always 1) 0 => { Name => 'PhotoshopVersion', Format => 'int32u' },
418 4 => { Name => 'HasRealMergedData', Format => 'int8u', PrintConv => { 0 => 'No', 1 => 'Yes' } },
419 5 => { Name => 'WriterName', Format => 'var_ustr32' },
420 9 => { Name => 'ReaderName', Format => 'var_ustr32' },
421 # (always 1) 13 => { Name => 'FileVersion', Format => 'int32u' },
422);
423
424# Print Scale
425%Image::ExifTool::Photoshop::PrintScaleInfo = (
426 PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
427 WRITE_PROC => \&Image::ExifTool::WriteBinaryData,
428 CHECK_PROC => \&Image::ExifTool::CheckBinaryData,
429 FIRST_ENTRY => 0,
430 GROUPS => { 2 => 'Image' },
431 0 => {
432 Name => 'PrintStyle',
433 Format => 'int16u',
434 PrintConv => {
435 0 => 'Centered',
436 1 => 'Size to Fit',
437 2 => 'User Defined',
438 },
439 },
440 2 => { Name => 'PrintPosition', Format => 'float[2]' },
441 10 => { Name => 'PrintScale', Format => 'float' },
442);
443
444# Pixel Aspect Ratio
445%Image::ExifTool::Photoshop::PixelInfo = (
446 PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
447 WRITE_PROC => \&Image::ExifTool::WriteBinaryData,
448 CHECK_PROC => \&Image::ExifTool::CheckBinaryData,
449 FIRST_ENTRY => 0,
450 GROUPS => { 2 => 'Image' },
451 # 0 - version
452 4 => { Name => 'PixelAspectRatio', Format => 'double' },
453);
454
455# Photoshop PSD file header
456%Image::ExifTool::Photoshop::Header = (
457 PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
458 FORMAT => 'int16u',
459 GROUPS => { 2 => 'Image' },
460 NOTES => 'This information is found in the PSD file header.',
461 6 => 'NumChannels',
462 7 => { Name => 'ImageHeight', Format => 'int32u' },
463 9 => { Name => 'ImageWidth', Format => 'int32u' },
464 11 => 'BitDepth',
465 12 => {
466 Name => 'ColorMode',
467 PrintConvColumns => 2,
468 PrintConv => {
469 0 => 'Bitmap',
470 1 => 'Grayscale',
471 2 => 'Indexed',
472 3 => 'RGB',
473 4 => 'CMYK',
474 7 => 'Multichannel',
475 8 => 'Duotone',
476 9 => 'Lab',
477 },
478 },
479);
480
481# Layer information
482%Image::ExifTool::Photoshop::Layers = (
483 PROCESS_PROC => \&ProcessLayers,
484 GROUPS => { 2 => 'Image' },
485 NOTES => 'Tags extracted from Photoshop layer information.',
486 # tags extracted from layer information
487 # (tag ID's are for convenience only)
488 _xcnt => { Name => 'LayerCount', Format => 'int16u' },
489 _xrct => {
490 Name => 'LayerRectangles',
491 Format => 'int32u',
492 Count => 4,
493 List => 1,
494 Notes => 'top left bottom right',
495 },
496 _xnam => { Name => 'LayerNames',
497 Format => 'string',
498 List => 1,
499 ValueConv => q{
500 my $charset = $self->Options('CharsetPhotoshop') || 'Latin';
501 return $self->Decode($val, $charset);
502 },
503 },
504 _xbnd => {
505 Name => 'LayerBlendModes',
506 Format => 'undef',
507 List => 1,
508 RawConv => 'GetByteOrder() eq "II" ? pack "N*", unpack "V*", $val : $val',
509 PrintConv => {
510 pass => 'Pass Through',
511 norm => 'Normal',
512 diss => 'Dissolve',
513 dark => 'Darken',
514 'mul '=> 'Multiply',
515 idiv => 'Color Burn',
516 lbrn => 'Linear Burn',
517 dkCl => 'Darker Color',
518 lite => 'Lighten',
519 scrn => 'Screen',
520 'div '=> 'Color Dodge',
521 lddg => 'Linear Dodge',
522 lgCl => 'Lighter Color',
523 over => 'Overlay',
524 sLit => 'Soft Light',
525 hLit => 'Hard Light',
526 vLit => 'Vivid Light',
527 lLit => 'Linear Light',
528 pLit => 'Pin Light',
529 hMix => 'Hard Mix',
530 diff => 'Difference',
531 smud => 'Exclusion',
532 fsub => 'Subtract',
533 fdiv => 'Divide',
534 'hue '=> 'Hue',
535 'sat '=> 'Saturation',
536 colr => 'Color',
537 'lum '=> 'Luminosity',
538 },
539 },
540 _xopc => {
541 Name => 'LayerOpacities',
542 Format => 'int8u',
543 List => 1,
544 ValueConv => '100 * $val / 255',
545 PrintConv => 'sprintf("%d%%",$val)',
546 },
547 # tags extracted from additional layer information (tag ID's are real)
548 # - must be able to accommodate a blank entry to preserve the list ordering
549 luni => {
550 Name => 'LayerUnicodeNames',
551 List => 1,
552 RawConv => q{
553 return '' if length($val) < 4;
554 my $len = Get32u(\$val, 0);
555 return $self->Decode(substr($val, 4, $len * 2), 'UCS2');
556 },
557 },
558 lyid => {
559 Name => 'LayerIDs',
560 Description => 'Layer IDs',
561 Format => 'int32u',
562 List => 1,
563 Unknown => 1,
564 },
565 shmd => { # layer metadata (undocumented structure)
566 # (for now, only extract layerTime. May also contain "layerXMP" --
567 # it would be nice to decode this but I need a sample)
568 Name => 'LayerModifyDates',
569 Groups => { 2 => 'Time' },
570 List => 1,
571 RawConv => q{
572 return '' unless $val =~ /layerTime(doub|buod)(.{8})/s;
573 my $tmp = $2;
574 return GetDouble(\$tmp, 0);
575 },
576 ValueConv => 'length $val ? ConvertUnixTime($val,1) : ""',
577 PrintConv => 'length $val ? $self->ConvertDateTime($val) : ""',
578 },
579);
580
581# tags extracted from ImageSourceData found in TIFF images (ref PH)
582%Image::ExifTool::Photoshop::DocumentData = (
583 PROCESS_PROC => \&ProcessDocumentData,
584 GROUPS => { 2 => 'Image' },
585 Layr => {
586 Name => 'Layers',
587 SubDirectory => { TagTable => 'Image::ExifTool::Photoshop::Layers' },
588 },
589 Lr16 => { # (NC)
590 Name => 'Layers',
591 SubDirectory => { TagTable => 'Image::ExifTool::Photoshop::Layers' },
592 },
593);
594
595# image data
596%Image::ExifTool::Photoshop::ImageData = (
597 PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
598 GROUPS => { 2 => 'Image' },
599 0 => {
600 Name => 'Compression',
601 Format => 'int16u',
602 PrintConv => {
603 0 => 'Uncompressed',
604 1 => 'RLE',
605 2 => 'ZIP without prediction',
606 3 => 'ZIP with prediction',
607 },
608 },
609);
610
611# tags for unknown resource types
612%Image::ExifTool::Photoshop::Unknown = (
613 GROUPS => { 2 => 'Unknown' },
614);
615
616# define reference to IPTCDigest tagInfo hash for convenience
617$iptcDigestInfo = $Image::ExifTool::Photoshop::Main{0x0425};
618
619
620#------------------------------------------------------------------------------
621# AutoLoad our writer routines when necessary
622#
623sub AUTOLOAD
624{
625 return Image::ExifTool::DoAutoLoad($AUTOLOAD, @_);
626}
627
628#------------------------------------------------------------------------------
629# Convert pascal string(s) to something we can use
630# Inputs: 1) Pascal string data
631# Returns: Strings, concatenated with ', '
632sub ConvertPascalString($$)
633{
634 my ($et, $inStr) = @_;
635 my $outStr = '';
636 my $len = length($inStr);
637 my $i=0;
638 while ($i < $len) {
639 my $n = ord(substr($inStr, $i, 1));
640 last if $i + $n >= $len;
641 $i and $outStr .= ', ';
642 $outStr .= substr($inStr, $i+1, $n);
643 $i += $n + 1;
644 }
645 my $charset = $et->Options('CharsetPhotoshop') || 'Latin';
646 return $et->Decode($outStr, $charset);
647}
648
649#------------------------------------------------------------------------------
650# Process Photoshop layers and mask information section of PSD/PSB file
651# Inputs: 0) ExifTool ref, 1) DirInfo ref, 2) tag table ref
652# Returns: 1 on success (and seeks to the end of this section)
653sub ProcessLayersAndMask($$$)
654{
655 local $_;
656 my ($et, $dirInfo, $tagTablePtr) = @_;
657 my $raf = $$dirInfo{RAF};
658 my $fileType = $$et{VALUE}{FileType};
659 my $data;
660
661 return 0 unless $fileType eq 'PSD' or $fileType eq 'PSB'; # (no layer section in CS1 files)
662
663 # (some words are 4 bytes in PSD files and 8 bytes in PSB)
664 my ($psb, $psiz) = $fileType eq 'PSB' ? (1, 8) : (undef, 4);
665
666 # read the layer information header
667 my $n = $psiz * 2 + 2;
668 $raf->Read($data, $n) == $n or return 0;
669 my $tot = $psb ? Get64u(\$data, 0) : Get32u(\$data, 0); # length of layer and mask info
670 return 1 if $tot == 0;
671 my $end = $raf->Tell() - $psiz - 2 + $tot;
672 $data = substr $data, $psiz;
673 my $len = $psb ? Get64u(\$data, 0) : Get32u(\$data, 0); # length of layer info section
674 my $num = Get16s(\$data, $psiz);
675 # check for Lr16 block if layers length is 0 (ref https://forums.adobe.com/thread/1540914)
676 if ($len == 0 and $num == 0) {
677 $raf->Read($data,10) == 10 or return 0;
678 if ($data =~ /^..8BIMLr16/s) {
679 $raf->Read($data, $psiz+2) == $psiz+2 or return 0;
680 $len = $psb ? Get64u(\$data, 0) : Get32u(\$data, 0);
681 } elsif ($data =~ /^..8BIMMt16/s) { # (have seen Mt16 before Lr16, ref PH)
682 $raf->Read($data, $psiz) == $psiz or return 0;
683 $raf->Read($data, 8) == 8 or return 0;
684 if ($data eq '8BIMLr16') {
685 $raf->Read($data, $psiz+2) == $psiz+2 or return 0;
686 $len = $psb ? Get64u(\$data, 0) : Get32u(\$data, 0);
687 } else {
688 $raf->Seek(-18-$psiz, 1) or return 0;
689 }
690 } else {
691 $raf->Seek(-10, 1) or return 0;
692 }
693 }
694 $len += 2; # include layer count with layer info section
695 $raf->Seek(-2, 1) or return 0;
696 my %dinfo = (
697 RAF => $raf,
698 DirLen => $len,
699 );
700 $$et{IsPSB} = $psb; # set PSB flag
701 ProcessLayers($et, \%dinfo, $tagTablePtr);
702
703 # seek to the end of this section and return success flag
704 return $raf->Seek($end, 0) ? 1 : 0;
705}
706
707#------------------------------------------------------------------------------
708# Process Photoshop layers (beginning with layer count)
709# Inputs: 0) ExifTool ref, 1) DirInfo ref, 2) tag table ref
710# Returns: 1 on success
711# Notes: Uses ExifTool IsPSB member to determine whether file is PSB format
712sub ProcessLayers($$$)
713{
714 local $_;
715 my ($et, $dirInfo, $tagTablePtr) = @_;
716 my ($i, $n, %count, $buff, $buf2);
717 my $raf = $$dirInfo{RAF};
718 my $dirLen = $$dirInfo{DirLen};
719 my $verbose = $$et{OPTIONS}{Verbose};
720 my %dinfo = ( DataPt => \$buff, Base => $raf->Tell() );
721 my $pos = 0;
722 return 0 if $dirLen < 2;
723 $raf->Read($buff, 2) == 2 or return 0;
724 my $num = Get16s(\$buff, 0);
725 $num = -$num if $num < 0; # (first channel is transparency data if negative)
726 $et->VerboseDir('Layers', $num, $dirLen);
727 $et->HandleTag($tagTablePtr, '_xcnt', $num, Start => $pos, Size => 2, %dinfo); # LayerCount
728 my $oldIndent = $$et{INDENT};
729 $$et{INDENT} .= '| ';
730
731 $pos += 2;
732 my $psb = $$et{IsPSB}; # is PSB format?
733 my $psiz = $psb ? 8 : 4;
734 for ($i=0; $i<$num; ++$i) {
735 $et->VPrint(0, $oldIndent.'+ [Layer '.($i+1)." of $num]\n");
736 last if $pos + 18 > $dirLen;
737 $raf->Read($buff, 18) == 18 or last;
738 $dinfo{DataPos} = $pos;
739 # save the layer rectangle
740 $et->HandleTag($tagTablePtr, '_xrct', undef, Start => 0, Size => 16, %dinfo);
741 my $numChannels = Get16u(\$buff, 16);
742 $n = (2 + $psiz) * $numChannels; # size of channel information
743 $raf->Seek($n, 1) or last;
744 $pos += 18 + $n;
745 last if $pos + 20 > $dirLen;
746 $raf->Read($buff, 20) == 20 or last;
747 $dinfo{DataPos} = $pos;
748 my $sig = substr($buff, 0, 4);
749 $sig =~ /^(8BIM|MIB8)$/ or last; # verify signature
750 $et->HandleTag($tagTablePtr, '_xbnd', undef, Start => 4, Size => 4, %dinfo);
751 $et->HandleTag($tagTablePtr, '_xopc', undef, Start => 8, Size => 1, %dinfo);
752 my $nxt = $pos + 16 + Get32u(\$buff, 12);
753 $n = Get32u(\$buff, 16); # get size of layer mask data
754 $pos += 20 + $n; # skip layer mask data
755 last if $pos + 4 > $dirLen;
756 $raf->Seek($n, 1) and $raf->Read($buff, 4) == 4 or last;
757 $n = Get32u(\$buff, 0); # get size of layer blending ranges
758 $pos += 4 + $n; # skip layer blending ranges data
759 last if $pos + 1 > $dirLen;
760 $raf->Seek($n, 1) and $raf->Read($buff, 1) == 1 or last;
761 $n = Get8u(\$buff, 0); # get length of layer name
762 last if $pos + 1 + $n > $dirLen;
763 $raf->Read($buff, $n) == $n or last;
764 $dinfo{DataPos} = $pos + 1;
765 $et->HandleTag($tagTablePtr, '_xnam', undef, Start => 0, Size => $n, %dinfo);
766 my $frag = ($n + 1) & 0x3;
767 $raf->Seek(4 - $frag, 1) or last if $frag;
768 $n = ($n + 4) & 0xfffffffc; # +1 for length byte then pad to multiple of 4 bytes
769 $pos += $n;
770 # process additional layer info
771 while ($pos + 12 <= $nxt) {
772 $raf->Read($buff, 12) == 12 or last;
773 my $dat = substr($buff, 0, 8);
774 $dat = pack 'N*', unpack 'V*', $dat if GetByteOrder() eq 'II';
775 my $sig = substr($dat, 0, 4);
776 last unless $sig eq '8BIM' or $sig eq '8B64'; # verify signature
777 my $tag = substr($dat, 4, 4);
778 # (some structures have an 8-byte size word [augh!]
779 # --> it would be great if '8B64' indicated a 64-bit version, and this may well
780 # be the case, but it is not mentioned in the Photoshop file format specification)
781 if ($psb and $tag =~ /^(LMsk|Lr16|Lr32|Layr|Mt16|Mt32|Mtrn|Alph|FMsk|lnk2|FEid|FXid|PxSD)$/) {
782 last if $pos + 16 > $nxt;
783 $raf->Read($buf2, 4) == 4 or last;
784 $buff .= $buf2;
785 $n = Get64u(\$buff, 8);
786 $pos += 4;
787 } else {
788 $n = Get32u(\$buff, 8);
789 }
790 $pos += 12;
791 last if $pos + $n > $nxt;
792 $frag = $n & 0x3;
793 if ($$tagTablePtr{$tag} or $verbose) {
794 # pad with empty entries if necessary to keep the same index for each item in the layer
795 $count{$tag} = 0 unless defined $count{$tag};
796 $raf->Read($buff, $n) == $n or last;
797 $dinfo{DataPos} = $pos;
798 while ($count{$tag} < $i) {
799 $et->HandleTag($tagTablePtr, $tag, '');
800 ++$count{$tag};
801 }
802 $et->HandleTag($tagTablePtr, $tag, undef, Start => 0, Size => $n, %dinfo);
803 ++$count{$tag};
804 if ($frag) {
805 $raf->Seek(4 - $frag, 1) or last;
806 $n += 4 - $frag; # pad to multiple of 4 bytes (PH NC)
807 }
808 } else {
809 $n += 4 - $frag if $frag;
810 $raf->Seek($n, 1) or last;
811 }
812 $pos += $n; # step to start of next structure
813 }
814 $pos = $nxt;
815 }
816 $$et{INDENT} = $oldIndent;
817 return 1;
818}
819
820#------------------------------------------------------------------------------
821# Process Photoshop ImageSourceData
822# Inputs: 0) ExifTool ref, 1) dirInfo ref, 2) tag table ref
823# Returns: 1 on success
824sub ProcessDocumentData($$$)
825{
826 my ($et, $dirInfo, $tagTablePtr) = @_;
827 my $verbose = $$et{OPTIONS}{Verbose};
828 my $raf = $$dirInfo{RAF};
829 my $dirLen = $$dirInfo{DirLen};
830 my $pos = 36; # length of header
831 my ($buff, $n, $err);
832
833 $et->VerboseDir('Photoshop Document Data', undef, $dirLen);
834 unless ($raf) {
835 my $dataPt = $$dirInfo{DataPt};
836 my $start = $$dirInfo{DirStart} || 0;
837 $raf = new File::RandomAccess($dataPt);
838 $raf->Seek($start, 0) if $start;
839 $dirLen = length $$dataPt - $start unless defined $dirLen;
840 $et->VerboseDump($dataPt, Start => $start, Len => $dirLen, Base => $$dirInfo{Base});
841 }
842 unless ($raf->Read($buff, $pos) == $pos and
843 $buff =~ /^Adobe Photoshop Document Data (Block|V0002)\0/)
844 {
845 $et->Warn('Invalid Photoshop Document Data');
846 return 0;
847 }
848 my $psb = ($1 eq 'V0002');
849 my %dinfo = ( DataPt => \$buff );
850 $$et{IsPSB} = $psb; # set PSB flag (needed when handling Layers directory)
851 while ($pos + 12 <= $dirLen) {
852 $raf->Read($buff, 8) == 8 or $err = 'Error reading document data', last;
853 # set byte order according to byte order of first signature
854 SetByteOrder($buff =~ /^(8BIM|8B64)/ ? 'MM' : 'II') if $pos == 36;
855 $buff = pack 'N*', unpack 'V*', $buff if GetByteOrder() eq 'II';
856 my $sig = substr($buff, 0, 4);
857 $sig eq '8BIM' or $sig eq '8B64' or $err = 'Bad photoshop resource', last; # verify signature
858 my $tag = substr($buff, 4, 4);
859 if ($psb and $tag =~ /^(LMsk|Lr16|Lr32|Layr|Mt16|Mt32|Mtrn|Alph|FMsk|lnk2|FEid|FXid|PxSD)$/) {
860 $pos + 16 > $dirLen and $err = 'Short PSB resource', last;
861 $raf->Read($buff, 8) == 8 or $err = 'Error reading PSB resource', last;
862 $n = Get64u(\$buff, 0);
863 $pos += 4;
864 } else {
865 $raf->Read($buff, 4) == 4 or $err = 'Error reading PSD resource', last;
866 $n = Get32u(\$buff, 0);
867 }
868 $pos += 12;
869 $pos + $n > $dirLen and $err = 'Truncated photoshop resource', last;
870 my $pad = (4 - ($n & 3)) & 3; # number of padding bytes
871 my $tagInfo = $$tagTablePtr{$tag};
872 if ($tagInfo or $verbose) {
873 if ($tagInfo and $$tagInfo{SubDirectory}) {
874 my $fpos = $raf->Tell() + $n + $pad;
875 my $subTable = GetTagTable($$tagInfo{SubDirectory}{TagTable});
876 $et->ProcessDirectory({ RAF => $raf, DirLen => $n }, $subTable);
877 $raf->Seek($fpos, 0) or $err = 'Seek error', last;
878 } else {
879 $dinfo{DataPos} = $raf->Tell();
880 $dinfo{Start} = 0;
881 $dinfo{Size} = $n;
882 $raf->Read($buff, $n) == $n or $err = 'Error reading photoshop resource', last;
883 $et->HandleTag($tagTablePtr, $tag, undef, %dinfo);
884 $raf->Seek($pad, 1) or $err = 'Seek error', last;
885 }
886 } else {
887 $raf->Seek($n + $pad, 1) or $err = 'Seek error', last;
888 }
889 $pos += $n + $pad; # step to start of next structure
890 }
891 $err and $et->Warn($err);
892 return 1;
893}
894
895#------------------------------------------------------------------------------
896# Process Photoshop APP13 record
897# Inputs: 0) ExifTool object reference, 1) Reference to directory information
898# 2) Tag table reference
899# Returns: 1 on success
900sub ProcessPhotoshop($$$)
901{
902 my ($et, $dirInfo, $tagTablePtr) = @_;
903 my $dataPt = $$dirInfo{DataPt};
904 my $pos = $$dirInfo{DirStart};
905 my $dirEnd = $pos + $$dirInfo{DirLen};
906 my $verbose = $et->Options('Verbose');
907 my $success = 0;
908
909 # ignore non-standard XMP while in strict MWG compatibility mode
910 if (($Image::ExifTool::MWG::strict or $et->Options('Validate')) and
911 $$et{FILE_TYPE} =~ /^(JPEG|TIFF|PSD)$/)
912 {
913 my $path = $et->MetadataPath();
914 unless ($path =~ /^(JPEG-APP13-Photoshop|TIFF-IFD0-Photoshop|PSD)$/) {
915 if ($Image::ExifTool::MWG::strict) {
916 $et->Warn("Ignored non-standard Photoshop at $path");
917 return 1;
918 } else {
919 $et->Warn("Non-standard Photoshop at $path", 1);
920 }
921 }
922 }
923 if ($$et{FILE_TYPE} eq 'JPEG' and $$dirInfo{Parent} ne 'APP13') {
924 $$et{LOW_PRIORITY_DIR}{'*'} = 1; # lower priority of all these tags
925 }
926 SetByteOrder('MM'); # Photoshop is always big-endian
927 $verbose and $et->VerboseDir('Photoshop', 0, $$dirInfo{DirLen});
928
929 # scan through resource blocks:
930 # Format: 0) Type, 4 bytes - '8BIM' (or the rare 'PHUT', 'DCSR', 'AgHg' or 'MeSa')
931 # 1) TagID,2 bytes
932 # 2) Name, pascal string padded to even no. bytes
933 # 3) Size, 4 bytes - N
934 # 4) Data, N bytes
935 while ($pos + 8 < $dirEnd) {
936 my $type = substr($$dataPt, $pos, 4);
937 my ($ttPtr, $extra, $val, $name);
938 if ($type eq '8BIM') {
939 $ttPtr = $tagTablePtr;
940 } elsif ($type =~ /^(PHUT|DCSR|AgHg|MeSa)$/) { # (PHUT~ImageReady, MeSa~PhotoDeluxe)
941 $ttPtr = GetTagTable('Image::ExifTool::Photoshop::Unknown');
942 } else {
943 $type =~ s/([^\w])/sprintf("\\x%.2x",ord($1))/ge;
944 $et->Warn(qq{Bad Photoshop IRB resource "$type"});
945 last;
946 }
947 my $tag = Get16u($dataPt, $pos + 4);
948 $pos += 6; # point to start of name
949 my $nameLen = Get8u($dataPt, $pos);
950 my $namePos = ++$pos;
951 # skip resource block name (pascal string, padded to an even # of bytes)
952 $pos += $nameLen;
953 ++$pos unless $nameLen & 0x01;
954 if ($pos + 4 > $dirEnd) {
955 $et->Warn("Bad Photoshop resource block");
956 last;
957 }
958 my $size = Get32u($dataPt, $pos);
959 $pos += 4;
960 if ($size + $pos > $dirEnd) {
961 $et->Warn("Bad Photoshop resource data size $size");
962 last;
963 }
964 $success = 1;
965 if ($nameLen) {
966 $name = substr($$dataPt, $namePos, $nameLen);
967 $extra = qq{, Name="$name"};
968 } else {
969 $name = '';
970 }
971 my $tagInfo = $et->GetTagInfo($ttPtr, $tag);
972 # append resource name to value if requested (braced by "/#...#/")
973 if ($tagInfo and defined $$tagInfo{SetResourceName} and
974 $$tagInfo{SetResourceName} eq '1' and $name !~ m{/#})
975 {
976 $val = substr($$dataPt, $pos, $size) . '/#' . $name . '#/';
977 }
978 $et->HandleTag($ttPtr, $tag, $val,
979 TagInfo => $tagInfo,
980 Extra => $extra,
981 DataPt => $dataPt,
982 DataPos => $$dirInfo{DataPos},
983 Size => $size,
984 Start => $pos,
985 Base => $$dirInfo{Base},
986 Parent => $$dirInfo{DirName},
987 );
988 $size += 1 if $size & 0x01; # size is padded to an even # bytes
989 $pos += $size;
990 }
991 delete $$et{LOW_PRIORITY_DIR}{'*'};
992 return $success;
993}
994
995#------------------------------------------------------------------------------
996# extract information from Photoshop PSD file
997# Inputs: 0) ExifTool object reference, 1) dirInfo reference
998# Returns: 1 if this was a valid PSD file, -1 on write error
999sub ProcessPSD($$)
1000{
1001 my ($et, $dirInfo) = @_;
1002 my $raf = $$dirInfo{RAF};
1003 my $outfile = $$dirInfo{OutFile};
1004 my ($data, $err, $tagTablePtr);
1005
1006 $raf->Read($data, 30) == 30 or return 0;
1007 $data =~ /^8BPS\0([\x01\x02])/ or return 0;
1008 SetByteOrder('MM');
1009 $et->SetFileType($1 eq "\x01" ? 'PSD' : 'PSB'); # set the FileType tag
1010 my %dirInfo = (
1011 DataPt => \$data,
1012 DirStart => 0,
1013 DirName => 'Photoshop',
1014 );
1015 my $len = Get32u(\$data, 26);
1016 if ($outfile) {
1017 Write($outfile, $data) or $err = 1;
1018 $raf->Read($data, $len) == $len or return -1;
1019 Write($outfile, $data) or $err = 1; # write color mode data
1020 # initialize map of where things are written
1021 $et->InitWriteDirs(\%psdMap);
1022 } else {
1023 # process the header
1024 $tagTablePtr = GetTagTable('Image::ExifTool::Photoshop::Header');
1025 $dirInfo{DirLen} = 30;
1026 $et->ProcessDirectory(\%dirInfo, $tagTablePtr);
1027 $raf->Seek($len, 1) or $err = 1; # skip over color mode data
1028 }
1029 # read image resource section
1030 $raf->Read($data, 4) == 4 or $err = 1;
1031 $len = Get32u(\$data, 0);
1032 $raf->Read($data, $len) == $len or $err = 1;
1033 $tagTablePtr = GetTagTable('Image::ExifTool::Photoshop::Main');
1034 $dirInfo{DirLen} = $len;
1035 my $rtnVal = 1;
1036 if ($outfile) {
1037 # rewrite IRB resources
1038 $data = WritePhotoshop($et, \%dirInfo, $tagTablePtr);
1039 if ($data) {
1040 $len = Set32u(length $data);
1041 Write($outfile, $len, $data) or $err = 1;
1042 # look for trailer and edit if necessary
1043 my $trailInfo = Image::ExifTool::IdentifyTrailer($raf);
1044 if ($trailInfo) {
1045 my $tbuf = '';
1046 $$trailInfo{OutFile} = \$tbuf; # rewrite trailer(s)
1047 # rewrite all trailers to buffer
1048 if ($et->ProcessTrailers($trailInfo)) {
1049 my $copyBytes = $$trailInfo{DataPos} - $raf->Tell();
1050 if ($copyBytes >= 0) {
1051 # copy remaining PSD file up to start of trailer
1052 while ($copyBytes) {
1053 my $n = ($copyBytes > 65536) ? 65536 : $copyBytes;
1054 $raf->Read($data, $n) == $n or $err = 1;
1055 Write($outfile, $data) or $err = 1;
1056 $copyBytes -= $n;
1057 }
1058 # write the trailer (or not)
1059 $et->WriteTrailerBuffer($trailInfo, $outfile) or $err = 1;
1060 } else {
1061 $et->Warn('Overlapping trailer');
1062 undef $trailInfo;
1063 }
1064 } else {
1065 undef $trailInfo;
1066 }
1067 }
1068 unless ($trailInfo) {
1069 # copy over the rest of the file
1070 while ($raf->Read($data, 65536)) {
1071 Write($outfile, $data) or $err = 1;
1072 }
1073 }
1074 } else {
1075 $err = 1;
1076 }
1077 $rtnVal = -1 if $err;
1078 } elsif ($err) {
1079 $et->Warn('File format error');
1080 } else {
1081 # read IRB resources
1082 ProcessPhotoshop($et, \%dirInfo, $tagTablePtr);
1083 # read layer and mask information section
1084 $dirInfo{RAF} = $raf;
1085 $tagTablePtr = GetTagTable('Image::ExifTool::Photoshop::Layers');
1086 my $oldIndent = $$et{INDENT};
1087 $$et{INDENT} .= '| ';
1088 if (ProcessLayersAndMask($et, \%dirInfo, $tagTablePtr) and
1089 # read compression mode from image data section
1090 $raf->Read($data,2) == 2)
1091 {
1092 my %dirInfo = (
1093 DataPt => \$data,
1094 DataPos => $raf->Tell() - 2,
1095 );
1096 $tagTablePtr = GetTagTable('Image::ExifTool::Photoshop::ImageData');
1097 $et->ProcessDirectory(\%dirInfo, $tagTablePtr);
1098 }
1099 $$et{INDENT} = $oldIndent;
1100 # process trailers if they exist
1101 my $trailInfo = Image::ExifTool::IdentifyTrailer($raf);
1102 $et->ProcessTrailers($trailInfo) if $trailInfo;
1103 }
1104 return $rtnVal;
1105}
1106
11071; # end
1108
1109
1110__END__
1111
1112=head1 NAME
1113
1114Image::ExifTool::Photoshop - Read/write Photoshop IRB meta information
1115
1116=head1 SYNOPSIS
1117
1118This module is loaded automatically by Image::ExifTool when required.
1119
1120=head1 DESCRIPTION
1121
1122Photoshop writes its own format of meta information called a Photoshop IRB
1123resource which is located in the APP13 record of JPEG files. This module
1124contains the definitions to read this information.
1125
1126=head1 NOTES
1127
1128Photoshop IRB blocks may have an associated resource name. These names are
1129usually just an empty string, but if not empty they are displayed in the
1130verbose level 2 (or greater) output. A special C<SetResourceName> flag may
1131be set to '1' in the tag information hash to cause the resource name to be
1132appended to the value when extracted. If this is done, the returned value
1133has the form "VALUE/#NAME#/". When writing, the writer routine looks for
1134this syntax (if C<SetResourceName> is defined), and and uses the embedded
1135name to set the name of the new resource. This allows the resource names to
1136be preserved when copying Photoshop information via user-defined tags.
1137
1138=head1 AUTHOR
1139
1140Copyright 2003-2021, Phil Harvey (philharvey66 at gmail.com)
1141
1142This library is free software; you can redistribute it and/or modify it
1143under the same terms as Perl itself.
1144
1145=head1 REFERENCES
1146
1147=over 4
1148
1149=item L<http://www.fine-view.com/jp/lab/doc/ps6ffspecsv2.pdf>
1150
1151=item L<http://www.ozhiker.com/electronics/pjmt/jpeg_info/irb_jpeg_qual.html>
1152
1153=item L<http://www.fileformat.info/format/psd/egff.htm>
1154
1155=item L<http://libpsd.graphest.com/files/Photoshop%20File%20Formats.pdf>
1156
1157=item L<http://www.adobe.com/devnet-apps/photoshop/fileformatashtml/>
1158
1159=back
1160
1161=head1 SEE ALSO
1162
1163L<Image::ExifTool::TagNames/Photoshop Tags>,
1164L<Image::ExifTool(3pm)|Image::ExifTool>,
1165L<Image::MetaData::JPEG(3pm)|Image::MetaData::JPEG>
1166
1167=cut
Note: See TracBrowser for help on using the repository browser.