| 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 |  | 85 | no  integer; | 
|  | 15 |  |  |  |  | 33 |  | 
|  | 15 |  |  |  |  | 86 |  | 
| 8 | 15 |  |  | 15 |  | 394 | use strict; | 
|  | 15 |  |  |  |  | 36 |  | 
|  | 15 |  |  |  |  | 705 |  | 
| 9 | 15 |  |  | 15 |  | 74 | use warnings; | 
|  | 15 |  |  |  |  | 34 |  | 
|  | 15 |  |  |  |  | 7961 |  | 
| 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 | 6 | 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 |  |  |  |  | 17 | 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 |  |  |  |  | 9 | my $limit = index $this->data(0, $this->size()), "[user]"; | 
| 50 | 2 | 100 |  |  |  | 11 | $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 |  |  |  |  | 7 | 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 |  |  |  |  | 10 | $this->store_record('MakerInfo', $ASCII, \ $preamble, length $preamble); | 
| 59 |  |  |  |  |  |  | # each group will be written to a different subdirectory | 
| 60 | 2 |  |  |  |  | 4 | 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 |  |  |  | 173 | if (/^$groupname$/) { $dirref = $this->provide_subdirectory($1); } | 
|  | 3 |  |  |  |  | 13 |  | 
| 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 |  |  |  |  | 56 | else { my ($tag, $vals) = split /=/, $_; | 
| 70 | 22 |  |  |  |  | 75 | $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 |  |  |  |  | 8 | my $binary = $this->data($limit, $this->size() - $limit); | 
| 76 |  |  |  |  |  |  | # ... then this variable is slowly consumed | 
| 77 | 2 |  |  |  |  | 28 | 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 |  |  |  |  | 6 | $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 |  |  |  |  | 4 | 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 |  |  |  | 7 | $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 |  |  |  |  | 4 | $binary = substr($binary, length($tag) + length($val) + 2); | 
| 93 | 1 |  |  |  |  | 5 | $this->store_record($tag, $UNDEF, \$val, length $val); | 
| 94 |  |  |  |  |  |  | } | 
| 95 |  |  |  |  |  |  | } | 
| 96 |  |  |  |  |  |  |  | 
| 97 |  |  |  |  |  |  | # successful load | 
| 98 |  |  |  |  |  |  | 1; |