exiftool_vendored 12.62.0 → 12.64.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (52) hide show
  1. checksums.yaml +4 -4
  2. data/bin/Changes +50 -1
  3. data/bin/MANIFEST +4 -0
  4. data/bin/META.json +4 -1
  5. data/bin/META.yml +4 -1
  6. data/bin/Makefile.PL +7 -1
  7. data/bin/README +50 -46
  8. data/bin/config_files/guano.config +161 -0
  9. data/bin/exiftool +88 -62
  10. data/bin/lib/Image/ExifTool/7Z.pm +793 -0
  11. data/bin/lib/Image/ExifTool/Apple.pm +6 -3
  12. data/bin/lib/Image/ExifTool/Canon.pm +1 -0
  13. data/bin/lib/Image/ExifTool/CanonRaw.pm +4 -4
  14. data/bin/lib/Image/ExifTool/CanonVRD.pm +4 -1
  15. data/bin/lib/Image/ExifTool/Exif.pm +31 -14
  16. data/bin/lib/Image/ExifTool/FujiFilm.pm +3 -3
  17. data/bin/lib/Image/ExifTool/GPS.pm +5 -2
  18. data/bin/lib/Image/ExifTool/Geotag.pm +4 -1
  19. data/bin/lib/Image/ExifTool/Jpeg2000.pm +226 -28
  20. data/bin/lib/Image/ExifTool/Lang/fr.pm +1467 -202
  21. data/bin/lib/Image/ExifTool/MPF.pm +2 -1
  22. data/bin/lib/Image/ExifTool/Matroska.pm +16 -1
  23. data/bin/lib/Image/ExifTool/MinoltaRaw.pm +2 -2
  24. data/bin/lib/Image/ExifTool/Nikon.pm +419 -5
  25. data/bin/lib/Image/ExifTool/NikonCustom.pm +13 -3
  26. data/bin/lib/Image/ExifTool/PDF.pm +9 -1
  27. data/bin/lib/Image/ExifTool/PLIST.pm +8 -1
  28. data/bin/lib/Image/ExifTool/PNG.pm +6 -6
  29. data/bin/lib/Image/ExifTool/PhaseOne.pm +5 -5
  30. data/bin/lib/Image/ExifTool/QuickTime.pm +74 -21
  31. data/bin/lib/Image/ExifTool/QuickTimeStream.pl +20 -19
  32. data/bin/lib/Image/ExifTool/README +2 -2
  33. data/bin/lib/Image/ExifTool/RIFF.pm +11 -9
  34. data/bin/lib/Image/ExifTool/Shortcuts.pm +2 -1
  35. data/bin/lib/Image/ExifTool/SigmaRaw.pm +4 -4
  36. data/bin/lib/Image/ExifTool/Sony.pm +103 -8
  37. data/bin/lib/Image/ExifTool/TagLookup.pm +4738 -4630
  38. data/bin/lib/Image/ExifTool/TagNames.pod +249 -5
  39. data/bin/lib/Image/ExifTool/Validate.pm +17 -1
  40. data/bin/lib/Image/ExifTool/WriteExif.pl +9 -7
  41. data/bin/lib/Image/ExifTool/WriteQuickTime.pl +21 -9
  42. data/bin/lib/Image/ExifTool/WriteXMP.pl +2 -2
  43. data/bin/lib/Image/ExifTool/Writer.pl +28 -10
  44. data/bin/lib/Image/ExifTool/XMP.pm +14 -2
  45. data/bin/lib/Image/ExifTool/XMP2.pl +32 -0
  46. data/bin/lib/Image/ExifTool/XMPStruct.pl +96 -28
  47. data/bin/lib/Image/ExifTool/ZIP.pm +5 -5
  48. data/bin/lib/Image/ExifTool.pm +67 -39
  49. data/bin/lib/Image/ExifTool.pod +83 -52
  50. data/bin/perl-Image-ExifTool.spec +44 -44
  51. data/lib/exiftool_vendored/version.rb +1 -1
  52. metadata +4 -2
@@ -139,7 +139,7 @@ my @delGroups = qw(
139
139
  Adobe AFCP APP0 APP1 APP2 APP3 APP4 APP5 APP6 APP7 APP8 APP9 APP10 APP11
140
140
  APP12 APP13 APP14 APP15 CanonVRD CIFF Ducky EXIF ExifIFD File FlashPix
141
141
  FotoStation GlobParamIFD GPS ICC_Profile IFD0 IFD1 Insta360 InteropIFD IPTC
142
- ItemList JFIF Jpeg2000 Keys MakerNotes Meta MetaIFD Microsoft MIE MPF
142
+ ItemList JFIF Jpeg2000 JUMBF Keys MakerNotes Meta MetaIFD Microsoft MIE MPF
143
143
  NikonApp NikonCapture PDF PDF-update PhotoMechanic Photoshop PNG PNG-pHYs
144
144
  PrintIM QuickTime RMETA RSRC SubIFD Trailer UserData XML XML-* XMP XMP-*
145
145
  );
@@ -1028,7 +1028,7 @@ TAG: foreach $tagInfo (@matchingTags) {
1028
1028
  foreach (@vals) {
1029
1029
  if (ref $_ eq 'HASH') {
1030
1030
  require 'Image/ExifTool/XMPStruct.pl';
1031
- $_ = Image::ExifTool::XMP::SerializeStruct($_);
1031
+ $_ = Image::ExifTool::XMP::SerializeStruct($self, $_);
1032
1032
  }
1033
1033
  print $out "$$self{INDENT2}$verb $wgrp1:$tag$fromList if value is '${_}'\n";
1034
1034
  }
@@ -1293,6 +1293,7 @@ sub SetNewValuesFromFile($$;@)
1293
1293
  HexTagIDs => $$options{HexTagIDs},
1294
1294
  IgnoreMinorErrors=>$$options{IgnoreMinorErrors},
1295
1295
  IgnoreTags => $$options{IgnoreTags},
1296
+ ImageHashType => $$options{ImageHashType},
1296
1297
  Lang => $$options{Lang},
1297
1298
  LargeFileSupport=> $$options{LargeFileSupport},
1298
1299
  List => 1,
@@ -1313,6 +1314,7 @@ sub SetNewValuesFromFile($$;@)
1313
1314
  ScanForXMP => $$options{ScanForXMP},
1314
1315
  StrictDate => defined $$options{StrictDate} ? $$options{StrictDate} : 1,
1315
1316
  Struct => $structOpt,
1317
+ StructFormat => $$options{StructFormat},
1316
1318
  SystemTags => $$options{SystemTags},
1317
1319
  TimeZone => $$options{TimeZone},
1318
1320
  Unknown => $$options{Unknown},
@@ -1763,7 +1765,14 @@ GNV_TagInfo: foreach $tagInfo (@tagInfoList) {
1763
1765
  }
1764
1766
  }
1765
1767
  # return our value(s)
1766
- return @$vals if wantarray;
1768
+ if (wantarray) {
1769
+ # remove duplicates if requested
1770
+ if (@$vals > 1 and $self->Options('NoDups')) {
1771
+ my %seen;
1772
+ @$vals = grep { !$seen{$_}++ } @$vals;
1773
+ }
1774
+ return @$vals;
1775
+ }
1767
1776
  return $$vals[0];
1768
1777
  }
1769
1778
 
@@ -3283,7 +3292,7 @@ sub InsertTagValues($$$;$$$)
3283
3292
  }
3284
3293
  } elsif (ref $val eq 'HASH') {
3285
3294
  require 'Image/ExifTool/XMPStruct.pl';
3286
- $val = Image::ExifTool::XMP::SerializeStruct($val);
3295
+ $val = Image::ExifTool::XMP::SerializeStruct($self, $val);
3287
3296
  } elsif (not defined $val) {
3288
3297
  $val = $$self{OPTIONS}{MissingTagValue} if $asList;
3289
3298
  }
@@ -3767,6 +3776,8 @@ sub GetNewValueHash($$;$$$$)
3767
3776
  # this is a bit tricky: we want to add to a protected nvHash only if we
3768
3777
  # are adding a conditional delete ($_[5] true or DelValue with no Shift)
3769
3778
  # or accumulating List items (NoReplace true)
3779
+ # (NOTE: this should be looked into --> lists may be accumulated instead of being replaced
3780
+ # as expected when copying to the same list from different dynamic -tagsFromFile source files)
3770
3781
  if ($protect and not ($opts{create} and ($$nvHash{NoReplace} or $_[5] or
3771
3782
  ($$nvHash{DelValue} and not defined $$nvHash{Shift}))))
3772
3783
  {
@@ -5598,6 +5609,8 @@ sub WriteJPEG($$)
5598
5609
  $s =~ /^(Meta|META|Exif)\0\0/ and $dirName = 'Meta';
5599
5610
  } elsif ($marker == 0xe5) {
5600
5611
  $s =~ /^RMETA\0/ and $dirName = 'RMETA';
5612
+ } elsif ($marker == 0xeb) {
5613
+ $s =~ /^JP/ and $dirName = 'JUMBF';
5601
5614
  } elsif ($marker == 0xec) {
5602
5615
  $s =~ /^Ducky/ and $dirName = 'Ducky';
5603
5616
  } elsif ($marker == 0xed) {
@@ -6463,6 +6476,11 @@ sub WriteJPEG($$)
6463
6476
  $segType = 'Ricoh RMETA';
6464
6477
  $$delGroup{RMETA} and $del = 1;
6465
6478
  }
6479
+ } elsif ($marker == 0xeb) { # APP10 (JUMBF)
6480
+ if ($$segDataPt =~ /^JP/) {
6481
+ $segType = 'JUMBF';
6482
+ $$delGroup{JUMBF} and $del = 1;
6483
+ }
6466
6484
  } elsif ($marker == 0xec) { # APP12 (Ducky)
6467
6485
  if ($$segDataPt =~ /^Ducky/) {
6468
6486
  $segType = 'Ducky';
@@ -6880,14 +6898,14 @@ sub SetFileTime($$;$$$$)
6880
6898
  }
6881
6899
 
6882
6900
  #------------------------------------------------------------------------------
6883
- # Add data to MD5 checksum
6901
+ # Add data to hash checksum
6884
6902
  # Inputs: 0) ExifTool ref, 1) RAF ref, 2) data size (or undef to read to end of file),
6885
6903
  # 3) data name (or undef for no warnings or messages), 4) flag for no verbose message
6886
- # Returns: number of bytes read and MD5'd
6887
- sub ImageDataMD5($$$;$$)
6904
+ # Returns: number of bytes read and hashed
6905
+ sub ImageDataHash($$$;$$)
6888
6906
  {
6889
6907
  my ($self, $raf, $size, $type, $noMsg) = @_;
6890
- my $md5 = $$self{ImageDataMD5} or return;
6908
+ my $hash = $$self{ImageDataHash} or return;
6891
6909
  my ($bytesRead, $n) = (0, 65536);
6892
6910
  my $buff;
6893
6911
  for (;;) {
@@ -6900,11 +6918,11 @@ sub ImageDataMD5($$$;$$)
6900
6918
  $self->Warn("Error reading $type data") if $type and defined $size;
6901
6919
  last;
6902
6920
  }
6903
- $md5->add($buff);
6921
+ $hash->add($buff);
6904
6922
  $bytesRead += length $buff;
6905
6923
  }
6906
6924
  if ($$self{OPTIONS}{Verbose} and $bytesRead and $type and not $noMsg) {
6907
- $self->VPrint(0, "$$self{INDENT}(ImageDataMD5: $bytesRead bytes of $type data)\n");
6925
+ $self->VPrint(0, "$$self{INDENT}(ImageDataHash: $bytesRead bytes of $type data)\n");
6908
6926
  }
6909
6927
  return $bytesRead;
6910
6928
  }
@@ -50,7 +50,7 @@ use Image::ExifTool::Exif;
50
50
  use Image::ExifTool::GPS;
51
51
  require Exporter;
52
52
 
53
- $VERSION = '3.59';
53
+ $VERSION = '3.60';
54
54
  @ISA = qw(Exporter);
55
55
  @EXPORT_OK = qw(EscapeXML UnescapeXML);
56
56
 
@@ -144,6 +144,7 @@ my %xmpNS = (
144
144
  xmpTPg => 'http://ns.adobe.com/xap/1.0/t/pg/',
145
145
  xmpidq => 'http://ns.adobe.com/xmp/Identifier/qual/1.0/',
146
146
  xmpPLUS => 'http://ns.adobe.com/xap/1.0/PLUS/',
147
+ panorama => 'http://ns.adobe.com/photoshop/1.0/panorama-profile',
147
148
  dex => 'http://ns.optimasc.com/dex/1.0/',
148
149
  mediapro => 'http://ns.iview-multimedia.com/mediapro/1.0/',
149
150
  expressionmedia => 'http://ns.microsoft.com/expressionmedia/1.0/',
@@ -199,6 +200,7 @@ my %xmpNS = (
199
200
  ast => 'http://ns.nikon.com/asteroid/1.0/',
200
201
  nine => 'http://ns.nikon.com/nine/1.0/',
201
202
  hdr_metadata => 'http://ns.adobe.com/hdr-metadata/1.0/',
203
+ hdrgm => 'http://ns.adobe.com/hdr-gain-map/1.0/',
202
204
  );
203
205
 
204
206
  # build reverse namespace lookup
@@ -709,6 +711,10 @@ my %sRangeMask = (
709
711
  Name => 'xmpPLUS',
710
712
  SubDirectory => { TagTable => 'Image::ExifTool::XMP::xmpPLUS' },
711
713
  },
714
+ panorama => {
715
+ Name => 'panorama',
716
+ SubDirectory => { TagTable => 'Image::ExifTool::XMP::panorama' },
717
+ },
712
718
  plus => {
713
719
  Name => 'plus',
714
720
  SubDirectory => { TagTable => 'Image::ExifTool::PLUS::XMP' },
@@ -909,6 +915,10 @@ my %sRangeMask = (
909
915
  Name => 'hdr',
910
916
  SubDirectory => { TagTable => 'Image::ExifTool::XMP::hdr' },
911
917
  },
918
+ hdrgm => {
919
+ Name => 'hdrgm',
920
+ SubDirectory => { TagTable => 'Image::ExifTool::XMP::hdrgm' },
921
+ },
912
922
  );
913
923
 
914
924
  # hack to allow XML containing Dublin Core metadata to be handled like XMP (eg. EPUB - see ZIP.pm)
@@ -2566,7 +2576,9 @@ my %sPantryItem = (
2566
2576
  %xmpTableDefaults,
2567
2577
  GROUPS => { 1 => 'XMP-et', 2 => 'Image' },
2568
2578
  NAMESPACE => 'et',
2569
- OriginalImageMD5 => { Notes => 'used to store ExifTool ImageDataMD5 digest' },
2579
+ OriginalImageHash => { Notes => 'used to store ExifTool ImageDataHash digest' },
2580
+ OriginalImageHashType => { Notes => "ImageHashType API setting, default 'MD5'" },
2581
+ OriginalImageMD5 => { Notes => 'deprecated' },
2570
2582
  );
2571
2583
 
2572
2584
  # table to add tags in other namespaces
@@ -1388,6 +1388,17 @@ my %sSubVersion = (
1388
1388
  ReuseAllowed => { Writable => 'boolean' },
1389
1389
  );
1390
1390
 
1391
+ %Image::ExifTool::XMP::panorama = (
1392
+ %xmpTableDefaults,
1393
+ GROUPS => { 1 => 'XMP-panorama', 2 => 'Image' },
1394
+ NAMESPACE => 'panorama',
1395
+ NOTES => 'Adobe Photoshop Panorama-profile tags.',
1396
+ Transformation => { },
1397
+ VirtualFocalLength => { Writable => 'real' },
1398
+ VirtualImageXCenter => { Writable => 'real' },
1399
+ VirtualImageYCenter => { Writable => 'real' },
1400
+ );
1401
+
1391
1402
  # Creative Commons namespace properties (cc) (ref 5)
1392
1403
  %Image::ExifTool::XMP::cc = (
1393
1404
  %xmpTableDefaults,
@@ -2094,6 +2105,27 @@ my %sSubVersion = (
2094
2105
  scene_referred => { Name => 'SceneReferred', Writable => 'boolean' },
2095
2106
  );
2096
2107
 
2108
+ # HDR Gain Map metadata namespace
2109
+ %Image::ExifTool::XMP::hdrgm = (
2110
+ %xmpTableDefaults,
2111
+ GROUPS => { 1 => 'XMP-hdrgm', 2 => 'Image' },
2112
+ NAMESPACE => 'hdrgm',
2113
+ TABLE_DESC => 'XMP HDR Gain Map Metadata',
2114
+ NOTES => 'Tags used in Adobe gain map images.',
2115
+ Version => { Avoid => 1 },
2116
+ BaseRenditionIsHDR => { Writable => 'boolean' },
2117
+ # this is a pain in the ass: List items below may or may not be lists
2118
+ # according to the Adobe specification -- I don't know how to handle tags
2119
+ # with a variable format like this, so just make them lists here for now
2120
+ OffsetSDR => { Writable => 'real', List => 'Seq' },
2121
+ OffsetHDR => { Writable => 'real', List => 'Seq' },
2122
+ HDRCapacityMin => { Writable => 'real' },
2123
+ HDRCapacityMax => { Writable => 'real' },
2124
+ GainMapMin => { Writable => 'real', List => 'Seq' },
2125
+ GainMapMax => { Writable => 'real', List => 'Seq' },
2126
+ Gamma => { Writable => 'real', List => 'Seq', Avoid => 1 },
2127
+ );
2128
+
2097
2129
  # SVG namespace properties (ref 9)
2098
2130
  %Image::ExifTool::XMP::SVG = (
2099
2131
  GROUPS => { 0 => 'SVG', 1 => 'SVG', 2 => 'Image' },
@@ -14,42 +14,55 @@ use vars qw(%specialStruct %stdXlatNS);
14
14
  use Image::ExifTool qw(:Utils);
15
15
  use Image::ExifTool::XMP;
16
16
 
17
- sub SerializeStruct($;$);
18
- sub InflateStruct($;$);
17
+ sub SerializeStruct($$;$);
18
+ sub InflateStruct($$;$);
19
19
  sub DumpStruct($;$);
20
20
  sub CheckStruct($$$);
21
21
  sub AddNewStruct($$$$$$);
22
22
  sub ConvertStruct($$$$;$);
23
+ sub EscapeJSON($;$);
24
+
25
+ # lookups for JSON characters that we escape specially
26
+ my %jsonChar = ( '"'=>'"', '\\'=>'\\', "\b"=>'b', "\f"=>'f', "\n"=>'n', "\r"=>'r', "\t"=>'t' );
27
+ my %jsonEsc = ( '"'=>'"', '\\'=>'\\', 'b'=>"\b", 'f'=>"\f", 'n'=>"\n", 'r'=>"\r", 't'=>"\t" );
23
28
 
24
29
  #------------------------------------------------------------------------------
25
30
  # Serialize a structure (or other object) into a simple string
26
- # Inputs: 0) HASH ref, ARRAY ref, or SCALAR, 1) closing bracket (or undef)
27
- # Returns: serialized structure string
31
+ # Inputs: 0) ExifTool ref, 1) HASH ref, ARRAY ref, or SCALAR, 2) closing bracket (or undef)
32
+ # Returns: serialized structure string (in format specified by StructFormat option)
28
33
  # eg) "{field=text with {braces|}|, and a comma, field2=val2,field3={field4=[a,b]}}"
29
- sub SerializeStruct($;$)
34
+ sub SerializeStruct($$;$)
30
35
  {
31
- my ($obj, $ket) = @_;
36
+ my ($et, $obj, $ket) = @_;
32
37
  my ($key, $val, @vals, $rtnVal);
38
+ my $sfmt = $et->Options('StructFormat');
33
39
 
34
40
  if (ref $obj eq 'HASH') {
35
41
  # support hashes with ordered keys
36
42
  my @keys = $$obj{_ordered_keys_} ? @{$$obj{_ordered_keys_}} : sort keys %$obj;
37
43
  foreach $key (@keys) {
38
- push @vals, $key . '=' . SerializeStruct($$obj{$key}, '}');
44
+ my $hdr = $sfmt ? EscapeJSON($key) . ':' : $key . '=';
45
+ push @vals, $hdr . SerializeStruct($et, $$obj{$key}, '}');
39
46
  }
40
47
  $rtnVal = '{' . join(',', @vals) . '}';
41
48
  } elsif (ref $obj eq 'ARRAY') {
42
49
  foreach $val (@$obj) {
43
- push @vals, SerializeStruct($val, ']');
50
+ push @vals, SerializeStruct($et, $val, ']');
44
51
  }
45
52
  $rtnVal = '[' . join(',', @vals) . ']';
46
53
  } elsif (defined $obj) {
47
54
  $obj = $$obj if ref $obj eq 'SCALAR';
48
55
  # escape necessary characters in string (closing bracket plus "," and "|")
49
- my $pat = $ket ? "\\$ket|,|\\|" : ',|\\|';
50
- ($rtnVal = $obj) =~ s/($pat)/|$1/g;
51
- # also must escape opening bracket or whitespace at start of string
52
- $rtnVal =~ s/^([\s\[\{])/|$1/;
56
+ if ($sfmt) {
57
+ $rtnVal = EscapeJSON($obj, $sfmt eq 'JSONQ');
58
+ } else {
59
+ my $pat = $ket ? "\\$ket|,|\\|" : ',|\\|';
60
+ ($rtnVal = $obj) =~ s/($pat)/|$1/g;
61
+ # also must escape opening bracket or whitespace at start of string
62
+ $rtnVal =~ s/^([\s\[\{])/|$1/;
63
+ }
64
+ } elsif ($sfmt) {
65
+ $rtnVal = 'null';
53
66
  } else {
54
67
  $rtnVal = ''; # allow undefined list items
55
68
  }
@@ -58,21 +71,25 @@ sub SerializeStruct($;$)
58
71
 
59
72
  #------------------------------------------------------------------------------
60
73
  # Inflate structure (or other object) from a serialized string
61
- # Inputs: 0) reference to object in string form (serialized using the '|' escape)
62
- # 1) extra delimiter for scalar values delimiters
74
+ # Inputs: 0) ExifTool ref, 1) reference to object in string form
75
+ # (serialized using the '|' escape, or JSON)
76
+ # 2) extra delimiter for scalar values delimiters
63
77
  # Returns: 0) object as a SCALAR, HASH ref, or ARRAY ref (or undef on error),
64
78
  # 1) warning string (or undef)
65
79
  # Notes: modifies input string to remove parsed objects
66
- sub InflateStruct($;$)
80
+ sub InflateStruct($$;$)
67
81
  {
68
- my ($obj, $delim) = @_;
82
+ my ($et, $obj, $delim) = @_;
69
83
  my ($val, $warn, $part);
84
+ my $sfmt = $et->Options('StructFormat');
70
85
 
71
86
  if ($$obj =~ s/^\s*\{//) {
72
87
  my %struct;
73
- while ($$obj =~ s/^\s*([-\w:]+#?)\s*=//s) {
88
+ for (;;) {
89
+ last unless $sfmt ? $$obj =~ s/^\s*"(.*?)"\s*://s :
90
+ $$obj =~ s/^\s*([-\w:]+#?)\s*=//s;
74
91
  my $tag = $1;
75
- my ($v, $w) = InflateStruct($obj, '}');
92
+ my ($v, $w) = InflateStruct($et, $obj, '}');
76
93
  $warn = $w if $w and not $warn;
77
94
  return(undef, $warn) unless defined $v;
78
95
  $struct{$tag} = $v;
@@ -94,7 +111,7 @@ sub InflateStruct($;$)
94
111
  } elsif ($$obj =~ s/^\s*\[//) {
95
112
  my @list;
96
113
  for (;;) {
97
- my ($v, $w) = InflateStruct($obj, ']');
114
+ my ($v, $w) = InflateStruct($et, $obj, ']');
98
115
  $warn = $w if $w and not $warn;
99
116
  return(undef, $warn) unless defined $v;
100
117
  push @list, $v;
@@ -105,20 +122,71 @@ sub InflateStruct($;$)
105
122
  $val = \@list;
106
123
  } else {
107
124
  $$obj =~ s/^\s+//s; # remove leading whitespace
108
- # read scalar up to specified delimiter (or "," if not defined)
109
- $val = '';
110
- $delim = $delim ? "\\$delim|,|\\||\$" : ',|\\||$';
111
- for (;;) {
112
- $$obj =~ s/^(.*?)($delim)//s or last;
113
- $val .= $1;
114
- last unless $2;
115
- $2 eq '|' or $$obj = $2 . $$obj, last;
116
- $$obj =~ s/^(.)//s and $val .= $1; # add escaped character
125
+ if ($sfmt) {
126
+ if ($$obj =~ s/^"//) {
127
+ $val = '';
128
+ while ($$obj =~ s/(.*?)"//) {
129
+ $val .= $1;
130
+ last unless $val =~ /([\\]+)$/ and length($1) & 0x01;
131
+ substr($val, -1, 1) = '"'; # (was an escaped quote)
132
+ }
133
+ if ($val =~ s/^base64://) {
134
+ $val = DecodeBase64($val);
135
+ } else {
136
+ # un-escape characters in JSON string
137
+ $val =~ s/\\(.)/$jsonEsc{$1}||'\\'.$1/egs;
138
+ }
139
+ } elsif ($$obj =~ s/^(true|false)\b//) {
140
+ $val = '"' . ucfirst($1) . '"';
141
+ } elsif ($$obj =~ s/^([+-]?(?=\d|\.\d)\d*(\.\d*)?([Ee]([+-]?\d+))?)//) {
142
+ $val = $1;
143
+ } else {
144
+ $warn or $warn = 'Unknown JSON object';
145
+ $val = '""';
146
+ }
147
+ } else {
148
+ # read scalar up to specified delimiter (or "," if not defined)
149
+ $delim = $delim ? "\\$delim|,|\\||\$" : ',|\\||$';
150
+ $val = '';
151
+ for (;;) {
152
+ $$obj =~ s/^(.*?)($delim)//s or last;
153
+ $val .= $1;
154
+ last unless $2;
155
+ $2 eq '|' or $$obj = $2 . $$obj, last;
156
+ $$obj =~ s/^(.)//s and $val .= $1; # add escaped character
157
+ }
117
158
  }
118
159
  }
119
160
  return($val, $warn);
120
161
  }
121
162
 
163
+ #------------------------------------------------------------------------------
164
+ # Escape string for JSON
165
+ # Inputs: 0) string, 1) flag to force numbers to be quoted too
166
+ # Returns: Escaped string (quoted if necessary)
167
+ sub EscapeJSON($;$)
168
+ {
169
+ my ($str, $quote) = @_;
170
+ unless ($quote) {
171
+ return 'null' unless defined $str;
172
+ # JSON boolean (true or false)
173
+ return lc($str) if $str =~ /^(true|false)$/i;
174
+ # JSON number (see json.org for numerical format)
175
+ # return $str if $str =~ /^-?(\d|[1-9]\d+)(\.\d+)?(e[-+]?\d+)?$/i;
176
+ # (these big numbers caused problems for some JSON parsers, so be more conservative)
177
+ return $str if $str =~ /^-?(\d|[1-9]\d{1,14})(\.\d{1,16})?(e[-+]?\d{1,3})?$/i;
178
+ }
179
+ return '""' unless defined $str;
180
+ # encode JSON string in base64 if necessary
181
+ return '"base64:' . EncodeBase64($str, 1) . '"' if Image::ExifTool::IsUTF8(\$str) < 0;
182
+ # escape special characters
183
+ $str =~ s/(["\t\n\r\\])/\\$jsonChar{$1}/sg;
184
+ $str =~ tr/\0//d; # remove all nulls
185
+ # escape other control characters with \u
186
+ $str =~ s/([\0-\x1f])/sprintf("\\u%.4X",ord $1)/sge;
187
+ return '"' . $str . '"'; # return the quoted string
188
+ }
189
+
122
190
  #------------------------------------------------------------------------------
123
191
  # Get XMP language code from tag name string
124
192
  # Inputs: 0) tag name string
@@ -20,7 +20,7 @@ use strict;
20
20
  use vars qw($VERSION $warnString);
21
21
  use Image::ExifTool qw(:DataAccess :Utils);
22
22
 
23
- $VERSION = '1.29';
23
+ $VERSION = '1.30';
24
24
 
25
25
  sub WarnProc($) { $warnString = $_[0]; }
26
26
 
@@ -259,8 +259,8 @@ my %iWorkType = (
259
259
  %Image::ExifTool::ZIP::RAR5 = (
260
260
  GROUPS => { 2 => 'Other' },
261
261
  VARS => { NO_ID => 1 },
262
- NOTES => 'These tags are extracted from RAR v5 archive files.',
263
- RARVersion => { },
262
+ NOTES => 'These tags are extracted from RAR v5 and 7z archive files.',
263
+ FileVersion => { },
264
264
  CompressedSize => { },
265
265
  ModifyDate => {
266
266
  Groups => { 2 => 'Time' },
@@ -310,7 +310,7 @@ sub ProcessRAR($$)
310
310
  $et->SetFileType();
311
311
  SetByteOrder('II');
312
312
  my $tagTablePtr = GetTagTable('Image::ExifTool::ZIP::RAR5');
313
- $et->HandleTag($tagTablePtr, 'RARVersion', 4);
313
+ $et->HandleTag($tagTablePtr, 'FileVersion', 'RAR v4');
314
314
  $tagTablePtr = GetTagTable('Image::ExifTool::ZIP::RAR');
315
315
 
316
316
  for (;;) {
@@ -356,7 +356,7 @@ sub ProcessRAR($$)
356
356
  return 0 unless $raf->Read($buff, 1) and $buff eq "\0";
357
357
  $et->SetFileType();
358
358
  my $tagTablePtr = GetTagTable('Image::ExifTool::ZIP::RAR5');
359
- $et->HandleTag($tagTablePtr, 'RARVersion', 5);
359
+ $et->HandleTag($tagTablePtr, 'FileVersion', 'RAR v5');
360
360
  $$et{INDENT} .= '| ';
361
361
 
362
362
  # loop through header blocks