line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
########################################################### |
2
|
|
|
|
|
|
|
# A Perl package for showing/modifying JPEG (meta)data. # |
3
|
|
|
|
|
|
|
# Copyright (C) 2004,2005,2006 Stefano Bettelli # |
4
|
|
|
|
|
|
|
# See the COPYING and LICENSE files for license terms. # |
5
|
|
|
|
|
|
|
########################################################### |
6
|
|
|
|
|
|
|
package Image::MetaData::JPEG; |
7
|
14
|
|
|
14
|
|
90
|
use Image::MetaData::JPEG::Segment; |
|
14
|
|
|
|
|
30
|
|
|
14
|
|
|
|
|
455
|
|
8
|
14
|
|
|
14
|
|
76
|
no integer; |
|
14
|
|
|
|
|
29
|
|
|
14
|
|
|
|
|
85
|
|
9
|
14
|
|
|
14
|
|
295
|
use strict; |
|
14
|
|
|
|
|
31
|
|
|
14
|
|
|
|
|
406
|
|
10
|
14
|
|
|
14
|
|
72
|
use warnings; |
|
14
|
|
|
|
|
33
|
|
|
14
|
|
|
|
|
13488
|
|
11
|
|
|
|
|
|
|
|
12
|
|
|
|
|
|
|
########################################################### |
13
|
|
|
|
|
|
|
# This method accepts a string and returns a list whose # |
14
|
|
|
|
|
|
|
# elements are not larger than the length limit imposed # |
15
|
|
|
|
|
|
|
# by a JPEG segment: a segment cannot have a length which # |
16
|
|
|
|
|
|
|
# couldn't be written in a 2-byte unsigned integer, that # |
17
|
|
|
|
|
|
|
# is 2^16 - 1; since the byte count must be written in # |
18
|
|
|
|
|
|
|
# this space, the real comment is limited to 2^16 - 3. # |
19
|
|
|
|
|
|
|
# The length of all but the last element in the list is # |
20
|
|
|
|
|
|
|
# maximal. The input string is not changed. Note that "" # |
21
|
|
|
|
|
|
|
# maps to (""), while an undefined value maps to (). So, # |
22
|
|
|
|
|
|
|
# it is possible to specify an empty comment. # |
23
|
|
|
|
|
|
|
########################################################### |
24
|
|
|
|
|
|
|
{ my $max_length = 2**16 - 3; |
25
|
|
|
|
|
|
|
sub split_comment_string { |
26
|
26
|
100
|
|
26
|
0
|
91
|
return () unless defined $_[0]; |
27
|
18
|
|
|
|
|
67
|
map { substr $_[0], $max_length*$_, $max_length } |
|
24
|
|
|
|
|
779
|
|
28
|
|
|
|
|
|
|
0 .. (-1 + length $_[0]) / $max_length; |
29
|
|
|
|
|
|
|
} |
30
|
|
|
|
|
|
|
} |
31
|
|
|
|
|
|
|
|
32
|
|
|
|
|
|
|
########################################################### |
33
|
|
|
|
|
|
|
# This method returns the number of comment segments in # |
34
|
|
|
|
|
|
|
# the picture (it should be as fast as possible). # |
35
|
|
|
|
|
|
|
########################################################### |
36
|
|
|
|
|
|
|
sub get_number_of_comments { |
37
|
10
|
|
|
10
|
1
|
52
|
my ($this) = @_; |
38
|
|
|
|
|
|
|
# return the length of the output of this method |
39
|
10
|
|
|
|
|
33
|
return scalar $this->get_segments('COM'); |
40
|
|
|
|
|
|
|
} |
41
|
|
|
|
|
|
|
|
42
|
|
|
|
|
|
|
########################################################### |
43
|
|
|
|
|
|
|
# This method returns a list, with an element for each # |
44
|
|
|
|
|
|
|
# comment block in the file (the element contains the # |
45
|
|
|
|
|
|
|
# comment string). Note that an empty list can be retur- # |
46
|
|
|
|
|
|
|
# ned (in case there are no comment blocks). # |
47
|
|
|
|
|
|
|
########################################################### |
48
|
|
|
|
|
|
|
sub get_comments { |
49
|
14
|
|
|
14
|
1
|
1233
|
my ($this) = @_; |
50
|
|
|
|
|
|
|
# loop over all segments, and return the appropriate |
51
|
|
|
|
|
|
|
# field of those which are comments. |
52
|
14
|
|
|
|
|
56
|
my @com_segs = $this->get_segments('COM'); |
53
|
14
|
|
|
|
|
34
|
return map { $_->search_record_value('Comment') } @com_segs; |
|
47
|
|
|
|
|
270
|
|
54
|
|
|
|
|
|
|
} |
55
|
|
|
|
|
|
|
|
56
|
|
|
|
|
|
|
########################################################### |
57
|
|
|
|
|
|
|
# This method adds one or more new comment segments to # |
58
|
|
|
|
|
|
|
# the JPEG file, based on the string passed by the user. # |
59
|
|
|
|
|
|
|
# If there is already at least one comment segment, the # |
60
|
|
|
|
|
|
|
# new segments are created right after the last one. # |
61
|
|
|
|
|
|
|
# Otherwise, the standard position search is applied. # |
62
|
|
|
|
|
|
|
# ------------------------------------------------------- # |
63
|
|
|
|
|
|
|
# In case the passed string is too big (there is a 64KB # |
64
|
|
|
|
|
|
|
# limit in JPEG segments), it is broken down in smaller # |
65
|
|
|
|
|
|
|
# strings and multiple "Comment" segments are inserted in # |
66
|
|
|
|
|
|
|
# the file (they are contiguous). # |
67
|
|
|
|
|
|
|
########################################################### |
68
|
|
|
|
|
|
|
sub add_comment { |
69
|
13
|
|
|
13
|
1
|
1609
|
my ($this, $string) = @_; |
70
|
|
|
|
|
|
|
# create one or more comment blocks, based on the user |
71
|
|
|
|
|
|
|
# string; the string must be split if it is too long. |
72
|
14
|
|
|
|
|
104
|
my @new_comments = |
73
|
13
|
|
|
|
|
36
|
map { new Image::MetaData::JPEG::Segment("COM", \ $_) } |
74
|
|
|
|
|
|
|
split_comment_string($string); |
75
|
|
|
|
|
|
|
# get the list of comment indexes |
76
|
13
|
|
|
|
|
51
|
my @indexes = $this->get_segments('COM', 'INDEXES'); |
77
|
|
|
|
|
|
|
# our position is right after the last comment |
78
|
13
|
100
|
|
|
|
47
|
my $position = @indexes ? 1 + $indexes[$#indexes] : undef; |
79
|
|
|
|
|
|
|
# actually insert the comments (we don't need update() here); |
80
|
|
|
|
|
|
|
# if position is undefined, the standard search is used |
81
|
13
|
|
|
|
|
146
|
$this->insert_segments(\ @new_comments, $position); |
82
|
|
|
|
|
|
|
} |
83
|
|
|
|
|
|
|
|
84
|
|
|
|
|
|
|
########################################################### |
85
|
|
|
|
|
|
|
# This method replaces the $index-th comment segment with # |
86
|
|
|
|
|
|
|
# one or more new segments based on $string (the index of # |
87
|
|
|
|
|
|
|
# the first comment segment is 0). If $string is too big # |
88
|
|
|
|
|
|
|
# (see add_comment), it is broken down and multiple seg- # |
89
|
|
|
|
|
|
|
# ments are created. If $string is undef, the comment # |
90
|
|
|
|
|
|
|
# segment is erased. If $index is out-of-bound, only a # |
91
|
|
|
|
|
|
|
# warning is printed. # |
92
|
|
|
|
|
|
|
########################################################### |
93
|
|
|
|
|
|
|
sub set_comment { |
94
|
15
|
|
|
15
|
1
|
2256
|
my ($this, $index, $string) = @_; |
95
|
|
|
|
|
|
|
# return immediately if $index is negative or undefined |
96
|
15
|
50
|
|
|
|
40
|
return $this->warn('Undefined $index') unless defined $index; |
97
|
15
|
100
|
|
|
|
47
|
return $this->warn("Negative index ($index)") if $index < 0; |
98
|
|
|
|
|
|
|
# get the list of comment segment indexes |
99
|
14
|
|
|
|
|
44
|
my @indexes = $this->get_segments('COM', 'INDEXES'); |
100
|
|
|
|
|
|
|
# if $index is out of bound, warn and return. |
101
|
14
|
100
|
|
|
|
56
|
return $this->warn("Index $index out of bound [0,$#indexes]") |
102
|
|
|
|
|
|
|
if ($#indexes < $index); |
103
|
|
|
|
|
|
|
# otherwise, set an index to the target comment segment |
104
|
13
|
|
|
|
|
26
|
my $position = $indexes[$index]; |
105
|
|
|
|
|
|
|
# create one or more comment blocks, based on the user |
106
|
|
|
|
|
|
|
# string; the string must be split if it is too long. |
107
|
10
|
|
|
|
|
48
|
my @new_comments = |
108
|
13
|
|
|
|
|
33
|
map { new Image::MetaData::JPEG::Segment('COM', \ $_) } |
109
|
|
|
|
|
|
|
split_comment_string($string); |
110
|
|
|
|
|
|
|
# replace the target segment with the new segments created |
111
|
|
|
|
|
|
|
# from the user string; @new_comments is the void list if |
112
|
|
|
|
|
|
|
# $string is undefined (this stands for comment deletion). |
113
|
|
|
|
|
|
|
# Since all comments are deleted or added, but not modified, |
114
|
|
|
|
|
|
|
# there is no need to call update here! |
115
|
13
|
|
|
|
|
56
|
$this->insert_segments(\ @new_comments, $position, 1); |
116
|
|
|
|
|
|
|
} |
117
|
|
|
|
|
|
|
|
118
|
|
|
|
|
|
|
########################################################### |
119
|
|
|
|
|
|
|
# This method eliminates the $index-th comment segment # |
120
|
|
|
|
|
|
|
# (first index is 0). It is only a shortcut for the more # |
121
|
|
|
|
|
|
|
# general set_comment (called with $string = undef). # |
122
|
|
|
|
|
|
|
########################################################### |
123
|
|
|
|
|
|
|
sub remove_comment { |
124
|
9
|
|
|
9
|
1
|
20
|
my ($this, $index) = @_; |
125
|
|
|
|
|
|
|
# call set_comment with an undefined string |
126
|
9
|
|
|
|
|
25
|
$this->set_comment($index, undef); |
127
|
|
|
|
|
|
|
} |
128
|
|
|
|
|
|
|
|
129
|
|
|
|
|
|
|
########################################################### |
130
|
|
|
|
|
|
|
# This method (a wrapper around the drop_segments method) # |
131
|
|
|
|
|
|
|
# eliminates all comments currently present in the pic. # |
132
|
|
|
|
|
|
|
########################################################### |
133
|
|
|
|
|
|
|
sub remove_all_comments { |
134
|
3
|
|
|
3
|
1
|
1806
|
my ($this) = @_; |
135
|
|
|
|
|
|
|
# use this more general method |
136
|
3
|
|
|
|
|
19
|
$this->drop_segments('^COM$'); |
137
|
|
|
|
|
|
|
} |
138
|
|
|
|
|
|
|
|
139
|
|
|
|
|
|
|
########################################################### |
140
|
|
|
|
|
|
|
# This method joins some comments into a single one, with # |
141
|
|
|
|
|
|
|
# the supplied separation string. This utility is neces- # |
142
|
|
|
|
|
|
|
# sary because there are readers out there which do not # |
143
|
|
|
|
|
|
|
# read past the first comment. This method overwrites the # |
144
|
|
|
|
|
|
|
# first comment selected by the arguments and delete the # |
145
|
|
|
|
|
|
|
# others. A warning is issued for each illegal comment # |
146
|
|
|
|
|
|
|
# index (undefined, not a number, out of range). # |
147
|
|
|
|
|
|
|
# The final comment length is checked (<64Kb). # |
148
|
|
|
|
|
|
|
# ------------------------------------------------------- # |
149
|
|
|
|
|
|
|
# If no separation string is provided, it defaults to \n. # |
150
|
|
|
|
|
|
|
# If no index is provided in @selection, it is assumed # |
151
|
|
|
|
|
|
|
# that the method must join all the comments into the # |
152
|
|
|
|
|
|
|
# first one, and delete the others. # |
153
|
|
|
|
|
|
|
########################################################### |
154
|
|
|
|
|
|
|
sub join_comments { |
155
|
6
|
|
|
6
|
1
|
6005
|
my ($this, $separation, @selection) = @_; |
156
|
|
|
|
|
|
|
# get all the comment indexes |
157
|
6
|
|
|
|
|
33
|
my @indexes = $this->get_segments('COM', 'INDEXES'); |
158
|
|
|
|
|
|
|
# get all the comment strings |
159
|
6
|
|
|
|
|
25
|
my @comments = $this->get_comments(); |
160
|
|
|
|
|
|
|
# an undefined separation string defaults to "\n" |
161
|
6
|
50
|
|
|
|
20
|
$separation = "\n" unless defined $separation; |
162
|
|
|
|
|
|
|
# an undefined @selection stands for "all the indexes" |
163
|
6
|
100
|
|
|
|
19
|
@selection = 0..$#indexes unless @selection; |
164
|
|
|
|
|
|
|
# discard the elements of @selection which do not make |
165
|
|
|
|
|
|
|
# sense, and leave the others in ascending numerical order |
166
|
11
|
|
|
|
|
102
|
@selection = sort {$a <=> $b} map { |
|
15
|
|
|
|
|
21
|
|
167
|
6
|
|
|
|
|
13
|
my $error = undef; |
168
|
15
|
100
|
66
|
|
|
101
|
if (! defined $_) { $error = "Undefined comment index"; } |
|
1
|
100
|
|
|
|
3
|
|
|
|
100
|
|
|
|
|
|
169
|
2
|
|
|
|
|
9
|
elsif ($_ =~ /[^\d]/) { $error = "'$_' not a whole number"; } |
170
|
1
|
|
|
|
|
6
|
elsif ($_<0 || $_>$#indexes) { $error = "index $_ out of range"; } |
171
|
15
|
100
|
|
|
|
55
|
$this->die("$error: discarding index") if defined $error; |
172
|
11
|
50
|
|
|
|
43
|
defined $error ? () : $_; |
173
|
|
|
|
|
|
|
} @selection; |
174
|
|
|
|
|
|
|
# return immediately if @selection is empty |
175
|
2
|
50
|
|
|
|
8
|
$this->die('No valid comment indexes') unless @selection; |
176
|
|
|
|
|
|
|
# concatenate valid comments in a single string (write a copy |
177
|
|
|
|
|
|
|
# of the separation string between every two comments). |
178
|
2
|
|
|
|
|
6
|
my $joint_comment = join $separation, map { $comments[$_] } @selection; |
|
8
|
|
|
|
|
486
|
|
179
|
|
|
|
|
|
|
# extract the first comment segment index in the selection list |
180
|
|
|
|
|
|
|
# as the target segment index. Then remove all other comments; |
181
|
|
|
|
|
|
|
# be careful to remove comments starting from higher indexes! |
182
|
2
|
|
|
|
|
9
|
my $target_index = shift @selection; |
183
|
2
|
|
|
|
|
8
|
$this->remove_comment($_) for (sort {$b <=> $a} @selection); |
|
5
|
|
|
|
|
18
|
|
184
|
|
|
|
|
|
|
# replace the target comment with $joint_comment |
185
|
2
|
|
|
|
|
9
|
$this->set_comment($target_index, $joint_comment); |
186
|
|
|
|
|
|
|
} |
187
|
|
|
|
|
|
|
|
188
|
|
|
|
|
|
|
# successful package load |
189
|
|
|
|
|
|
|
1; |