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   3823 use Image::MetaData::JPEG::Backtrace;
  16         19  
  16         454  
8             use Image::MetaData::JPEG::data::Tables
9 16     16   62 qw(:Endianness :RecordTypes :RecordProps :Lookups);
  16         20  
  16         3401  
10 16     16   82 no integer;
  16         17  
  16         91  
11 16     16   282 use strict;
  16         19  
  16         352  
12 16     16   54 use warnings;
  16         25  
  16         33527  
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 510 sub warn { my ($this, $message) = @_;
22 2 100       8 warn Image::MetaData::JPEG::Backtrace::backtrace
23             ($message, "Warning" . $this->info(), $this)
24             if $Image::MetaData::JPEG::show_warnings; }
25 30     30 0 45 sub die { my ($this, $message) = @_;
26 30         69 die Image::MetaData::JPEG::Backtrace::backtrace
27             ($message,"Fatal error" . $this->info(), $this);}
28 31     31 0 40 sub info { my ($this) = @_;
29 31   100     203 my $key = (ref $this && $this->{key}) || '';
30 31   100     124 my $type = (ref $this && $this->{type}) || '';
31 31         158 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 42035     42035 0 76078 my ($pkg, $akey, $atype, $dataref, $count, $endian) = @_;
81             # die immediately if $dataref is not a reference
82 42035 100       61509 $pkg->die('Reference not found') unless ref $dataref;
83             # create a Record object with some fields filled
84 42032         151556 my $this = bless {
85             key => $akey,
86             type => $atype,
87             values => [],
88             extra => undef,
89             }, $pkg;
90             # use big endian as default endianness
91 42032 100       67294 $endian = $BIG_ENDIAN unless defined $endian;
92             # get the actual length of the $$dataref scalar
93 42032         42174 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 42032         61058 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 42031 100       72474 $expected = $count ? $count : $current if $expected == 0;
    100          
102             # Throw an error if the supplied memory area is incorrectly sized
103 42031 100       58368 $this->die("Incorrect size (expected $expected, found $current)")
104             if ($current != $expected);
105             # get a reference to the internal value list
106 42021         38061 my $tokens = $this->{values};
107             # read the type length (used only for integers and rationals)
108 42021         38355 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 42021         52709 my $cat = $this->get_category();
113 42021 50       172545 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 42019 100       67332 $this->die('Empty token list') if @$tokens == 0;
121             # return the blessed reference
122 42018         92299 return $this;
123             }
124              
125             ###########################################################
126             # Syntactic sugar for a type test. The two arguments are #
127             # $this and the numeric type. #
128             ###########################################################
129 33796     33796 0 72141 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 99448     99448 0 201691 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 32826     32826 0 73763 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 68666     68666 0 64838 my ($this, $type, $count) = @_;
163             # if count is unspecified, set it to 1
164 68666 100       94205 $count = 1 unless defined $count;
165             # die if the type is unknown or undefined
166 68666 100       86333 $this->die('Undefined record type') unless defined $type;
167 68665 100 66     212559 $this->die("Unknown record type ($type)")
168             if $type < 0 || $type > $#JPEG_RECORD_TYPE_LENGTH;
169             # return the type length times $count
170 68656         106698 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 1945 my ($pkg, $key, $type, $count, $tokens) = @_;
184             # create a dummy Record, the "fix" its type and its value list
185 1439         2942 my $record = new Image::MetaData::JPEG::Record($key, $ASCII, \ "");
186 1439         3227 @$record{'type', 'values'} = ($type, $tokens);
187             # try to get back the record properties; return undef if it fails
188 1439         2083 (undef, undef, my $new_count, my $dataref) = eval { $record->get() };
  1439         2387  
189 1439 50       3546 return undef unless defined $dataref;
190             # if $count was previously undefined, listen to the Record encoder
191 1439 100       2822 $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       2763 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         2588 $record = new Image::MetaData::JPEG::Record($key, $type, $dataref, $count);
199             # return undef if the number of values does not match
200 1431         3017 my $new_tokens = $record->{values};
201 1431 50       2638 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         2603 for (0..$#$tokens) {
206 12341 100       12368 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         4907 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 29094     29094 0 45764 my ($this, $index) = @_;
227             # get a reference to the value list
228 29094         26909 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 29094 100 100     105377 goto VALUE_INDEX if defined $index || @$values == 1;
232 95 50       149 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 28999 100       40326 VALUE_INDEX:
239             # $index defaults to zero
240             $index = 0 unless defined $index;
241             # get the last legal index
242 28999         24910 my $last_index = $#$values;
243             # check that $index is legal, throw an exception otherwise
244 28999 100       40342 $this->die("Out-of-bound index ($index > $last_index)")
245             if $index > $last_index;
246             # return the desired value
247 28997         63142 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 215 my ($this, $new_value, $index) = @_;
258             # get a reference to the value list
259 148         199 my $values = $this->{values};
260             # set the first value if index is defined
261 148 50       350 $index = 0 unless defined $index;
262             # check out-of-bound condition
263 148         192 my $last_index = $#$values;
264 148 50       344 $this->die("Out-of-bound index ($index > $last_index)")
265             if $index > $last_index;
266             # set the value
267 148         422 $$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 821 sub to_signed { ($_[0] >= $msb[$_[1]]) ? ($_[0] - 2*$msb[$_[1]]) : $_[0] }
282 575 100   575 0 1485 sub to_unsigned { ($_[0] < 0) ? ($_[0] + 2*$msb[$_[1]]) : $_[0] }
283 47081     47081 0 34467 sub to_number { my $v=0; for (unpack "C*", $_[0]) { ($v<<=8) += $_; } $v }
  47081         62005  
  106371         101146  
  47081         71352  
284 324     324 0 438 sub to_nibbles { map { chr(vec($_[0], $_, 4)) } reverse (0..1) }
  648         1678  
285 4     4 0 5 sub to_byte { my $b="x"; vec($b,$_^1,4) = ord($_[$_]) for (0..1) ; $b }
  4         26  
  4         8  
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 21037     21037 0 25109 my ($this, $n, $dataref, $endian) = @_;
303             # safety check on endianness
304 21037 100       102552 $this->die('Unknown endianness') unless $endian =~ $ENDIANNESS_OK;
305             # prepare the list of raw tokens
306 21036         80152 my @tokens = unpack "a$n" x (length($$dataref)/$n), $$dataref;
307             # correct the tokens for endianness if necessary
308 21036 100       36070 @tokens = map { scalar reverse } @tokens if $endian eq $LITTLE_ENDIAN;
  2701         5630  
309             # rework the raw token list for nibbles.
310 21036 100       29977 @tokens = map { to_nibbles($_) } @tokens if $this->is($NIBBLES);
  324         533  
311             # convert to 1-byte digits and concatenate them (assuming big-endian)
312 21036         26449 @tokens = map { to_number($_) } @tokens;
  47081         50095  
313             # correction for signedness.
314 21036 100       29239 @tokens = map { to_signed($_, $n) } @tokens if $this->is_signed();
  308         560  
315             # return the token list
316 21036         37497 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 10954 my ($this, $n, $endian) = @_;
328             # safety check on endianness
329 9352 100       43356 $this->die('Unknown endianness') unless $endian =~ $ENDIANNESS_OK;
330             # copy the value list (the original should not be touched)
331 9351         7506 my @tokens = @{$this->{values}};
  9351         23206  
332             # correction for signedness
333 9351 100       12976 @tokens = map { to_unsigned($_, $n) } @tokens if $this->is_signed();
  575         970  
334             # convert the number into 1-byte digits (assuming big-endian)
335 9351         12355 @tokens = map { my $enc = ""; vec($enc, 0, 8*$n) = $_; $enc } @tokens;
  67631         46886  
  67631         76057  
  67631         92311  
336             # reconstruct the raw token list for nibbles.
337 9351 100       17950 @tokens = map { to_byte($tokens[2*$_], $tokens[2*$_+1]) } 0..(@tokens)/2-1
  4         12  
338             if $this->is($NIBBLES);
339             # correct the tokens for endianness if necessary
340 9351 100       15002 @tokens = map { scalar reverse } @tokens if $endian eq $LITTLE_ENDIAN;
  1868         2459  
341             # reconstruct a string from the list of raw tokens
342 9351         29240 my $data = pack "a$n" x (scalar @tokens), @tokens;
343             # return a reference to the reconstructed string
344 9351         15816 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 35 my ($this, $n, $dataref, $endian) = @_;
358             # safety check on endianness
359 26 100       158 $this->die('Unknown endianness') unless $endian =~ $ENDIANNESS_OK;
360             # prepare the list of raw tokens
361 25         136 my @tokens = unpack "a$n" x (length($$dataref)/$n), $$dataref;
362             # correct the tokens for endianness if necessary (to native endianness)
363 25 100       67 @tokens = map { scalar reverse } @tokens if $endian ne $NATIVE_ENDIANNESS;
  79         143  
364             # select the correct conversion format (single/double/extended)
365 25         56 my $format = ('f', 'd', 'D')[$n/4 - 1];
366             # loop over all tokens (numbers) and extract them
367 25         32 @tokens = map { unpack $format, $_ } @tokens;
  99         127  
368             # return the token list
369 25         58 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 34 my ($this, $n, $endian) = @_;
381             # safety check on endianness
382 31 100       175 $this->die('Unknown endianness') unless $endian =~ $ENDIANNESS_OK;
383             # get a simpler reference to the value list
384 30         28 my @tokens = @{$this->{values}};
  30         62  
385             # select the correct conversion format (single/double/extended)
386 30         74 my $format = ('f', 'd', 'D')[$n/4 - 1];
387             # loop over all tokens (floating point numbers)
388 30         43 @tokens = map { pack $format, $_ } @tokens;
  135         206  
389             # correct the tokens for endianness if necessary (from native endianness)
390 30 100       73 @tokens = map { scalar reverse } @tokens if $endian ne $NATIVE_ENDIANNESS;
  123         216  
391             # reconstruct a string from the list of raw tokens
392 30         63 my $data = join '', @tokens;
393             # return a reference to the reconstructed string
394 30         50 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 28147 my ($this, $endian) = @_;
408             # use big endian as default endianness
409 15362 100       22911 $endian = $BIG_ENDIAN unless defined $endian;
410             # get the record type and a reference to the internal value list
411 15362         14728 my $type = $this->{type};
412 15362         13110 my $tokens = $this->{values};
413 15362         17871 my $category = $this->get_category();
414             # read the type length (only used for integers and rationals)
415 15362         14436 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       52703 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       38586 my $count = length($$dataref) / ( $category =~ /S|p/ ? 1 : $tlength );
429             # return the result, depending on the context
430 15360 100       54408 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 818 my $maxlen = 40;
451             # running variables
452 970         1105 my ($left, $string) = (length $_[0], '');
453 970         1089 my ($delim, $dropnull) = @_[1,2];
454             # loop over all characters in the string
455 970         1397 for (0..(length($_[0])-1)) {
456             # get a copy of the current character
457 10646         9263 my $token = substr($_[0], $_, 1);
458             # translate it to a string if it is non-printing
459 10646         11753 $token =~ s/[\000-\037\177-\377]/sprintf "\\%02x",ord($&)/e;
  2939         5435  
460             # stop here if the overall string becomes too long
461 10646 100       15270 last if length($token) + length($string) > $maxlen;
462             # update running variables
463 10490         6591 --$left; $string .= $token; }
  10490         9634  
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         1186 $string = "${delim}$string${delim}";
468 970 100       3716 $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       3800 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 2321 my ($this, $names) = @_;
483             # some internal parameters
484 2439         1963 my $maxlen = 25; my $max_tokens = 7;
  2439         1816  
485             # try not to die every time if $names is undefined ...
486 2439 50       3643 $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         2970 my $descriptor = $this->{key};
490 2439         1871 my $numerictag = undef;
491             # however, if it is a number we need more work
492 2439 100       7192 if ($descriptor =~ /^\d*$/) {
493             # get the relevant hash for the description of this record
494 1622         3111 my $section_hash = JPEG_lookup(@$names);
495             # fix the numeric tag
496 1622         1420 $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       4529 $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         2808 my $tabbing = " \t" x (scalar @$names);
506             # prepare the description (don't make it exceed $maxlen characters).
507 2439 100       3892 $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         5167 my $description = sprintf "%s[%${maxlen}s]", $tabbing, $descriptor;
512             # show also the numeric tag for this record (if present)
513 2439 100       4412 $description .= defined $numerictag ?
514             sprintf "<0x%04x>", $numerictag : "<......>";
515             # show the tag type as a string
516 2439         4350 $description .= sprintf " = [%9s] ", $JPEG_RECORD_TYPE_NAME[$this->{type}];
517             # show the "extra" field if present
518 2439 100       3700 $description .= "<$this->{extra}>" if defined $this->{extra};
519             # take a reference to the list of objects to process
520 2439         2197 my $tokens = $this->{values};
521             # we want to write at most $max_tokens tokens in the value list
522 2439         2540 my $extra = $#$tokens - $max_tokens;
523 2439 100       3113 my $token_limit = $extra > 0 ? $max_tokens : $#$tokens;
524             # some auxiliary variables (depending only on the record type)
525 2439 100       3570 my $intfs = $this->is_signed() ? '%d' : '%u';
526 2439 100       3471 my $sep = $this->is($ASCII) ? '"' : "'" ;
527 2439     970   5984 my $text = sub { string_manipulator($_[0], $sep, $this->is($ASCII)) };
  970         1316  
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         2102 my $f = '/';
532 2439         4017 foreach (@$tokens[0..$token_limit]) {
533             # update the flip flop
534 3393 100       4184 $f = $f eq ' ' ? '/' : ' ';
535             # some auxiliary variables
536 3393         3999 my $category = $this->get_category();
537             # show something, depending on category and type
538 3393 50       10625 $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       4348 $description .= " ... ($extra more values)" if $extra > 0;
548 2439         1958 $description .= "\n";
549             # return the descriptive string
550 2439         8249 return $description;
551             }
552              
553             # successful package load
554             1;