File Coverage

blib/lib/Image/MetaData/JPEG/Record.pm
Criterion Covered Total %
statement 185 185 100.0
branch 124 134 92.5
condition 9 10 90.0
subroutine 29 29 100.0
pod 0 23 0.0
total 347 381 91.0


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::Record;
7 16     16   5912 use Image::MetaData::JPEG::Backtrace;
  16         32  
  16         628  
8             use Image::MetaData::JPEG::data::Tables
9 16     16   91 qw(:Endianness :RecordTypes :RecordProps :Lookups);
  16         33  
  16         5542  
10 16     16   103 no integer;
  16         33  
  16         160  
11 16     16   398 use strict;
  16         36  
  16         549  
12 16     16   83 use warnings;
  16         31  
  16         60953  
13              
14             ###########################################################
15             # These simple methods should be used instead of standard #
16             # "warn" and "die" in this package; they print a much #
17             # more elaborated error message (including a stack trace).#
18             # Warnings can be turned off altogether simply by setting #
19             # Image::MetaData::JPEG::show_warnings to false. #
20             ###########################################################
21 2     2 0 703 sub warn { my ($this, $message) = @_;
22 2 100       12 warn Image::MetaData::JPEG::Backtrace::backtrace
23             ($message, "Warning" . $this->info(), $this)
24             if $Image::MetaData::JPEG::show_warnings; }
25 29     29 0 61 sub die { my ($this, $message) = @_;
26 29         92 die Image::MetaData::JPEG::Backtrace::backtrace
27             ($message,"Fatal error" . $this->info(), $this);}
28 30     30 0 48 sub info { my ($this) = @_;
29 30   100     175 my $key = (ref $this && $this->{key}) || '';
30 30   100     164 my $type = (ref $this && $this->{type}) || '';
31 30         196 return " [key $key] [type $type]"; }
32              
33             ###########################################################
34             # A regular expression matching a legal endianness value. #
35             ###########################################################
36             my $ENDIANNESS_OK = qr/$BIG_ENDIAN|$LITTLE_ENDIAN/o;
37              
38             ###########################################################
39             # Constructor for a generic key - values pair for storing #
40             # properties to be found in JPEG segments. The key is #
41             # either a numeric value (whose exact meaning depends on #
42             # the segment type, and can be found by means of lookup #
43             # tables), or a descriptive string. The values are to be #
44             # found in the scalar pointed to by the data reference, #
45             # and they come togheter with a value type; the meaning #
46             # of the value type is taken by the APP1 type table, but #
47             # this standard can be used also for the other segments #
48             # (but it is not stored in the file on disk, exception #
49             # made for some APP segments). The count must be given #
50             # for fixed-length types. The enddianness must be given #
51             # for numeric properties with more than 1 byte. #
52             #=========================================================#
53             # The "values" are a sequence, so this field is a list; #
54             # it stores $count elements for numeric records, and a #
55             # single scalar for non-numeric ones ("count", in this #
56             # case, corresponds to the size of $$dataref; if $count #
57             # is undefined, no length test is performed on $$dataref).#
58             #=========================================================#
59             # Types are as follows: #
60             # 0 NIBBLES two 4-bit unsigned integers (private) #
61             # 1 BYTE An 8-bit unsigned integer #
62             # 2 ASCII A variable length ASCII string #
63             # 3 SHORT A 16-bit unsigned integer #
64             # 4 LONG A 32-bit unsigned integer #
65             # 5 RATIONAL Two LONGs (numerator and denominator) #
66             # 6 SBYTE An 8-bit signed integer #
67             # 7 UNDEFINED A generic variable length string #
68             # 8 SSHORT A 16-bit signed integer #
69             # 9 SLONG A 32-bit signed integer (2's complem.) #
70             # 10 SRATIONAL Two SLONGs (numerator and denominator) #
71             # 11 FLOAT A 32-bit float (a single float) #
72             # 12 DOUBLE A 64-bit float (a double float) #
73             # 13 REFERENCE A Perl list reference (internal) #
74             #=========================================================#
75             # Added a new field, "extra", which can be used to store #
76             # additional information one does not know where to put. #
77             # (The need originated from APP13 record descriptions). #
78             ###########################################################
79             sub new {
80 41859     41859 0 135859 my ($pkg, $akey, $atype, $dataref, $count, $endian) = @_;
81             # die immediately if $dataref is not a reference
82 41859 100       94549 $pkg->die('Reference not found') unless ref $dataref;
83             # create a Record object with some fields filled
84 41856         276760 my $this = bless {
85             key => $akey,
86             type => $atype,
87             values => [],
88             extra => undef,
89             }, $pkg;
90             # use big endian as default endianness
91 41856 100       112015 $endian = $BIG_ENDIAN unless defined $endian;
92             # get the actual length of the $$dataref scalar
93 41856         70107 my $current = length($$dataref);
94             # estimate the right length of $data for numeric types
95             # (remember that some types can return "no expectation", i.e. 0).
96 41856         108744 my $expected = $pkg->get_size($atype, $count);
97             # for variable-length records (those with $expected == 0), the length
98             # test must be run against $count, so we update $expected here if
99             # necessary (if $count was not given a value at call time, $expected
100             # is set to $current and the length test will never fail).
101 41855 100       114952 $expected = $count ? $count : $current if $expected == 0;
    100          
102             # Throw an error if the supplied memory area is incorrectly sized
103 41855 100       95385 $this->die("Incorrect size (expected $expected, found $current)")
104             if ($current != $expected);
105             # get a reference to the internal value list
106 41846         78140 my $tokens = $this->{values};
107             # read the type length (used only for integers and rationals)
108 41846         78142 my $tlength = $JPEG_RECORD_TYPE_LENGTH[$this->{type}];
109             # References, strings and undefined data can be immediately saved
110             # (1 element). All integer types can be treated toghether, and
111             # rationals can be treated as integer (halving the type length).
112 41846         98025 my $cat = $this->get_category();
113 41846 50       245217 push @$tokens,
    100          
    100          
    100          
114             $cat =~ /S|p/ ? $$dataref :
115             $cat eq 'I' ? $this->decode_integers($tlength , $dataref, $endian) :
116             $cat eq 'R' ? $this->decode_integers($tlength/2, $dataref, $endian) :
117             $cat eq 'F' ? $this->decode_floating($tlength , $dataref, $endian) :
118             $this->die('Unknown category');
119             # die if the token list is empty
120 41844 100       101311 $this->die('Empty token list') if @$tokens == 0;
121             # return the blessed reference
122 41843         168946 return $this;
123             }
124              
125             ###########################################################
126             # Syntactic sugar for a type test. The two arguments are #
127             # $this and the numeric type. #
128             ###########################################################
129 33628     33628 0 113340 sub is { return $_[1] == $_[0]{type}; }
130              
131             ###########################################################
132             # This method returns a character describing the category #
133             # which the type of the current record belongs to. #
134             # There are currently only five categories: #
135             # references : 'p' -> Perl references (internal) #
136             # integer : 'I' -> NIBBLES, (S)BYTE, (S)SHORT,(S)LONG #
137             # string-like : 'S' -> ASCII, UNDEF #
138             # fractional : 'R' -> RATIONAL, SRATIONAL #
139             # float.-point: 'F' -> FLOAT, DOUBLE #
140             # The method is sufficiently clear to use $_[0] instead #
141             # of $this (is it a speedup ?) #
142             ###########################################################
143 99255     99255 0 367478 sub get_category { return $JPEG_RECORD_TYPE_CATEGORY[$_[0]{type}]; }
144              
145             ###########################################################
146             # This method returns true or false depending on the #
147             # record type being a signed integer or not (i.e. being #
148             # SBYTE, SSHORT, SLONG or SRATIONAL). The method is #
149             # sufficiently simple to use $_[0] instead of $this. #
150             ###########################################################
151 32658     32658 0 117984 sub is_signed { return $JPEG_RECORD_TYPE_SIGN[$_[0]{type}] eq 'Y'; }
152              
153             ###########################################################
154             # This method calculates a record memory footprint; it #
155             # needs the record type and the record count. This method #
156             # is class static (it can be called without an underlying #
157             # object), so it cannot use $this. $count defaults to 1. #
158             # Remember that a type length of zero means that size #
159             # should not be tested (this comes from TYPE_LENGHT = 0). #
160             ###########################################################
161             sub get_size {
162 68318     68318 0 108010 my ($this, $type, $count) = @_;
163             # if count is unspecified, set it to 1
164 68318 100       158014 $count = 1 unless defined $count;
165             # die if the type is unknown or undefined
166 68318 100       151636 $this->die('Undefined record type') unless defined $type;
167 68317 100 66     312983 $this->die("Unknown record type ($type)")
168             if $type < 0 || $type > $#JPEG_RECORD_TYPE_LENGTH;
169             # return the type length times $count
170 68308         190502 return $JPEG_RECORD_TYPE_LENGTH[$type] * $count;
171             }
172              
173             ###########################################################
174             # This class static method receives a number of Record #
175             # features (key, type and count) and a list of values, #
176             # and tries to build a Record with that type and count #
177             # containing those values. On success, it returns the #
178             # record reference, on failure it returns undef. #
179             # ------------------------------------------------------- #
180             # Floating point values are matched to six decimal digits #
181             ###########################################################
182             sub check_consistency {
183 1439     1439 0 3348 my ($pkg, $key, $type, $count, $tokens) = @_;
184             # create a dummy Record, the "fix" its type and its value list
185 1439         4572 my $record = new Image::MetaData::JPEG::Record($key, $ASCII, \ "");
186 1439         5262 @$record{'type', 'values'} = ($type, $tokens);
187             # try to get back the record properties; return undef if it fails
188 1439         2891 (undef, undef, my $new_count, my $dataref) = eval { $record->get() };
  1439         3347  
189 1439 50       4128 return undef unless defined $dataref;
190             # if $count was previously undefined, listen to the Record encoder
191 1439 100       2931 $count = $new_count unless defined $count;
192             # if counts are already different, there is no hope (this
193             # can happen if $count was faulty: we haven't used it sofar).
194 1439 100       3103 return undef if $count != $new_count;
195             # build the real record by re-parsing the data reference; in my
196             # opinion this should never fail, so I don't check the result.
197             # Does this provide more chances to find a bug?
198 1431         4270 $record = new Image::MetaData::JPEG::Record($key, $type, $dataref, $count);
199             # return undef if the number of values does not match
200 1431         5253 my $new_tokens = $record->{values};
201 1431 50       3545 return undef unless scalar @$tokens == scalar @$new_tokens;
202             # the new record can however have a value list different from
203             # what we hope, since some data types could wrap. So we now
204             # compare the value lists and return undef if they differ.
205 1431         4911 for (0..$#$tokens) {
206 12341 100       35366 return undef if ($record->get_category() eq 'F') ?
    100          
207             # due to the nature of floating point values, the comparison
208             # is limited to six decimal digits (the new token has a precision
209             # of 23 or 52 binary digits, while the old one is just a string)
210             sprintf("%.6g",$$new_tokens[$_]) ne sprintf("%.6g",$$tokens[$_]) :
211             # for all other types, compare the plain values
212             $$new_tokens[$_] ne $$tokens[$_]; }
213             # if you get here, everything is ok: return the record reference
214 1430         7270 return $record;
215             }
216              
217             ###########################################################
218             # This method returns a particular value in the value #
219             # list, its index being the only argument. If the index #
220             # is undefined (not supplied), the sum of all values is #
221             # returned. The index is checked for out-of-bound errors. #
222             #=========================================================#
223             # For string-like records, "sum" -> "concatenation". #
224             ###########################################################
225             sub get_value {
226 29052     29052 0 85284 my ($this, $index) = @_;
227             # get a reference to the value list
228 29052         44995 my $values = $this->{values};
229             # access a single value if an index is defined or
230             # there is only one value (follow to sum otherwise)
231 29052 100 100     151520 goto VALUE_INDEX if defined $index || @$values == 1;
232 81 50       201 VALUE_SUM:
233             return ($this->get_category() eq 'S') ?
234             # perform concatenation for string-like values
235             join "", @$values :
236             # perform addition for numeric values
237             eval (join "+", @$values);
238 28971 100       71306 VALUE_INDEX:
239             # $index defaults to zero
240             $index = 0 unless defined $index;
241             # get the last legal index
242 28971         53441 my $last_index = $#$values;
243             # check that $index is legal, throw an exception otherwise
244 28971 100       59201 $this->die("Out-of-bound index ($index > $last_index)")
245             if $index > $last_index;
246             # return the desired value
247 28969         107570 return $$values[$index];
248             }
249              
250             ###########################################################
251             # This method sets a particular value in the value list. #
252             # If the index is undefined (not supplied), the first #
253             # (0th) value is set. The index is check for out-of-bound #
254             # errors. This method is dangerous: call only internally. #
255             ###########################################################
256             sub set_value {
257 148     148 0 282 my ($this, $new_value, $index) = @_;
258             # get a reference to the value list
259 148         260 my $values = $this->{values};
260             # set the first value if index is defined
261 148 50       371 $index = 0 unless defined $index;
262             # check out-of-bound condition
263 148         247 my $last_index = $#$values;
264 148 50       383 $this->die("Out-of-bound index ($index > $last_index)")
265             if $index > $last_index;
266             # set the value
267 148         521 $$values[$index] = $new_value;
268             }
269              
270             ###########################################################
271             # These private functions take signed/unsigned integers #
272             # and return their unsigned/signed version; the type #
273             # length in bytes must also be specified. $_[0] is the #
274             # original value, $_[1] is the type length. $msb[$n] is #
275             # an unsigned integer with the 8*$n-th bit turned up. #
276             # There is also a function for converting binary data as #
277             # a string into a big-endian number (iteratively) and a #
278             # function for interchanging bytes with nibble pairs. #
279             ###########################################################
280             { my @msb = map { 2**(8*$_ - 1) } 0..20;
281 308 100   308 0 1313 sub to_signed { ($_[0] >= $msb[$_[1]]) ? ($_[0] - 2*$msb[$_[1]]) : $_[0] }
282 575 100   575 0 2426 sub to_unsigned { ($_[0] < 0) ? ($_[0] + 2*$msb[$_[1]]) : $_[0] }
283 46373     46373 0 53899 sub to_number { my $v=0; for (unpack "C*", $_[0]) { ($v<<=8) += $_; } $v }
  46373         106132  
  105655         200615  
  46373         150882  
284 272     272 0 514 sub to_nibbles { map { chr(vec($_[0], $_, 4)) } reverse (0..1) }
  544         2036  
285 4     4 0 8 sub to_byte { my $b="x"; vec($b,$_^1,4) = ord($_[$_]) for (0..1) ; $b }
  4         34  
  4         25  
286             }
287              
288             ###########################################################
289             # This method decodes a sequence of 8$n-bit integers, and #
290             # correctly takes into account signedness and endianness. #
291             # The data size must be validated in advance: in this #
292             # routine it must be a multiple of the type size ($n). #
293             #=========================================================#
294             # NIBBLES are treated apart. A "nibble record" is indeed #
295             # a pair of 4-bit values, so the type length is 1, but #
296             # each element must enter two values into @tokens. They #
297             # are always big-endian and unsigned. #
298             #=========================================================#
299             # Don't use shift operators, which are a bit too tricky.. #
300             ###########################################################
301             sub decode_integers {
302 20869     20869 0 42756 my ($this, $n, $dataref, $endian) = @_;
303             # safety check on endianness
304 20869 100       150584 $this->die('Unknown endianness') unless $endian =~ $ENDIANNESS_OK;
305             # prepare the list of raw tokens
306 20868         118299 my @tokens = unpack "a$n" x (length($$dataref)/$n), $$dataref;
307             # correct the tokens for endianness if necessary
308 20868 100       54300 @tokens = map { scalar reverse } @tokens if $endian eq $LITTLE_ENDIAN;
  2701         9736  
309             # rework the raw token list for nibbles.
310 20868 100       61260 @tokens = map { to_nibbles($_) } @tokens if $this->is($NIBBLES);
  272         613  
311             # convert to 1-byte digits and concatenate them (assuming big-endian)
312 20868         38584 @tokens = map { to_number($_) } @tokens;
  46373         96508  
313             # correction for signedness.
314 20868 100       47509 @tokens = map { to_signed($_, $n) } @tokens if $this->is_signed();
  308         973  
315             # return the token list
316 20868         63057 return @tokens;
317             }
318              
319             ###########################################################
320             # This method encodes the content of $this->{values} into #
321             # a sequence of 8$n-bit integers, correctly taking into #
322             # account signedness and endianness. The return value is #
323             # a reference to the encoded scalar, ready to be written #
324             # to disk. See decode_integers() for further details. #
325             ###########################################################
326             sub encode_integers {
327 9352     9352 0 17820 my ($this, $n, $endian) = @_;
328             # safety check on endianness
329 9352 100       68050 $this->die('Unknown endianness') unless $endian =~ $ENDIANNESS_OK;
330             # copy the value list (the original should not be touched)
331 9351         10672 my @tokens = @{$this->{values}};
  9351         44528  
332             # correction for signedness
333 9351 100       21280 @tokens = map { to_unsigned($_, $n) } @tokens if $this->is_signed();
  575         1485  
334             # convert the number into 1-byte digits (assuming big-endian)
335 9351         16500 @tokens = map { my $enc = ""; vec($enc, 0, 8*$n) = $_; $enc } @tokens;
  67631         84839  
  67631         151398  
  67631         177792  
336             # reconstruct the raw token list for nibbles.
337 9351 100       28264 @tokens = map { to_byte($tokens[2*$_], $tokens[2*$_+1]) } 0..(@tokens)/2-1
  4         17  
338             if $this->is($NIBBLES);
339             # correct the tokens for endianness if necessary
340 9351 100       26679 @tokens = map { scalar reverse } @tokens if $endian eq $LITTLE_ENDIAN;
  1868         5806  
341             # reconstruct a string from the list of raw tokens
342 9351         41265 my $data = pack "a$n" x (scalar @tokens), @tokens;
343             # return a reference to the reconstructed string
344 9351         41324 return \ $data;
345             }
346              
347             ###########################################################
348             # This method decodes a data area containing a sequence #
349             # of floating point values, correctly taking into account #
350             # the endianness. The type size $n can therefore be only #
351             # 4, 8 or 12 (but you will not be able to store extended #
352             # precision numbers unless your system provides support #
353             # for them [a Cray?]). The data size must be validated in #
354             # advance: here it must be a multiple of the type size. #
355             ###########################################################
356             sub decode_floating {
357 26     26 0 51 my ($this, $n, $dataref, $endian) = @_;
358             # safety check on endianness
359 26 100       181 $this->die('Unknown endianness') unless $endian =~ $ENDIANNESS_OK;
360             # prepare the list of raw tokens
361 25         195 my @tokens = unpack "a$n" x (length($$dataref)/$n), $$dataref;
362             # correct the tokens for endianness if necessary (to native endianness)
363 25 100       79 @tokens = map { scalar reverse } @tokens if $endian ne $NATIVE_ENDIANNESS;
  79         238  
364             # select the correct conversion format (single/double/extended)
365 25         64 my $format = ('f', 'd', 'D')[$n/4 - 1];
366             # loop over all tokens (numbers) and extract them
367 25         74 @tokens = map { unpack $format, $_ } @tokens;
  99         203  
368             # return the token list
369 25         95 return @tokens;
370             }
371              
372             ###########################################################
373             # This method encodes the content of $this->{values} into #
374             # a sequence of floating point numbers, correctly taking #
375             # into account the endianness. The returned value is a #
376             # reference to the encoded scalar, ready to be written to #
377             # disk. See decode_floating() for further details. #
378             ###########################################################
379             sub encode_floating {
380 31     31 0 60 my ($this, $n, $endian) = @_;
381             # safety check on endianness
382 31 100       325 $this->die('Unknown endianness') unless $endian =~ $ENDIANNESS_OK;
383             # get a simpler reference to the value list
384 30         41 my @tokens = @{$this->{values}};
  30         95  
385             # select the correct conversion format (single/double/extended)
386 30         170 my $format = ('f', 'd', 'D')[$n/4 - 1];
387             # loop over all tokens (floating point numbers)
388 30         69 @tokens = map { pack $format, $_ } @tokens;
  135         550  
389             # correct the tokens for endianness if necessary (from native endianness)
390 30 100       115 @tokens = map { scalar reverse } @tokens if $endian ne $NATIVE_ENDIANNESS;
  123         473  
391             # reconstruct a string from the list of raw tokens
392 30         90 my $data = join '', @tokens;
393             # return a reference to the reconstructed string
394 30         86 return \ $data;
395             }
396              
397             ###########################################################
398             # This method returns the content of the record: in list #
399             # context it returns (key, type, count, data_reference). #
400             # The reference points to a packed scalar, ready to be #
401             # written to disk. In scalar context, it returns "data", #
402             # i.e. the dereferentiated data_reference. This is tricky #
403             # (but handy for other routines). The endianness argument #
404             # defaults to $BIG_ENDIAN. See ctor for further details. #
405             ###########################################################
406             sub get {
407 15362     15362 0 41312 my ($this, $endian) = @_;
408             # use big endian as default endianness
409 15362 100       42240 $endian = $BIG_ENDIAN unless defined $endian;
410             # get the record type and a reference to the internal value list
411 15362         25519 my $type = $this->{type};
412 15362         21960 my $tokens = $this->{values};
413 15362         29178 my $category = $this->get_category();
414             # read the type length (only used for integers and rationals)
415 15362         24428 my $tlength = $JPEG_RECORD_TYPE_LENGTH[$type];
416             # References, strings and undefined data contain a single value
417             # (to be taken a reference at). All integer types can be treated
418             # toghether, and rationals can be treated as integer (halving the
419             # type length). Floating points still to be coded.
420 15362 50       109793 my $dataref =
    100          
    100          
    100          
421             $category =~ /S|p/ ? \ $$tokens[0] :
422             $category eq 'I' ? $this->encode_integers($tlength , $endian) :
423             $category eq 'R' ? $this->encode_integers($tlength/2, $endian) :
424             $category eq 'F' ? $this->encode_floating($tlength , $endian) :
425             $this->die('Unknown category');
426             # calculate the "count" (the number of elements for numeric types
427             # and the length of $$dataref for references, strings, undefined)
428 15360 100       66643 my $count = length($$dataref) / ( $category =~ /S|p/ ? 1 : $tlength );
429             # return the result, depending on the context
430 15360 100       95311 wantarray ? ($this->{key}, $type, $count, $dataref) : $$dataref;
431             }
432              
433             ###########################################################
434             # This routine reworks $ASCII and $UNDEF record values #
435             # before displaying them. In particular, unreasonably #
436             # long strings are trimmed and non-printing characters #
437             # are replaced with their hexadecimal representation. #
438             # Strings are then enclosed between delimiters, and null- #
439             # terminated ones can have their last character chopped #
440             # off (but a dot is added after the closing delimiter). #
441             # Remember to copy the string to avoid side-effects! #
442             # ------------------------------------------------------- #
443             # $_[0] --> this contains the string to be modified. #
444             # $_[1] --> this contains the string delimiter (" or ') #
445             # $_[2] --> true if the last null char is to be replaced #
446             ###########################################################
447             sub string_manipulator {
448             # max length of the part of the string we want to display
449             # (after conversion of non-printing chars to hex repr.)
450 970     970 0 1089 my $maxlen = 40;
451             # running variables
452 970         1538 my ($left, $string) = (length $_[0], '');
453 970         1685 my ($delim, $dropnull) = @_[1,2];
454             # loop over all characters in the string
455 970         1913 for (0..(length($_[0])-1)) {
456             # get a copy of the current character
457 10646         13561 my $token = substr($_[0], $_, 1);
458             # translate it to a string if it is non-printing
459 10646         23042 $token =~ s/[\000-\037\177-\377]/sprintf "\\%02x",ord($&)/e;
  2939         8701  
460             # stop here if the overall string becomes too long
461 10646 100       21513 last if length($token) + length($string) > $maxlen;
462             # update running variables
463 10490         9825 --$left; $string .= $token; }
  10490         15083  
464             # transform the terminating null character into a dot if the
465             # string does not start with a slash, then put delimiters
466             # around the string (the dot remains outside, however).
467 970         1684 $string = "${delim}$string${delim}";
468 970 100       4841 $string =~ s/^(.*)\\00${delim}$/$1${delim}\./ if $dropnull;
469             # print the reworked string (if the string was shortened,
470             # add a notice to the end and use a fixed length field)
471 970 100       6348 sprintf($left ? '%-'.(3+$maxlen)."s($left more chars)" : '%-s', $string);
472             }
473              
474             ###########################################################
475             # This method returns a string describing the content of #
476             # the record. The argument is a reference to an array of #
477             # names, which are to be used as successive keys in a #
478             # general hash keeping translations of numeric tags. #
479             # No argument is needed if the key is already non-numeric.#
480             ###########################################################
481             sub get_description {
482 2439     2439 0 3330 my ($this, $names) = @_;
483             # some internal parameters
484 2439         3048 my $maxlen = 25; my $max_tokens = 7;
  2439         2548  
485             # try not to die every time if $names is undefined ...
486 2439 50       25863 $names = [] unless defined $names;
487             # assume that the key is a string (so, it is its own
488             # description, and no numeric value is to be shown)
489 2439         5284 my $descriptor = $this->{key};
490 2439         2740 my $numerictag = undef;
491             # however, if it is a number we need more work
492 2439 100       8732 if ($descriptor =~ /^\d*$/) {
493             # get the relevant hash for the description of this record
494 1622         4843 my $section_hash = JPEG_lookup(@$names);
495             # fix the numeric tag
496 1622         2731 $numerictag = $descriptor;
497             # extract a description string; if there is no entry in the
498             # hash for this key, replace the descriptor with a sort of
499             # error message (non-existent tags differ from undefined ones)
500 1622 50       6624 $descriptor =
    100          
501             ! exists $$section_hash{$descriptor} ? "?? Unknown record ??" :
502             ! defined $$section_hash{$descriptor} ? "?? Nameless record ??" :
503             $$section_hash{$descriptor} }
504             # calculate an appropriate tabbing
505 2439         5410 my $tabbing = " \t" x (scalar @$names);
506             # prepare the description (don't make it exceed $maxlen characters).
507 2439 100       4999 $descriptor = substr($descriptor, 0, $maxlen/2)
508             . "..." . substr($descriptor, - $maxlen/2 + 3)
509             if length($descriptor) > $maxlen;
510             # initialise the string to be returned at the end
511 2439         7175 my $description = sprintf "%s[%${maxlen}s]", $tabbing, $descriptor;
512             # show also the numeric tag for this record (if present)
513 2439 100       9763 $description .= defined $numerictag ?
514             sprintf "<0x%04x>", $numerictag : "<......>";
515             # show the tag type as a string
516 2439         6681 $description .= sprintf " = [%9s] ", $JPEG_RECORD_TYPE_NAME[$this->{type}];
517             # show the "extra" field if present
518 2439 100       5411 $description .= "<$this->{extra}>" if defined $this->{extra};
519             # take a reference to the list of objects to process
520 2439         3481 my $tokens = $this->{values};
521             # we want to write at most $max_tokens tokens in the value list
522 2439         3838 my $extra = $#$tokens - $max_tokens;
523 2439 100       15423 my $token_limit = $extra > 0 ? $max_tokens : $#$tokens;
524             # some auxiliary variables (depending only on the record type)
525 2439 100       5055 my $intfs = $this->is_signed() ? '%d' : '%u';
526 2439 100       5266 my $sep = $this->is($ASCII) ? '"' : "'" ;
527 2439     970   8385 my $text = sub { string_manipulator($_[0], $sep, $this->is($ASCII)) };
  970         2152  
528             # integers, strings and floating points are written in sequence;
529             # rationals must be written in pairs (use a flip-flop);
530             # undefined values are written on a byte per byte basis.
531 2439         3836 my $f = '/';
532 2439         5482 foreach (@$tokens[0..$token_limit]) {
533             # update the flip flop
534 3393 100       6298 $f = $f eq ' ' ? '/' : ' ';
535             # some auxiliary variables
536 3393         6337 my $category = $this->get_category();
537             # show something, depending on category and type
538 3393 50       16643 $description .=
    100          
    100          
    100          
    100          
539             $category eq 'p' ? sprintf ' --> 0x%06x', $_ :
540             $category eq 'S' ? sprintf '%s' , &$text($_) :
541             $category eq 'I' ? sprintf ' '.$intfs , $_ :
542             $category eq 'F' ? sprintf ' %g' , $_ :
543             $category eq 'R' ? sprintf '%s'.$intfs , $f, $_ :
544             $this->die('Unknown error condition'); }
545             # terminate the line; remember to put a warning note if there were
546             # more than $max_tokens element to display, then return the description
547 2439 100       5472 $description .= " ... ($extra more values)" if $extra > 0;
548 2439         2800 $description .= "\n";
549             # return the descriptive string
550 2439         13076 return $description;
551             }
552              
553             # successful package load
554             1;