source: gsdl/trunk/perllib/cpan/Image/ExifTool/AFCP.pm@ 16842

Last change on this file since 16842 was 16842, checked in by davidb, 16 years ago

ExifTool added to cpan area to support metadata extraction from files such as JPEG. Primarily targetted as Image files (hence the Image folder name decided upon by the ExifTool author) it also can handle video such as flash and audio such as Wav

File size: 10.0 KB
Line 
1#------------------------------------------------------------------------------
2# File: AFCP.pm
3#
4# Description: Read/write AFCP trailer
5#
6# Revisions: 12/26/2005 - P. Harvey Created
7#
8# References: 1) http://www.tocarte.com/media/axs_afcp_spec.pdf
9#------------------------------------------------------------------------------
10
11package Image::ExifTool::AFCP;
12
13use strict;
14use vars qw($VERSION);
15use Image::ExifTool qw(:DataAccess :Utils);
16
17$VERSION = '1.02';
18
19sub ProcessAFCP($$);
20
21%Image::ExifTool::AFCP::Main = (
22 PROCESS_PROC => \&ProcessAFCP,
23 NOTES => q{
24AFCP stands for AXS File Concatenation Protocol, and is a poorly designed
25protocol for appending information to the end of files. This can be used as
26an auxiliary technique to store IPTC information in images, but is
27incompatible with some file formats.
28
29ExifTool will read and write (but not create) AFCP IPTC information in JPEG
30images, but not other image formats.
31 },
32 IPTC => { SubDirectory => { TagTable => 'Image::ExifTool::IPTC::Main' } },
33 TEXT => 'Text',
34 Nail => {
35 Name => 'ThumbnailImage',
36 ValueConv => q{
37 my $img = substr($val, 18);
38 return $self->ValidateImage(\$img,$tag);
39 },
40 },
41 PrVw => {
42 Name => 'PreviewImage',
43 ValueConv => q{
44 my $img = substr($val, 18);
45 return $self->ValidateImage(\$img,$tag);
46 },
47 },
48);
49
50#------------------------------------------------------------------------------
51# Read/write AFCP information in a file
52# Inputs: 0) ExifTool object reference, 1) dirInfo reference
53# (Set 'ScanForAFCP' member in dirInfo to scan from current position for AFCP)
54# Returns: 1 on success, 0 if this file didn't contain AFCP information
55# -1 on write error or if the offsets were incorrect on reading
56# - updates DataPos to point to actual AFCP start if ScanForAFCP is set
57# - updates DirLen to trailer length
58# - returns Fixup reference in dirInfo hash when writing
59sub ProcessAFCP($$)
60{
61 my ($exifTool, $dirInfo) = @_;
62 my $raf = $$dirInfo{RAF};
63 my $curPos = $raf->Tell();
64 my $offset = $$dirInfo{Offset} || 0; # offset from end of file
65 my $rtnVal = 0;
66
67NoAFCP: for (;;) {
68 my ($buff, $fix, $dirBuff, $valBuff, $fixup, $vers);
69 # look for AXS trailer
70 last unless $raf->Seek(-12-$offset, 2) and
71 $raf->Read($buff, 12) == 12 and
72 $buff =~ /^(AXS(!|\*))/;
73 my $endPos = $raf->Tell();
74 my $hdr = $1;
75 SetByteOrder($2 eq '!' ? 'MM' : 'II');
76 my $startPos = Get32u(\$buff, 4);
77 if ($raf->Seek($startPos, 0) and $raf->Read($buff, 12) == 12 and $buff =~ /^$hdr/) {
78 $fix = 0;
79 } else {
80 $rtnVal = -1;
81 # look for start of AXS trailer if 'ScanForAFCP'
82 last unless $$dirInfo{ScanForAFCP} and $raf->Seek($curPos, 0);
83 my $actualPos = $curPos;
84 # first look for header right at current position
85 for (;;) {
86 last if $raf->Read($buff, 12) == 12 and $buff =~ /^$hdr/;
87 last NoAFCP if $actualPos != $curPos;
88 # scan for AXS header (could be after preview image)
89 for (;;) {
90 my $buf2;
91 $raf->Read($buf2, 65536) or last NoAFCP;
92 $buff .= $buf2;
93 if ($buff =~ /$hdr/g) {
94 $actualPos += pos($buff) - length($hdr);
95 last; # ok, now go back and re-read header
96 }
97 $buf2 = substr($buf2, -3); # only need last 3 bytes for next test
98 $actualPos += length($buff) - length($buf2);
99 $buff = $buf2;
100 }
101 last unless $raf->Seek($actualPos, 0); # seek to start of AFCP
102 }
103 # calculate shift for fixing AFCP offsets
104 $fix = $actualPos - $startPos;
105 }
106 # set variables returned in dirInfo hash
107 $$dirInfo{DataPos} = $startPos + $fix; # actual start position
108 $$dirInfo{DirLen} = $endPos - ($startPos + $fix);
109
110 $rtnVal = 1;
111 my $verbose = $exifTool->Options('Verbose');
112 my $out = $exifTool->Options('TextOut');
113 my $outfile = $$dirInfo{OutFile};
114 if ($outfile) {
115 # allow all AFCP information to be deleted
116 if ($exifTool->{DEL_GROUP}->{AFCP}) {
117 $verbose and print $out " Deleting AFCP\n";
118 ++$exifTool->{CHANGED};
119 last;
120 }
121 $dirBuff = $valBuff = '';
122 require Image::ExifTool::Fixup;
123 $fixup = $$dirInfo{Fixup};
124 $fixup or $fixup = $$dirInfo{Fixup} = new Image::ExifTool::Fixup;
125 $vers = substr($buff, 4, 2); # get version number
126 } else {
127 $exifTool->DumpTrailer($dirInfo) if $verbose or $exifTool->{HTML_DUMP};
128 }
129 # read AFCP directory data
130 my $numEntries = Get16u(\$buff, 6);
131 my $dir;
132 unless ($raf->Read($dir, 12 * $numEntries) == 12 * $numEntries) {
133 $exifTool->Error('Error reading AFCP directory', 1);
134 last;
135 }
136 if ($verbose > 2 and not $outfile) {
137 my $dat = $buff . $dir;
138 print $out " AFCP Directory:\n";
139 Image::ExifTool::HexDump(\$dat, undef,
140 Addr => $$dirInfo{DataPos},
141 Width => 12,
142 Prefix => $exifTool->{INDENT},
143 Out => $out,
144 );
145 }
146 $fix and $exifTool->Warn("Adjusted AFCP offsets by $fix", 1);
147#
148# process AFCP directory
149#
150 my $tagTablePtr = GetTagTable('Image::ExifTool::AFCP::Main');
151 my ($index, $entry);
152 for ($index=0; $index<$numEntries; ++$index) {
153 my $entry = 12 * $index;
154 my $tag = substr($dir, $entry, 4);
155 my $size = Get32u(\$dir, $entry + 4);
156 my $offset = Get32u(\$dir, $entry + 8);
157 if ($size < 0x80000000 and
158 $raf->Seek($offset+$fix, 0) and
159 $raf->Read($buff, $size) == $size)
160 {
161 if ($outfile) {
162 # rewrite this information
163 my $tagInfo = $exifTool->GetTagInfo($tagTablePtr, $tag);
164 if ($tagInfo and $$tagInfo{SubDirectory}) {
165 my %subdirInfo = (
166 DataPt => \$buff,
167 DirStart => 0,
168 DirLen => $size,
169 DataPos => $offset + $fix,
170 Parent => 'AFCP',
171 );
172 my $subTable = GetTagTable($tagInfo->{SubDirectory}->{TagTable});
173 my $newDir = $exifTool->WriteDirectory(\%subdirInfo, $subTable);
174 if (defined $newDir) {
175 $size = length $newDir;
176 $buff = $newDir;
177 }
178 }
179 $fixup->AddFixup(length($dirBuff) + 8);
180 $dirBuff .= $tag . Set32u($size) . Set32u(length $valBuff);
181 $valBuff .= $buff;
182 } else {
183 # extract information
184 $exifTool->HandleTag($tagTablePtr, $tag, $buff,
185 DataPt => \$buff,
186 Size => $size,
187 Index => $index,
188 DataPos => $offset + $fix,
189 );
190 }
191 } else {
192 $exifTool->Warn("Bad AFCP directory");
193 $rtnVal = -1 if $outfile;
194 last;
195 }
196 }
197 if ($outfile and length($dirBuff)) {
198 my $outPos = Tell($outfile); # get current outfile position
199 # apply fixup to directory pointers
200 my $valPos = $outPos + 12; # start of value data
201 $fixup->{Shift} += $valPos + length($dirBuff);
202 $fixup->ApplyFixup(\$dirBuff);
203 # write the AFCP header, directory, value data and EOF record (with zero checksums)
204 Write($outfile, $hdr, $vers, Set16u(length($dirBuff)/12), Set32u(0),
205 $dirBuff, $valBuff, $hdr, Set32u($outPos), Set32u(0)) or $rtnVal = -1;
206 # complete fixup so the calling routine can apply further shifts
207 $fixup->AddFixup(length($dirBuff) + length($valBuff) + 4);
208 $fixup->{Start} += $valPos;
209 $fixup->{Shift} -= $valPos;
210 }
211 last;
212 }
213 return $rtnVal;
214}
215
2161; # end
217
218__END__
219
220=head1 NAME
221
222Image::ExifTool::AFCP - Read/write AFCP trailer
223
224=head1 SYNOPSIS
225
226This module is used by Image::ExifTool
227
228=head1 DESCRIPTION
229
230This module contains definitions required by Image::ExifTool to extract
231information from the AFCP trailer. Although the AFCP specification is
232compatible with various file formats, ExifTool currently only processes AFCP
233in JPEG images.
234
235=head1 NOTES
236
237AFCP is a specification which allows meta information (including IPTC) to be
238appended to the end of a file.
239
240It is a poorly designed protocol because (like TIFF) it uses absolute
241offsets to specify data locations. This is a huge blunder because it makes
242the AFCP information dependent on the file length, so it is easily
243invalidated by image editing software which doesn't recognize the AFCP
244trailer to fix up these offsets when the file length changes. ExifTool will
245attempt to fix these invalid offsets if possible.
246
247Scanning for AFCP information may be time consuming, especially when reading
248from a sequential device, since the information is at the end of the file.
249In these instances, the ExifTool FastScan option may be used to disable
250scanning for AFCP information.
251
252=head1 AUTHOR
253
254Copyright 2003-2007, Phil Harvey (phil at owl.phy.queensu.ca)
255
256This library is free software; you can redistribute it and/or modify it
257under the same terms as Perl itself.
258
259=head1 REFERENCES
260
261=over 4
262
263=item L<http://www.tocarte.com/media/axs_afcp_spec.pdf>
264
265=back
266
267=head1 SEE ALSO
268
269L<Image::ExifTool::TagNames/AFCP Tags>,
270L<Image::ExifTool(3pm)|Image::ExifTool>
271
272=cut
273
Note: See TracBrowser for help on using the repository browser.