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 )