$VERSION
=
'1.07'
;
sub
ProcessAFCP($$);
%Image::ExifTool::AFCP::Main
= (
PROCESS_PROC
=> \
&ProcessAFCP
,
NOTES
=>
q{
AFCP stands for AXS File Concatenation Protocol, and is a poorly designed
protocol for appending information to the end of files. This can be used as
an auxiliary technique to store IPTC information in images, but is
incompatible with some file formats.
ExifTool will read and write (but not create) AFCP IPTC information in JPEG
and TIFF images.
See
for the AFCP specification.
}
,
IPTC
=> {
SubDirectory
=> {
TagTable
=>
'Image::ExifTool::IPTC::Main'
} },
TEXT
=>
'Text'
,
Nail
=> {
Name
=>
'ThumbnailImage'
,
Groups
=> {
2
=>
'Preview'
},
RawConv
=>
q{
pos($val) = 10;
my $start = ($val =~ /\xff\xd8\xff/g) ? pos($val) - 3 : 18;
my $img = substr($val, $start);
return $self->ValidateImage(\$img, $tag);
}
,
},
PrVw
=> {
Name
=>
'PreviewImage'
,
Groups
=> {
2
=>
'Preview'
},
RawConv
=>
q{
pos($val) = 10;
my $start = ($val =~ /\xff\xd8\xff/g) ? pos($val) - 3 : 18;
my $img = substr($val, $start);
return $self->ValidateImage(\$img, $tag);
}
,
},
);
sub
ProcessAFCP($$)
{
my
(
$et
,
$dirInfo
) =
@_
;
my
$raf
=
$$dirInfo
{RAF};
my
$curPos
=
$raf
->Tell();
my
$offset
=
$$dirInfo
{Offset} || 0;
my
$rtnVal
= 0;
NoAFCP:
for
(;;) {
my
(
$buff
,
$fix
,
$dirBuff
,
$valBuff
,
$fixup
,
$vers
);
last
unless
$raf
->Seek(-12-
$offset
, 2) and
$raf
->Read(
$buff
, 12) == 12 and
$buff
=~ /^(AXS(!|\*))/;
my
$endPos
=
$raf
->Tell();
my
$hdr
= $1;
SetByteOrder($2 eq
'!'
?
'MM'
:
'II'
);
my
$startPos
= Get32u(\
$buff
, 4);
if
(
$raf
->Seek(
$startPos
, 0) and
$raf
->Read(
$buff
, 12) == 12 and
$buff
=~ /^
$hdr
/) {
$fix
= 0;
}
else
{
$rtnVal
= -1;
last
unless
$$dirInfo
{ScanForAFCP} and
$raf
->Seek(
$curPos
, 0);
my
$actualPos
=
$curPos
;
for
(;;) {
last
if
$raf
->Read(
$buff
, 12) == 12 and
$buff
=~ /^
$hdr
/;
last
NoAFCP
if
$actualPos
!=
$curPos
;
for
(;;) {
my
$buf2
;
$raf
->Read(
$buf2
, 65536) or
last
NoAFCP;
$buff
.=
$buf2
;
if
(
$buff
=~ /
$hdr
/g) {
$actualPos
+=
pos
(
$buff
) -
length
(
$hdr
);
last
;
}
$buf2
=
substr
(
$buf2
, -3);
$actualPos
+=
length
(
$buff
) -
length
(
$buf2
);
$buff
=
$buf2
;
}
last
unless
$raf
->Seek(
$actualPos
, 0);
}
$fix
=
$actualPos
-
$startPos
;
}
$$dirInfo
{DataPos} =
$startPos
+
$fix
;
$$dirInfo
{DirLen} =
$endPos
- (
$startPos
+
$fix
);
$rtnVal
= 1;
my
$verbose
=
$et
->Options(
'Verbose'
);
my
$out
=
$et
->Options(
'TextOut'
);
my
$outfile
=
$$dirInfo
{OutFile};
if
(
$outfile
) {
if
(
$$et
{DEL_GROUP}{AFCP}) {
$verbose
and
print
$out
" Deleting AFCP\n"
;
++
$$et
{CHANGED};
last
;
}
$dirBuff
=
$valBuff
=
''
;
$fixup
=
$$dirInfo
{Fixup};
$fixup
or
$fixup
=
$$dirInfo
{Fixup} = new Image::ExifTool::Fixup;
$vers
=
substr
(
$buff
, 4, 2);
}
else
{
$et
->DumpTrailer(
$dirInfo
)
if
$verbose
or
$$et
{HTML_DUMP};
}
my
$numEntries
= Get16u(\
$buff
, 6);
my
$dir
;
unless
(
$raf
->Read(
$dir
, 12 *
$numEntries
) == 12 *
$numEntries
) {
$et
->Error(
'Error reading AFCP directory'
, 1);
last
;
}
if
(
$verbose
> 2 and not
$outfile
) {
my
$dat
=
$buff
.
$dir
;
print
$out
" AFCP Directory:\n"
;
HexDump(\
$dat
,
undef
,
Addr
=>
$$dirInfo
{DataPos},
Width
=> 12,
Prefix
=>
$$et
{INDENT},
Out
=>
$out
,
);
}
$fix
and
$et
->Warn(
"Adjusted AFCP offsets by $fix"
, 1);
my
$tagTablePtr
= GetTagTable(
'Image::ExifTool::AFCP::Main'
);
my
(
$index
,
$entry
);
for
(
$index
=0;
$index
<
$numEntries
; ++
$index
) {
my
$entry
= 12 *
$index
;
my
$tag
=
substr
(
$dir
,
$entry
, 4);
my
$size
= Get32u(\
$dir
,
$entry
+ 4);
my
$offset
= Get32u(\
$dir
,
$entry
+ 8);
if
(
$size
< 0x80000000 and
$raf
->Seek(
$offset
+
$fix
, 0) and
$raf
->Read(
$buff
,
$size
) ==
$size
)
{
if
(
$outfile
) {
my
$tagInfo
=
$et
->GetTagInfo(
$tagTablePtr
,
$tag
);
if
(
$tagInfo
and
$$tagInfo
{SubDirectory}) {
my
%subdirInfo
= (
DataPt
=> \
$buff
,
DirStart
=> 0,
DirLen
=>
$size
,
DataPos
=>
$offset
+
$fix
,
Parent
=>
'AFCP'
,
);
my
$subTable
= GetTagTable(
$tagInfo
->{SubDirectory}->{TagTable});
my
$newDir
=
$et
->WriteDirectory(\
%subdirInfo
,
$subTable
);
if
(
defined
$newDir
) {
$size
=
length
$newDir
;
$buff
=
$newDir
;
}
}
$fixup
->AddFixup(
length
(
$dirBuff
) + 8);
$dirBuff
.=
$tag
. Set32u(
$size
) . Set32u(
length
$valBuff
);
$valBuff
.=
$buff
;
}
else
{
$et
->HandleTag(
$tagTablePtr
,
$tag
,
$buff
,
DataPt
=> \
$buff
,
Size
=>
$size
,
Index
=>
$index
,
DataPos
=>
$offset
+
$fix
,
);
}
}
else
{
$et
->Warn(
"Bad AFCP directory"
);
$rtnVal
= -1
if
$outfile
;
last
;
}
}
if
(
$outfile
and
length
(
$dirBuff
)) {
my
$outPos
= Tell(
$outfile
);
my
$valPos
=
$outPos
+ 12;
$fixup
->{Shift} +=
$valPos
+
length
(
$dirBuff
);
$fixup
->ApplyFixup(\
$dirBuff
);
Write(
$outfile
,
$hdr
,
$vers
, Set16u(
length
(
$dirBuff
)/12), Set32u(0),
$dirBuff
,
$valBuff
,
$hdr
, Set32u(
$outPos
), Set32u(0)) or
$rtnVal
= -1;
$fixup
->AddFixup(
length
(
$dirBuff
) +
length
(
$valBuff
) + 4);
$fixup
->{Start} +=
$valPos
;
$fixup
->{Shift} -=
$valPos
;
}
last
;
}
return
$rtnVal
;
}
1;