|  line  | 
 stmt  | 
 bran  | 
 cond  | 
 sub  | 
 pod  | 
 time  | 
 code  | 
| 
1
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # IPTCInfo: extractor for IPTC metadata embedded in images  | 
| 
2
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # Copyright (C) 2000-2004 Josh Carter   | 
| 
3
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # All rights reserved.  | 
| 
4
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 #  | 
| 
5
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # This program is free software; you can redistribute it and/or modify  | 
| 
6
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # it under the same terms as Perl itself.  | 
| 
7
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
8
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 package Image::IPTCInfo;  | 
| 
9
 | 
1
 | 
 
 | 
 
 | 
  
1
  
 | 
 
 | 
1995
 | 
 use IO::File;  | 
| 
 
 | 
1
 | 
 
 | 
 
 | 
 
 | 
 
 | 
11883
 | 
    | 
| 
 
 | 
1
 | 
 
 | 
 
 | 
 
 | 
 
 | 
146
 | 
    | 
| 
10
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
11
 | 
1
 | 
 
 | 
 
 | 
  
1
  
 | 
 
 | 
8
 | 
 use vars qw($VERSION);  | 
| 
 
 | 
1
 | 
 
 | 
 
 | 
 
 | 
 
 | 
2
 | 
    | 
| 
 
 | 
1
 | 
 
 | 
 
 | 
 
 | 
 
 | 
71
 | 
    | 
| 
12
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 $VERSION = '1.95';  | 
| 
13
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
14
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 #  | 
| 
15
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # Global vars  | 
| 
16
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 #  | 
| 
17
 | 
1
 | 
 
 | 
 
 | 
 
 | 
 
 | 
6175
 | 
 use vars ('%datasets',		  # master list of dataset id's  | 
| 
18
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 		  '%datanames',       # reverse mapping (for saving)  | 
| 
19
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 		  '%listdatasets',	  # master list of repeating dataset id's  | 
| 
20
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 		  '%listdatanames',   # reverse  | 
| 
21
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 		  '$MAX_FILE_OFFSET', # maximum offset for blind scan  | 
| 
22
 | 
1
 | 
 
 | 
 
 | 
  
1
  
 | 
 
 | 
5
 | 
 		  );  | 
| 
 
 | 
1
 | 
 
 | 
 
 | 
 
 | 
 
 | 
15
 | 
    | 
| 
23
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
24
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 $MAX_FILE_OFFSET = 8192; # default blind scan depth  | 
| 
25
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
26
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # Debug off for production use  | 
| 
27
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 my $debugMode = 0;  | 
| 
28
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 my $error;  | 
| 
29
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	    | 
| 
30
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 #####################################  | 
| 
31
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # These names match the codes defined in ITPC's IIM record 2.  | 
| 
32
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # This hash is for non-repeating data items; repeating ones  | 
| 
33
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # are in %listdatasets below.  | 
| 
34
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 %datasets = (  | 
| 
35
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 #	0	=> 'record version',		# skip -- binary data  | 
| 
36
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	5	=> 'object name',  | 
| 
37
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	7	=> 'edit status',  | 
| 
38
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	8	=> 'editorial update',  | 
| 
39
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	10	=> 'urgency',  | 
| 
40
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	12	=> 'subject reference',  | 
| 
41
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	15	=> 'category',  | 
| 
42
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 #	20	=> 'supplemental category',	# in listdatasets (see below)  | 
| 
43
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	22	=> 'fixture identifier',  | 
| 
44
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 #	25	=> 'keywords',				# in listdatasets  | 
| 
45
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	26	=> 'content location code',  | 
| 
46
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	27	=> 'content location name',  | 
| 
47
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	30	=> 'release date',  | 
| 
48
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	35	=> 'release time',  | 
| 
49
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	37	=> 'expiration date',  | 
| 
50
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	38	=> 'expiration time',  | 
| 
51
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	40	=> 'special instructions',  | 
| 
52
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	42	=> 'action advised',  | 
| 
53
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	45	=> 'reference service',  | 
| 
54
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	47	=> 'reference date',  | 
| 
55
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	50	=> 'reference number',  | 
| 
56
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	55	=> 'date created',  | 
| 
57
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	60	=> 'time created',  | 
| 
58
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	62	=> 'digital creation date',  | 
| 
59
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	63	=> 'digital creation time',  | 
| 
60
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	65	=> 'originating program',  | 
| 
61
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	70	=> 'program version',  | 
| 
62
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	75	=> 'object cycle',  | 
| 
63
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	80	=> 'by-line',  | 
| 
64
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	85	=> 'by-line title',  | 
| 
65
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	90	=> 'city',  | 
| 
66
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	92	=> 'sub-location',  | 
| 
67
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	95	=> 'province/state',  | 
| 
68
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	100	=> 'country/primary location code',  | 
| 
69
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	101	=> 'country/primary location name',  | 
| 
70
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	103	=> 'original transmission reference',  | 
| 
71
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	105	=> 'headline',  | 
| 
72
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	110	=> 'credit',  | 
| 
73
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	115	=> 'source',  | 
| 
74
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	116	=> 'copyright notice',  | 
| 
75
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 #	118	=> 'contact',            # in listdatasets  | 
| 
76
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	120	=> 'caption/abstract',  | 
| 
77
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	121	=> 'local caption',  | 
| 
78
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	122	=> 'writer/editor',  | 
| 
79
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 #	125	=> 'rasterized caption', # unsupported (binary data)  | 
| 
80
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	130	=> 'image type',  | 
| 
81
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	131	=> 'image orientation',  | 
| 
82
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	135	=> 'language identifier',  | 
| 
83
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	200	=> 'custom1', # These are NOT STANDARD, but are used by  | 
| 
84
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	201	=> 'custom2', # Fotostation. Use at your own risk. They're  | 
| 
85
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	202	=> 'custom3', # here in case you need to store some special  | 
| 
86
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	203	=> 'custom4', # stuff, but note that other programs won't   | 
| 
87
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	204	=> 'custom5', # recognize them and may blow them away if   | 
| 
88
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	205	=> 'custom6', # you open and re-save the file. (Except with  | 
| 
89
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	206	=> 'custom7', # Fotostation, of course.)  | 
| 
90
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	207	=> 'custom8',  | 
| 
91
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	208	=> 'custom9',  | 
| 
92
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	209	=> 'custom10',  | 
| 
93
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	210	=> 'custom11',  | 
| 
94
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	211	=> 'custom12',  | 
| 
95
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	212	=> 'custom13',  | 
| 
96
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	213	=> 'custom14',  | 
| 
97
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	214	=> 'custom15',  | 
| 
98
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	215	=> 'custom16',  | 
| 
99
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	216	=> 'custom17',  | 
| 
100
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	217	=> 'custom18',  | 
| 
101
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	218	=> 'custom19',  | 
| 
102
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	219	=> 'custom20',  | 
| 
103
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	);  | 
| 
104
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
105
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # this will get filled in if we save data back to file  | 
| 
106
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 %datanames = ();  | 
| 
107
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
108
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 %listdatasets = (  | 
| 
109
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	20	=> 'supplemental category',  | 
| 
110
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	25	=> 'keywords',  | 
| 
111
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	118	=> 'contact',  | 
| 
112
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	);  | 
| 
113
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
114
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # this will get filled in if we save data back to file  | 
| 
115
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 %listdatanames = ();  | 
| 
116
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	  | 
| 
117
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 #######################################################################  | 
| 
118
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # New, Save, Destroy, Error  | 
| 
119
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 #######################################################################  | 
| 
120
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
121
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 #  | 
| 
122
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # new  | 
| 
123
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 #   | 
| 
124
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # $info = new IPTCInfo('image filename goes here')  | 
| 
125
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 #   | 
| 
126
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # Returns IPTCInfo object filled with metadata from the given image   | 
| 
127
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # file. File on disk will be closed, and changes made to the IPTCInfo  | 
| 
128
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # object will *not* be flushed back to disk.  | 
| 
129
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 #  | 
| 
130
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 sub new  | 
| 
131
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 {  | 
| 
132
 | 
5
 | 
 
 | 
 
 | 
  
5
  
 | 
  
0
  
 | 
335
 | 
 	my ($pkg, $file, $force) = @_;  | 
| 
133
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
134
 | 
5
 | 
 
 | 
 
 | 
 
 | 
 
 | 
9
 | 
 	my $input_is_handle = eval {$file->isa('IO::Handle')};  | 
| 
 
 | 
5
 | 
 
 | 
 
 | 
 
 | 
 
 | 
45
 | 
    | 
| 
135
 | 
5
 | 
  
 50
  
 | 
  
 66
  
 | 
 
 | 
 
 | 
25
 | 
 	if ($input_is_handle and not $file->isa('IO::Seekable')) {  | 
| 
136
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
 		$error = "Handle must be seekable."; Log($error);  | 
| 
 
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
    | 
| 
137
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
 		return undef;  | 
| 
138
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	}  | 
| 
139
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
140
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	#  | 
| 
141
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	# Open file and snarf data from it.  | 
| 
142
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	#  | 
| 
143
 | 
5
 | 
  
100
  
 | 
 
 | 
 
 | 
 
 | 
31
 | 
 	my $handle = $input_is_handle ? $file : IO::File->new($file);  | 
| 
144
 | 
5
 | 
  
 50
  
 | 
 
 | 
 
 | 
 
 | 
314
 | 
 	unless($handle)  | 
| 
145
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	{  | 
| 
146
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
 		$error = "Can't open file: $!"; Log($error);  | 
| 
 
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
    | 
| 
147
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
 		return undef;  | 
| 
148
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	}  | 
| 
149
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
150
 | 
5
 | 
 
 | 
 
 | 
 
 | 
 
 | 
10
 | 
 	binmode($handle);  | 
| 
151
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
152
 | 
5
 | 
 
 | 
 
 | 
 
 | 
 
 | 
14
 | 
 	my $datafound = ScanToFirstIMMTag($handle);  | 
| 
153
 | 
5
 | 
  
 50
  
 | 
  
 33
  
 | 
 
 | 
 
 | 
15
 | 
 	unless ($datafound || defined($force))  | 
| 
154
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	{  | 
| 
155
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
 		$error = "No IPTC data found."; Log($error);  | 
| 
 
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
    | 
| 
156
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 		# don't close unless we opened it  | 
| 
157
 | 
0
 | 
  
  0
  
 | 
 
 | 
 
 | 
 
 | 
0
 | 
 		$handle->close() unless $input_is_handle;  | 
| 
158
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
 		return undef;  | 
| 
159
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	}  | 
| 
160
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
161
 | 
5
 | 
 
 | 
 
 | 
 
 | 
 
 | 
25
 | 
 	my $self = bless  | 
| 
162
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	{  | 
| 
163
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 		'_data'		=> {},	# empty hashes; wil be  | 
| 
164
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 		'_listdata'	=> {},	# filled in CollectIIMInfo  | 
| 
165
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 		'_handle'   => $handle,  | 
| 
166
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	}, $pkg;  | 
| 
167
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
168
 | 
5
 | 
  
100
  
 | 
 
 | 
 
 | 
 
 | 
20
 | 
 	$self->{_filename} = $file unless $input_is_handle;  | 
| 
169
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
170
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	# Do the real snarfing here  | 
| 
171
 | 
5
 | 
  
 50
  
 | 
 
 | 
 
 | 
 
 | 
19
 | 
 	$self->CollectIIMInfo() if $datafound;  | 
| 
172
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	  | 
| 
173
 | 
5
 | 
  
100
  
 | 
 
 | 
 
 | 
 
 | 
23
 | 
 	$handle->close() unless $input_is_handle;  | 
| 
174
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 		  | 
| 
175
 | 
5
 | 
 
 | 
 
 | 
 
 | 
 
 | 
89
 | 
 	return $self;  | 
| 
176
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 }  | 
| 
177
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
178
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 #  | 
| 
179
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # create  | 
| 
180
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 #  | 
| 
181
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # Like new, but forces an object to always be returned. This allows  | 
| 
182
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # you to start adding stuff to files that don't have IPTC info and then  | 
| 
183
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # save it.  | 
| 
184
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 #  | 
| 
185
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 sub create  | 
| 
186
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 {  | 
| 
187
 | 
0
 | 
 
 | 
 
 | 
  
0
  
 | 
  
0
  
 | 
0
 | 
 	my ($pkg, $filename) = @_;  | 
| 
188
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
189
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
 	return new($pkg, $filename, 'force');  | 
| 
190
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 }  | 
| 
191
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
192
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 #  | 
| 
193
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # Save  | 
| 
194
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 #  | 
| 
195
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # Saves JPEG with IPTC data back to the same file it came from.  | 
| 
196
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 #  | 
| 
197
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 sub Save  | 
| 
198
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 {  | 
| 
199
 | 
1
 | 
 
 | 
 
 | 
  
1
  
 | 
  
0
  
 | 
5
 | 
 	my ($self, $options) = @_;  | 
| 
200
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
201
 | 
1
 | 
 
 | 
 
 | 
 
 | 
 
 | 
6
 | 
 	return $self->SaveAs($self->{'_filename'}, $options);  | 
| 
202
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 }  | 
| 
203
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
204
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 #  | 
| 
205
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # Save  | 
| 
206
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 #  | 
| 
207
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # Saves JPEG with IPTC data to a given file name.  | 
| 
208
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 #  | 
| 
209
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 sub SaveAs  | 
| 
210
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 {  | 
| 
211
 | 
2
 | 
 
 | 
 
 | 
  
2
  
 | 
  
0
  
 | 
8
 | 
 	my ($self, $newfile, $options) = @_;  | 
| 
212
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
213
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	#  | 
| 
214
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	# Open file and snarf data from it.  | 
| 
215
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	#  | 
| 
216
 | 
2
 | 
  
 50
  
 | 
 
 | 
 
 | 
 
 | 
14
 | 
 	my $handle = $self->{_filename} ? IO::File->new($self->{_filename}) : $self->{_handle};  | 
| 
217
 | 
2
 | 
  
 50
  
 | 
 
 | 
 
 | 
 
 | 
147
 | 
 	unless($handle)  | 
| 
218
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	{  | 
| 
219
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
 		$error = "Can't open file: $!"; Log($error);  | 
| 
 
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
    | 
| 
220
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
 		return undef;  | 
| 
221
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	}  | 
| 
222
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
223
 | 
2
 | 
 
 | 
 
 | 
 
 | 
 
 | 
7
 | 
 	$handle->seek(0, 0);  | 
| 
224
 | 
2
 | 
 
 | 
 
 | 
 
 | 
 
 | 
18
 | 
 	binmode($handle);  | 
| 
225
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
226
 | 
2
 | 
  
 50
  
 | 
 
 | 
 
 | 
 
 | 
5
 | 
 	unless (FileIsJPEG($handle))  | 
| 
227
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	{  | 
| 
228
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
 		$error = "Source file is not a JPEG; I can only save JPEGs. Sorry.";  | 
| 
229
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
 		Log($error);  | 
| 
230
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
 		return undef;  | 
| 
231
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	}  | 
| 
232
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
233
 | 
2
 | 
 
 | 
 
 | 
 
 | 
 
 | 
7
 | 
 	my $ret = JPEGCollectFileParts($handle, $options);  | 
| 
234
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
235
 | 
2
 | 
  
 50
  
 | 
 
 | 
 
 | 
 
 | 
6
 | 
 	if ($ret == 0)  | 
| 
236
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	{  | 
| 
237
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
 		Log("collectfileparts failed");  | 
| 
238
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
 		return undef;  | 
| 
239
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	}  | 
| 
240
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
241
 | 
2
 | 
  
 50
  
 | 
 
 | 
 
 | 
 
 | 
8
 | 
 	if ($self->{_filename}) {  | 
| 
242
 | 
2
 | 
 
 | 
 
 | 
 
 | 
 
 | 
7
 | 
 		$handle->close();  | 
| 
243
 | 
2
 | 
  
 50
  
 | 
 
 | 
 
 | 
 
 | 
39
 | 
 		unless ($handle = IO::File->new($newfile, ">")) {  | 
| 
244
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
 			$error = "Can't open output file: $!"; Log($error);  | 
| 
 
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
    | 
| 
245
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
 			return undef;  | 
| 
246
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 		}  | 
| 
247
 | 
2
 | 
 
 | 
 
 | 
 
 | 
 
 | 
335
 | 
 		binmode($handle);  | 
| 
248
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	} else {  | 
| 
249
 | 
0
 | 
  
  0
  
 | 
 
 | 
 
 | 
 
 | 
0
 | 
 		unless ($handle->truncate(0)) {  | 
| 
250
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
 			$error = "Can't truncate, handle might be read-only"; Log($error);  | 
| 
 
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
    | 
| 
251
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
 			return undef;  | 
| 
252
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 		}  | 
| 
253
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	}  | 
| 
254
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
255
 | 
2
 | 
 
 | 
 
 | 
 
 | 
 
 | 
57
 | 
 	my ($start, $end, $adobe) = @$ret;  | 
| 
256
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
257
 | 
2
 | 
  
 50
  
 | 
  
 33
  
 | 
 
 | 
 
 | 
9
 | 
 	if (defined($options) && defined($options->{'discardAdobeParts'}))  | 
| 
258
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	{  | 
| 
259
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
 		undef $adobe;  | 
| 
260
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	}  | 
| 
261
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
262
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
263
 | 
2
 | 
 
 | 
 
 | 
 
 | 
 
 | 
14
 | 
 	$handle->print($start);  | 
| 
264
 | 
2
 | 
 
 | 
 
 | 
 
 | 
 
 | 
32
 | 
 	$handle->print($self->PhotoshopIIMBlock($adobe, $self->PackedIIMData()));  | 
| 
265
 | 
2
 | 
 
 | 
 
 | 
 
 | 
 
 | 
16
 | 
 	$handle->print($end);  | 
| 
266
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
267
 | 
2
 | 
  
 50
  
 | 
 
 | 
 
 | 
 
 | 
22
 | 
 	$handle->close() if $self->{_filename};  | 
| 
268
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 		  | 
| 
269
 | 
2
 | 
 
 | 
 
 | 
 
 | 
 
 | 
140
 | 
 	return 1;  | 
| 
270
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 }  | 
| 
271
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
272
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 #  | 
| 
273
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # DESTROY  | 
| 
274
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 #   | 
| 
275
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # Called when object is destroyed. No action necessary in this case.  | 
| 
276
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 #  | 
| 
277
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 sub DESTROY  | 
| 
278
 | 
0
 | 
 
 | 
 
 | 
  
0
  
 | 
 
 | 
0
 | 
 {  | 
| 
279
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	# no action necessary  | 
| 
280
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 }  | 
| 
281
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
282
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 #  | 
| 
283
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # Error  | 
| 
284
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 #  | 
| 
285
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # Returns description of the last error.  | 
| 
286
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 #  | 
| 
287
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 sub Error  | 
| 
288
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 {  | 
| 
289
 | 
0
 | 
 
 | 
 
 | 
  
0
  
 | 
  
0
  
 | 
0
 | 
 	return $error;  | 
| 
290
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 }  | 
| 
291
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
292
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 #######################################################################  | 
| 
293
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # Attributes for clients  | 
| 
294
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 #######################################################################  | 
| 
295
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
296
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 #  | 
| 
297
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # Attribute/SetAttribute  | 
| 
298
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 #   | 
| 
299
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # Returns/Changes value of a given data item.  | 
| 
300
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 #  | 
| 
301
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 sub Attribute  | 
| 
302
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 {  | 
| 
303
 | 
4
 | 
 
 | 
 
 | 
  
4
  
 | 
  
0
  
 | 
30
 | 
 	my ($self, $attribute) = @_;  | 
| 
304
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
305
 | 
4
 | 
 
 | 
 
 | 
 
 | 
 
 | 
13
 | 
 	return $self->{_data}->{$attribute};  | 
| 
306
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 }  | 
| 
307
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
308
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 sub SetAttribute  | 
| 
309
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 {  | 
| 
310
 | 
2
 | 
 
 | 
 
 | 
  
2
  
 | 
  
0
  
 | 
50
 | 
 	my ($self, $attribute, $newval) = @_;  | 
| 
311
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
312
 | 
2
 | 
 
 | 
 
 | 
 
 | 
 
 | 
7
 | 
 	$self->{_data}->{$attribute} = $newval;  | 
| 
313
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 }  | 
| 
314
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
315
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 sub ClearAttributes  | 
| 
316
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 {  | 
| 
317
 | 
0
 | 
 
 | 
 
 | 
  
0
  
 | 
  
0
  
 | 
0
 | 
 	my $self = shift;  | 
| 
318
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
319
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
 	$self->{_data} = {};  | 
| 
320
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 }  | 
| 
321
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
322
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 sub ClearAllData  | 
| 
323
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 {  | 
| 
324
 | 
0
 | 
 
 | 
 
 | 
  
0
  
 | 
  
0
  
 | 
0
 | 
 	my $self = shift;  | 
| 
325
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
326
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
 	$self->{_data} = {};  | 
| 
327
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
 	$self->{_listdata} = {};  | 
| 
328
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 }  | 
| 
329
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
330
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 #  | 
| 
331
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # Keywords/Clear/Add  | 
| 
332
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 #   | 
| 
333
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # Returns reference to a list of keywords/clears the keywords  | 
| 
334
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # list/adds a keyword.  | 
| 
335
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 #  | 
| 
336
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 sub Keywords  | 
| 
337
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 {  | 
| 
338
 | 
0
 | 
 
 | 
 
 | 
  
0
  
 | 
  
0
  
 | 
0
 | 
 	my $self = shift;  | 
| 
339
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
 	return $self->{_listdata}->{'keywords'};  | 
| 
340
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 }  | 
| 
341
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
342
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 sub ClearKeywords  | 
| 
343
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 {  | 
| 
344
 | 
0
 | 
 
 | 
 
 | 
  
0
  
 | 
  
0
  
 | 
0
 | 
 	my $self = shift;  | 
| 
345
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
 	$self->{_listdata}->{'keywords'} = undef;  | 
| 
346
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 }  | 
| 
347
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
348
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 sub AddKeyword  | 
| 
349
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 {  | 
| 
350
 | 
0
 | 
 
 | 
 
 | 
  
0
  
 | 
  
0
  
 | 
0
 | 
 	my ($self, $add) = @_;  | 
| 
351
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	  | 
| 
352
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
 	$self->AddListData('keywords', $add);  | 
| 
353
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 }  | 
| 
354
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
355
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 #  | 
| 
356
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # SupplementalCategories/Clear/Add  | 
| 
357
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 #   | 
| 
358
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # Returns reference to a list of supplemental categories.  | 
| 
359
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 #  | 
| 
360
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 sub SupplementalCategories  | 
| 
361
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 {  | 
| 
362
 | 
0
 | 
 
 | 
 
 | 
  
0
  
 | 
  
0
  
 | 
0
 | 
 	my $self = shift;  | 
| 
363
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
 	return $self->{_listdata}->{'supplemental category'};  | 
| 
364
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 }  | 
| 
365
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
366
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 sub ClearSupplementalCategories  | 
| 
367
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 {  | 
| 
368
 | 
0
 | 
 
 | 
 
 | 
  
0
  
 | 
  
0
  
 | 
0
 | 
 	my $self = shift;  | 
| 
369
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
 	$self->{_listdata}->{'supplemental category'} = undef;  | 
| 
370
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 }  | 
| 
371
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
372
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 sub AddSupplementalCategories  | 
| 
373
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 {  | 
| 
374
 | 
0
 | 
 
 | 
 
 | 
  
0
  
 | 
  
0
  
 | 
0
 | 
 	my ($self, $add) = @_;  | 
| 
375
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	  | 
| 
376
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
 	$self->AddListData('supplemental category', $add);  | 
| 
377
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 }  | 
| 
378
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
379
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 #  | 
| 
380
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # Contacts/Clear/Add  | 
| 
381
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 #   | 
| 
382
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # Returns reference to a list of contactss/clears the contacts  | 
| 
383
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # list/adds a contact.  | 
| 
384
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 #  | 
| 
385
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 sub Contacts  | 
| 
386
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 {  | 
| 
387
 | 
0
 | 
 
 | 
 
 | 
  
0
  
 | 
  
0
  
 | 
0
 | 
 	my $self = shift;  | 
| 
388
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
 	return $self->{_listdata}->{'contact'};  | 
| 
389
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 }  | 
| 
390
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
391
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 sub ClearContacts  | 
| 
392
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 {  | 
| 
393
 | 
0
 | 
 
 | 
 
 | 
  
0
  
 | 
  
0
  
 | 
0
 | 
 	my $self = shift;  | 
| 
394
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
 	$self->{_listdata}->{'contact'} = undef;  | 
| 
395
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 }  | 
| 
396
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
397
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 sub AddContact  | 
| 
398
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 {  | 
| 
399
 | 
0
 | 
 
 | 
 
 | 
  
0
  
 | 
  
0
  
 | 
0
 | 
 	my ($self, $add) = @_;  | 
| 
400
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	  | 
| 
401
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
 	$self->AddListData('contact', $add);  | 
| 
402
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 }  | 
| 
403
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
404
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 sub AddListData  | 
| 
405
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 {  | 
| 
406
 | 
0
 | 
 
 | 
 
 | 
  
0
  
 | 
  
0
  
 | 
0
 | 
 	my ($self, $list, $add) = @_;  | 
| 
407
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
408
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	# did user pass in a list ref?  | 
| 
409
 | 
0
 | 
  
  0
  
 | 
 
 | 
 
 | 
 
 | 
0
 | 
 	if (ref($add) eq 'ARRAY')  | 
| 
410
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	{  | 
| 
411
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 		# yes, add list contents  | 
| 
412
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
 		push(@{$self->{_listdata}->{$list}}, @$add);  | 
| 
 
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
    | 
| 
413
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	}  | 
| 
414
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	else  | 
| 
415
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	{  | 
| 
416
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 		# no, just a literal item  | 
| 
417
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
 		push(@{$self->{_listdata}->{$list}}, $add);  | 
| 
 
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
    | 
| 
418
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	}  | 
| 
419
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 }  | 
| 
420
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
421
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 #######################################################################  | 
| 
422
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # XML, SQL export  | 
| 
423
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 #######################################################################  | 
| 
424
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
425
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 #  | 
| 
426
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # ExportXML  | 
| 
427
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 #   | 
| 
428
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # $xml = $info->ExportXML('entity-name', \%extra-data,  | 
| 
429
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 #                         'optional output file name');  | 
| 
430
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 #   | 
| 
431
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # Exports XML containing all image metadata. Attribute names are  | 
| 
432
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # translated into XML tags, making adjustments to spaces and slashes  | 
| 
433
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # for compatibility. (Spaces become underbars, slashes become dashes.)  | 
| 
434
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # Caller provides an entity name; all data will be contained within  | 
| 
435
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # this entity. Caller optionally provides a reference to a hash of   | 
| 
436
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # extra data. This will be output into the XML, too. Keys must be   | 
| 
437
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # valid XML tag names. Optionally provide a filename, and the XML   | 
| 
438
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # will be dumped into there.  | 
| 
439
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 #  | 
| 
440
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 sub ExportXML  | 
| 
441
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 {  | 
| 
442
 | 
0
 | 
 
 | 
 
 | 
  
0
  
 | 
  
0
  
 | 
0
 | 
 	my ($self, $basetag, $extraRef, $filename) = @_;  | 
| 
443
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
 	my $out;  | 
| 
444
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	  | 
| 
445
 | 
0
 | 
  
  0
  
 | 
 
 | 
 
 | 
 
 | 
0
 | 
 	$basetag = 'photo' unless length($basetag);  | 
| 
446
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	  | 
| 
447
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
 	$out .= "<$basetag>\n";  | 
| 
448
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
449
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	# dump extra info first, if any  | 
| 
450
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
 	foreach my $key (keys %$extraRef)  | 
| 
451
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	{  | 
| 
452
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
 		$out .= "\t<$key>" . $extraRef->{$key} . "$key>\n";  | 
| 
453
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	}  | 
| 
454
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	  | 
| 
455
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	# dump our stuff  | 
| 
456
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
 	foreach my $key (keys %{$self->{_data}})  | 
| 
 
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
    | 
| 
457
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	{  | 
| 
458
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
 		my $cleankey = $key;  | 
| 
459
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
 		$cleankey =~ s/ /_/g;  | 
| 
460
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
 		$cleankey =~ s/\//-/g;  | 
| 
461
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 		  | 
| 
462
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
 		$out .= "\t<$cleankey>" . $self->{_data}->{$key} . "$cleankey>\n";  | 
| 
463
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	}  | 
| 
464
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
465
 | 
0
 | 
  
  0
  
 | 
 
 | 
 
 | 
 
 | 
0
 | 
 	if (defined ($self->Keywords()))  | 
| 
466
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	{  | 
| 
467
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 		# print keywords  | 
| 
468
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
 		$out .= "\t\n";  | 
| 
469
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 		  | 
| 
470
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
 		foreach my $keyword (@{$self->Keywords()})  | 
| 
 
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
    | 
| 
471
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 		{  | 
| 
472
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
 			$out .= "\t\t$keyword\n";  | 
| 
473
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 		}  | 
| 
474
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 		  | 
| 
475
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
 		$out .= "\t\n";  | 
| 
476
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	}  | 
| 
477
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
478
 | 
0
 | 
  
  0
  
 | 
 
 | 
 
 | 
 
 | 
0
 | 
 	if (defined ($self->SupplementalCategories()))  | 
| 
479
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	{  | 
| 
480
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 		# print supplemental categories  | 
| 
481
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
 		$out .= "\t\n";  | 
| 
482
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 		  | 
| 
483
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
 		foreach my $category (@{$self->SupplementalCategories()})  | 
| 
 
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
    | 
| 
484
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 		{  | 
| 
485
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
 			$out .= "\t\t$category\n";  | 
| 
486
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 		}  | 
| 
487
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 		  | 
| 
488
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
 		$out .= "\t\n";  | 
| 
489
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	}  | 
| 
490
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
491
 | 
0
 | 
  
  0
  
 | 
 
 | 
 
 | 
 
 | 
0
 | 
 	if (defined ($self->Contacts()))  | 
| 
492
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	{  | 
| 
493
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 		# print contacts  | 
| 
494
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
 		$out .= "\t\n";  | 
| 
495
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 		  | 
| 
496
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
 		foreach my $contact (@{$self->Contacts()})  | 
| 
 
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
    | 
| 
497
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 		{  | 
| 
498
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
 			$out .= "\t\t$contact\n";  | 
| 
499
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 		}  | 
| 
500
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 		  | 
| 
501
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
 		$out .= "\t\n";  | 
| 
502
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	}  | 
| 
503
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
504
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	# close base tag  | 
| 
505
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
 	$out .= "$basetag>\n";  | 
| 
506
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
507
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	# export to file if caller asked for it.  | 
| 
508
 | 
0
 | 
  
  0
  
 | 
 
 | 
 
 | 
 
 | 
0
 | 
 	if (length($filename))  | 
| 
509
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	{  | 
| 
510
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
 		open(XMLOUT, ">$filename");  | 
| 
511
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
 		print XMLOUT $out;  | 
| 
512
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
 		close(XMLOUT);  | 
| 
513
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	}  | 
| 
514
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	  | 
| 
515
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
 	return $out;  | 
| 
516
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 }  | 
| 
517
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
518
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 #  | 
| 
519
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # ExportSQL  | 
| 
520
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 #   | 
| 
521
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # my %mappings = (  | 
| 
522
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 #   'IPTC dataset name here'    => 'your table column name here',  | 
| 
523
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 #   'caption/abstract'          => 'caption',  | 
| 
524
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 #   'city'                      => 'city',  | 
| 
525
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 #   'province/state'            => 'state); # etc etc etc.  | 
| 
526
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 #   | 
| 
527
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # $statement = $info->ExportSQL('mytable', \%mappings, \%extra-data);  | 
| 
528
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 #  | 
| 
529
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # Returns a SQL statement to insert into your given table name   | 
| 
530
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # a set of values from the image. Caller passes in a reference to  | 
| 
531
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # a hash which maps IPTC dataset names into column names for the  | 
| 
532
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # database table. Optionally pass in a ref to a hash of extra data  | 
| 
533
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # which will also be included in the insert statement. Keys in that  | 
| 
534
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # hash must be valid column names.  | 
| 
535
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 #  | 
| 
536
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 sub ExportSQL  | 
| 
537
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 {  | 
| 
538
 | 
0
 | 
 
 | 
 
 | 
  
0
  
 | 
  
0
  
 | 
0
 | 
 	my ($self, $tablename, $mappingsRef, $extraRef) = @_;  | 
| 
539
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
 	my ($statement, $columns, $values);  | 
| 
540
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	  | 
| 
541
 | 
0
 | 
  
  0
  
 | 
  
  0
  
 | 
 
 | 
 
 | 
0
 | 
 	return undef if (($tablename eq undef) || ($mappingsRef eq undef));  | 
| 
542
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
543
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	# start with extra data, if any  | 
| 
544
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
 	foreach my $column (keys %$extraRef)  | 
| 
545
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	{  | 
| 
546
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
 		my $value = $extraRef->{$column};  | 
| 
547
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
 		$value =~ s/'/''/g; # escape single quotes  | 
| 
548
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 		  | 
| 
549
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
 		$columns .= $column . ", ";  | 
| 
550
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
 		$values  .= "\'$value\', ";  | 
| 
551
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	}  | 
| 
552
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	  | 
| 
553
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	# process our data  | 
| 
554
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
 	foreach my $attribute (keys %$mappingsRef)  | 
| 
555
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	{  | 
| 
556
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
 		my $value = $self->Attribute($attribute);  | 
| 
557
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
 		$value =~ s/'/''/g; # escape single quotes  | 
| 
558
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 		  | 
| 
559
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
 		$columns .= $mappingsRef->{$attribute} . ", ";  | 
| 
560
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
 		$values  .= "\'$value\', ";  | 
| 
561
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	}  | 
| 
562
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	  | 
| 
563
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	# must trim the trailing ", " from both  | 
| 
564
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
 	$columns =~ s/, $//;  | 
| 
565
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
 	$values  =~ s/, $//;  | 
| 
566
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
567
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
 	$statement = "INSERT INTO $tablename ($columns) VALUES ($values)";  | 
| 
568
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	  | 
| 
569
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
 	return $statement;  | 
| 
570
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 }  | 
| 
571
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
572
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 #######################################################################  | 
| 
573
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # File parsing functions (private)  | 
| 
574
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 #######################################################################  | 
| 
575
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
576
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 #  | 
| 
577
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # ScanToFirstIMMTag  | 
| 
578
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 #  | 
| 
579
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # Scans to first IIM Record 2 tag in the file. The will either use  | 
| 
580
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # smart scanning for JPEGs or blind scanning for other file types.  | 
| 
581
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 #  | 
| 
582
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 sub ScanToFirstIMMTag  | 
| 
583
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 {  | 
| 
584
 | 
5
 | 
 
 | 
 
 | 
  
5
  
 | 
  
0
  
 | 
7
 | 
 	my $handle = shift @_;  | 
| 
585
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
586
 | 
5
 | 
  
 50
  
 | 
 
 | 
 
 | 
 
 | 
12
 | 
 	if (FileIsJPEG($handle))  | 
| 
587
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	{  | 
| 
588
 | 
5
 | 
 
 | 
 
 | 
 
 | 
 
 | 
13
 | 
 		Log("File is JPEG, proceeding with JPEGScan");  | 
| 
589
 | 
5
 | 
 
 | 
 
 | 
 
 | 
 
 | 
13
 | 
 		return JPEGScan($handle);  | 
| 
590
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	}  | 
| 
591
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	else  | 
| 
592
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	{  | 
| 
593
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
 		Log("File not a JPEG, trying BlindScan");  | 
| 
594
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
 		return BlindScan($handle);  | 
| 
595
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	}  | 
| 
596
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 }  | 
| 
597
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
598
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 #  | 
| 
599
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # FileIsJPEG  | 
| 
600
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 #  | 
| 
601
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # Checks to see if this file is a JPEG/JFIF or not. Will reset the  | 
| 
602
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # file position back to 0 after it's done in either case.  | 
| 
603
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 #  | 
| 
604
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 sub FileIsJPEG  | 
| 
605
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 {  | 
| 
606
 | 
7
 | 
 
 | 
 
 | 
  
7
  
 | 
  
0
  
 | 
13
 | 
 	my $handle = shift @_;  | 
| 
607
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
608
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	# reset to beginning just in case  | 
| 
609
 | 
7
 | 
 
 | 
 
 | 
 
 | 
 
 | 
32
 | 
 	$handle->seek(0, 0);  | 
| 
610
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
611
 | 
7
 | 
  
 50
  
 | 
 
 | 
 
 | 
 
 | 
63
 | 
 	if ($debugMode)  | 
| 
612
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	{  | 
| 
613
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
 		Log("Opening 16 bytes of file:\n");  | 
| 
614
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
 		my $dump;  | 
| 
615
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
 		$handle->read($dump, 16);  | 
| 
616
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
 		HexDump($dump);  | 
| 
617
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
 		$handle->seek(0, 0);  | 
| 
618
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	}  | 
| 
619
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
620
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	# check start of file marker  | 
| 
621
 | 
7
 | 
 
 | 
 
 | 
 
 | 
 
 | 
7
 | 
 	my ($ff, $soi);  | 
| 
622
 | 
7
 | 
  
 50
  
 | 
 
 | 
 
 | 
 
 | 
26
 | 
 	$handle->read($ff, 1) || goto notjpeg;  | 
| 
623
 | 
7
 | 
 
 | 
 
 | 
 
 | 
 
 | 
155
 | 
 	$handle->read($soi, 1);  | 
| 
624
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	  | 
| 
625
 | 
7
 | 
  
 50
  
 | 
  
 33
  
 | 
 
 | 
 
 | 
67
 | 
 	goto notjpeg unless (ord($ff) == 0xff && ord($soi) == 0xd8);  | 
| 
626
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
627
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	# now check for APP0 marker. I'll assume that anything with a SOI  | 
| 
628
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	# followed by APP0 is "close enough" for our purposes. (We're not  | 
| 
629
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	# dinking with image data, so anything following the JPEG tagging  | 
| 
630
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	# system should work.)  | 
| 
631
 | 
7
 | 
 
 | 
 
 | 
 
 | 
 
 | 
9
 | 
 	my ($app0, $len, $jpeg);  | 
| 
632
 | 
7
 | 
 
 | 
 
 | 
 
 | 
 
 | 
21
 | 
 	$handle->read($ff, 1);  | 
| 
633
 | 
7
 | 
 
 | 
 
 | 
 
 | 
 
 | 
41
 | 
 	$handle->read($app0, 1);  | 
| 
634
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
635
 | 
7
 | 
  
 50
  
 | 
 
 | 
 
 | 
 
 | 
44
 | 
 	goto notjpeg unless (ord($ff) == 0xff);  | 
| 
636
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
637
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	# reset to beginning of file  | 
| 
638
 | 
7
 | 
 
 | 
 
 | 
 
 | 
 
 | 
19
 | 
 	$handle->seek(0, 0);  | 
| 
639
 | 
7
 | 
 
 | 
 
 | 
 
 | 
 
 | 
86
 | 
 	return 1;  | 
| 
640
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
641
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
   notjpeg:  | 
| 
642
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	$handle->seek(0, 0);  | 
| 
643
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
 	return 0;  | 
| 
644
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 }  | 
| 
645
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
646
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 #  | 
| 
647
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # JPEGScan  | 
| 
648
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 #  | 
| 
649
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # Assuming the file is a JPEG (see above), this will scan through the  | 
| 
650
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # markers looking for the APP13 marker, where IPTC/IIM data should be  | 
| 
651
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # found. While this isn't a formally defined standard, all programs  | 
| 
652
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # have (supposedly) adopted Adobe's technique of putting the data in  | 
| 
653
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # APP13.  | 
| 
654
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 #  | 
| 
655
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 sub JPEGScan  | 
| 
656
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 {  | 
| 
657
 | 
5
 | 
 
 | 
 
 | 
  
5
  
 | 
  
0
  
 | 
8
 | 
 	my $handle = shift @_;  | 
| 
658
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
659
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	# Skip past start of file marker  | 
| 
660
 | 
5
 | 
 
 | 
 
 | 
 
 | 
 
 | 
6
 | 
 	my ($ff, $soi);  | 
| 
661
 | 
5
 | 
  
 50
  
 | 
 
 | 
 
 | 
 
 | 
15
 | 
 	$handle->read($ff, 1) || return 0;  | 
| 
662
 | 
5
 | 
 
 | 
 
 | 
 
 | 
 
 | 
66
 | 
 	$handle->read($soi, 1);  | 
| 
663
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	  | 
| 
664
 | 
5
 | 
  
 50
  
 | 
  
 33
  
 | 
 
 | 
 
 | 
67
 | 
 	unless (ord($ff) == 0xff && ord($soi) == 0xd8)  | 
| 
665
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	{  | 
| 
666
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
 		$error = "JPEGScan: invalid start of file"; Log($error);  | 
| 
 
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
    | 
| 
667
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
 		return 0;  | 
| 
668
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	}  | 
| 
669
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
670
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	# Scan for the APP13 marker which will contain our IPTC info (I hope).  | 
| 
671
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
672
 | 
5
 | 
 
 | 
 
 | 
 
 | 
 
 | 
12
 | 
 	my $marker = JPEGNextMarker($handle);  | 
| 
673
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
674
 | 
5
 | 
 
 | 
 
 | 
 
 | 
 
 | 
15
 | 
 	while (ord($marker) != 0xed)  | 
| 
675
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	{  | 
| 
676
 | 
5
 | 
  
 50
  
 | 
 
 | 
 
 | 
 
 | 
14
 | 
 		if (ord($marker) == 0)  | 
| 
677
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
 		{ $error = "Marker scan failed"; Log($error); return 0; }  | 
| 
 
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
    | 
| 
 
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
    | 
| 
678
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
679
 | 
5
 | 
  
 50
  
 | 
 
 | 
 
 | 
 
 | 
12
 | 
 		if (ord($marker) == 0xd9)  | 
| 
680
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
 		{ $error = "Marker scan hit end of image marker";  | 
| 
681
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
 		  Log($error); return 0; }  | 
| 
 
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
    | 
| 
682
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
683
 | 
5
 | 
  
 50
  
 | 
 
 | 
 
 | 
 
 | 
12
 | 
 		if (ord($marker) == 0xda)  | 
| 
684
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
 		{ $error = "Marker scan hit start of image data";  | 
| 
685
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
 		  Log($error); return 0; }  | 
| 
 
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
    | 
| 
686
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
687
 | 
5
 | 
  
 50
  
 | 
 
 | 
 
 | 
 
 | 
587
 | 
 		if (JPEGSkipVariable($handle) == 0)  | 
| 
688
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
 		{ $error = "JPEGSkipVariable failed";  | 
| 
689
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
 		  Log($error); return 0; }  | 
| 
 
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
    | 
| 
690
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
691
 | 
5
 | 
 
 | 
 
 | 
 
 | 
 
 | 
9
 | 
 		$marker = JPEGNextMarker($handle);  | 
| 
692
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	}  | 
| 
693
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
694
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	# If were's here, we must have found the right marker. Now  | 
| 
695
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	# BlindScan through the data.  | 
| 
696
 | 
5
 | 
 
 | 
 
 | 
 
 | 
 
 | 
9
 | 
 	return BlindScan($handle, JPEGGetVariableLength($handle));  | 
| 
697
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 }  | 
| 
698
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
699
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 #  | 
| 
700
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # JPEGNextMarker  | 
| 
701
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 #  | 
| 
702
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # Scans to the start of the next valid-looking marker. Return value is  | 
| 
703
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # the marker id.  | 
| 
704
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 #  | 
| 
705
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 sub JPEGNextMarker  | 
| 
706
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 {  | 
| 
707
 | 
14
 | 
 
 | 
 
 | 
  
14
  
 | 
  
0
  
 | 
20
 | 
 	my $handle = shift @_;  | 
| 
708
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
709
 | 
14
 | 
 
 | 
 
 | 
 
 | 
 
 | 
16
 | 
 	my $byte;  | 
| 
710
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
711
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	# Find 0xff byte. We should already be on it.  | 
| 
712
 | 
14
 | 
  
 50
  
 | 
 
 | 
 
 | 
 
 | 
43
 | 
 	$handle->read($byte, 1) || return 0;  | 
| 
713
 | 
14
 | 
 
 | 
 
 | 
 
 | 
 
 | 
127
 | 
 	while (ord($byte) != 0xff)  | 
| 
714
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	{  | 
| 
715
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
 		Log("JPEGNextMarker: warning: bogus stuff in JPEG file");  | 
| 
716
 | 
0
 | 
  
  0
  
 | 
 
 | 
 
 | 
 
 | 
0
 | 
 		$handle->read($byte, 1) || return 0;  | 
| 
717
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	}  | 
| 
718
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
719
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	# Now skip any extra 0xffs, which are valid padding.  | 
| 
720
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	do  | 
| 
721
 | 
14
 | 
 
 | 
 
 | 
 
 | 
 
 | 
17
 | 
 	{  | 
| 
722
 | 
14
 | 
  
 50
  
 | 
 
 | 
 
 | 
 
 | 
34
 | 
 		$handle->read($byte, 1) || return 0;  | 
| 
723
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	} while (ord($byte) == 0xff);  | 
| 
724
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
725
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	# $byte should now contain the marker id.  | 
| 
726
 | 
14
 | 
 
 | 
 
 | 
 
 | 
 
 | 
139
 | 
 	Log("JPEGNextMarker: at marker " . unpack("H*", $byte));  | 
| 
727
 | 
14
 | 
 
 | 
 
 | 
 
 | 
 
 | 
39
 | 
 	return $byte;  | 
| 
728
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 }  | 
| 
729
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
730
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 #  | 
| 
731
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # JPEGGetVariableLength  | 
| 
732
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 #  | 
| 
733
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # Gets length of current variable-length section. File position at  | 
| 
734
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # start must be on the marker itself, e.g. immediately after call to  | 
| 
735
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # JPEGNextMarker. File position is updated to just past the length  | 
| 
736
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # field.  | 
| 
737
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 #  | 
| 
738
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 sub JPEGGetVariableLength  | 
| 
739
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 {  | 
| 
740
 | 
14
 | 
 
 | 
 
 | 
  
14
  
 | 
  
0
  
 | 
86
 | 
 	my $handle = shift @_;  | 
| 
741
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
742
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	# Get the marker parameter length count  | 
| 
743
 | 
14
 | 
 
 | 
 
 | 
 
 | 
 
 | 
12
 | 
 	my $length;  | 
| 
744
 | 
14
 | 
  
 50
  
 | 
 
 | 
 
 | 
 
 | 
35
 | 
 	$handle->read($length, 2) || return 0;  | 
| 
745
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 		  | 
| 
746
 | 
14
 | 
 
 | 
 
 | 
 
 | 
 
 | 
101
 | 
 	($length) = unpack("n", $length);  | 
| 
747
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
748
 | 
14
 | 
 
 | 
 
 | 
 
 | 
 
 | 
48
 | 
 	Log("JPEG variable length: $length");  | 
| 
749
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
750
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	# Length includes itself, so must be at least 2  | 
| 
751
 | 
14
 | 
  
 50
  
 | 
 
 | 
 
 | 
 
 | 
30
 | 
 	if ($length < 2)  | 
| 
752
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	{  | 
| 
753
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
 		Log("JPEGGetVariableLength: erroneous JPEG marker length");  | 
| 
754
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
 		return 0;  | 
| 
755
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	}  | 
| 
756
 | 
14
 | 
 
 | 
 
 | 
 
 | 
 
 | 
17
 | 
 	$length -= 2;  | 
| 
757
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
758
 | 
14
 | 
 
 | 
 
 | 
 
 | 
 
 | 
24
 | 
 	return $length;  | 
| 
759
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 }  | 
| 
760
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
761
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 #  | 
| 
762
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # JPEGSkipVariable  | 
| 
763
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 #  | 
| 
764
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # Skips variable-length section of JPEG block. Should always be called  | 
| 
765
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # between calls to JPEGNextMarker to ensure JPEGNextMarker is at the  | 
| 
766
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # start of data it can properly parse.  | 
| 
767
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 #  | 
| 
768
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 sub JPEGSkipVariable  | 
| 
769
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 {  | 
| 
770
 | 
9
 | 
 
 | 
 
 | 
  
9
  
 | 
  
0
  
 | 
14
 | 
 	my $handle = shift;  | 
| 
771
 | 
9
 | 
 
 | 
 
 | 
 
 | 
 
 | 
10
 | 
 	my $rSave = shift;  | 
| 
772
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
773
 | 
9
 | 
 
 | 
 
 | 
 
 | 
 
 | 
15
 | 
 	my $length = JPEGGetVariableLength($handle);  | 
| 
774
 | 
9
 | 
  
 50
  
 | 
 
 | 
 
 | 
 
 | 
18
 | 
 	return if ($length == 0);  | 
| 
775
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
776
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	# Skip remaining bytes  | 
| 
777
 | 
9
 | 
 
 | 
 
 | 
 
 | 
 
 | 
10
 | 
 	my $temp;  | 
| 
778
 | 
9
 | 
  
100
  
 | 
  
 66
  
 | 
 
 | 
 
 | 
45
 | 
 	if (defined($rSave) || $debugMode)  | 
| 
779
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	{  | 
| 
780
 | 
4
 | 
  
 50
  
 | 
 
 | 
 
 | 
 
 | 
12
 | 
 		unless ($handle->read($temp, $length))  | 
| 
781
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 		{  | 
| 
782
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
 			Log("JPEGSkipVariable: read failed while skipping var data");  | 
| 
783
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
 			return 0;  | 
| 
784
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 		}  | 
| 
785
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
786
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 		# prints out a heck of a lot of stuff  | 
| 
787
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 		# HexDump($temp);  | 
| 
788
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	}  | 
| 
789
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	else  | 
| 
790
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	{  | 
| 
791
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 		# Just seek  | 
| 
792
 | 
5
 | 
  
 50
  
 | 
 
 | 
 
 | 
 
 | 
16
 | 
 		unless($handle->seek($length, 1))  | 
| 
793
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 		{  | 
| 
794
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
 			Log("JPEGSkipVariable: read failed while skipping var data");  | 
| 
795
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
 			return 0;  | 
| 
796
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 		}  | 
| 
797
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	}  | 
| 
798
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
799
 | 
9
 | 
  
100
  
 | 
 
 | 
 
 | 
 
 | 
122
 | 
 	$$rSave = $temp if defined($rSave);  | 
| 
800
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
801
 | 
9
 | 
 
 | 
 
 | 
 
 | 
 
 | 
29
 | 
 	return 1;  | 
| 
802
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 }  | 
| 
803
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
804
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 #  | 
| 
805
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # BlindScan  | 
| 
806
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 #  | 
| 
807
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # Scans blindly to first IIM Record 2 tag in the file. This method may  | 
| 
808
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # or may not work on any arbitrary file type, but it doesn't hurt to  | 
| 
809
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # check. We expect to see this tag within the first 8k of data. (This  | 
| 
810
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # limit may need to be changed or eliminated depending on how other  | 
| 
811
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # programs choose to store IIM.)  | 
| 
812
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 #  | 
| 
813
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 sub BlindScan  | 
| 
814
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 {  | 
| 
815
 | 
5
 | 
 
 | 
 
 | 
  
5
  
 | 
  
0
  
 | 
7
 | 
 	my $handle = shift;  | 
| 
816
 | 
5
 | 
 
 | 
  
 33
  
 | 
 
 | 
 
 | 
12
 | 
     my $maxoff = shift() || $MAX_FILE_OFFSET;  | 
| 
817
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
       | 
| 
818
 | 
5
 | 
 
 | 
 
 | 
 
 | 
 
 | 
14
 | 
 	Log("BlindScan: starting scan, max length $maxoff");  | 
| 
819
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	  | 
| 
820
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	# start digging  | 
| 
821
 | 
5
 | 
 
 | 
 
 | 
 
 | 
 
 | 
7
 | 
 	my $offset = 0;  | 
| 
822
 | 
5
 | 
 
 | 
 
 | 
 
 | 
 
 | 
12
 | 
 	while ($offset <= $maxoff)  | 
| 
823
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	{  | 
| 
824
 | 
135
 | 
 
 | 
 
 | 
 
 | 
 
 | 
114
 | 
 		my $temp;  | 
| 
825
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 		  | 
| 
826
 | 
135
 | 
  
 50
  
 | 
 
 | 
 
 | 
 
 | 
301
 | 
 		unless ($handle->read($temp, 1))  | 
| 
827
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 		{  | 
| 
828
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
 			Log("BlindScan: hit EOF while scanning");  | 
| 
829
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
 			return 0;  | 
| 
830
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 		}  | 
| 
831
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
832
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 		# look for tag identifier 0x1c  | 
| 
833
 | 
135
 | 
  
100
  
 | 
 
 | 
 
 | 
 
 | 
824
 | 
 		if (ord($temp) == 0x1c)  | 
| 
834
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 		{  | 
| 
835
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 			# if we found that, look for record 2, dataset 0  | 
| 
836
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 			# (record version number)  | 
| 
837
 | 
5
 | 
 
 | 
 
 | 
 
 | 
 
 | 
6
 | 
 			my ($record, $dataset);  | 
| 
838
 | 
5
 | 
 
 | 
 
 | 
 
 | 
 
 | 
10
 | 
 			$handle->read($record, 1);  | 
| 
839
 | 
5
 | 
 
 | 
 
 | 
 
 | 
 
 | 
29
 | 
 			$handle->read($dataset, 1);  | 
| 
840
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 			  | 
| 
841
 | 
5
 | 
  
 50
  
 | 
 
 | 
 
 | 
 
 | 
26
 | 
 			if (ord($record) == 2)  | 
| 
842
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 			{  | 
| 
843
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 				# found it. seek to start of this tag and return.  | 
| 
844
 | 
5
 | 
 
 | 
 
 | 
 
 | 
 
 | 
15
 | 
 				Log("BlindScan: found IIM start at offset $offset");  | 
| 
845
 | 
5
 | 
 
 | 
 
 | 
 
 | 
 
 | 
16
 | 
 				$handle->seek(-3, 1); # seek rel to current position  | 
| 
846
 | 
5
 | 
 
 | 
 
 | 
 
 | 
 
 | 
76
 | 
 				return $offset;  | 
| 
847
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 			}  | 
| 
848
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 			else  | 
| 
849
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 			{  | 
| 
850
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 				# didn't find it. back up 2 to make up for  | 
| 
851
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 				# those reads above.  | 
| 
852
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
 				$handle->seek(-2, 1); # seek rel to current position  | 
| 
853
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 			}  | 
| 
854
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 		}  | 
| 
855
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 		  | 
| 
856
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 		# no tag, keep scanning  | 
| 
857
 | 
130
 | 
 
 | 
 
 | 
 
 | 
 
 | 
274
 | 
 		$offset++;  | 
| 
858
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	}  | 
| 
859
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	  | 
| 
860
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
 	return 0;  | 
| 
861
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 }  | 
| 
862
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
863
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 #  | 
| 
864
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # CollectIIMInfo  | 
| 
865
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 #  | 
| 
866
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # Assuming file is seeked to start of IIM data (using above), this  | 
| 
867
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # reads all the data into our object's hashes  | 
| 
868
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 #  | 
| 
869
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 sub CollectIIMInfo  | 
| 
870
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 {  | 
| 
871
 | 
5
 | 
 
 | 
 
 | 
  
5
  
 | 
  
0
  
 | 
7
 | 
 	my $self = shift;  | 
| 
872
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	  | 
| 
873
 | 
5
 | 
 
 | 
 
 | 
 
 | 
 
 | 
9
 | 
 	my $handle = $self->{_handle};  | 
| 
874
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	  | 
| 
875
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	# NOTE: file should already be at the start of the first  | 
| 
876
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	# IPTC code: record 2, dataset 0.  | 
| 
877
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	  | 
| 
878
 | 
5
 | 
 
 | 
 
 | 
 
 | 
 
 | 
6
 | 
 	while (1)  | 
| 
879
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	{  | 
| 
880
 | 
90
 | 
 
 | 
 
 | 
 
 | 
 
 | 
85
 | 
 		my $header;  | 
| 
881
 | 
90
 | 
  
 50
  
 | 
 
 | 
 
 | 
 
 | 
215
 | 
 		return unless $handle->read($header, 5);  | 
| 
882
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 		  | 
| 
883
 | 
90
 | 
 
 | 
 
 | 
 
 | 
 
 | 
628
 | 
 		($tag, $record, $dataset, $length) = unpack("CCCn", $header);  | 
| 
884
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
885
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 		# bail if we're past end of IIM record 2 data  | 
| 
886
 | 
90
 | 
  
100
  
 | 
  
 66
  
 | 
 
 | 
 
 | 
358
 | 
 		return unless ($tag == 0x1c) && ($record == 2);  | 
| 
887
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 		  | 
| 
888
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 		# print "tag     : " . $tag . "\n";  | 
| 
889
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 		# print "record  : " . $record . "\n";  | 
| 
890
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 		# print "dataset : " . $dataset . "\n";  | 
| 
891
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 		# print "length  : " . $length  . "\n";  | 
| 
892
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	  | 
| 
893
 | 
85
 | 
 
 | 
 
 | 
 
 | 
 
 | 
79
 | 
 		my $value;  | 
| 
894
 | 
85
 | 
 
 | 
 
 | 
 
 | 
 
 | 
243
 | 
 		$handle->read($value, $length);  | 
| 
895
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 		  | 
| 
896
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 		# try to extract first into _listdata (keywords, categories)  | 
| 
897
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 		# and, if unsuccessful, into _data. Tags which are not in the  | 
| 
898
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 		# current IIM spec (version 4) are currently discarded.  | 
| 
899
 | 
85
 | 
  
100
  
 | 
 
 | 
 
 | 
 
 | 
579
 | 
 		if (exists $listdatasets{$dataset})  | 
| 
 
 | 
 
 | 
  
100
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
900
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 		{  | 
| 
901
 | 
20
 | 
 
 | 
 
 | 
 
 | 
 
 | 
31
 | 
 			my $dataname = $listdatasets{$dataset};  | 
| 
902
 | 
20
 | 
 
 | 
 
 | 
 
 | 
 
 | 
21
 | 
 			my $listref  = $listdata{$dataname};  | 
| 
903
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 			  | 
| 
904
 | 
20
 | 
 
 | 
 
 | 
 
 | 
 
 | 
20
 | 
 			push(@{$self->{_listdata}->{$dataname}}, $value);  | 
| 
 
 | 
20
 | 
 
 | 
 
 | 
 
 | 
 
 | 
79
 | 
    | 
| 
905
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 		}  | 
| 
906
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 		elsif (exists $datasets{$dataset})  | 
| 
907
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 		{  | 
| 
908
 | 
60
 | 
 
 | 
 
 | 
 
 | 
 
 | 
90
 | 
 			my $dataname = $datasets{$dataset};  | 
| 
909
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	  | 
| 
910
 | 
60
 | 
 
 | 
 
 | 
 
 | 
 
 | 
161
 | 
 			$self->{_data}->{$dataname} = $value;  | 
| 
911
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 		}  | 
| 
912
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 		# else discard  | 
| 
913
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	}  | 
| 
914
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 }  | 
| 
915
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
916
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 #######################################################################  | 
| 
917
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # File Saving  | 
| 
918
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 #######################################################################  | 
| 
919
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
920
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 #  | 
| 
921
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # JPEGCollectFileParts  | 
| 
922
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 #  | 
| 
923
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # Collects all pieces of the file except for the IPTC info that we'll  | 
| 
924
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # replace when saving. Returns the stuff before the info, stuff after,  | 
| 
925
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # and the contents of the Adobe Resource Block that the IPTC data goes  | 
| 
926
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # in. Returns undef if a file parsing error occured.  | 
| 
927
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 #  | 
| 
928
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 sub JPEGCollectFileParts  | 
| 
929
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 {  | 
| 
930
 | 
2
 | 
 
 | 
 
 | 
  
2
  
 | 
  
0
  
 | 
3
 | 
 	my $handle = shift;  | 
| 
931
 | 
2
 | 
 
 | 
 
 | 
 
 | 
 
 | 
3
 | 
 	my ($options) = @_;  | 
| 
932
 | 
2
 | 
 
 | 
 
 | 
 
 | 
 
 | 
3
 | 
 	my ($start, $end, $adobeParts);  | 
| 
933
 | 
2
 | 
 
 | 
 
 | 
 
 | 
 
 | 
4
 | 
 	my $discardAppParts = 0;  | 
| 
934
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
935
 | 
2
 | 
  
 50
  
 | 
  
 33
  
 | 
 
 | 
 
 | 
17
 | 
 	if (defined($options) && defined($options->{'discardAppParts'}))  | 
| 
936
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
 	{ $discardAppParts = 1; }  | 
| 
937
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
938
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	# Start at beginning of file  | 
| 
939
 | 
2
 | 
 
 | 
 
 | 
 
 | 
 
 | 
7
 | 
 	$handle->seek(0, 0);  | 
| 
940
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
941
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	# Skip past start of file marker  | 
| 
942
 | 
2
 | 
 
 | 
 
 | 
 
 | 
 
 | 
14
 | 
 	my ($ff, $soi);  | 
| 
943
 | 
2
 | 
  
 50
  
 | 
 
 | 
 
 | 
 
 | 
7
 | 
 	$handle->read($ff, 1) || return 0;  | 
| 
944
 | 
2
 | 
 
 | 
 
 | 
 
 | 
 
 | 
28
 | 
 	$handle->read($soi, 1);  | 
| 
945
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	  | 
| 
946
 | 
2
 | 
  
 50
  
 | 
  
 33
  
 | 
 
 | 
 
 | 
21
 | 
 	unless (ord($ff) == 0xff && ord($soi) == 0xd8)  | 
| 
947
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	{  | 
| 
948
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
 		$error = "JPEGScan: invalid start of file"; Log($error);  | 
| 
 
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
    | 
| 
949
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
 		return 0;  | 
| 
950
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	}  | 
| 
951
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
952
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	#  | 
| 
953
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	# Begin building start of file  | 
| 
954
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	#  | 
| 
955
 | 
2
 | 
 
 | 
 
 | 
 
 | 
 
 | 
4
 | 
 	$start .= pack("CC", 0xff, 0xd8);  | 
| 
956
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
957
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	# Get first marker in file. This will be APP0 for JFIF or APP1 for  | 
| 
958
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	# EXIF.  | 
| 
959
 | 
2
 | 
 
 | 
 
 | 
 
 | 
 
 | 
5
 | 
 	my $marker = JPEGNextMarker($handle);  | 
| 
960
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
961
 | 
2
 | 
 
 | 
 
 | 
 
 | 
 
 | 
2
 | 
 	my $app0data;  | 
| 
962
 | 
2
 | 
  
 50
  
 | 
 
 | 
 
 | 
 
 | 
10
 | 
 	if (JPEGSkipVariable($handle, \$app0data) == 0)  | 
| 
963
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
 	{ $error = "JPEGSkipVariable failed";  | 
| 
964
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
 	  Log($error); return 0; }  | 
| 
 
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
    | 
| 
965
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
966
 | 
2
 | 
  
 50
  
 | 
  
 33
  
 | 
 
 | 
 
 | 
10
 | 
 	if (ord($marker) == 0xe0 || !$discardAppParts)  | 
| 
967
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	{  | 
| 
968
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 		# Always include APP0 marker at start if it's present.  | 
| 
969
 | 
2
 | 
 
 | 
 
 | 
 
 | 
 
 | 
8
 | 
 		$start .= pack("CC", 0xff, ord($marker));  | 
| 
970
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 		# Remember that the length must include itself (2 bytes)  | 
| 
971
 | 
2
 | 
 
 | 
 
 | 
 
 | 
 
 | 
5
 | 
 		$start .= pack("n", length($app0data) + 2);  | 
| 
972
 | 
2
 | 
 
 | 
 
 | 
 
 | 
 
 | 
3
 | 
 		$start .= $app0data;  | 
| 
973
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	}  | 
| 
974
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	else  | 
| 
975
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	{  | 
| 
976
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 		# Manually insert APP0 if we're trashing application parts, since  | 
| 
977
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 		# all JFIF format images should start with the version block.  | 
| 
978
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
 		$start .= pack("CC", 0xff, 0xe0);  | 
| 
979
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
 		$start .= pack("n", 16);    # length (including these 2 bytes)  | 
| 
980
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
 		$start .= "JFIF";           # format  | 
| 
981
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
 		$start .= pack("CC", 1, 2); # call it version 1.2 (current JFIF)  | 
| 
982
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
 		$start .= pack(C8, 0);      # zero everything else  | 
| 
983
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	}  | 
| 
984
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
985
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	#  | 
| 
986
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	# Now scan through all markers in file until we hit image data or  | 
| 
987
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	# IPTC stuff.  | 
| 
988
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	#  | 
| 
989
 | 
2
 | 
 
 | 
 
 | 
 
 | 
 
 | 
5
 | 
 	$marker = JPEGNextMarker($handle);  | 
| 
990
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
991
 | 
2
 | 
 
 | 
 
 | 
 
 | 
 
 | 
3
 | 
 	while (1)  | 
| 
992
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	{  | 
| 
993
 | 
2
 | 
  
 50
  
 | 
 
 | 
 
 | 
 
 | 
7
 | 
 		if (ord($marker) == 0)  | 
| 
994
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
 		{ $error = "Marker scan failed"; Log($error); return 0; }  | 
| 
 
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
    | 
| 
 
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
    | 
| 
995
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
996
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 		# Check for end of image  | 
| 
997
 | 
2
 | 
  
 50
  
 | 
 
 | 
 
 | 
 
 | 
7
 | 
 		if (ord($marker) == 0xd9)  | 
| 
998
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 		{  | 
| 
999
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
 			Log("JPEGCollectFileParts: saw end of image marker");  | 
| 
1000
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
 			$end .= pack("CC", 0xff, ord($marker));  | 
| 
1001
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
 			goto doneScanning;  | 
| 
1002
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 		}  | 
| 
1003
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
1004
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 		# Check for start of compressed data  | 
| 
1005
 | 
2
 | 
  
 50
  
 | 
 
 | 
 
 | 
 
 | 
6
 | 
 		if (ord($marker) == 0xda)  | 
| 
1006
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 		{  | 
| 
1007
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
 			Log("JPEGCollectFileParts: saw start of compressed data");  | 
| 
1008
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
 			$end .= pack("CC", 0xff, ord($marker));  | 
| 
1009
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
 			goto doneScanning;  | 
| 
1010
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 		}  | 
| 
1011
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
1012
 | 
2
 | 
 
 | 
 
 | 
 
 | 
 
 | 
3
 | 
 		my $partdata;  | 
| 
1013
 | 
2
 | 
  
 50
  
 | 
 
 | 
 
 | 
 
 | 
5
 | 
 		if (JPEGSkipVariable($handle, \$partdata) == 0)  | 
| 
1014
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
 		{ $error = "JPEGSkipVariable failed";  | 
| 
1015
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
 		  Log($error); return 0; }  | 
| 
 
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
    | 
| 
1016
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
1017
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 		# Take all parts aside from APP13, which we'll replace  | 
| 
1018
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 		# ourselves.  | 
| 
1019
 | 
2
 | 
  
 50
  
 | 
  
 33
  
 | 
 
 | 
 
 | 
25
 | 
 		if ($discardAppParts && ord($marker) >= 0xe0 && ord($marker) <= 0xef)  | 
| 
 
 | 
 
 | 
  
 50
  
 | 
  
 33
  
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
1020
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 		{  | 
| 
1021
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 			# Skip all application markers, including Adobe parts  | 
| 
1022
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
 			undef $adobeParts;  | 
| 
1023
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 		}  | 
| 
1024
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 		elsif (ord($marker) == 0xed)  | 
| 
1025
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 		{  | 
| 
1026
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 			# Collect the adobe stuff from part 13  | 
| 
1027
 | 
2
 | 
 
 | 
 
 | 
 
 | 
 
 | 
8
 | 
 			$adobeParts = CollectAdobeParts($partdata);  | 
| 
1028
 | 
2
 | 
 
 | 
 
 | 
 
 | 
 
 | 
38
 | 
 			goto doneScanning;  | 
| 
1029
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 		}  | 
| 
1030
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 		else  | 
| 
1031
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 		{  | 
| 
1032
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 			# Append all other parts to start section  | 
| 
1033
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
 			$start .= pack("CC", 0xff, ord($marker));  | 
| 
1034
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
 			$start .= pack("n", length($partdata) + 2);  | 
| 
1035
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
 			$start .= $partdata;  | 
| 
1036
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 		}  | 
| 
1037
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
1038
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
 		$marker = JPEGNextMarker($handle);  | 
| 
1039
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	}  | 
| 
1040
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
1041
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
   doneScanning:  | 
| 
1042
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
1043
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	#  | 
| 
1044
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	# Append rest of file to $end  | 
| 
1045
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	#  | 
| 
1046
 | 
2
 | 
 
 | 
 
 | 
 
 | 
 
 | 
4
 | 
 	my $buffer;  | 
| 
1047
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
1048
 | 
2
 | 
 
 | 
 
 | 
 
 | 
 
 | 
21
 | 
 	while ($handle->read($buffer, 16384))  | 
| 
1049
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	{  | 
| 
1050
 | 
2
 | 
 
 | 
 
 | 
 
 | 
 
 | 
79
 | 
 		$end .= $buffer;  | 
| 
1051
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	}  | 
| 
1052
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
1053
 | 
2
 | 
 
 | 
 
 | 
 
 | 
 
 | 
30
 | 
 	return [$start, $end, $adobeParts];  | 
| 
1054
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 }  | 
| 
1055
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
1056
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 #  | 
| 
1057
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # CollectAdobeParts  | 
| 
1058
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 #  | 
| 
1059
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # Part APP13 contains yet another markup format, one defined by Adobe.  | 
| 
1060
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # See "File Formats Specification" in the Photoshop SDK (avail from  | 
| 
1061
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # www.adobe.com). We must take everything but the IPTC data so that  | 
| 
1062
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # way we can write the file back without losing everything else  | 
| 
1063
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # Photoshop stuffed into the APP13 block.  | 
| 
1064
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 #  | 
| 
1065
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 sub CollectAdobeParts  | 
| 
1066
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 {  | 
| 
1067
 | 
2
 | 
 
 | 
 
 | 
  
2
  
 | 
  
0
  
 | 
5
 | 
 	my ($data) = @_;  | 
| 
1068
 | 
2
 | 
 
 | 
 
 | 
 
 | 
 
 | 
3
 | 
 	my $length = length($data);  | 
| 
1069
 | 
2
 | 
 
 | 
 
 | 
 
 | 
 
 | 
3
 | 
 	my $offset = 0;  | 
| 
1070
 | 
2
 | 
 
 | 
 
 | 
 
 | 
 
 | 
4
 | 
 	my $out = '';  | 
| 
1071
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
1072
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	# Skip preamble  | 
| 
1073
 | 
2
 | 
 
 | 
 
 | 
 
 | 
 
 | 
8
 | 
 	$offset = length('Photoshop 3.0 ');  | 
| 
1074
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
1075
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	# Process everything  | 
| 
1076
 | 
2
 | 
 
 | 
 
 | 
 
 | 
 
 | 
7
 | 
 	while ($offset < $length)  | 
| 
1077
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	{  | 
| 
1078
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 		# Get OSType and ID  | 
| 
1079
 | 
24
 | 
 
 | 
 
 | 
 
 | 
 
 | 
63
 | 
 		my ($ostype, $id1, $id2) = unpack("NCC", substr($data, $offset, 6));  | 
| 
1080
 | 
24
 | 
  
 50
  
 | 
 
 | 
 
 | 
 
 | 
64
 | 
 		last unless (($offset += 6) < $length); # $offset += 6;  | 
| 
1081
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
1082
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 		# printf("CollectAdobeParts: ID %2.2x %2.2x\n", $id1, $id2);  | 
| 
1083
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 		  | 
| 
1084
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 		# Get pascal string  | 
| 
1085
 | 
24
 | 
 
 | 
 
 | 
 
 | 
 
 | 
43
 | 
 		my ($stringlen) = unpack("C", substr($data, $offset, 1));  | 
| 
1086
 | 
24
 | 
  
 50
  
 | 
 
 | 
 
 | 
 
 | 
59
 | 
 		last unless (++$offset < $length); # $offset += 1;  | 
| 
1087
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
1088
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 		# printf("CollectAdobeParts: str len %d\n", $stringlen);  | 
| 
1089
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 		  | 
| 
1090
 | 
24
 | 
 
 | 
 
 | 
 
 | 
 
 | 
39
 | 
 		my $string = substr($data, $offset, $stringlen);  | 
| 
1091
 | 
24
 | 
 
 | 
 
 | 
 
 | 
 
 | 
26
 | 
 		$offset += $stringlen;  | 
| 
1092
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 		# round up if odd  | 
| 
1093
 | 
24
 | 
  
 50
  
 | 
 
 | 
 
 | 
 
 | 
48
 | 
 		$offset++ if ($stringlen % 2 != 0);  | 
| 
1094
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 		# there should be a null if string len is 0  | 
| 
1095
 | 
24
 | 
  
 50
  
 | 
 
 | 
 
 | 
 
 | 
47
 | 
 		$offset++ if ($stringlen == 0);  | 
| 
1096
 | 
24
 | 
  
 50
  
 | 
 
 | 
 
 | 
 
 | 
47
 | 
 		last unless ($offset < $length);  | 
| 
1097
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
1098
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 		# Get variable-size data  | 
| 
1099
 | 
24
 | 
 
 | 
 
 | 
 
 | 
 
 | 
45
 | 
 		my ($size) = unpack("N", substr($data, $offset, 4));  | 
| 
1100
 | 
24
 | 
  
 50
  
 | 
 
 | 
 
 | 
 
 | 
48
 | 
 		last unless (($offset += 4) < $length);  # $offset += 4;  | 
| 
1101
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
1102
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 		# printf("CollectAdobeParts: size %d\n", $size);  | 
| 
1103
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
1104
 | 
24
 | 
 
 | 
 
 | 
 
 | 
 
 | 
41
 | 
 		my $var = substr($data, $offset, $size);  | 
| 
1105
 | 
24
 | 
 
 | 
 
 | 
 
 | 
 
 | 
25
 | 
 		$offset += $size;  | 
| 
1106
 | 
24
 | 
  
100
  
 | 
 
 | 
 
 | 
 
 | 
48
 | 
 		$offset++ if ($size % 2 != 0); # round up if odd  | 
| 
1107
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
1108
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 		# skip IIM data (0x0404), but write everything else out  | 
| 
1109
 | 
24
 | 
  
100
  
 | 
  
100
  
 | 
 
 | 
 
 | 
84
 | 
 		unless ($id1 == 4 && $id2 == 4)  | 
| 
1110
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 		{  | 
| 
1111
 | 
22
 | 
 
 | 
 
 | 
 
 | 
 
 | 
43
 | 
 			$out .= pack("NCC", $ostype, $id1, $id2);  | 
| 
1112
 | 
22
 | 
 
 | 
 
 | 
 
 | 
 
 | 
33
 | 
 			$out .= pack("C", $stringlen);  | 
| 
1113
 | 
22
 | 
 
 | 
 
 | 
 
 | 
 
 | 
21
 | 
 			$out .= $string;  | 
| 
1114
 | 
22
 | 
  
 50
  
 | 
  
 33
  
 | 
 
 | 
 
 | 
52
 | 
 			$out .= pack("C", 0) if ($stringlen == 0 || $stringlen % 2 != 0);  | 
| 
1115
 | 
22
 | 
 
 | 
 
 | 
 
 | 
 
 | 
31
 | 
 			$out .= pack("N", $size);  | 
| 
1116
 | 
22
 | 
 
 | 
 
 | 
 
 | 
 
 | 
29
 | 
 			$out .= $var;  | 
| 
1117
 | 
22
 | 
  
100
  
 | 
  
 66
  
 | 
 
 | 
 
 | 
94
 | 
 			$out .= pack("C", 0) if ($size % 2 != 0 && length($out) % 2 != 0);  | 
| 
1118
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 		}  | 
| 
1119
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	}  | 
| 
1120
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
1121
 | 
2
 | 
 
 | 
 
 | 
 
 | 
 
 | 
6
 | 
 	return $out;  | 
| 
1122
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 }  | 
| 
1123
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
1124
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 #  | 
| 
1125
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # PackedIIMData  | 
| 
1126
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 #  | 
| 
1127
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # Assembles and returns our _data and _listdata into IIM format for  | 
| 
1128
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # embedding into an image.  | 
| 
1129
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 #  | 
| 
1130
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 sub PackedIIMData  | 
| 
1131
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 {  | 
| 
1132
 | 
2
 | 
 
 | 
 
 | 
  
2
  
 | 
  
0
  
 | 
4
 | 
 	my $self = shift;  | 
| 
1133
 | 
2
 | 
 
 | 
 
 | 
 
 | 
 
 | 
3
 | 
 	my $out;  | 
| 
1134
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
1135
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	# First, we need to build a mapping of datanames to dataset  | 
| 
1136
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	# numbers if we haven't already.  | 
| 
1137
 | 
2
 | 
  
100
  
 | 
 
 | 
 
 | 
 
 | 
8
 | 
 	unless (scalar(keys %datanames))  | 
| 
1138
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	{  | 
| 
1139
 | 
1
 | 
 
 | 
 
 | 
 
 | 
 
 | 
12
 | 
 		foreach my $dataset (keys %datasets)  | 
| 
1140
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 		{  | 
| 
1141
 | 
63
 | 
 
 | 
 
 | 
 
 | 
 
 | 
93
 | 
 			my $dataname = $datasets{$dataset};  | 
| 
1142
 | 
63
 | 
 
 | 
 
 | 
 
 | 
 
 | 
171
 | 
 			$datanames{$dataname} = $dataset;  | 
| 
1143
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 		}  | 
| 
1144
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	}  | 
| 
1145
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
1146
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	# Ditto for the lists  | 
| 
1147
 | 
2
 | 
  
100
  
 | 
 
 | 
 
 | 
 
 | 
10
 | 
 	unless (scalar(keys %listdatanames))  | 
| 
1148
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	{  | 
| 
1149
 | 
1
 | 
 
 | 
 
 | 
 
 | 
 
 | 
3
 | 
 		foreach my $dataset (keys %listdatasets)  | 
| 
1150
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 		{  | 
| 
1151
 | 
3
 | 
 
 | 
 
 | 
 
 | 
 
 | 
5
 | 
 			my $dataname = $listdatasets{$dataset};  | 
| 
1152
 | 
3
 | 
 
 | 
 
 | 
 
 | 
 
 | 
8
 | 
 			$listdatanames{$dataname} = $dataset;  | 
| 
1153
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 		}  | 
| 
1154
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	}  | 
| 
1155
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
1156
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	# Print record version  | 
| 
1157
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	# tag - record - dataset - len (short) - 2 (short)  | 
| 
1158
 | 
2
 | 
 
 | 
 
 | 
 
 | 
 
 | 
5
 | 
 	$out .= pack("CCCnn", 0x1c, 2, 0, 2, 2);  | 
| 
1159
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
1160
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	# Iterate over data sets  | 
| 
1161
 | 
2
 | 
 
 | 
 
 | 
 
 | 
 
 | 
3
 | 
 	foreach my $key (keys %{$self->{_data}})  | 
| 
 
 | 
2
 | 
 
 | 
 
 | 
 
 | 
 
 | 
19
 | 
    | 
| 
1162
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	{  | 
| 
1163
 | 
24
 | 
 
 | 
 
 | 
 
 | 
 
 | 
39
 | 
 		my $dataset = $datanames{$key};  | 
| 
1164
 | 
24
 | 
 
 | 
 
 | 
 
 | 
 
 | 
42
 | 
 		my $value   = $self->{_data}->{$key};  | 
| 
1165
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
1166
 | 
24
 | 
  
 50
  
 | 
 
 | 
 
 | 
 
 | 
63
 | 
 		if ($dataset == 0)  | 
| 
1167
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
 		{ Log("PackedIIMData: illegal dataname $key"); next; }  | 
| 
 
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
    | 
| 
1168
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
1169
 | 
24
 | 
  
 50
  
 | 
 
 | 
 
 | 
 
 | 
41
 | 
         next unless $value;  | 
| 
1170
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
1171
 | 
24
 | 
 
 | 
 
 | 
 
 | 
 
 | 
29
 | 
 		my ($tag, $record) = (0x1c, 0x02);  | 
| 
1172
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
1173
 | 
24
 | 
 
 | 
 
 | 
 
 | 
 
 | 
47
 | 
 		$out .= pack("CCCn", $tag, $record, $dataset, length($value));  | 
| 
1174
 | 
24
 | 
 
 | 
 
 | 
 
 | 
 
 | 
40
 | 
 		$out .= $value;  | 
| 
1175
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	}  | 
| 
1176
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
1177
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	# Do the same for list data sets  | 
| 
1178
 | 
2
 | 
 
 | 
 
 | 
 
 | 
 
 | 
7
 | 
 	foreach my $key (keys %{$self->{_listdata}})  | 
| 
 
 | 
2
 | 
 
 | 
 
 | 
 
 | 
 
 | 
7
 | 
    | 
| 
1179
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	{  | 
| 
1180
 | 
4
 | 
 
 | 
 
 | 
 
 | 
 
 | 
7
 | 
 		my $dataset = $listdatanames{$key};  | 
| 
1181
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
1182
 | 
4
 | 
  
 50
  
 | 
 
 | 
 
 | 
 
 | 
11
 | 
 		if ($dataset == 0)  | 
| 
1183
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
 		{ Log("PackedIIMData: illegal dataname $key"); next; }  | 
| 
 
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
    | 
| 
1184
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
1185
 | 
4
 | 
 
 | 
 
 | 
 
 | 
 
 | 
5
 | 
 		foreach my $value (@{$self->{_listdata}->{$key}})  | 
| 
 
 | 
4
 | 
 
 | 
 
 | 
 
 | 
 
 | 
10
 | 
    | 
| 
1186
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 		{  | 
| 
1187
 | 
8
 | 
  
 50
  
 | 
 
 | 
 
 | 
 
 | 
14
 | 
 		    next unless $value;  | 
| 
1188
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 		      | 
| 
1189
 | 
8
 | 
 
 | 
 
 | 
 
 | 
 
 | 
11
 | 
 			my ($tag, $record) = (0x1c, 0x02);  | 
| 
1190
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
1191
 | 
8
 | 
 
 | 
 
 | 
 
 | 
 
 | 
16
 | 
 			$out .= pack("CCCn", $tag, $record, $dataset, length($value));  | 
| 
1192
 | 
8
 | 
 
 | 
 
 | 
 
 | 
 
 | 
19
 | 
 			$out .= $value;  | 
| 
1193
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 		}  | 
| 
1194
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	}  | 
| 
1195
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
1196
 | 
2
 | 
 
 | 
 
 | 
 
 | 
 
 | 
10
 | 
 	return $out;  | 
| 
1197
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 }  | 
| 
1198
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
1199
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 #  | 
| 
1200
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # PhotoshopIIMBlock  | 
| 
1201
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 #  | 
| 
1202
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # Assembles the blob of Photoshop "resource data" that includes our  | 
| 
1203
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # fresh IIM data (from PackedIIMData) and the other Adobe parts we  | 
| 
1204
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # found in the file, if there were any.  | 
| 
1205
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 #  | 
| 
1206
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 sub PhotoshopIIMBlock  | 
| 
1207
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 {  | 
| 
1208
 | 
2
 | 
 
 | 
 
 | 
  
2
  
 | 
  
0
  
 | 
4
 | 
 	my ($self, $otherparts, $data) = @_;  | 
| 
1209
 | 
2
 | 
 
 | 
 
 | 
 
 | 
 
 | 
4
 | 
 	my $resourceBlock;  | 
| 
1210
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	my $out;  | 
| 
1211
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
1212
 | 
2
 | 
 
 | 
 
 | 
 
 | 
 
 | 
3
 | 
 	$resourceBlock .= "Photoshop 3.0";  | 
| 
1213
 | 
2
 | 
 
 | 
 
 | 
 
 | 
 
 | 
3
 | 
 	$resourceBlock .= pack("C", 0);  | 
| 
1214
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	# Photoshop identifier  | 
| 
1215
 | 
2
 | 
 
 | 
 
 | 
 
 | 
 
 | 
3
 | 
 	$resourceBlock .= "8BIM";  | 
| 
1216
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	# 0x0404 is IIM data, 00 is required empty string  | 
| 
1217
 | 
2
 | 
 
 | 
 
 | 
 
 | 
 
 | 
4
 | 
 	$resourceBlock .= pack("CCCC", 0x04, 0x04, 0, 0);  | 
| 
1218
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	# length of data as 32-bit, network-byte order  | 
| 
1219
 | 
2
 | 
 
 | 
 
 | 
 
 | 
 
 | 
5
 | 
 	$resourceBlock .= pack("N", length($data));  | 
| 
1220
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	# Now tack data on there  | 
| 
1221
 | 
2
 | 
 
 | 
 
 | 
 
 | 
 
 | 
4
 | 
 	$resourceBlock .= $data;  | 
| 
1222
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	# Pad with a blank if not even size  | 
| 
1223
 | 
2
 | 
  
 50
  
 | 
 
 | 
 
 | 
 
 | 
8
 | 
 	$resourceBlock .= pack("C", 0) if (length($data) % 2 != 0);  | 
| 
1224
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	# Finally tack on other data  | 
| 
1225
 | 
2
 | 
  
 50
  
 | 
 
 | 
 
 | 
 
 | 
16
 | 
 	$resourceBlock .= $otherparts if defined($otherparts);  | 
| 
1226
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
1227
 | 
2
 | 
 
 | 
 
 | 
 
 | 
 
 | 
4
 | 
 	$out .= pack("CC", 0xff, 0xed); # JPEG start of block, APP13  | 
| 
1228
 | 
2
 | 
 
 | 
 
 | 
 
 | 
 
 | 
4
 | 
 	$out .= pack("n", length($resourceBlock) + 2); # length  | 
| 
1229
 | 
2
 | 
 
 | 
 
 | 
 
 | 
 
 | 
5
 | 
 	$out .= $resourceBlock;  | 
| 
1230
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
1231
 | 
2
 | 
 
 | 
 
 | 
 
 | 
 
 | 
9
 | 
 	return $out;  | 
| 
1232
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 }  | 
| 
1233
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
1234
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 #######################################################################  | 
| 
1235
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # Helpers, docs  | 
| 
1236
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 #######################################################################  | 
| 
1237
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
1238
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 #  | 
| 
1239
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # Log: just prints a message to STDERR if $debugMode is on.  | 
| 
1240
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 #  | 
| 
1241
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 sub Log  | 
| 
1242
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 {  | 
| 
1243
 | 
43
 | 
  
 50
  
 | 
 
 | 
  
43
  
 | 
  
0
  
 | 
99
 | 
 	if ($debugMode)  | 
| 
1244
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	{ my $message = shift; print STDERR "**IPTC** $message\n"; }  | 
| 
 
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
1245
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 }   | 
| 
1246
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
1247
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 #  | 
| 
1248
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # HexDump  | 
| 
1249
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 #  | 
| 
1250
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # Very helpful when debugging.  | 
| 
1251
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 #  | 
| 
1252
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 sub HexDump  | 
| 
1253
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 {  | 
| 
1254
 | 
0
 | 
 
 | 
 
 | 
  
0
  
 | 
  
0
  
 | 
 
 | 
 	my $dump = shift;  | 
| 
1255
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	my $len  = length($dump);  | 
| 
1256
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	my $offset = 0;  | 
| 
1257
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	my ($dcol1, $dcol2);  | 
| 
1258
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
1259
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	while ($offset < $len)  | 
| 
1260
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	{  | 
| 
1261
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 		my $temp = substr($dump, $offset++, 1);  | 
| 
1262
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
1263
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 		my $hex = unpack("H*", $temp);  | 
| 
1264
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 		$dcol1 .= " " . $hex;  | 
| 
1265
 | 
0
 | 
  
  0
  
 | 
  
  0
  
 | 
 
 | 
 
 | 
 
 | 
 		if (ord($temp) >= 0x21 && ord($temp) <= 0x7e)  | 
| 
1266
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 		{ $dcol2 .= " $temp"; }  | 
| 
1267
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 		else  | 
| 
1268
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 		{ $dcol2 .= " ."; }  | 
| 
1269
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
1270
 | 
0
 | 
  
  0
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 		if ($offset % 16 == 0)  | 
| 
1271
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 		{  | 
| 
1272
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 			print STDERR $dcol1 . " | " . $dcol2 . "\n";  | 
| 
1273
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 			undef $dcol1; undef $dcol2;  | 
| 
 
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
1274
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 		}  | 
| 
1275
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	}  | 
| 
1276
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
1277
 | 
0
 | 
  
  0
  
 | 
  
  0
  
 | 
 
 | 
 
 | 
 
 | 
 	if (defined($dcol1) || defined($dcol2))  | 
| 
1278
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	{  | 
| 
1279
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 		print STDERR $dcol1 . " | " . $dcol2 . "\n";  | 
| 
1280
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 		undef $dcol1; undef $dcol2;  | 
| 
 
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
1281
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	}  | 
| 
1282
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 }  | 
| 
1283
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
1284
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 #  | 
| 
1285
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # JPEGDebugScan  | 
| 
1286
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 #  | 
| 
1287
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # Also very helpful when debugging.  | 
| 
1288
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 #  | 
| 
1289
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 sub JPEGDebugScan  | 
| 
1290
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 {  | 
| 
1291
 | 
0
 | 
 
 | 
 
 | 
  
0
  
 | 
  
0
  
 | 
 
 | 
 	my $filename = shift;  | 
| 
1292
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	my $handle = IO::File->new($filename);  | 
| 
1293
 | 
0
 | 
  
  0
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	$handle or die "Can't open $filename: $!";  | 
| 
1294
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
1295
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	# Skip past start of file marker  | 
| 
1296
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	my ($ff, $soi);  | 
| 
1297
 | 
0
 | 
  
  0
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	$handle->read($ff, 1) || return 0;  | 
| 
1298
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	$handle->read($soi, 1);  | 
| 
1299
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	  | 
| 
1300
 | 
0
 | 
  
  0
  
 | 
  
  0
  
 | 
 
 | 
 
 | 
 
 | 
 	unless (ord($ff) == 0xff && ord($soi) == 0xd8)  | 
| 
1301
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	{  | 
| 
1302
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 		Log("JPEGScan: invalid start of file");  | 
| 
1303
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 		goto done;  | 
| 
1304
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	}  | 
| 
1305
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
1306
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	# scan to 0xDA (start of scan), dumping the markers we see between  | 
| 
1307
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	# here and there.  | 
| 
1308
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	my $marker = JPEGNextMarker($handle);  | 
| 
1309
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
1310
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	while (ord($marker) != 0xda)  | 
| 
1311
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	{  | 
| 
1312
 | 
0
 | 
  
  0
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 		if (ord($marker) == 0)  | 
| 
1313
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 		{ Log("Marker scan failed"); goto done; }  | 
| 
 
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
1314
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
1315
 | 
0
 | 
  
  0
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 		if (ord($marker) == 0xd9)  | 
| 
 
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
1316
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 		{Log("Marker scan hit end of image marker"); goto done; }  | 
| 
1317
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
1318
 | 
0
 | 
  
  0
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 		if (JPEGSkipVariable($handle) == 0)  | 
| 
1319
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 		{ Log("JPEGSkipVariable failed"); return 0; }  | 
| 
 
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
1320
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
1321
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 		$marker = JPEGNextMarker($handle);  | 
| 
1322
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	}  | 
| 
1323
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
1324
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 done:  | 
| 
1325
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	$handle->close();  | 
| 
1326
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 }  | 
| 
1327
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
1328
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # sucessful package load  | 
| 
1329
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 1;  | 
| 
1330
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
1331
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 __END__  |