use
vars
qw($VERSION $AUTOLOAD $iptcDigestInfo %printFlags)
;
$VERSION
=
'1.72'
;
sub
ProcessPhotoshop($$$);
sub
WritePhotoshop($$$);
sub
ProcessLayers($$$);
sub
ProcessChannelOptions($$$);
%printFlags
= (
0
=>
'Labels'
,
1
=>
'Corner crop marks'
,
2
=>
'Color bars'
,
3
=>
'Registration marks'
,
4
=>
'Negative'
,
5
=>
'Emulsion down'
,
6
=>
'Interpolate'
,
7
=>
'Description'
,
8
=>
'Print flags'
,
);
my
%psdMap
= (
IPTC
=>
'Photoshop'
,
XMP
=>
'Photoshop'
,
EXIFInfo
=>
'Photoshop'
,
IFD0
=>
'EXIFInfo'
,
IFD1
=>
'IFD0'
,
ICC_Profile
=>
'Photoshop'
,
ExifIFD
=>
'IFD0'
,
GPS
=>
'IFD0'
,
SubIFD
=>
'IFD0'
,
GlobParamIFD
=>
'IFD0'
,
PrintIM
=>
'IFD0'
,
InteropIFD
=>
'ExifIFD'
,
MakerNotes
=>
'ExifIFD'
,
);
my
%thumbnailInfo
= (
Writable
=>
'undef'
,
Protected
=> 1,
RawConv
=>
'my $img=substr($val,0x1c); $self->ValidateImage(\$img,$tag)'
,
ValueConvInv
=>
q{
my $et = Image::ExifTool->new;
my @tags = qw{ImageWidth ImageHeight FileType}
;
my
$info
=
$et
->ImageInfo(\
$val
,
@tags
);
my
(
$w
,
$h
,
$type
) =
@$info
{
@tags
};
$w
and
$h
and
$type
and
$type
eq
'JPEG'
or
warn
(
"Not a valid JPEG image\n"
),
return
undef
;
my
$wbytes
=
int
((
$w
* 24 + 31) / 32) * 4;
return
pack
(
'N6n2'
, 1,
$w
,
$h
,
$wbytes
,
$wbytes
*
$h
,
length
(
$val
), 24, 1) .
$val
;
},
);
my
%unicodeString
= (
ValueConv
=>
sub
{
my
(
$val
,
$et
) =
@_
;
return
'<err>'
if
length
(
$val
) < 4;
my
$len
=
unpack
(
'N'
,
$val
) * 2;
return
'<err>'
if
length
(
$val
) < 4 +
$len
;
return
$et
->Decode(
substr
(
$val
, 4,
$len
),
'UCS2'
,
'MM'
);
},
ValueConvInv
=>
sub
{
my
(
$val
,
$et
) =
@_
;
return
pack
(
'N'
,
length
$val
) .
$et
->Encode(
$val
,
'UCS2'
,
'MM'
);
},
);
%Image::ExifTool::Photoshop::Main
= (
GROUPS
=> {
2
=>
'Image'
},
PROCESS_PROC
=> \
&ProcessPhotoshop
,
WRITE_PROC
=> \
&WritePhotoshop
,
0x03e8
=> {
Unknown
=> 1,
Name
=>
'Photoshop2Info'
},
0x03e9
=> {
Unknown
=> 1,
Name
=>
'MacintoshPrintInfo'
},
0x03ea
=> {
Unknown
=> 1,
Name
=>
'XMLData'
,
Binary
=> 1 },
0x03eb
=> {
Unknown
=> 1,
Name
=>
'Photoshop2ColorTable'
},
0x03ed
=> {
Name
=>
'ResolutionInfo'
,
SubDirectory
=> {
TagTable
=>
'Image::ExifTool::Photoshop::Resolution'
,
},
},
0x03ee
=> {
Name
=>
'AlphaChannelsNames'
,
ValueConv
=>
'Image::ExifTool::Photoshop::ConvertPascalString($self,$val)'
,
},
0x03ef
=> {
Unknown
=> 1,
Name
=>
'DisplayInfo'
},
0x03f0
=> {
Unknown
=> 1,
Name
=>
'PStringCaption'
},
0x03f1
=> {
Unknown
=> 1,
Name
=>
'BorderInformation'
},
0x03f2
=> {
Unknown
=> 1,
Name
=>
'BackgroundColor'
},
0x03f3
=> {
Unknown
=> 1,
Name
=>
'PrintFlags'
,
Format
=>
'int8u'
,
PrintConv
=>
q{
my $byte = 0;
my @bits = $val =~ /\d+/g;
$byte = ($byte << 1) | ($_ ? 1 : 0) foreach reverse @bits;
return DecodeBits($byte, \%Image::ExifTool::Photoshop::printFlags);
}
,
},
0x03f4
=> {
Unknown
=> 1,
Name
=>
'BW_HalftoningInfo'
},
0x03f5
=> {
Unknown
=> 1,
Name
=>
'ColorHalftoningInfo'
},
0x03f6
=> {
Unknown
=> 1,
Name
=>
'DuotoneHalftoningInfo'
},
0x03f7
=> {
Unknown
=> 1,
Name
=>
'BW_TransferFunc'
},
0x03f8
=> {
Unknown
=> 1,
Name
=>
'ColorTransferFuncs'
},
0x03f9
=> {
Unknown
=> 1,
Name
=>
'DuotoneTransferFuncs'
},
0x03fa
=> {
Unknown
=> 1,
Name
=>
'DuotoneImageInfo'
},
0x03fb
=> {
Unknown
=> 1,
Name
=>
'EffectiveBW'
,
Format
=>
'int8u'
},
0x03fc
=> {
Unknown
=> 1,
Name
=>
'ObsoletePhotoshopTag1'
},
0x03fd
=> {
Unknown
=> 1,
Name
=>
'EPSOptions'
},
0x03fe
=> {
Unknown
=> 1,
Name
=>
'QuickMaskInfo'
},
0x03ff
=> {
Unknown
=> 1,
Name
=>
'ObsoletePhotoshopTag2'
},
0x0400
=> {
Unknown
=> 1,
Name
=>
'TargetLayerID'
,
Format
=>
'int16u'
},
0x0401
=> {
Unknown
=> 1,
Name
=>
'WorkingPath'
},
0x0402
=> {
Unknown
=> 1,
Name
=>
'LayersGroupInfo'
,
Format
=>
'int16u'
},
0x0403
=> {
Unknown
=> 1,
Name
=>
'ObsoletePhotoshopTag3'
},
0x0404
=> {
Name
=>
'IPTCData'
,
SubDirectory
=> {
DirName
=>
'IPTC'
,
TagTable
=>
'Image::ExifTool::IPTC::Main'
,
},
},
0x0405
=> {
Unknown
=> 1,
Name
=>
'RawImageMode'
},
0x0406
=> {
Name
=>
'JPEG_Quality'
,
SubDirectory
=> {
TagTable
=>
'Image::ExifTool::Photoshop::JPEG_Quality'
,
},
},
0x0408
=> {
Unknown
=> 1,
Name
=>
'GridGuidesInfo'
},
0x0409
=> {
Name
=>
'PhotoshopBGRThumbnail'
,
Notes
=>
'this is a JPEG image, but in BGR format instead of RGB'
,
%thumbnailInfo
,
Groups
=> {
2
=>
'Preview'
},
},
0x040a
=> {
Name
=>
'CopyrightFlag'
,
Writable
=>
'int8u'
,
Groups
=> {
2
=>
'Author'
},
ValueConv
=>
'join(" ",unpack("C*", $val))'
,
ValueConvInv
=>
'pack("C*",split(" ",$val))'
,
PrintConv
=> {
0
=>
'False'
,
1
=>
'True'
,
},
},
0x040b
=> {
Name
=>
'URL'
,
Writable
=>
'string'
,
Groups
=> {
2
=>
'Author'
},
},
0x040c
=> {
Name
=>
'PhotoshopThumbnail'
,
%thumbnailInfo
,
Groups
=> {
2
=>
'Preview'
},
},
0x040d
=> {
Name
=>
'GlobalAngle'
,
Writable
=>
'int32u'
,
ValueConv
=>
'unpack("N",$val)'
,
ValueConvInv
=>
'pack("N",$val)'
,
},
0x040e
=> {
Unknown
=> 1,
Name
=>
'ColorSamplersResource'
},
0x040f
=> {
Name
=>
'ICC_Profile'
,
SubDirectory
=> {
TagTable
=>
'Image::ExifTool::ICC_Profile::Main'
,
},
},
0x0410
=> {
Unknown
=> 1,
Name
=>
'Watermark'
,
Format
=>
'int8u'
},
0x0411
=> {
Unknown
=> 1,
Name
=>
'ICC_Untagged'
,
Format
=>
'int8u'
},
0x0412
=> {
Unknown
=> 1,
Name
=>
'EffectsVisible'
,
Format
=>
'int8u'
},
0x0413
=> {
Unknown
=> 1,
Name
=>
'SpotHalftone'
},
0x0414
=> {
Unknown
=> 1,
Name
=>
'IDsBaseValue'
,
Description
=>
'IDs Base Value'
,
Format
=>
'int32u'
},
0x0415
=> {
Unknown
=> 1,
Name
=>
'UnicodeAlphaNames'
},
0x0416
=> {
Unknown
=> 1,
Name
=>
'IndexedColorTableCount'
,
Format
=>
'int16u'
},
0x0417
=> {
Unknown
=> 1,
Name
=>
'TransparentIndex'
,
Format
=>
'int16u'
},
0x0419
=> {
Name
=>
'GlobalAltitude'
,
Writable
=>
'int32u'
,
ValueConv
=>
'unpack("N",$val)'
,
ValueConvInv
=>
'pack("N",$val)'
,
},
0x041a
=> {
Name
=>
'SliceInfo'
,
SubDirectory
=> {
TagTable
=>
'Image::ExifTool::Photoshop::SliceInfo'
},
},
0x041b
=> {
Name
=>
'WorkflowURL'
,
%unicodeString
},
0x041c
=> {
Unknown
=> 1,
Name
=>
'JumpToXPEP'
},
0x041d
=> {
Unknown
=> 1,
Name
=>
'AlphaIdentifiers'
},
0x041e
=> {
Name
=>
'URL_List'
,
List
=> 1,
Writable
=> 1,
ValueConv
=>
sub
{
my
(
$val
,
$et
) =
@_
;
return
'<err>'
if
length
(
$val
) < 4;
my
$num
=
unpack
(
'N'
,
$val
);
my
(
$i
,
@vals
);
my
$pos
= 4;
for
(
$i
=0;
$i
<
$num
; ++
$i
) {
$pos
+= 8;
last
if
length
(
$val
) <
$pos
+ 4;
my
$len
=
unpack
(
"x${pos}N"
,
$val
) * 2;
last
if
length
(
$val
) <
$pos
+ 4 +
$len
;
push
@vals
,
$et
->Decode(
substr
(
$val
,
$pos
+4,
$len
),
'UCS2'
,
'MM'
);
$pos
+= 4 +
$len
;
}
return
\
@vals
;
},
},
0x0421
=> {
Name
=>
'VersionInfo'
,
SubDirectory
=> {
TagTable
=>
'Image::ExifTool::Photoshop::VersionInfo'
,
},
},
0x0422
=> {
Name
=>
'EXIFInfo'
,
SubDirectory
=> {
TagTable
=>
'Image::ExifTool::Exif::Main'
,
ProcessProc
=> \
&Image::ExifTool::ProcessTIFF
,
WriteProc
=> \
&Image::ExifTool::WriteTIFF
,
},
},
0x0423
=> {
Unknown
=> 1,
Name
=>
'ExifInfo2'
,
Binary
=> 1 },
0x0424
=> {
Name
=>
'XMP'
,
SubDirectory
=> {
TagTable
=>
'Image::ExifTool::XMP::Main'
,
},
},
0x0425
=> {
Name
=>
'IPTCDigest'
,
Writable
=>
'string'
,
Protected
=> 1,
Notes
=>
q{
this tag indicates provides a way for XMP-aware applications to indicate
that the XMP is synchronized with the IPTC. The MWG recommendation is to
ignore the XMP if IPTCDigest exists and doesn't match the CurrentIPTCDigest.
When writing, special values of "new" and "old" represent the digests of the
IPTC from the edited and original files respectively, and are undefined if
the IPTC does not exist in the respective file. Set this to "new" as an
indication that the XMP is synchronized with the IPTC
}
,
ValueConv
=>
'unpack("H*", $val)'
,
ValueConvInv
=>
q{
if (lc($val) eq 'new' or lc($val) eq 'old') {
{
local $SIG{'__WARN__'}
=
sub
{ };
}
warn
"Digest::MD5 must be installed\n"
;
return
undef
;
}
return
pack
(
'H*'
,
$val
)
if
$val
=~ /^[0-9a-f]{32}$/i;
warn
"Value must be 'new', 'old' or 32 hexadecimal digits\n"
;
return
undef
;
}
},
0x0426
=> {
Name
=>
'PrintScaleInfo'
,
SubDirectory
=> {
TagTable
=>
'Image::ExifTool::Photoshop::PrintScaleInfo'
},
},
0x0428
=> {
Name
=>
'PixelInfo'
,
SubDirectory
=> {
TagTable
=>
'Image::ExifTool::Photoshop::PixelInfo'
},
},
0x0429
=> {
Unknown
=> 1,
Name
=>
'LayerComps'
},
0x042a
=> {
Unknown
=> 1,
Name
=>
'AlternateDuotoneColors'
},
0x042b
=> {
Unknown
=> 1,
Name
=>
'AlternateSpotColors'
},
0x042d
=> {
Name
=>
'LayerSelectionIDs'
,
Description
=>
'Layer Selection IDs'
,
Unknown
=> 1,
ValueConv
=>
q{
my ($n, @a) = unpack("nN*",$val);
$#a = $n - 1 if $n > @a;
return join(' ', @a);
}
,
},
0x042e
=> {
Unknown
=> 1,
Name
=>
'HDRToningInfo'
},
0x042f
=> {
Unknown
=> 1,
Name
=>
'PrintInfo'
},
0x0430
=> {
Unknown
=> 1,
Name
=>
'LayerGroupsEnabledID'
,
Format
=>
'int8u'
},
0x0431
=> {
Unknown
=> 1,
Name
=>
'ColorSamplersResource2'
},
0x0432
=> {
Unknown
=> 1,
Name
=>
'MeasurementScale'
},
0x0433
=> {
Unknown
=> 1,
Name
=>
'TimelineInfo'
},
0x0434
=> {
Unknown
=> 1,
Name
=>
'SheetDisclosure'
},
0x0435
=> {
Name
=>
'ChannelOptions'
,
SubDirectory
=> {
TagTable
=>
'Image::ExifTool::Photoshop::ChannelOptions'
,
Start
=> 4,
},
},
0x0436
=> {
Unknown
=> 1,
Name
=>
'OnionSkins'
},
0x0438
=> {
Unknown
=> 1,
Name
=>
'CountInfo'
},
0x043a
=> {
Unknown
=> 1,
Name
=>
'PrintInfo2'
},
0x043b
=> {
Unknown
=> 1,
Name
=>
'PrintStyle'
},
0x043c
=> {
Unknown
=> 1,
Name
=>
'MacintoshNSPrintInfo'
},
0x043d
=> {
Unknown
=> 1,
Name
=>
'WindowsDEVMODE'
},
0x043e
=> {
Unknown
=> 1,
Name
=>
'AutoSaveFilePath'
},
0x043f
=> {
Unknown
=> 1,
Name
=>
'AutoSaveFormat'
},
0x0440
=> {
Unknown
=> 1,
Name
=>
'PathSelectionState'
},
0x0bb7
=> {
Name
=>
'ClippingPathName'
,
ValueConv
=>
q{
my $len = ord($val);
$val = substr($val, 0, $len+1) if $len < length($val);
return Image::ExifTool::Photoshop::ConvertPascalString($self,$val);
}
,
},
0x0bb8
=> {
Unknown
=> 1,
Name
=>
'OriginPathInfo'
},
0x1b58
=> {
Unknown
=> 1,
Name
=>
'ImageReadyVariables'
},
0x1b59
=> {
Unknown
=> 1,
Name
=>
'ImageReadyDataSets'
},
0x1f40
=> {
Unknown
=> 1,
Name
=>
'LightroomWorkflow'
},
0x2710
=> {
Unknown
=> 1,
Name
=>
'PrintFlagsInfo'
},
);
%Image::ExifTool::Photoshop::ChannelOptions
= (
PROCESS_PROC
=> \
&ProcessChannelOptions
,
VARS
=> {
IS_BINARY
=> 1 },
GROUPS
=> {
2
=>
'Image'
},
NOTES
=>
'These tags relate only to the appearance of a channel.'
,
0
=> {
Name
=>
'ChannelColorSpace'
,
Format
=>
'int16u'
,
PrintConv
=> {
0
=>
'RGB'
,
1
=>
'HSB'
,
2
=>
'CMYK'
,
7
=>
'Lab'
,
8
=>
'Grayscale'
,
},
},
2
=> {
Name
=>
'ChannelColorData'
,
Format
=>
'int16u[4]'
,
},
11
=> {
Name
=>
'ChannelOpacity'
,
PrintConv
=>
'"$val%"'
,
},
12
=> {
Name
=>
'ChannelColorIndicates'
,
PrintConv
=> {
0
=>
'Selected Areas'
,
1
=>
'Masked Areas'
,
2
=>
'Spot Color'
,
},
},
);
%Image::ExifTool::Photoshop::JPEG_Quality
= (
PROCESS_PROC
=> \
&Image::ExifTool::ProcessBinaryData
,
WRITE_PROC
=> \
&Image::ExifTool::WriteBinaryData
,
CHECK_PROC
=> \
&Image::ExifTool::CheckBinaryData
,
DATAMEMBER
=> [ 1 ],
FORMAT
=>
'int16s'
,
GROUPS
=> {
2
=>
'Image'
},
0
=> {
Name
=>
'PhotoshopQuality'
,
Writable
=> 1,
PrintConv
=>
'$val + 4'
,
PrintConvInv
=>
'$val - 4'
,
},
1
=> {
Name
=>
'PhotoshopFormat'
,
RawConv
=>
'$$self{PhotoshopFormat} = $val'
,
PrintConv
=> {
0x0000
=>
'Standard'
,
0x0001
=>
'Optimized'
,
0x0101
=>
'Progressive'
,
},
},
2
=> {
Name
=>
'ProgressiveScans'
,
Condition
=>
'$$self{PhotoshopFormat} == 0x0101'
,
PrintConv
=> {
1
=>
'3 Scans'
,
2
=>
'4 Scans'
,
3
=>
'5 Scans'
,
},
},
);
%Image::ExifTool::Photoshop::SliceInfo
= (
PROCESS_PROC
=> \
&Image::ExifTool::ProcessBinaryData
,
20
=> {
Name
=>
'SlicesGroupName'
,
Format
=>
'var_ustr32'
},
24
=> {
Name
=>
'NumSlices'
,
Format
=>
'int32u'
},
);
%Image::ExifTool::Photoshop::Resolution
= (
PROCESS_PROC
=> \
&Image::ExifTool::ProcessBinaryData
,
WRITE_PROC
=> \
&Image::ExifTool::WriteBinaryData
,
CHECK_PROC
=> \
&Image::ExifTool::CheckBinaryData
,
FORMAT
=>
'int16u'
,
FIRST_ENTRY
=> 0,
WRITABLE
=> 1,
GROUPS
=> {
2
=>
'Image'
},
0
=> {
Name
=>
'XResolution'
,
Format
=>
'int32u'
,
Priority
=> 0,
ValueConv
=>
'$val / 0x10000'
,
ValueConvInv
=>
'int($val * 0x10000 + 0.5)'
,
PrintConv
=>
'int($val * 100 + 0.5) / 100'
,
PrintConvInv
=>
'$val'
,
},
2
=> {
Name
=>
'DisplayedUnitsX'
,
PrintConv
=> {
1
=>
'inches'
,
2
=>
'cm'
,
},
},
4
=> {
Name
=>
'YResolution'
,
Format
=>
'int32u'
,
Priority
=> 0,
ValueConv
=>
'$val / 0x10000'
,
ValueConvInv
=>
'int($val * 0x10000 + 0.5)'
,
PrintConv
=>
'int($val * 100 + 0.5) / 100'
,
PrintConvInv
=>
'$val'
,
},
6
=> {
Name
=>
'DisplayedUnitsY'
,
PrintConv
=> {
1
=>
'inches'
,
2
=>
'cm'
,
},
},
);
%Image::ExifTool::Photoshop::VersionInfo
= (
PROCESS_PROC
=> \
&Image::ExifTool::ProcessBinaryData
,
WRITE_PROC
=> \
&Image::ExifTool::WriteBinaryData
,
CHECK_PROC
=> \
&Image::ExifTool::CheckBinaryData
,
FIRST_ENTRY
=> 0,
GROUPS
=> {
2
=>
'Image'
},
4
=> {
Name
=>
'HasRealMergedData'
,
Format
=>
'int8u'
,
PrintConv
=> {
0
=>
'No'
,
1
=>
'Yes'
} },
5
=> {
Name
=>
'WriterName'
,
Format
=>
'var_ustr32'
},
9
=> {
Name
=>
'ReaderName'
,
Format
=>
'var_ustr32'
},
);
%Image::ExifTool::Photoshop::PrintScaleInfo
= (
PROCESS_PROC
=> \
&Image::ExifTool::ProcessBinaryData
,
WRITE_PROC
=> \
&Image::ExifTool::WriteBinaryData
,
CHECK_PROC
=> \
&Image::ExifTool::CheckBinaryData
,
FIRST_ENTRY
=> 0,
GROUPS
=> {
2
=>
'Image'
},
0
=> {
Name
=>
'PrintStyle'
,
Format
=>
'int16u'
,
PrintConv
=> {
0
=>
'Centered'
,
1
=>
'Size to Fit'
,
2
=>
'User Defined'
,
},
},
2
=> {
Name
=>
'PrintPosition'
,
Format
=>
'float[2]'
},
10
=> {
Name
=>
'PrintScale'
,
Format
=>
'float'
},
);
%Image::ExifTool::Photoshop::PixelInfo
= (
PROCESS_PROC
=> \
&Image::ExifTool::ProcessBinaryData
,
WRITE_PROC
=> \
&Image::ExifTool::WriteBinaryData
,
CHECK_PROC
=> \
&Image::ExifTool::CheckBinaryData
,
FIRST_ENTRY
=> 0,
GROUPS
=> {
2
=>
'Image'
},
4
=> {
Name
=>
'PixelAspectRatio'
,
Format
=>
'double'
},
);
%Image::ExifTool::Photoshop::Header
= (
PROCESS_PROC
=> \
&Image::ExifTool::ProcessBinaryData
,
FORMAT
=>
'int16u'
,
GROUPS
=> {
2
=>
'Image'
},
NOTES
=>
'This information is found in the PSD file header.'
,
6
=>
'NumChannels'
,
7
=> {
Name
=>
'ImageHeight'
,
Format
=>
'int32u'
},
9
=> {
Name
=>
'ImageWidth'
,
Format
=>
'int32u'
},
11
=>
'BitDepth'
,
12
=> {
Name
=>
'ColorMode'
,
PrintConvColumns
=> 2,
PrintConv
=> {
0
=>
'Bitmap'
,
1
=>
'Grayscale'
,
2
=>
'Indexed'
,
3
=>
'RGB'
,
4
=>
'CMYK'
,
7
=>
'Multichannel'
,
8
=>
'Duotone'
,
9
=>
'Lab'
,
},
},
);
%Image::ExifTool::Photoshop::Layers
= (
PROCESS_PROC
=> \
&ProcessLayers
,
GROUPS
=> {
2
=>
'Image'
},
NOTES
=>
'Tags extracted from Photoshop layer information.'
,
_xcnt
=> {
Name
=>
'LayerCount'
,
Format
=>
'int16u'
},
_xrct
=> {
Name
=>
'LayerRectangles'
,
Format
=>
'int32u'
,
Count
=> 4,
List
=> 1,
Notes
=>
'top left bottom right'
,
},
_xnam
=> {
Name
=>
'LayerNames'
,
Format
=>
'string'
,
List
=> 1,
ValueConv
=>
q{
my $charset = $self->Options('CharsetPhotoshop') || 'Latin';
return $self->Decode($val, $charset);
}
,
},
_xbnd
=> {
Name
=>
'LayerBlendModes'
,
Format
=>
'undef'
,
List
=> 1,
RawConv
=>
'GetByteOrder() eq "II" ? pack "N*", unpack "V*", $val : $val'
,
PrintConv
=> {
pass
=>
'Pass Through'
,
norm
=>
'Normal'
,
diss
=>
'Dissolve'
,
dark
=>
'Darken'
,
'mul '
=>
'Multiply'
,
idiv
=>
'Color Burn'
,
lbrn
=>
'Linear Burn'
,
dkCl
=>
'Darker Color'
,
lite
=>
'Lighten'
,
scrn
=>
'Screen'
,
'div '
=>
'Color Dodge'
,
lddg
=>
'Linear Dodge'
,
lgCl
=>
'Lighter Color'
,
over
=>
'Overlay'
,
sLit
=>
'Soft Light'
,
hLit
=>
'Hard Light'
,
vLit
=>
'Vivid Light'
,
lLit
=>
'Linear Light'
,
pLit
=>
'Pin Light'
,
hMix
=>
'Hard Mix'
,
diff
=>
'Difference'
,
smud
=>
'Exclusion'
,
fsub
=>
'Subtract'
,
fdiv
=>
'Divide'
,
'hue '
=>
'Hue'
,
'sat '
=>
'Saturation'
,
colr
=>
'Color'
,
'lum '
=>
'Luminosity'
,
},
},
_xopc
=> {
Name
=>
'LayerOpacities'
,
Format
=>
'int8u'
,
List
=> 1,
ValueConv
=>
'100 * $val / 255'
,
PrintConv
=>
'sprintf("%d%%",$val)'
,
},
_xvis
=> {
Name
=>
'LayerVisible'
,
Format
=>
'int8u'
,
List
=> 1,
ValueConv
=>
'$val & 0x02'
,
PrintConv
=> {
0x02
=>
'No'
,
0x00
=>
'Yes'
},
},
luni
=> {
Name
=>
'LayerUnicodeNames'
,
List
=> 1,
RawConv
=>
q{
return '' if length($val) < 4;
my $len = Get32u(\$val, 0);
return $self->Decode(substr($val, 4, $len * 2), 'UCS2');
}
,
},
lyid
=> {
Name
=>
'LayerIDs'
,
Description
=>
'Layer IDs'
,
Format
=>
'int32u'
,
List
=> 1,
Unknown
=> 1,
},
lclr
=> {
Name
=>
'LayerColors'
,
Format
=>
'int16u'
,
Count
=> 1,
List
=> 1,
PrintConv
=> {
0
=>
'None'
,
1
=>
'Red'
,
2
=>
'Orange'
,
3
=>
'Yellow'
,
4
=>
'Green'
,
5
=>
'Blue'
,
6
=>
'Violet'
,
7
=>
'Gray'
,
},
},
shmd
=> {
Name
=>
'LayerModifyDates'
,
Groups
=> {
2
=>
'Time'
},
List
=> 1,
RawConv
=>
q{
return '' unless $val =~ /layerTime(doub|buod)(.{8}
)/s;
my
$tmp
= $2;
return
GetDouble(\
$tmp
, 0);
},
ValueConv
=>
'length $val ? ConvertUnixTime($val,1) : ""'
,
PrintConv
=>
'length $val ? $self->ConvertDateTime($val) : ""'
,
},
lsct
=> {
Name
=>
'LayerSections'
,
Format
=>
'int32u'
,
Count
=> 1,
List
=> 1,
PrintConv
=> {
0
=>
'Layer'
,
1
=>
'Folder (open)'
,
2
=>
'Folder (closed)'
,
3
=>
'Divider'
},
},
);
%Image::ExifTool::Photoshop::DocumentData
= (
PROCESS_PROC
=> \
&ProcessDocumentData
,
GROUPS
=> {
2
=>
'Image'
},
Layr
=> {
Name
=>
'Layers'
,
SubDirectory
=> {
TagTable
=>
'Image::ExifTool::Photoshop::Layers'
},
},
Lr16
=> {
Name
=>
'Layers'
,
SubDirectory
=> {
TagTable
=>
'Image::ExifTool::Photoshop::Layers'
},
},
);
%Image::ExifTool::Photoshop::ImageData
= (
PROCESS_PROC
=> \
&Image::ExifTool::ProcessBinaryData
,
GROUPS
=> {
2
=>
'Image'
},
0
=> {
Name
=>
'Compression'
,
Format
=>
'int16u'
,
PrintConv
=> {
0
=>
'Uncompressed'
,
1
=>
'RLE'
,
2
=>
'ZIP without prediction'
,
3
=>
'ZIP with prediction'
,
},
},
);
%Image::ExifTool::Photoshop::Unknown
= (
GROUPS
=> {
2
=>
'Unknown'
},
);
$iptcDigestInfo
=
$Image::ExifTool::Photoshop::Main
{0x0425};
sub
AUTOLOAD
{
return
Image::ExifTool::DoAutoLoad(
$AUTOLOAD
,
@_
);
}
sub
ConvertPascalString($$)
{
my
(
$et
,
$inStr
) =
@_
;
my
$outStr
=
''
;
my
$len
=
length
(
$inStr
);
my
$i
=0;
while
(
$i
<
$len
) {
my
$n
=
ord
(
substr
(
$inStr
,
$i
, 1));
last
if
$i
+
$n
>=
$len
;
$i
and
$outStr
.=
', '
;
$outStr
.=
substr
(
$inStr
,
$i
+1,
$n
);
$i
+=
$n
+ 1;
}
my
$charset
=
$et
->Options(
'CharsetPhotoshop'
) ||
'Latin'
;
return
$et
->Decode(
$outStr
,
$charset
);
}
sub
ProcessLayersAndMask($$$)
{
local
$_
;
my
(
$et
,
$dirInfo
,
$tagTablePtr
) =
@_
;
my
$raf
=
$$dirInfo
{RAF};
my
$fileType
=
$$et
{FileType};
my
$data
;
return
0
unless
$fileType
eq
'PSD'
or
$fileType
eq
'PSB'
;
my
(
$psb
,
$psiz
) =
$fileType
eq
'PSB'
? (1, 8) : (
undef
, 4);
my
$n
=
$psiz
* 2 + 2;
$raf
->Read(
$data
,
$n
) ==
$n
or
return
0;
my
$tot
=
$psb
? Get64u(\
$data
, 0) : Get32u(\
$data
, 0);
return
1
if
$tot
== 0;
my
$end
=
$raf
->Tell() -
$psiz
- 2 +
$tot
;
$data
=
substr
$data
,
$psiz
;
my
$len
=
$psb
? Get64u(\
$data
, 0) : Get32u(\
$data
, 0);
my
$num
= Get16s(\
$data
,
$psiz
);
if
(
$len
== 0 and
$num
== 0) {
$raf
->Read(
$data
,10) == 10 or
return
0;
if
(
$data
=~ /^..8BIMLr16/s) {
$raf
->Read(
$data
,
$psiz
+2) ==
$psiz
+2 or
return
0;
$len
=
$psb
? Get64u(\
$data
, 0) : Get32u(\
$data
, 0);
}
elsif
(
$data
=~ /^..8BIMMt16/s) {
$raf
->Read(
$data
,
$psiz
) ==
$psiz
or
return
0;
$raf
->Read(
$data
, 8) == 8 or
return
0;
if
(
$data
eq
'8BIMLr16'
) {
$raf
->Read(
$data
,
$psiz
+2) ==
$psiz
+2 or
return
0;
$len
=
$psb
? Get64u(\
$data
, 0) : Get32u(\
$data
, 0);
}
else
{
$raf
->Seek(-18-
$psiz
, 1) or
return
0;
}
}
else
{
$raf
->Seek(-10, 1) or
return
0;
}
}
$len
+= 2;
$raf
->Seek(-2, 1) or
return
0;
my
%dinfo
= (
RAF
=>
$raf
,
DirLen
=>
$len
,
);
$$et
{IsPSB} =
$psb
;
ProcessLayers(
$et
, \
%dinfo
,
$tagTablePtr
);
return
$raf
->Seek(
$end
, 0) ? 1 : 0;
}
sub
ProcessChannelOptions($$$)
{
my
(
$et
,
$dirInfo
,
$tagTablePtr
) =
@_
;
my
$end
=
$$dirInfo
{DirStart} +
$$dirInfo
{DirLen};
$$dirInfo
{DirLen} = 13;
my
$i
;
for
(
$i
=0;
$$dirInfo
{DirStart} + 13 <=
$end
; ++
$i
) {
$$et
{SET_GROUP1} =
"Channel$i"
;
$et
->ProcessBinaryData(
$dirInfo
,
$tagTablePtr
);
$$dirInfo
{DirStart} += 13;
}
delete
$$et
{SET_GROUP1};
return
1;
}
sub
ProcessLayers($$$)
{
local
$_
;
my
(
$et
,
$dirInfo
,
$tagTablePtr
) =
@_
;
my
(
$i
,
$n
,
%count
,
$buff
,
$buf2
);
my
$raf
=
$$dirInfo
{RAF};
my
$dirLen
=
$$dirInfo
{DirLen};
my
$verbose
=
$$et
{OPTIONS}{Verbose};
my
%dinfo
= (
DataPt
=> \
$buff
,
Base
=>
$raf
->Tell() );
my
$pos
= 0;
return
0
if
$dirLen
< 2;
$raf
->Read(
$buff
, 2) == 2 or
return
0;
my
$num
= Get16s(\
$buff
, 0);
$num
= -
$num
if
$num
< 0;
$et
->VerboseDir(
'Layers'
,
$num
,
$dirLen
);
$et
->HandleTag(
$tagTablePtr
,
'_xcnt'
,
$num
,
Start
=>
$pos
,
Size
=> 2,
%dinfo
);
my
$oldIndent
=
$$et
{INDENT};
$$et
{INDENT} .=
'| '
;
$pos
+= 2;
my
$psb
=
$$et
{IsPSB};
my
$psiz
=
$psb
? 8 : 4;
for
(
$i
=0;
$i
<
$num
; ++
$i
) {
$et
->VPrint(0,
$oldIndent
.
'+ [Layer '
.(
$i
+1).
" of $num]\n"
);
last
if
$pos
+ 18 >
$dirLen
;
$raf
->Read(
$buff
, 18) == 18 or
last
;
$dinfo
{DataPos} =
$pos
;
$et
->HandleTag(
$tagTablePtr
,
'_xrct'
,
undef
,
Start
=> 0,
Size
=> 16,
%dinfo
);
my
$numChannels
= Get16u(\
$buff
, 16);
$n
= (2 +
$psiz
) *
$numChannels
;
$raf
->Seek(
$n
, 1) or
last
;
$pos
+= 18 +
$n
;
last
if
$pos
+ 20 >
$dirLen
;
$raf
->Read(
$buff
, 20) == 20 or
last
;
$dinfo
{DataPos} =
$pos
;
my
$sig
=
substr
(
$buff
, 0, 4);
$sig
=~ /^(8BIM|MIB8)$/ or
last
;
$et
->HandleTag(
$tagTablePtr
,
'_xbnd'
,
undef
,
Start
=> 4,
Size
=> 4,
%dinfo
);
$et
->HandleTag(
$tagTablePtr
,
'_xopc'
,
undef
,
Start
=> 8,
Size
=> 1,
%dinfo
);
$et
->HandleTag(
$tagTablePtr
,
'_xvis'
,
undef
,
Start
=>10,
Size
=> 1,
%dinfo
);
my
$nxt
=
$pos
+ 16 + Get32u(\
$buff
, 12);
$n
= Get32u(\
$buff
, 16);
$pos
+= 20 +
$n
;
last
if
$pos
+ 4 >
$dirLen
;
$raf
->Seek(
$n
, 1) and
$raf
->Read(
$buff
, 4) == 4 or
last
;
$n
= Get32u(\
$buff
, 0);
$pos
+= 4 +
$n
;
last
if
$pos
+ 1 >
$dirLen
;
$raf
->Seek(
$n
, 1) and
$raf
->Read(
$buff
, 1) == 1 or
last
;
$n
= Get8u(\
$buff
, 0);
last
if
$pos
+ 1 +
$n
>
$dirLen
;
$raf
->Read(
$buff
,
$n
) ==
$n
or
last
;
$dinfo
{DataPos} =
$pos
+ 1;
$et
->HandleTag(
$tagTablePtr
,
'_xnam'
,
undef
,
Start
=> 0,
Size
=>
$n
,
%dinfo
);
my
$frag
= (
$n
+ 1) & 0x3;
$raf
->Seek(4 -
$frag
, 1) or
last
if
$frag
;
$n
= (
$n
+ 4) & 0xfffffffc;
$pos
+=
$n
;
while
(
$pos
+ 12 <=
$nxt
) {
$raf
->Read(
$buff
, 12) == 12 or
last
;
my
$dat
=
substr
(
$buff
, 0, 8);
$dat
=
pack
'N*'
,
unpack
'V*'
,
$dat
if
GetByteOrder() eq
'II'
;
my
$sig
=
substr
(
$dat
, 0, 4);
last
unless
$sig
eq
'8BIM'
or
$sig
eq
'8B64'
;
my
$tag
=
substr
(
$dat
, 4, 4);
if
(
$psb
and
$tag
=~ /^(LMsk|Lr16|Lr32|Layr|Mt16|Mt32|Mtrn|Alph|FMsk|lnk2|FEid|FXid|PxSD)$/) {
last
if
$pos
+ 16 >
$nxt
;
$raf
->Read(
$buf2
, 4) == 4 or
last
;
$buff
.=
$buf2
;
$n
= Get64u(\
$buff
, 8);
$pos
+= 4;
}
else
{
$n
= Get32u(\
$buff
, 8);
}
$pos
+= 12;
last
if
$pos
+
$n
>
$nxt
;
$frag
=
$n
& 0x3;
if
(
$$tagTablePtr
{
$tag
} or
$verbose
) {
$count
{
$tag
} = 0
unless
defined
$count
{
$tag
};
$raf
->Read(
$buff
,
$n
) ==
$n
or
last
;
$dinfo
{DataPos} =
$pos
;
while
(
$count
{
$tag
} <
$i
) {
$et
->HandleTag(
$tagTablePtr
,
$tag
,
$tag
eq
'lsct'
? 0 :
''
);
++
$count
{
$tag
};
}
$et
->HandleTag(
$tagTablePtr
,
$tag
,
undef
,
Start
=> 0,
Size
=>
$n
,
%dinfo
);
++
$count
{
$tag
};
if
(
$frag
) {
$raf
->Seek(4 -
$frag
, 1) or
last
;
$n
+= 4 -
$frag
;
}
}
else
{
$n
+= 4 -
$frag
if
$frag
;
$raf
->Seek(
$n
, 1) or
last
;
}
$pos
+=
$n
;
}
$pos
=
$nxt
;
}
foreach
(
sort
keys
%count
) {
while
(
$count
{
$_
} <
$num
) {
$et
->HandleTag(
$tagTablePtr
,
$_
,
$_
eq
'lsct'
? 0 :
''
);
++
$count
{
$_
};
}
}
$$et
{INDENT} =
$oldIndent
;
return
1;
}
sub
ProcessDocumentData($$$)
{
my
(
$et
,
$dirInfo
,
$tagTablePtr
) =
@_
;
my
$verbose
=
$$et
{OPTIONS}{Verbose};
my
$raf
=
$$dirInfo
{RAF};
my
$dirLen
=
$$dirInfo
{DirLen};
my
$pos
= 36;
my
(
$buff
,
$n
,
$err
);
$et
->VerboseDir(
'Photoshop Document Data'
,
undef
,
$dirLen
);
unless
(
$raf
) {
my
$dataPt
=
$$dirInfo
{DataPt};
my
$start
=
$$dirInfo
{DirStart} || 0;
$raf
= File::RandomAccess->new(
$dataPt
);
$raf
->Seek(
$start
, 0)
if
$start
;
$dirLen
=
length
$$dataPt
-
$start
unless
defined
$dirLen
;
$et
->VerboseDump(
$dataPt
,
Start
=>
$start
,
Len
=>
$dirLen
,
Base
=>
$$dirInfo
{Base});
}
unless
(
$raf
->Read(
$buff
,
$pos
) ==
$pos
and
$buff
=~ /^Adobe Photoshop Document Data (Block|V0002)\0/)
{
$et
->Warn(
'Invalid Photoshop Document Data'
);
return
0;
}
my
$psb
= ($1 eq
'V0002'
);
my
%dinfo
= (
DataPt
=> \
$buff
);
$$et
{IsPSB} =
$psb
;
while
(
$pos
+ 12 <=
$dirLen
) {
$raf
->Read(
$buff
, 8) == 8 or
$err
=
'Error reading document data'
,
last
;
SetByteOrder(
$buff
=~ /^(8BIM|8B64)/ ?
'MM'
:
'II'
)
if
$pos
== 36;
$buff
=
pack
'N*'
,
unpack
'V*'
,
$buff
if
GetByteOrder() eq
'II'
;
my
$sig
=
substr
(
$buff
, 0, 4);
$sig
eq
'8BIM'
or
$sig
eq
'8B64'
or
$err
=
'Bad photoshop resource'
,
last
;
my
$tag
=
substr
(
$buff
, 4, 4);
if
(
$psb
and
$tag
=~ /^(LMsk|Lr16|Lr32|Layr|Mt16|Mt32|Mtrn|Alph|FMsk|lnk2|FEid|FXid|PxSD)$/) {
$pos
+ 16 >
$dirLen
and
$err
=
'Short PSB resource'
,
last
;
$raf
->Read(
$buff
, 8) == 8 or
$err
=
'Error reading PSB resource'
,
last
;
$n
= Get64u(\
$buff
, 0);
$pos
+= 4;
}
else
{
$raf
->Read(
$buff
, 4) == 4 or
$err
=
'Error reading PSD resource'
,
last
;
$n
= Get32u(\
$buff
, 0);
}
$pos
+= 12;
$pos
+
$n
>
$dirLen
and
$err
=
'Truncated photoshop resource'
,
last
;
my
$pad
= (4 - (
$n
& 3)) & 3;
my
$tagInfo
=
$$tagTablePtr
{
$tag
};
if
(
$tagInfo
or
$verbose
) {
if
(
$tagInfo
and
$$tagInfo
{SubDirectory}) {
my
$fpos
=
$raf
->Tell() +
$n
+
$pad
;
my
$subTable
= GetTagTable(
$$tagInfo
{SubDirectory}{TagTable});
$et
->ProcessDirectory({
RAF
=>
$raf
,
DirLen
=>
$n
},
$subTable
);
$raf
->Seek(
$fpos
, 0) or
$err
=
'Seek error'
,
last
;
}
else
{
$dinfo
{DataPos} =
$raf
->Tell();
$dinfo
{Start} = 0;
$dinfo
{Size} =
$n
;
$raf
->Read(
$buff
,
$n
) ==
$n
or
$err
=
'Error reading photoshop resource'
,
last
;
$et
->HandleTag(
$tagTablePtr
,
$tag
,
undef
,
%dinfo
);
$raf
->Seek(
$pad
, 1) or
$err
=
'Seek error'
,
last
;
}
}
else
{
$raf
->Seek(
$n
+
$pad
, 1) or
$err
=
'Seek error'
,
last
;
}
$pos
+=
$n
+
$pad
;
}
$err
and
$et
->Warn(
$err
);
return
1;
}
sub
ProcessPhotoshop($$$)
{
my
(
$et
,
$dirInfo
,
$tagTablePtr
) =
@_
;
my
$dataPt
=
$$dirInfo
{DataPt};
my
$pos
=
$$dirInfo
{DirStart};
my
$dirEnd
=
$pos
+
$$dirInfo
{DirLen};
my
$verbose
=
$et
->Options(
'Verbose'
);
my
$success
= 0;
if
((
$Image::ExifTool::MWG::strict
or
$et
->Options(
'Validate'
)) and
$$et
{FILE_TYPE} =~ /^(JPEG|TIFF|PSD)$/)
{
my
$path
=
$et
->MetadataPath();
unless
(
$path
=~ /^(JPEG-APP13-Photoshop|TIFF-IFD0-Photoshop|PSD)$/) {
if
(
$Image::ExifTool::MWG::strict
) {
$et
->Warn(
"Ignored non-standard Photoshop at $path"
);
return
1;
}
else
{
$et
->Warn(
"Non-standard Photoshop at $path"
, 1);
}
}
}
if
(
$$et
{FILE_TYPE} eq
'JPEG'
and
$$dirInfo
{Parent} ne
'APP13'
) {
$$et
{LOW_PRIORITY_DIR}{
'*'
} = 1;
}
SetByteOrder(
'MM'
);
$verbose
and
$et
->VerboseDir(
'Photoshop'
, 0,
$$dirInfo
{DirLen});
while
(
$pos
+ 8 <
$dirEnd
) {
my
$type
=
substr
(
$$dataPt
,
$pos
, 4);
my
(
$ttPtr
,
$extra
,
$val
,
$name
);
if
(
$type
eq
'8BIM'
) {
$ttPtr
=
$tagTablePtr
;
}
elsif
(
$type
=~ /^(PHUT|DCSR|AgHg|MeSa)$/) {
$ttPtr
= GetTagTable(
'Image::ExifTool::Photoshop::Unknown'
);
}
else
{
$type
=~ s/([^\w])/
sprintf
(
"\\x%.2x"
,
ord
($1))/ge;
$et
->Warn(
qq{Bad Photoshop IRB resource "$type"}
);
last
;
}
my
$tag
= Get16u(
$dataPt
,
$pos
+ 4);
$pos
+= 6;
my
$nameLen
= Get8u(
$dataPt
,
$pos
);
my
$namePos
= ++
$pos
;
$pos
+=
$nameLen
;
++
$pos
unless
$nameLen
& 0x01;
if
(
$pos
+ 4 >
$dirEnd
) {
$et
->Warn(
"Bad Photoshop resource block"
);
last
;
}
my
$size
= Get32u(
$dataPt
,
$pos
);
$pos
+= 4;
if
(
$size
+
$pos
>
$dirEnd
) {
$et
->Warn(
"Bad Photoshop resource data size $size"
);
last
;
}
$success
= 1;
if
(
$nameLen
) {
$name
=
substr
(
$$dataPt
,
$namePos
,
$nameLen
);
$extra
=
qq{, Name="$name"}
;
}
else
{
$name
=
''
;
}
my
$tagInfo
=
$et
->GetTagInfo(
$ttPtr
,
$tag
);
if
(
$tagInfo
and
defined
$$tagInfo
{SetResourceName} and
$$tagInfo
{SetResourceName} eq
'1'
and
$name
!~ m{/
{
$val
=
substr
(
$$dataPt
,
$pos
,
$size
) .
'/#'
.
$name
.
'#/'
;
}
$et
->HandleTag(
$ttPtr
,
$tag
,
$val
,
TagInfo
=>
$tagInfo
,
Extra
=>
$extra
,
DataPt
=>
$dataPt
,
DataPos
=>
$$dirInfo
{DataPos},
Size
=>
$size
,
Start
=>
$pos
,
Base
=>
$$dirInfo
{Base},
Parent
=>
$$dirInfo
{DirName},
);
$size
+= 1
if
$size
& 0x01;
$pos
+=
$size
;
}
if
(
$$et
{VALUE}{IPTCDigest} and
$$et
{VALUE}{CurrentIPTCDigest} and
$$et
{VALUE}{IPTCDigest} ne
$$et
{VALUE}{CurrentIPTCDigest})
{
$et
->Warn(
'IPTCDigest is not current. XMP may be out of sync'
);
}
delete
$$et
{LOW_PRIORITY_DIR}{
'*'
};
return
$success
;
}
sub
ProcessPSD($$)
{
my
(
$et
,
$dirInfo
) =
@_
;
my
$raf
=
$$dirInfo
{RAF};
my
$outfile
=
$$dirInfo
{OutFile};
my
(
$data
,
$err
,
$tagTablePtr
);
$raf
->Read(
$data
, 30) == 30 or
return
0;
$data
=~ /^8BPS\0([\x01\x02])/ or
return
0;
SetByteOrder(
'MM'
);
$et
->SetFileType($1 eq
"\x01"
?
'PSD'
:
'PSB'
);
my
%dirInfo
= (
DataPt
=> \
$data
,
DirStart
=> 0,
DirName
=>
'Photoshop'
,
);
my
$len
= Get32u(\
$data
, 26);
if
(
$outfile
) {
Write(
$outfile
,
$data
) or
$err
= 1;
$raf
->Read(
$data
,
$len
) ==
$len
or
return
-1;
Write(
$outfile
,
$data
) or
$err
= 1;
$et
->InitWriteDirs(\
%psdMap
);
}
else
{
$tagTablePtr
= GetTagTable(
'Image::ExifTool::Photoshop::Header'
);
$dirInfo
{DirLen} = 30;
$et
->ProcessDirectory(\
%dirInfo
,
$tagTablePtr
);
$raf
->Seek(
$len
, 1) or
$err
= 1;
}
$raf
->Read(
$data
, 4) == 4 or
$err
= 1;
$len
= Get32u(\
$data
, 0);
$raf
->Read(
$data
,
$len
) ==
$len
or
$err
= 1;
$tagTablePtr
= GetTagTable(
'Image::ExifTool::Photoshop::Main'
);
$dirInfo
{DirLen} =
$len
;
my
$rtnVal
= 1;
if
(
$outfile
) {
$data
= WritePhotoshop(
$et
, \
%dirInfo
,
$tagTablePtr
);
if
(
$data
) {
$len
= Set32u(
length
$data
);
Write(
$outfile
,
$len
,
$data
) or
$err
= 1;
my
$trailInfo
= Image::ExifTool::IdentifyTrailer(
$raf
);
if
(
$trailInfo
) {
my
$tbuf
=
''
;
$$trailInfo
{OutFile} = \
$tbuf
;
if
(
$et
->ProcessTrailers(
$trailInfo
)) {
my
$copyBytes
=
$$trailInfo
{DataPos} -
$raf
->Tell();
if
(
$copyBytes
>= 0) {
while
(
$copyBytes
) {
my
$n
= (
$copyBytes
> 65536) ? 65536 :
$copyBytes
;
$raf
->Read(
$data
,
$n
) ==
$n
or
$err
= 1;
Write(
$outfile
,
$data
) or
$err
= 1;
$copyBytes
-=
$n
;
}
$et
->WriteTrailerBuffer(
$trailInfo
,
$outfile
) or
$err
= 1;
}
else
{
$et
->Warn(
'Overlapping trailer'
);
undef
$trailInfo
;
}
}
else
{
undef
$trailInfo
;
}
}
unless
(
$trailInfo
) {
while
(
$raf
->Read(
$data
, 65536)) {
Write(
$outfile
,
$data
) or
$err
= 1;
}
}
}
else
{
$err
= 1;
}
$rtnVal
= -1
if
$err
;
}
elsif
(
$err
) {
$et
->Warn(
'File format error'
);
}
else
{
ProcessPhotoshop(
$et
, \
%dirInfo
,
$tagTablePtr
);
$dirInfo
{RAF} =
$raf
;
$tagTablePtr
= GetTagTable(
'Image::ExifTool::Photoshop::Layers'
);
my
$oldIndent
=
$$et
{INDENT};
$$et
{INDENT} .=
'| '
;
if
(ProcessLayersAndMask(
$et
, \
%dirInfo
,
$tagTablePtr
) and
$raf
->Read(
$data
,2) == 2)
{
my
%dirInfo
= (
DataPt
=> \
$data
,
DataPos
=>
$raf
->Tell() - 2,
);
$tagTablePtr
= GetTagTable(
'Image::ExifTool::Photoshop::ImageData'
);
$et
->ProcessDirectory(\
%dirInfo
,
$tagTablePtr
);
}
$$et
{INDENT} =
$oldIndent
;
my
$trailInfo
= Image::ExifTool::IdentifyTrailer(
$raf
);
$et
->ProcessTrailers(
$trailInfo
)
if
$trailInfo
;
}
return
$rtnVal
;
}
1;