File Coverage

blib/lib/Image/MetaData/JPEG/parsers/app12.pl
Criterion Covered Total %
statement 31 31 100.0
branch 6 8 75.0
condition n/a
subroutine 4 4 100.0
pod 0 1 0.0
total 41 44 93.1


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             #use Image::MetaData::JPEG::data::Tables qw();
7 15     15   65 no integer;
  15         19  
  15         69  
8 15     15   374 use strict;
  15         21  
  15         397  
9 15     15   54 use warnings;
  15         17  
  15         5387  
10              
11             ###########################################################
12             # This method parses an APP12 segment; this segment was #
13             # used around 1998 by at least Olympus, Agfa and Epson #
14             # as a non standard replacement for EXIF. Information is #
15             # semi-readeable (mainly ascii text), but the format is #
16             # undocument (let me know if you have any documentation!) #
17             #=========================================================#
18             # From the few examples I was able to find, my interpre- #
19             # tation of the APP12 format is the following: #
20             #---------------------------------------------------------#
21             # 1 line identification (maker info?) #
22             #----- multiple times ------------------------------------#
23             # 1 line group (a string in square brackets) #
24             # multiple lines records (key-value separated by '=') #
25             #----- multiple times ------------------------------------#
26             # characters group (a string in square brackets) #
27             # characters unintelligible data #
28             #=========================================================#
29             # Well, this description looks a mess, I know. It means #
30             # that after the identification line, there is some plain #
31             # ascii information (divided in groups, each group starts #
32             # with a line like "[picture info]", each key-value pair #
33             # span one line) followed by groups containing binary #
34             # data (so that splitting on line ends does not work!). #
35             # Line terminations are marked by '\r\n' = 0x0d0a. #
36             #=========================================================#
37             # Ref: ... ??? #
38             ###########################################################
39             sub parse_app12 {
40 2     2 0 4 my ($this) = @_;
41             # compile once and for all the following regular expression,
42             # which captures a [groupname]; the name can contain alphanumeric
43             # characters, underscores and spaces (this is a guess ...)
44 2         7 my $groupname = qr/^\[([ \w]*)\]/;
45             # search the string "[user]" in the data area; it seems to
46             # separate the ascii data area from the binary data area.
47             # If the string is not there ($limit = -1), convert this value
48             # to the past-the-end character.
49 2         6 my $limit = index $this->data(0, $this->size()), "[user]";
50 2 100       7 $limit = $this->size() if $limit == -1;
51             # get all segment data up to the $limit and split in lines
52             # (each line is terminated by carriage-return + line-feed)
53 2         6 my @lines = split /\r\n/, $this->data(0, $limit);
54             # extract the first line out of @lines, because it must be
55             # treated differently. It seems that this line contains some
56             # null characters, but I don't want to split it further ...
57 2         6 my $preamble = shift @lines;
58 2         6 $this->store_record('MakerInfo', $ASCII, \ $preamble, length $preamble);
59             # each group will be written to a different subdirectory
60 2         2 my $dirref = undef;
61             # for each line in the ascii data area, except the first ...
62 2         5 for (@lines) {
63             # if the line is like "[groupname]", extract the group name
64             # from the square brackets and create a new subdirectory
65 25 100       120 if (/^$groupname$/) { $dirref = $this->provide_subdirectory($1); }
  3         8  
66             # otherwise, split the line on "="; on the left we find the
67             # tag name, on the right the ascii value(s). Store, in the
68             # appropriate subdirectory, a non-numeric record.
69 22         39 else { my ($tag, $vals) = split /=/, $_;
70 22         47 $this->store_record($dirref,$tag,$ASCII,\$vals,length $vals); }
71             }
72             # it's time to take care of the binary data area. We can't rely
73             # on line terminations here, so a different strategy is necessary.
74             # First, the remainig of the data area is copied in a variable ...
75 2         6 my $binary = $this->data($limit, $this->size() - $limit);
76             # ... then this variable is slowly consumed
77 2         8 while (0 != length $binary) {
78             # match the [groupname] string. It must be at the beginning
79             # of $$binary_ref, otherwise something is going wrong ...
80 1         5 $binary =~ /$groupname/;
81 1 50       6 $this->die('Error while decoding binary data') if $-[0] != 0;
82             # the subgroup matches the groupname (without the square
83             # brackets); assume the rest, up to the end, is the value
84 1         3 my $tag = $1;
85 1         3 my $val = substr $binary, $+[0];
86             # but if we find another [groupname],
87             # we change our mind on where the value ends
88 1 50       11 $val = substr($val, 0, $-[0]) if $val =~ /$groupname/;
89             # take out the group name and the value from binary, then
90             # save them in a non-numeric record as undefined bytes (add
91             # 2 to the length sum, this counts the two square brackets)
92 1         3 $binary = substr($binary, length($tag) + length($val) + 2);
93 1         4 $this->store_record($tag, $UNDEF, \$val, length $val);
94             }
95             }
96              
97             # successful load
98             1;