Image-MetaData-JPEG
view release on metacpan or search on metacpan
lib/Image/MetaData/JPEG/parsers/app1_exif.pl view on Meta::CPAN
# The MakerNote tag is read by a call to parse_interop in #
# the IFD0@SubIFD; however, only the offset and size of #
# the MakerNote data area is read there -- the real pro- #
# cessing is done here (this method is called during the #
# analysis of IFD subdirectories in parse_ifd). #
###########################################################
sub parse_makernote {
my ($this, $dirnames, $mknt_offset, $base, $mknt_size) = @_;
# A MakerNote is always in APP1@IFD0@SubIFD; stop immediately
# if $dirnames disagrees with this assumption.
$this->die("Invalid \$dirnames ($dirnames)")
unless $dirnames =~ '^IFD0@SubIFD@[^@]*$';
# get the primary IFD reference and try to extract the maker
# (setup a fake string if this field is not found)
my $ifd0 = $this->search_record_value('IFD0');
my $mknt_maker = $this->search_record_value
(JPEG_lookup('APP1@IFD0@Make'), $ifd0) || 'Unknown Maker';
# try all possible MakerNote formats (+ catch-all rule)
my $mknt_found = undef;
for my $format (sort keys %$HASH_MAKERNOTES) {
# this quest must stop at the first positive match
next if $mknt_found;
# extract the property table for this MakerNote format
# (and skip it if it is only a temporary placeholder)
my $hash = $$HASH_MAKERNOTES{$format};
next if exists $$hash{ignore};
# get the maker and signature for this format
my $format_signature = $$hash{signature};
my $format_maker = $$hash{maker};
# skip if the maker or the signature is incompatible (the
# signature test is the initial part of the data area against
# a regular expression: save the match for later reference)
my $incipit_size = $mknt_size < 50 ? $mknt_size : 50;
my $incipit = $this->read_record($UNDEF, 0+$mknt_offset,$incipit_size);
next unless $mknt_maker =~ /$format_maker/;
next unless $incipit =~ /$format_signature/;
my $signature = $1; my $skip = length $signature;
# OK, we opted for this format
$mknt_found = 1;
# if the previous tests pass, it is time to fix the format and
# to create an appropriate subdirectory for the MakerNote records
my $mknt_dirname = $dirnames.'_'.$format;
my $mknt_dir = $this->provide_subdirectory($mknt_dirname);
# prepare also a special subdirectory for pseudofields
my $mknt_spcname = $mknt_dirname.'@special';
my $mknt_spc = $this->provide_subdirectory($mknt_spcname);
# the MakerNote's endianness can be different from that of the IFD;
# if a value is specified for this format, set it; otherwise, try to
# detect it by testing the first byte after the signature (preferred).
my $it_looks_big_endian = $this->data($mknt_offset+$skip, 1) eq "\000";
my $mknt_endianness = exists $$hash{endianness} ? $$hash{endianness} :
$it_looks_big_endian ? $BIG_ENDIAN : $LITTLE_ENDIAN;
# in general, the MakerNote's next-IFD link is zero, but some
# MakerNotes do not even have these four bytes: prepare the flag
my $next_flag = exists $$hash{nonext} ? 2 : 1;
# in general, MakerNote's offsets are computed from the APP1 segment
# TIFF base; however, some formats compute offsets from the beginning
# of the MakerNote itself: prepare an alternative base if necessary
my $mknt_base = exists $$hash{mkntstart} ? $mknt_offset : $base;
# some MakerNotes have a TIFF header on their own, freeing them
# from the relocation problem; values from this header overwrite
# the previously assigned values; records are saved in $mknt_dir.
if (exists $$hash{mkntTIFF}) {
($mknt_base, my $ifd_link, $mknt_endianness)
= $this->parse_TIFF_header($mknt_offset + $skip, $mknt_spc);
# update $skip to point to the beginning of the IFD
$skip += $ifd_link; }
# calculate the address of the beginning of the IFD (both with
# and without a TIFF header) or of an unstructured data area.
my $data_offset = $mknt_offset + $skip;
# Store the special MakerNote information in a special subdirectory
# (for instance, the raw MakerNote image, so that the block can at
# least be dumped to disk again in case its structure is unknown)
$this->store_record($mknt_spc, shift @$_, $UNDEF, @$_)
for (['ORIGINAL' , $mknt_offset, $mknt_size],
['SIGNATURE' , \$signature],
['ENDIANNESS', \$mknt_endianness],
['FORMAT' , \$format]);
# change locally the endianness value
local $this->{endianness} = $mknt_endianness;
# Unstructured case: the content of the MakerNote is simply
# a sequence of bytes, which must be decoded using $$hash{tags};
# execute inside an eval, to confine errors inside MakerNotes
if (exists $$hash{nonIFD}) { eval {
my $p = $$hash{tags};
$this->store_record($mknt_dir, @$_[0,1], $data_offset, $$_[2])
for map { $$p{$_} } sort { $a <=> $b } keys %$p;
$this->die('MakerNote size mismatch')
unless $format =~ /unknown/ ||
$data_offset == $mknt_offset + $mknt_size; } }
# Structured case: the content of the MakerNote is approximately
# a standard IFD, so parse_ifd is sufficient: it is called a se-
# cond time if an error occurs (+ cleanup of unreliable findings),
# but if this doesn't solve the problem, one reverts to 1st case.
else {
my $args = [$mknt_dirname, $data_offset, $mknt_base, $next_flag];
my $code = '@$mknt_dir=@$copy; $this->parse_ifd(@$args';
my $copy = [@$mknt_dir]; eval "$code)";
$this->warn('Using predictions'), eval "$code,1)" if $@;
$this->warn('Predictions failed'), eval "$code)" if $@;
};
# If any errors occured during the real MakerNote parsing,
# and additional special record is saved with the error message
# (this will be the last record in the MakerNote subdirectory)
$this->store_record($mknt_spc, 'ERROR',$ASCII,\$@) if $@;
# print "MESSAGE FROM MAKERNOTE:\n$@\n" if $@;
}
}
# successful load
1;
( run in 0.292 second using v1.01-cache-2.11-cpan-5511b514fd6 )