|  line  | 
 stmt  | 
 bran  | 
 cond  | 
 sub  | 
 pod  | 
 time  | 
 code  | 
| 
1
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 ###########################################################  | 
| 
2
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # A Perl package for showing/modifying JPEG (meta)data.   #  | 
| 
3
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # Copyright (C) 2004,2005,2006 Stefano Bettelli           #  | 
| 
4
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # See the COPYING and LICENSE files for license terms.    #  | 
| 
5
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 ###########################################################  | 
| 
6
 | 
15
 | 
 
 | 
 
 | 
  
15
  
 | 
 
 | 
63
 | 
 use Image::MetaData::JPEG::data::Tables qw(:TagsAPP1_Exif);  | 
| 
 
 | 
15
 | 
 
 | 
 
 | 
 
 | 
 
 | 
19
 | 
    | 
| 
 
 | 
15
 | 
 
 | 
 
 | 
 
 | 
 
 | 
2085
 | 
    | 
| 
7
 | 
15
 | 
 
 | 
 
 | 
  
15
  
 | 
 
 | 
83
 | 
 no  integer;  | 
| 
 
 | 
15
 | 
 
 | 
 
 | 
 
 | 
 
 | 
1033
 | 
    | 
| 
 
 | 
15
 | 
 
 | 
 
 | 
 
 | 
 
 | 
74
 | 
    | 
| 
8
 | 
15
 | 
 
 | 
 
 | 
  
15
  
 | 
 
 | 
368
 | 
 use strict;  | 
| 
 
 | 
15
 | 
 
 | 
 
 | 
 
 | 
 
 | 
15
 | 
    | 
| 
 
 | 
15
 | 
 
 | 
 
 | 
 
 | 
 
 | 
315
 | 
    | 
| 
9
 | 
15
 | 
 
 | 
 
 | 
  
15
  
 | 
 
 | 
50
 | 
 use warnings;  | 
| 
 
 | 
15
 | 
 
 | 
 
 | 
 
 | 
 
 | 
15
 | 
    | 
| 
 
 | 
15
 | 
 
 | 
 
 | 
 
 | 
 
 | 
17191
 | 
    | 
| 
10
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
11
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 ###########################################################  | 
| 
12
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # This method dumps an Exif APP1 segment. Basically, it   #  | 
| 
13
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # dumps the identifier, the two IFDs and the thumbnail.   #  | 
| 
14
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 ###########################################################  | 
| 
15
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 sub dump_app1_exif {  | 
| 
16
 | 
161
 | 
 
 | 
 
 | 
  
161
  
 | 
  
0
  
 | 
200
 | 
     my ($this) = @_;  | 
| 
17
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     # dump the identifier (not part of the TIFF header)  | 
| 
18
 | 
161
 | 
 
 | 
 
 | 
 
 | 
 
 | 
362
 | 
     my $identifier = $this->search_record('Identifier')->get();  | 
| 
19
 | 
161
 | 
 
 | 
 
 | 
 
 | 
 
 | 
485
 | 
     $this->set_data($identifier);  | 
| 
20
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     # dump the TIFF header; note that the offset returned by  | 
| 
21
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     # dump_TIFF_header is the current position in the newly written  | 
| 
22
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     # data area AFTER the identifier (i.e., the base is the base  | 
| 
23
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     # of the TIFF header), so it does not start from zero but from the  | 
| 
24
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     # value of $ifd0_link. Be aware that its meaning is slightly  | 
| 
25
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     # different from $offset in the parser.  | 
| 
26
 | 
161
 | 
 
 | 
 
 | 
 
 | 
 
 | 
398
 | 
     my ($header, $offset, $endianness) = $this->dump_TIFF_header();  | 
| 
27
 | 
161
 | 
 
 | 
 
 | 
 
 | 
 
 | 
427
 | 
     $this->set_data($header);  | 
| 
28
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     # locally set the current endianness to what we have found.  | 
| 
29
 | 
161
 | 
 
 | 
 
 | 
 
 | 
 
 | 
327
 | 
     local $this->{endianness} = $endianness;  | 
| 
30
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     # dump all the records of the 0th IFD, and update $offset to  | 
| 
31
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     # point after the end of the current data area (with respect  | 
| 
32
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     # to the TIFF header base). This must be done even if the IFD  | 
| 
33
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     # itself is empty (in order to find the next one).  | 
| 
34
 | 
161
 | 
  
100
  
 | 
 
 | 
 
 | 
 
 | 
371
 | 
     my $ifd1_link = defined $this->search_record('IFD1') ? 0 : 1;  | 
| 
35
 | 
161
 | 
 
 | 
 
 | 
 
 | 
 
 | 
467
 | 
     $offset += $this->set_data($this->dump_ifd('IFD0', $offset, $ifd1_link));  | 
| 
36
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     # same thing with the 1st IFD. We don't have to worry if this  | 
| 
37
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     # IFD is not there, because dump_ifd tests for this case.  | 
| 
38
 | 
161
 | 
 
 | 
 
 | 
 
 | 
 
 | 
458
 | 
     $offset += $this->set_data($this->dump_ifd('IFD1', $offset, 1));  | 
| 
39
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     # if there is thumbnail data in the main directory of this  | 
| 
40
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     # segment, it is time to dump it. Use the reference, because  | 
| 
41
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     # this can be quite large (some tens of kilobytes ....)  | 
| 
42
 | 
161
 | 
  
100
  
 | 
 
 | 
 
 | 
 
 | 
456
 | 
     if (my $th_record = $this->search_record('ThumbnailData')) {  | 
| 
43
 | 
82
 | 
 
 | 
 
 | 
 
 | 
 
 | 
236
 | 
 	(undef, undef, undef, my $tdataref) = $th_record->get();  | 
| 
44
 | 
82
 | 
 
 | 
 
 | 
 
 | 
 
 | 
229
 | 
 	$this->set_data($tdataref); }  | 
| 
45
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 }  | 
| 
46
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
47
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 ###########################################################  | 
| 
48
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # This method reconstructs a TIFF header and returns a    #  | 
| 
49
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # list with all the relevant values. Nothing is written   #  | 
| 
50
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # to the data area. Records are searched for in the       #  | 
| 
51
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # directory specified by the second argument.             #  | 
| 
52
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 ###########################################################  | 
| 
53
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 sub dump_TIFF_header {  | 
| 
54
 | 
162
 | 
 
 | 
 
 | 
  
162
  
 | 
  
0
  
 | 
196
 | 
     my ($this, $dirref) = @_;  | 
| 
55
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     # retrieve the endianness, and signature. It is not worth  | 
| 
56
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     # setting the temporary segment endianness here, do it later.  | 
| 
57
 | 
162
 | 
 
 | 
 
 | 
 
 | 
 
 | 
388
 | 
     my $endianness=$this->search_record('Endianness',$dirref)->get();  | 
| 
58
 | 
162
 | 
 
 | 
 
 | 
 
 | 
 
 | 
431
 | 
     my $signature =$this->search_record('Signature',$dirref)->get($endianness);  | 
| 
59
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     # create a string containing the TIFF header (we always  | 
| 
60
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     # choose the offset of the 0th IFD must to be 8 here).  | 
| 
61
 | 
162
 | 
 
 | 
 
 | 
 
 | 
 
 | 
211
 | 
     my $ifd0_len  = 8;  | 
| 
62
 | 
162
 | 
  
100
  
 | 
 
 | 
 
 | 
 
 | 
559
 | 
     my $ifd0_link = pack $endianness eq $BIG_ENDIAN ? "N" : "V", $ifd0_len;  | 
| 
63
 | 
162
 | 
 
 | 
 
 | 
 
 | 
 
 | 
317
 | 
     my $header = $endianness . $signature . $ifd0_link;  | 
| 
64
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     # return all relevant values in a list  | 
| 
65
 | 
162
 | 
 
 | 
 
 | 
 
 | 
 
 | 
398
 | 
     return ($header, $ifd0_len, $endianness);  | 
| 
66
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 }  | 
| 
67
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
68
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 ###########################################################  | 
| 
69
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # This is the core of the Exif APP1 dumping method. It    #  | 
| 
70
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # takes care to dump a whole IFD, including a special     #  | 
| 
71
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # treatement for thumbnails and makernotes. No action is  #  | 
| 
72
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # taken unless there is already a directory for this IFD  #  | 
| 
73
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # in the structured data area of the segment.             #  | 
| 
74
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # ------------------------------------------------------- #  | 
| 
75
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # Special treatement for tags holding an IFD offset (this #  | 
| 
76
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # includes makernotes); these tags are regenerated on the #  | 
| 
77
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # fly (since they are no more stored) and their value is  #  | 
| 
78
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # recalculated and written to the raw data area.          #  | 
| 
79
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # ------------------------------------------------------- #  | 
| 
80
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # New argument ($next), which specifies how the next_link #  | 
| 
81
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # pointer is to be treated: '0' --> the pointer is dumped #  | 
| 
82
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # with a non zero value; '1' --> the pointer is dumped    #  | 
| 
83
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # with value set to zero; '2' -->: the pointer is ignored #  | 
| 
84
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 ###########################################################  | 
| 
85
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 sub dump_ifd {  | 
| 
86
 | 
630
 | 
 
 | 
 
 | 
  
630
  
 | 
  
0
  
 | 
809
 | 
     my ($this, $dirnames, $offset, $next) = @_;  | 
| 
87
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     # set the next link flag to zero if it is undefined  | 
| 
88
 | 
630
 | 
  
 50
  
 | 
 
 | 
 
 | 
 
 | 
1103
 | 
     $next = 0 unless defined $next;  | 
| 
89
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     # retrieve the appropriate record list (specified by a '@' separated  | 
| 
90
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     # list of dir names in $dirnames to be interpreted in sequence). If  | 
| 
91
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     # this fails, return immediately with a reference to an empty string  | 
| 
92
 | 
630
 | 
 
 | 
 
 | 
 
 | 
 
 | 
1479
 | 
     my $dirref = $this->search_record_value($dirnames);  | 
| 
93
 | 
630
 | 
  
100
  
 | 
 
 | 
 
 | 
 
 | 
1346
 | 
     return \ (my $ns = '') unless $dirref;  | 
| 
94
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     # $short and $long are two useful format strings correctly taking  | 
| 
95
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     # into account the IFD endianness. $format is a format string for  | 
| 
96
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     # packing an Interoperability array  | 
| 
97
 | 
575
 | 
  
100
  
 | 
 
 | 
 
 | 
 
 | 
1219
 | 
     my $short   = $this->{endianness} eq $BIG_ENDIAN ? 'n' : 'v';  | 
| 
98
 | 
575
 | 
  
100
  
 | 
 
 | 
 
 | 
 
 | 
933
 | 
     my $long    = $this->{endianness} eq $BIG_ENDIAN ? 'N' : 'V';  | 
| 
99
 | 
575
 | 
 
 | 
 
 | 
 
 | 
 
 | 
1599
 | 
     my $format  = $short. $short . $long;  | 
| 
100
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     # retrieve the record list for this IFD, then eliminate the REFERENCE  | 
| 
101
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     # records (added by the parser routine, they were not in the JPEG file).  | 
| 
102
 | 
575
 | 
 
 | 
 
 | 
 
 | 
 
 | 
750
 | 
     my @records = grep { $_->{type} != $REFERENCE } @$dirref;  | 
| 
 
 | 
7246
 | 
 
 | 
 
 | 
 
 | 
 
 | 
9690
 | 
    | 
| 
103
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     # for each reference record with a non-undef extra field, regenerate  | 
| 
104
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     # the corresponding offset record (which can be retraced from the  | 
| 
105
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     # "extra" field) and insert it into the @records list with a dummy  | 
| 
106
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     # value (0). We can safely use $LONG as record type (new-style offsets).  | 
| 
107
 | 
392
 | 
 
 | 
 
 | 
 
 | 
 
 | 
1165
 | 
     push @records, map {  | 
| 
108
 | 
7246
 | 
  
100
  
 | 
 
 | 
 
 | 
 
 | 
12091
 | 
 	my $nt = JPEG_lookup($this->{name}, $dirnames, $_->{extra});  | 
| 
109
 | 
392
 | 
 
 | 
 
 | 
 
 | 
 
 | 
3168
 | 
 	new Image::MetaData::JPEG::Record($nt, $LONG, \ pack($long, 0)) }  | 
| 
110
 | 
575
 | 
 
 | 
 
 | 
 
 | 
 
 | 
743
 | 
     grep { $_->{type} == $REFERENCE && $_->{extra} } @$dirref;  | 
| 
111
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     # sort the accumulated records with respect to their tags (numeric).  | 
| 
112
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     # This is not, strictly speaking mandatory, but the file looks more  | 
| 
113
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     # polished after this (am I introducing any gratuitous incompatibility?)  | 
| 
114
 | 
575
 | 
 
 | 
 
 | 
 
 | 
 
 | 
1573
 | 
     @records = sort { $a->{key} <=> $b->{key} } @records;  | 
| 
 
 | 
10965
 | 
 
 | 
 
 | 
 
 | 
 
 | 
10091
 | 
    | 
| 
115
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     # the IFD data area is to be initialised with two bytes specifying  | 
| 
116
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     # the number of Interoperability arrays.  | 
| 
117
 | 
575
 | 
 
 | 
 
 | 
 
 | 
 
 | 
1253
 | 
     my $ifd_content = pack $short, scalar @records;  | 
| 
118
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     # Data areas too large for the Interop array will be saved in $extra;  | 
| 
119
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     # $remote should point to its beginning (from TIFF header base), so we  | 
| 
120
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     # must skip 12 bytes for each Interop. array, 2 bytes for the initial  | 
| 
121
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     # count (and 4 bytes for the next IFD link, unless $next is two).  | 
| 
122
 | 
575
 | 
 
 | 
 
 | 
 
 | 
 
 | 
1101
 | 
     my ($remote, $extra) = ($offset + 2 + 12*@records, '');  | 
| 
123
 | 
575
 | 
  
 50
  
 | 
 
 | 
 
 | 
 
 | 
1110
 | 
     $remote += 4 unless $next == 2;  | 
| 
124
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     # managing the thumbnail is not trivial. We want to be sure that  | 
| 
125
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     # its declared size corresponds to the reality and correct if  | 
| 
126
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     # this is not the case (is this a stupid idea?)  | 
| 
127
 | 
575
 | 
  
100
  
 | 
  
100
  
 | 
 
 | 
 
 | 
1501
 | 
     if ($dirnames eq 'IFD1' &&  | 
| 
128
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	(my $th_record = $this->search_record('ThumbnailData'))) {  | 
| 
129
 | 
82
 | 
 
 | 
 
 | 
 
 | 
 
 | 
213
 | 
 	(undef, undef, undef, my $tdataref) = $th_record->get();  | 
| 
130
 | 
82
 | 
 
 | 
 
 | 
 
 | 
 
 | 
221
 | 
 	for ($THTIFF_LENGTH, $THJPEG_LENGTH) {  | 
| 
131
 | 
164
 | 
 
 | 
 
 | 
 
 | 
 
 | 
366
 | 
 	    my $th_len = $this->search_record($_, $dirref);  | 
| 
132
 | 
164
 | 
  
100
  
 | 
 
 | 
 
 | 
 
 | 
570
 | 
 	    $th_len->set_value(length $$tdataref) if $th_len; } }  | 
| 
133
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     # the following tags can be found only in IFD1 in APP1, and concern  | 
| 
134
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     # the thumbnail location. They must be dealt with in a special way.  | 
| 
135
 | 
575
 | 
 
 | 
 
 | 
 
 | 
 
 | 
1557
 | 
     my %th_tags = ($THTIFF_OFFSET => undef, $THJPEG_OFFSET => undef);  | 
| 
136
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     # determine weather this IFD can have subidrectories or not; if so,  | 
| 
137
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     # get a special mapping table from %IFD_SUBDIRS (avoid autovivification)  | 
| 
138
 | 
575
 | 
 
 | 
 
 | 
 
 | 
 
 | 
1131
 | 
     my $path = join '@', $this->{name}, $dirnames;  | 
| 
139
 | 
575
 | 
  
100
  
 | 
 
 | 
 
 | 
 
 | 
1422
 | 
     my $mapping = exists $IFD_SUBDIRS{$path} ? $IFD_SUBDIRS{$path} : undef;  | 
| 
140
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     # loop on all selected records and dump them  | 
| 
141
 | 
575
 | 
 
 | 
 
 | 
 
 | 
 
 | 
713
 | 
     for my $record (@records) {  | 
| 
142
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	# extract all necessary information about this  | 
| 
143
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	# Interoperability array, with the correct endianness.  | 
| 
144
 | 
7243
 | 
 
 | 
 
 | 
 
 | 
 
 | 
14444
 | 
 	my ($tag, $type, $count, $dataref) = $record->get($this->{endianness});  | 
| 
145
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	# calculate the length of the array data, and correct $count  | 
| 
146
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	# for string-like records (it had been set to 1 during the  | 
| 
147
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	# parsing, it must be the data length in this case).  | 
| 
148
 | 
7243
 | 
 
 | 
 
 | 
 
 | 
 
 | 
7372
 | 
 	my $length = length $$dataref;  | 
| 
149
 | 
7243
 | 
  
100
  
 | 
 
 | 
 
 | 
 
 | 
12225
 | 
 	$count = $length if $record->get_category() eq 'S';  | 
| 
150
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	# the last four bytes in an interoperability array are either  | 
| 
151
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	# data or an address; prepare a variable for holding this value  | 
| 
152
 | 
7243
 | 
 
 | 
 
 | 
 
 | 
 
 | 
6799
 | 
 	my $record_end = '';  | 
| 
153
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	# if this IFD1 record specifies the thumbnail location, it needs  | 
| 
154
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	# a special treatment, since we cannot yet know where the thumbnail  | 
| 
155
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	# will be located. Write a bogus offset now and overwrite it later.  | 
| 
156
 | 
7243
 | 
  
100
  
 | 
  
100
  
 | 
 
 | 
 
 | 
33535
 | 
 	if ($dirnames eq 'IFD1' && exists $th_tags{$tag}) {  | 
| 
 
 | 
 
 | 
  
100
  
 | 
  
100
  
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
 
 | 
 
 | 
  
100
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
157
 | 
72
 | 
 
 | 
 
 | 
 
 | 
 
 | 
133
 | 
 	    $th_tags{$tag} = 8 + length $ifd_content;  | 
| 
158
 | 
72
 | 
 
 | 
 
 | 
 
 | 
 
 | 
82
 | 
 	    $record_end = "\000\000\000\000"; }  | 
| 
159
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	# if this Interop array is known to correspond to a subdirectory  | 
| 
160
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	# (use %$mapping for this), the subdirectory content is calculated  | 
| 
161
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	# on the fly, and stored in this IFD's remote data area. Its offset  | 
| 
162
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	# instead is saved at the end of the Interoperability array.  | 
| 
163
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	elsif ($mapping && exists $$mapping{$tag}) {  | 
| 
164
 | 
392
 | 
 
 | 
 
 | 
 
 | 
 
 | 
1190
 | 
 	    my $is_makernote = ($tag =~ $MAKERNOTE_TAG);  | 
| 
165
 | 
392
 | 
 
 | 
 
 | 
 
 | 
 
 | 
776
 | 
 	    my $extended_dirnames = $dirnames.'@'.$$mapping{$tag};  | 
| 
166
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	    # MakerNotes require a special treatment, including rewriting  | 
| 
167
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	    # type and count (one LONG is really many UNDEF bytes); other  | 
| 
168
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	    # subIFD's are written by a recursive dump_ifd (next link is 0).  | 
| 
169
 | 
392
 | 
  
100
  
 | 
 
 | 
 
 | 
 
 | 
1165
 | 
 	    my $subifd = $is_makernote ?  | 
| 
170
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 		$this->dump_makernote($extended_dirnames, $remote) :  | 
| 
171
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 		$this->dump_ifd($extended_dirnames, $remote, 1);  | 
| 
172
 | 
392
 | 
  
100
  
 | 
 
 | 
 
 | 
 
 | 
777
 | 
 	    $type = $UNDEF, $count = length($$subifd) if $is_makernote;  | 
| 
173
 | 
392
 | 
 
 | 
 
 | 
 
 | 
 
 | 
647
 | 
 	    $record_end = pack $long, $remote;  | 
| 
174
 | 
392
 | 
 
 | 
 
 | 
 
 | 
 
 | 
521
 | 
 	    $extra .= $$subifd; $remote += length $$subifd; }  | 
| 
 
 | 
392
 | 
 
 | 
 
 | 
 
 | 
 
 | 
641
 | 
    | 
| 
175
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	# if the data length is not larger than four bytes, we are ok.  | 
| 
176
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	# $$dataref is simply appended (with padding up to 4 bytes,  | 
| 
177
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	# AFTER $$dataref, independently of the IFD endianness).  | 
| 
178
 | 
3485
 | 
 
 | 
 
 | 
 
 | 
 
 | 
4922
 | 
 	elsif ($length <= 4) { $record_end = $$dataref . "\000"x(4-$length); }  | 
| 
179
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	# if $$dataref is too big, it must be packed in the $extra  | 
| 
180
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	# section, and its pointer appended here. Remember to update  | 
| 
181
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	# $remote for the next record of this type.  | 
| 
182
 | 
3294
 | 
 
 | 
 
 | 
 
 | 
 
 | 
4245
 | 
 	else { $record_end = pack $long, $remote;  | 
| 
183
 | 
3294
 | 
 
 | 
 
 | 
 
 | 
 
 | 
2770
 | 
 	       $remote += $length; $extra .= $$dataref; }  | 
| 
 
 | 
3294
 | 
 
 | 
 
 | 
 
 | 
 
 | 
3396
 | 
    | 
| 
184
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	# the interoperability array starts with tag, type and count,  | 
| 
185
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	# followed by $record_end (4 bytes): dump into the ifd data area  | 
| 
186
 | 
7243
 | 
 
 | 
 
 | 
 
 | 
 
 | 
15624
 | 
 	$ifd_content .= (pack $format, $tag, $type, $count) . $record_end; }  | 
| 
187
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     # after the Interop. arrays there can be a link to the next IFD  | 
| 
188
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     # (this takes 4 bytes). $next = 0 --> write the next IFD offset,  | 
| 
189
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     # = 1 --> write zero, 2 --> do not write these four bytes.  | 
| 
190
 | 
575
 | 
  
100
  
 | 
 
 | 
 
 | 
 
 | 
1081
 | 
     $ifd_content .= pack $long, $remote if $next == 0;  | 
| 
191
 | 
575
 | 
  
100
  
 | 
 
 | 
 
 | 
 
 | 
1218
 | 
     $ifd_content .= pack $long, 0       if $next == 1;  | 
| 
192
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     # then, we save the remote data area  | 
| 
193
 | 
575
 | 
 
 | 
 
 | 
 
 | 
 
 | 
2032
 | 
     $ifd_content .= $extra;  | 
| 
194
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     # if the thumbnail offset tags were found during the scan, we  | 
| 
195
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     # need to overwrite their values with a meaningful offset now.  | 
| 
196
 | 
575
 | 
 
 | 
 
 | 
 
 | 
 
 | 
1189
 | 
     for (keys %th_tags) {  | 
| 
197
 | 
1150
 | 
  
100
  
 | 
 
 | 
 
 | 
 
 | 
2235
 | 
 	next unless my $overwrite = $th_tags{$_};  | 
| 
198
 | 
72
 | 
 
 | 
 
 | 
 
 | 
 
 | 
184
 | 
 	my $tag_record = $this->search_record($_, $dirref);  | 
| 
199
 | 
72
 | 
 
 | 
 
 | 
 
 | 
 
 | 
166
 | 
 	$tag_record->set_value($remote);  | 
| 
200
 | 
72
 | 
 
 | 
 
 | 
 
 | 
 
 | 
191
 | 
 	my $new_offset = $tag_record->get($this->{endianness});  | 
| 
201
 | 
72
 | 
 
 | 
 
 | 
 
 | 
 
 | 
224
 | 
 	substr($ifd_content, $overwrite, length $new_offset) = $new_offset; }  | 
| 
202
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     # return a reference to the scalar which holds the binary dump  | 
| 
203
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     # of this IFD (to be saved in the caller routine, I think).  | 
| 
204
 | 
575
 | 
 
 | 
 
 | 
 
 | 
 
 | 
2722
 | 
     return \$ifd_content;  | 
| 
205
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 }  | 
| 
206
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
207
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 ###########################################################  | 
| 
208
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # This routine dumps all kinds of makernotes. Have a look #  | 
| 
209
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # at parse_makernote() for further details.               #  | 
| 
210
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 ###########################################################  | 
| 
211
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 sub dump_makernote {  | 
| 
212
 | 
87
 | 
 
 | 
 
 | 
  
87
  
 | 
  
0
  
 | 
124
 | 
     my ($this, $dirnames, $offset) = @_;  | 
| 
213
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     # look for a MakerNote subdirectory beginning with $dirnames: the  | 
| 
214
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     # actual name has the format appended, e.g., MakerNoteData_Canon.  | 
| 
215
 | 
87
 | 
 
 | 
 
 | 
 
 | 
 
 | 
751
 | 
     $dirnames =~ s/(.*@|)([^@]*)/$1/;  | 
| 
216
 | 
87
 | 
 
 | 
 
 | 
 
 | 
 
 | 
288
 | 
     my $dirref = $this->search_record_value($dirnames);  | 
| 
217
 | 
2201
 | 
 
 | 
 
 | 
 
 | 
 
 | 
4654
 | 
     $dirnames .= $_->{key}, $dirref = $_->get_value(), last  | 
| 
218
 | 
87
 | 
 
 | 
 
 | 
 
 | 
 
 | 
156
 | 
 	for (grep{$_->{key}=~/^$2/} @$dirref);  | 
| 
219
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     # Also look for the subdir with special information.  | 
| 
220
 | 
87
 | 
 
 | 
 
 | 
 
 | 
 
 | 
304
 | 
     my $spcref = $this->search_record_value($dirnames.'@special');  | 
| 
221
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     # entering here without the dir and its subdir being present is an error  | 
| 
222
 | 
87
 | 
  
 50
  
 | 
  
 33
  
 | 
 
 | 
 
 | 
415
 | 
     $this->die('MakerNote subdirs not found') unless $dirref && $spcref;  | 
| 
223
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     # read all MakerNote special values (added by the parser routine)  | 
| 
224
 | 
435
 | 
 
 | 
 
 | 
 
 | 
 
 | 
848
 | 
     my ($data, $signature, $endianness, $format, $error) =  | 
| 
225
 | 
87
 | 
 
 | 
 
 | 
 
 | 
 
 | 
179
 | 
 	map { $this->search_record_value($_, $spcref) }  | 
| 
226
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
             ('ORIGINAL', 'SIGNATURE', 'ENDIANNESS', 'FORMAT', 'ERROR');  | 
| 
227
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     # die and debug if the format record is not present  | 
| 
228
 | 
87
 | 
  
 50
  
 | 
 
 | 
 
 | 
 
 | 
242
 | 
     $this->die('FORMAT not found') unless $format;  | 
| 
229
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     # if the format is unknown or there was an error at parse time, it  | 
| 
230
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     # is wiser to return the original, unparsed content of the MakerNote  | 
| 
231
 | 
87
 | 
  
100
  
 | 
  
100
  
 | 
 
 | 
 
 | 
404
 | 
     if ($format =~ /unknown/ || defined $error) {  | 
| 
232
 | 
2
 | 
  
 50
  
 | 
 
 | 
 
 | 
 
 | 
5
 | 
 	$this->die('ORIGINAL data not found') unless $data; return \$data; };  | 
| 
 
 | 
2
 | 
 
 | 
 
 | 
 
 | 
 
 | 
5
 | 
    | 
| 
233
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     # also extract the property table for this MakerNote format  | 
| 
234
 | 
85
 | 
 
 | 
 
 | 
 
 | 
 
 | 
226
 | 
     my $hash = $$HASH_MAKERNOTES{$format};  | 
| 
235
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     # now, die if the signature or endianness is still undefined  | 
| 
236
 | 
85
 | 
  
 50
  
 | 
  
 33
  
 | 
 
 | 
 
 | 
371
 | 
     $this->die('Properties not found')unless defined $signature && $endianness;  | 
| 
237
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     # in general, the MakerNote's next-IFD link is zero, but some  | 
| 
238
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     # MakerNotes do not even have these four bytes: prepare the flag  | 
| 
239
 | 
85
 | 
  
 50
  
 | 
 
 | 
 
 | 
 
 | 
204
 | 
     my $next_flag = exists $$hash{nonext} ? 2 : 1;  | 
| 
240
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     # in general, MakerNote's offsets are computed from the APP1 segment  | 
| 
241
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     # TIFF base; however, some formats compute offsets from the beginning  | 
| 
242
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     # of the MakerNote itself: setup the offset base as required.  | 
| 
243
 | 
85
 | 
  
100
  
 | 
 
 | 
 
 | 
 
 | 
221
 | 
     $offset = length($signature) + (exists $$hash{mkntstart} ? 0 : $offset);  | 
| 
244
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     # initialise the data area with the detected signature  | 
| 
245
 | 
85
 | 
 
 | 
 
 | 
 
 | 
 
 | 
116
 | 
     $data = $signature;  | 
| 
246
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     # some MakerNotes have a TIFF header on their own, freeing them  | 
| 
247
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     # from the relocation problem; values from this header overwrite  | 
| 
248
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     # the previously assigned values; records are saved in $mknt_dir.  | 
| 
249
 | 
85
 | 
  
100
  
 | 
 
 | 
 
 | 
 
 | 
239
 | 
     if (exists $$hash{mkntTIFF}) {  | 
| 
250
 | 
1
 | 
 
 | 
 
 | 
 
 | 
 
 | 
4
 | 
 	my ($TIFF_header, $TIFF_offset, $TIFF_endianness)   | 
| 
251
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	    = $this->dump_TIFF_header($spcref);  | 
| 
252
 | 
1
 | 
  
 50
  
 | 
 
 | 
 
 | 
 
 | 
5
 | 
 	$this->die('Endianness mismatch') if $endianness ne $TIFF_endianness;  | 
| 
253
 | 
1
 | 
 
 | 
 
 | 
 
 | 
 
 | 
2
 | 
 	$data .= $TIFF_header; $offset = $TIFF_offset; }  | 
| 
 
 | 
1
 | 
 
 | 
 
 | 
 
 | 
 
 | 
2
 | 
    | 
| 
254
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     # Unstructured case: the content of the MakerNote is simply  | 
| 
255
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     # a sequence of bytes, which must be encoded using $$hash{tags}  | 
| 
256
 | 
85
 | 
  
100
  
 | 
 
 | 
 
 | 
 
 | 
200
 | 
     if (exists $$hash{nonIFD}) {  | 
| 
257
 | 
3608
 | 
 
 | 
 
 | 
 
 | 
 
 | 
3994
 | 
 	$data .= $this->search_record($$_[0], $dirref)->get($endianness)  | 
| 
258
 | 
82
 | 
 
 | 
 
 | 
 
 | 
 
 | 
116
 | 
 	    for map {$$hash{tags}{$_}} sort {$a <=> $b} keys %{$$hash{tags}}; }  | 
| 
 
 | 
15172
 | 
 
 | 
 
 | 
 
 | 
 
 | 
10606
 | 
    | 
| 
 
 | 
82
 | 
 
 | 
 
 | 
 
 | 
 
 | 
1024
 | 
    | 
| 
259
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     # Structured case: the content of the MakerNote can be dumped  | 
| 
260
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     # with dump_ifd (change locally the endianness value).  | 
| 
261
 | 
3
 | 
 
 | 
 
 | 
 
 | 
 
 | 
8
 | 
     else { local $this->{endianness} = $endianness;  | 
| 
262
 | 
3
 | 
 
 | 
 
 | 
 
 | 
 
 | 
5
 | 
 	   $data .= ${$this->dump_ifd($dirnames, $offset, $next_flag)} };  | 
| 
 
 | 
3
 | 
 
 | 
 
 | 
 
 | 
 
 | 
8
 | 
    | 
| 
263
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     # return the MakerNote as a binary object  | 
| 
264
 | 
85
 | 
 
 | 
 
 | 
 
 | 
 
 | 
687
 | 
     return \$data;  | 
| 
265
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 }  | 
| 
266
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
267
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # successful load  | 
| 
268
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 1;  |