exiftool_vendored 10.65.0 → 11.41.0

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of exiftool_vendored might be problematic. Click here for more details.

Files changed (205) hide show
  1. checksums.yaml +4 -4
  2. data/bin/Changes +818 -19
  3. data/bin/MANIFEST +38 -0
  4. data/bin/META.json +1 -1
  5. data/bin/META.yml +1 -1
  6. data/bin/README +48 -44
  7. data/bin/arg_files/exif2xmp.args +4 -1
  8. data/bin/arg_files/gps2xmp.args +4 -1
  9. data/bin/arg_files/iptcCore.args +8 -0
  10. data/bin/arg_files/xmp2exif.args +4 -1
  11. data/bin/arg_files/xmp2gps.args +4 -1
  12. data/bin/config_files/dji.config +131 -0
  13. data/bin/config_files/example.config +6 -2
  14. data/bin/config_files/gps2utm.config +256 -256
  15. data/bin/config_files/nksc.config +146 -0
  16. data/bin/config_files/picasa_faces.config +382 -382
  17. data/bin/exiftool +688 -408
  18. data/bin/fmt_files/gpx.fmt +10 -6
  19. data/bin/fmt_files/gpx_wpt.fmt +10 -6
  20. data/bin/fmt_files/kml.fmt +8 -5
  21. data/bin/lib/File/RandomAccess.pm +48 -8
  22. data/bin/lib/File/RandomAccess.pod +21 -2
  23. data/bin/lib/Image/ExifTool.pm +645 -256
  24. data/bin/lib/Image/ExifTool.pod +219 -164
  25. data/bin/lib/Image/ExifTool/AES.pm +1 -1
  26. data/bin/lib/Image/ExifTool/AFCP.pm +3 -8
  27. data/bin/lib/Image/ExifTool/AIFF.pm +12 -4
  28. data/bin/lib/Image/ExifTool/APE.pm +1 -1
  29. data/bin/lib/Image/ExifTool/APP12.pm +1 -1
  30. data/bin/lib/Image/ExifTool/ASF.pm +19 -6
  31. data/bin/lib/Image/ExifTool/Apple.pm +13 -5
  32. data/bin/lib/Image/ExifTool/Audible.pm +1 -1
  33. data/bin/lib/Image/ExifTool/BMP.pm +1 -1
  34. data/bin/lib/Image/ExifTool/BPG.pm +17 -15
  35. data/bin/lib/Image/ExifTool/BZZ.pm +1 -1
  36. data/bin/lib/Image/ExifTool/BigTIFF.pm +30 -15
  37. data/bin/lib/Image/ExifTool/BuildTagLookup.pm +103 -52
  38. data/bin/lib/Image/ExifTool/Canon.pm +684 -112
  39. data/bin/lib/Image/ExifTool/CanonCustom.pm +119 -9
  40. data/bin/lib/Image/ExifTool/CanonRaw.pm +1 -1
  41. data/bin/lib/Image/ExifTool/CanonVRD.pm +13 -26
  42. data/bin/lib/Image/ExifTool/CaptureOne.pm +1 -1
  43. data/bin/lib/Image/ExifTool/Casio.pm +1 -1
  44. data/bin/lib/Image/ExifTool/Charset.pm +1 -1
  45. data/bin/lib/Image/ExifTool/DICOM.pm +12 -5
  46. data/bin/lib/Image/ExifTool/DJI.pm +51 -3
  47. data/bin/lib/Image/ExifTool/DNG.pm +15 -8
  48. data/bin/lib/Image/ExifTool/DPX.pm +1 -1
  49. data/bin/lib/Image/ExifTool/DV.pm +1 -1
  50. data/bin/lib/Image/ExifTool/DarwinCore.pm +63 -23
  51. data/bin/lib/Image/ExifTool/DjVu.pm +4 -2
  52. data/bin/lib/Image/ExifTool/EXE.pm +30 -6
  53. data/bin/lib/Image/ExifTool/Exif.pm +351 -109
  54. data/bin/lib/Image/ExifTool/FITS.pm +148 -0
  55. data/bin/lib/Image/ExifTool/FLAC.pm +2 -2
  56. data/bin/lib/Image/ExifTool/FLIF.pm +1 -1
  57. data/bin/lib/Image/ExifTool/FLIR.pm +109 -13
  58. data/bin/lib/Image/ExifTool/Fixup.pm +1 -1
  59. data/bin/lib/Image/ExifTool/Flash.pm +3 -3
  60. data/bin/lib/Image/ExifTool/FlashPix.pm +433 -9
  61. data/bin/lib/Image/ExifTool/Font.pm +2 -2
  62. data/bin/lib/Image/ExifTool/FotoStation.pm +1 -1
  63. data/bin/lib/Image/ExifTool/FujiFilm.pm +336 -16
  64. data/bin/lib/Image/ExifTool/GE.pm +1 -1
  65. data/bin/lib/Image/ExifTool/GIF.pm +5 -7
  66. data/bin/lib/Image/ExifTool/GIMP.pm +39 -3
  67. data/bin/lib/Image/ExifTool/GPS.pm +48 -22
  68. data/bin/lib/Image/ExifTool/GeoTiff.pm +23 -23
  69. data/bin/lib/Image/ExifTool/Geotag.pm +80 -45
  70. data/bin/lib/Image/ExifTool/GoPro.pm +709 -0
  71. data/bin/lib/Image/ExifTool/H264.pm +40 -18
  72. data/bin/lib/Image/ExifTool/HP.pm +1 -1
  73. data/bin/lib/Image/ExifTool/HTML.pm +19 -12
  74. data/bin/lib/Image/ExifTool/HtmlDump.pm +37 -26
  75. data/bin/lib/Image/ExifTool/ICC_Profile.pm +297 -23
  76. data/bin/lib/Image/ExifTool/ID3.pm +12 -7
  77. data/bin/lib/Image/ExifTool/IPTC.pm +48 -19
  78. data/bin/lib/Image/ExifTool/ISO.pm +1 -1
  79. data/bin/lib/Image/ExifTool/ITC.pm +1 -1
  80. data/bin/lib/Image/ExifTool/Import.pm +13 -9
  81. data/bin/lib/Image/ExifTool/InDesign.pm +3 -5
  82. data/bin/lib/Image/ExifTool/JPEG.pm +22 -11
  83. data/bin/lib/Image/ExifTool/JPEGDigest.pm +1 -1
  84. data/bin/lib/Image/ExifTool/JSON.pm +3 -3
  85. data/bin/lib/Image/ExifTool/JVC.pm +1 -1
  86. data/bin/lib/Image/ExifTool/Jpeg2000.pm +2 -2
  87. data/bin/lib/Image/ExifTool/Kodak.pm +1233 -58
  88. data/bin/lib/Image/ExifTool/KyoceraRaw.pm +1 -1
  89. data/bin/lib/Image/ExifTool/LNK.pm +1 -1
  90. data/bin/lib/Image/ExifTool/Lang/cs.pm +1 -1
  91. data/bin/lib/Image/ExifTool/Lang/de.pm +33 -24
  92. data/bin/lib/Image/ExifTool/Lang/en_ca.pm +64 -2
  93. data/bin/lib/Image/ExifTool/Lang/en_gb.pm +64 -2
  94. data/bin/lib/Image/ExifTool/Lang/es.pm +8 -4
  95. data/bin/lib/Image/ExifTool/Lang/fi.pm +46 -4
  96. data/bin/lib/Image/ExifTool/Lang/fr.pm +5 -3
  97. data/bin/lib/Image/ExifTool/Lang/it.pm +6 -3
  98. data/bin/lib/Image/ExifTool/Lang/ja.pm +15 -3
  99. data/bin/lib/Image/ExifTool/Lang/ko.pm +5 -2
  100. data/bin/lib/Image/ExifTool/Lang/nl.pm +6 -3
  101. data/bin/lib/Image/ExifTool/Lang/pl.pm +2 -2
  102. data/bin/lib/Image/ExifTool/Lang/ru.pm +1 -1
  103. data/bin/lib/Image/ExifTool/Lang/sv.pm +1 -1
  104. data/bin/lib/Image/ExifTool/Lang/tr.pm +4 -2
  105. data/bin/lib/Image/ExifTool/Lang/zh_cn.pm +1 -1
  106. data/bin/lib/Image/ExifTool/Lang/zh_tw.pm +1 -1
  107. data/bin/lib/Image/ExifTool/Leaf.pm +1 -1
  108. data/bin/lib/Image/ExifTool/Lytro.pm +4 -8
  109. data/bin/lib/Image/ExifTool/M2TS.pm +10 -9
  110. data/bin/lib/Image/ExifTool/MIE.pm +12 -8
  111. data/bin/lib/Image/ExifTool/MIEUnits.pod +1 -1
  112. data/bin/lib/Image/ExifTool/MIFF.pm +1 -1
  113. data/bin/lib/Image/ExifTool/MNG.pm +1 -1
  114. data/bin/lib/Image/ExifTool/MOI.pm +1 -1
  115. data/bin/lib/Image/ExifTool/MPC.pm +1 -1
  116. data/bin/lib/Image/ExifTool/MPEG.pm +2 -3
  117. data/bin/lib/Image/ExifTool/MPF.pm +6 -6
  118. data/bin/lib/Image/ExifTool/MWG.pm +4 -4
  119. data/bin/lib/Image/ExifTool/MXF.pm +2 -2
  120. data/bin/lib/Image/ExifTool/MacOS.pm +184 -34
  121. data/bin/lib/Image/ExifTool/MakerNotes.pm +101 -18
  122. data/bin/lib/Image/ExifTool/Matroska.pm +1 -1
  123. data/bin/lib/Image/ExifTool/Microsoft.pm +5 -3
  124. data/bin/lib/Image/ExifTool/Minolta.pm +89 -62
  125. data/bin/lib/Image/ExifTool/MinoltaRaw.pm +1 -1
  126. data/bin/lib/Image/ExifTool/Motorola.pm +1 -1
  127. data/bin/lib/Image/ExifTool/Nikon.pm +1511 -380
  128. data/bin/lib/Image/ExifTool/NikonCapture.pm +1 -1
  129. data/bin/lib/Image/ExifTool/NikonCustom.pm +2758 -2935
  130. data/bin/lib/Image/ExifTool/Nintendo.pm +1 -1
  131. data/bin/lib/Image/ExifTool/OOXML.pm +1 -1
  132. data/bin/lib/Image/ExifTool/Ogg.pm +1 -1
  133. data/bin/lib/Image/ExifTool/Olympus.pm +47 -8
  134. data/bin/lib/Image/ExifTool/OpenEXR.pm +1 -1
  135. data/bin/lib/Image/ExifTool/Opus.pm +1 -1
  136. data/bin/lib/Image/ExifTool/PCX.pm +138 -0
  137. data/bin/lib/Image/ExifTool/PDF.pm +58 -42
  138. data/bin/lib/Image/ExifTool/PGF.pm +1 -1
  139. data/bin/lib/Image/ExifTool/PICT.pm +1 -1
  140. data/bin/lib/Image/ExifTool/PLIST.pm +12 -5
  141. data/bin/lib/Image/ExifTool/PLUS.pm +1 -1
  142. data/bin/lib/Image/ExifTool/PNG.pm +108 -10
  143. data/bin/lib/Image/ExifTool/PPM.pm +3 -3
  144. data/bin/lib/Image/ExifTool/PSP.pm +1 -1
  145. data/bin/lib/Image/ExifTool/Palm.pm +1 -1
  146. data/bin/lib/Image/ExifTool/Panasonic.pm +299 -31
  147. data/bin/lib/Image/ExifTool/PanasonicRaw.pm +201 -19
  148. data/bin/lib/Image/ExifTool/Pentax.pm +164 -143
  149. data/bin/lib/Image/ExifTool/PhaseOne.pm +12 -5
  150. data/bin/lib/Image/ExifTool/PhotoCD.pm +9 -10
  151. data/bin/lib/Image/ExifTool/PhotoMechanic.pm +1 -1
  152. data/bin/lib/Image/ExifTool/Photoshop.pm +230 -60
  153. data/bin/lib/Image/ExifTool/PostScript.pm +29 -4
  154. data/bin/lib/Image/ExifTool/PrintIM.pm +1 -1
  155. data/bin/lib/Image/ExifTool/Qualcomm.pm +2 -2
  156. data/bin/lib/Image/ExifTool/QuickTime.pm +1539 -279
  157. data/bin/lib/Image/ExifTool/QuickTimeStream.pl +1857 -0
  158. data/bin/lib/Image/ExifTool/README +84 -46
  159. data/bin/lib/Image/ExifTool/RIFF.pm +116 -23
  160. data/bin/lib/Image/ExifTool/RSRC.pm +1 -1
  161. data/bin/lib/Image/ExifTool/RTF.pm +6 -4
  162. data/bin/lib/Image/ExifTool/Radiance.pm +1 -1
  163. data/bin/lib/Image/ExifTool/Rawzor.pm +3 -2
  164. data/bin/lib/Image/ExifTool/Real.pm +1 -1
  165. data/bin/lib/Image/ExifTool/Reconyx.pm +261 -7
  166. data/bin/lib/Image/ExifTool/Red.pm +325 -0
  167. data/bin/lib/Image/ExifTool/Ricoh.pm +3 -7
  168. data/bin/lib/Image/ExifTool/Samsung.pm +95 -25
  169. data/bin/lib/Image/ExifTool/Sanyo.pm +1 -1
  170. data/bin/lib/Image/ExifTool/Scalado.pm +1 -1
  171. data/bin/lib/Image/ExifTool/Shift.pl +26 -12
  172. data/bin/lib/Image/ExifTool/Shortcuts.pm +9 -2
  173. data/bin/lib/Image/ExifTool/Sigma.pm +36 -30
  174. data/bin/lib/Image/ExifTool/SigmaRaw.pm +3 -8
  175. data/bin/lib/Image/ExifTool/Sony.pm +531 -177
  176. data/bin/lib/Image/ExifTool/SonyIDC.pm +63 -3
  177. data/bin/lib/Image/ExifTool/Stim.pm +2 -2
  178. data/bin/lib/Image/ExifTool/TagInfoXML.pm +23 -23
  179. data/bin/lib/Image/ExifTool/TagLookup.pm +6352 -5062
  180. data/bin/lib/Image/ExifTool/TagNames.pod +3024 -565
  181. data/bin/lib/Image/ExifTool/Theora.pm +1 -1
  182. data/bin/lib/Image/ExifTool/Torrent.pm +2 -2
  183. data/bin/lib/Image/ExifTool/Unknown.pm +1 -1
  184. data/bin/lib/Image/ExifTool/VCard.pm +47 -9
  185. data/bin/lib/Image/ExifTool/Validate.pm +391 -99
  186. data/bin/lib/Image/ExifTool/Vorbis.pm +1 -1
  187. data/bin/lib/Image/ExifTool/WTV.pm +319 -0
  188. data/bin/lib/Image/ExifTool/WriteCanonRaw.pl +1 -1
  189. data/bin/lib/Image/ExifTool/WriteExif.pl +91 -18
  190. data/bin/lib/Image/ExifTool/WriteIPTC.pl +6 -6
  191. data/bin/lib/Image/ExifTool/WritePDF.pl +13 -12
  192. data/bin/lib/Image/ExifTool/WritePNG.pl +1 -1
  193. data/bin/lib/Image/ExifTool/WritePhotoshop.pl +1 -1
  194. data/bin/lib/Image/ExifTool/WritePostScript.pl +2 -2
  195. data/bin/lib/Image/ExifTool/WriteQuickTime.pl +764 -121
  196. data/bin/lib/Image/ExifTool/WriteXMP.pl +176 -67
  197. data/bin/lib/Image/ExifTool/Writer.pl +490 -246
  198. data/bin/lib/Image/ExifTool/XMP.pm +216 -76
  199. data/bin/lib/Image/ExifTool/XMP2.pl +54 -10
  200. data/bin/lib/Image/ExifTool/XMPStruct.pl +14 -11
  201. data/bin/lib/Image/ExifTool/ZIP.pm +60 -15
  202. data/bin/lib/Image/ExifTool/iWork.pm +12 -5
  203. data/bin/perl-Image-ExifTool.spec +46 -44
  204. data/lib/exiftool_vendored/version.rb +1 -1
  205. metadata +14 -4
@@ -3,15 +3,18 @@
3
3
  #
4
4
  # Description: Example ExifTool print format file to generate a GPX track log
5
5
  #
6
- # Usage: exiftool -p gpx.fmt -d %Y-%m-%dT%H:%M:%SZ FILE [...] > out.gpx
6
+ # Usage: exiftool -p gpx.fmt -ee FILE [...] > out.gpx
7
7
  #
8
- # Requires: ExifTool version 8.10 or later
8
+ # Requires: ExifTool version 10.49 or later
9
9
  #
10
10
  # Revisions: 2010/02/05 - P. Harvey created
11
+ # 2018/01/04 - PH Added IF to be sure position exists
12
+ # 2018/01/06 - PH Use DateFmt function instead of -d option
11
13
  #
12
- # Notes: 1) All input files must contain GPSLatitude and GPSLongitude.
13
- # 2) The -fileOrder option may be used to control the order of the
14
- # generated track points.
14
+ # Notes: 1) Input file(s) must contain GPSLatitude and GPSLongitude.
15
+ # 2) The -ee option is to extract the full track from video files.
16
+ # 3) The -fileOrder option may be used to control the order of the
17
+ # generated track points when processing multiple files.
15
18
  #------------------------------------------------------------------------------
16
19
  #[HEAD]<?xml version="1.0" encoding="utf-8"?>
17
20
  #[HEAD]<gpx version="1.0"
@@ -22,9 +25,10 @@
22
25
  #[HEAD]<trk>
23
26
  #[HEAD]<number>1</number>
24
27
  #[HEAD]<trkseg>
28
+ #[IF] $gpslatitude $gpslongitude
25
29
  #[BODY]<trkpt lat="$gpslatitude#" lon="$gpslongitude#">
26
30
  #[BODY] <ele>$gpsaltitude#</ele>
27
- #[BODY] <time>$gpsdatetime</time>
31
+ #[BODY] <time>${gpsdatetime#;DateFmt("%Y-%m-%dT%H:%M:%SZ")}</time>
28
32
  #[BODY]</trkpt>
29
33
  #[TAIL]</trkseg>
30
34
  #[TAIL]</trk>
@@ -4,15 +4,18 @@
4
4
  # Description: Example ExifTool print format file to generate GPX waypoints
5
5
  # with pictures
6
6
  #
7
- # Usage: exiftool -p gpx_wpt.fmt -d %Y-%m-%dT%H:%M:%SZ FILE [...] > out.gpx
7
+ # Usage: exiftool -p gpx_wpt.fmt -ee FILE [...] > out.gpx
8
8
  #
9
- # Requires: ExifTool version 8.10 or later
9
+ # Requires: ExifTool version 10.49 or later
10
10
  #
11
11
  # Revisons: 2010/03/13 - Peter Grimm created
12
+ # 2018/01/04 - PH Added IF to be sure position exists
13
+ # 2018/01/06 - PH Use DateFmt function instead of -d option
12
14
  #
13
- # Notes: 1) All input files must contain GPSLatitude and GPSLongitude.
14
- # 2) The -fileOrder option may be used to control the order of the
15
- # generated track points.
15
+ # Notes: 1) Input file(s) must contain GPSLatitude and GPSLongitude.
16
+ # 2) The -ee option is to extract the full track from video files.
17
+ # 3) The -fileOrder option may be used to control the order of the
18
+ # generated track points when processing multiple files.
16
19
  #------------------------------------------------------------------------------
17
20
  #[HEAD]<?xml version="1.0" encoding="UTF-8" standalone="no" ?>
18
21
  #[HEAD]<gpx version="1.1"
@@ -20,9 +23,10 @@
20
23
  #[HEAD] xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
21
24
  #[HEAD] xmlns="http://www.topografix.com/GPX/1/1"
22
25
  #[HEAD] xsi:schemaLocation="http://www.topografix.com/GPX/1/1 http://www.topografix.com/GPX/1/1/gpx.xsd">
26
+ #[IF] $gpslatitude $gpslongitude
23
27
  #[BODY]<wpt lat="$gpslatitude#" lon="$gpslongitude#">
24
28
  #[BODY] <ele>$gpsaltitude#</ele>
25
- #[BODY] <time>$gpsdatetime</time>
29
+ #[BODY] <time>${gpsdatetime#;DateFmt("%Y-%m-%dT%H:%M:%SZ")}</time>
26
30
  #[BODY] <name>$filename</name>
27
31
  #[BODY] <link href="$directory/$filename"/>
28
32
  #[BODY] <sym>Scenic Area</sym>
@@ -11,16 +11,18 @@
11
11
  # Revisions: 2010/02/05 - P. Harvey created
12
12
  # 2013/02/05 - PH Fixed camera icon to work with new Google Earth
13
13
  # 2017/02/02 - PH Organize into folders based on file directory
14
+ # 2018/01/04 - PH Added IF to be sure position exists
14
15
  #
15
- # Notes: 1) All input files must contain GPSLatitude and GPSLongitude.
16
- # 2) For Google Earth to be able to find the images, the input
16
+ # Notes: 1) Input files must contain GPSLatitude and GPSLongitude.
17
+ # 2) Add the -ee option to extract the full track from video files.
18
+ # 3) For Google Earth to be able to find the images, the input
17
19
  # images must be specified using relative paths, and "out.kml"
18
20
  # must stay in the same directory as where the command was run.
19
- # 3) Google Earth is picky about the case of the image file extension,
21
+ # 4) Google Earth is picky about the case of the image file extension,
20
22
  # and may not be able to display the image if an upper-case
21
23
  # extension is used.
22
- # 4) The -fileOrder option may be used to control the order of the
23
- # generated placemarks.
24
+ # 5) The -fileOrder option may be used to control the order of the
25
+ # generated placemarks when processing multiple files.
24
26
  #------------------------------------------------------------------------------
25
27
  #[HEAD]<?xml version="1.0" encoding="UTF-8"?>
26
28
  #[HEAD]<kml xmlns="http://earth.google.com/kml/2.0">
@@ -38,6 +40,7 @@
38
40
  #[SECT] <Folder>
39
41
  #[SECT] <name>$directory</name>
40
42
  #[SECT] <open>0</open>
43
+ #[IF] $gpslatitude $gpslongitude
41
44
  #[BODY] <Placemark>
42
45
  #[BODY] <description><![CDATA[<br/><table><tr><td>
43
46
  #[BODY] <img src='$directory/$filename'
@@ -16,6 +16,7 @@
16
16
  # 11/26/2008 - P. Harvey Fixed bug in ReadLine when reading from a
17
17
  # scalar with a multi-character newline
18
18
  # 01/24/2009 - PH Protect against reading too much at once
19
+ # 10/04/2018 - PH Added NoBuffer option
19
20
  #
20
21
  # Notes: Calls the normal file i/o routines unless SeekTest() fails, in
21
22
  # which case the file is buffered in memory to allow random access.
@@ -24,7 +25,7 @@
24
25
  #
25
26
  # May also be used for string i/o (just pass a scalar reference)
26
27
  #
27
- # Legal: Copyright (c) 2003-2017 Phil Harvey (phil at owl.phy.queensu.ca)
28
+ # Legal: Copyright (c) 2003-2019 Phil Harvey (phil at owl.phy.queensu.ca)
28
29
  # This library is free software; you can redistribute it and/or
29
30
  # modify it under the same terms as Perl itself.
30
31
  #------------------------------------------------------------------------------
@@ -36,13 +37,14 @@ require 5.002;
36
37
  require Exporter;
37
38
 
38
39
  use vars qw($VERSION @ISA @EXPORT_OK);
39
- $VERSION = '1.10';
40
+ $VERSION = '1.11';
40
41
  @ISA = qw(Exporter);
41
42
 
42
43
  sub Read($$$);
43
44
 
44
45
  # constants
45
46
  my $CHUNK_SIZE = 8192; # size of chunks to read from file (must be power of 2)
47
+ my $SKIP_SIZE = 65536; # size to skip when fast-forwarding over sequential data
46
48
  my $SLURP_CHUNKS = 16; # read this many chunks at a time when slurping
47
49
 
48
50
  #------------------------------------------------------------------------------
@@ -60,6 +62,7 @@ sub new($$;$)
60
62
  # string i/o
61
63
  $self = {
62
64
  BUFF_PT => $filePt,
65
+ BASE => 0,
63
66
  POS => 0,
64
67
  LEN => length($$filePt),
65
68
  TESTED => -1,
@@ -71,8 +74,9 @@ sub new($$;$)
71
74
  $self = {
72
75
  FILE_PT => $filePt, # file pointer
73
76
  BUFF_PT => \$buff, # reference to file data
74
- POS => 0, # current position in file
75
- LEN => 0, # data length
77
+ BASE => 0, # location of start of buffer in file
78
+ POS => 0, # current position in buffer
79
+ LEN => 0, # length of data in buffer
76
80
  TESTED => 0, # 0=untested, 1=passed, -1=failed (requires buffering)
77
81
  };
78
82
  bless $self, $class;
@@ -118,7 +122,7 @@ sub Tell($)
118
122
  my $self = shift;
119
123
  my $rtnVal;
120
124
  if ($self->{TESTED} < 0) {
121
- $rtnVal = $self->{POS};
125
+ $rtnVal = $self->{POS} + $self->{BASE};
122
126
  } else {
123
127
  $rtnVal = tell($self->{FILE_PT});
124
128
  }
@@ -141,9 +145,11 @@ sub Seek($$;$)
141
145
  if ($self->{TESTED} < 0) {
142
146
  my $newPos;
143
147
  if ($whence == 0) {
144
- $newPos = $num; # from start of file
148
+ $newPos = $num - $self->{BASE}; # from start of file
145
149
  } elsif ($whence == 1) {
146
150
  $newPos = $num + $self->{POS}; # relative to current position
151
+ } elsif ($self->{NoBuffer} and $self->{FILE_PT}) {
152
+ $newPos = -1; # (can't seek relative to end if no buffering)
147
153
  } else {
148
154
  $self->Slurp(); # read whole file into buffer
149
155
  $newPos = $num + $self->{LEN}; # relative to end of file
@@ -192,6 +198,8 @@ sub Read($$$)
192
198
  }
193
199
  # read through our buffer if necessary
194
200
  if ($self->{TESTED} < 0) {
201
+ # purge old data before reading in NoBuffer mode
202
+ $self->Purge() or return 0 if $self->{NoBuffer};
195
203
  my $buff;
196
204
  my $newPos = $self->{POS} + $len;
197
205
  # number of bytes to read from file
@@ -244,6 +252,7 @@ sub ReadLine($$)
244
252
 
245
253
  if ($self->{TESTED} < 0) {
246
254
  my ($num, $buff);
255
+ $self->Purge() or return 0 if $self->{NoBuffer};
247
256
  my $pos = $self->{POS};
248
257
  if ($fp) {
249
258
  # make sure we have some data after the current position
@@ -311,9 +320,39 @@ sub Slurp($)
311
320
  }
312
321
  }
313
322
 
323
+ #------------------------------------------------------------------------------
324
+ # Purge internal buffer [internal use only]
325
+ # Inputs: 0) reference to RandomAccess object
326
+ # Returns: 1 on success, or 0 if current buffer position is negative
327
+ # Notes: This is called only in NoBuffer mode
328
+ sub Purge($)
329
+ {
330
+ my $self = shift;
331
+ return 1 unless $self->{FILE_PT};
332
+ return 0 if $self->{POS} < 0; # error if we can't read from here
333
+ if ($self->{POS} > $CHUNK_SIZE) {
334
+ my $purge = $self->{POS} - ($self->{POS} % $CHUNK_SIZE);
335
+ if ($purge >= $self->{LEN}) {
336
+ # read up to current position in 64k chunks, discarding as we go
337
+ while ($self->{POS} > $self->{LEN}) {
338
+ $self->{BASE} += $self->{LEN};
339
+ $self->{POS} -= $self->{LEN};
340
+ ${$self->{BUFF_PT}} = '';
341
+ $self->{LEN} = read($self->{FILE_PT}, ${$self->{BUFF_PT}}, $SKIP_SIZE);
342
+ last if $self->{LEN} < $SKIP_SIZE;
343
+ }
344
+ } elsif ($purge > 0) {
345
+ ${$self->{BUFF_PT}} = substr ${$self->{BUFF_PT}}, $purge;
346
+ $self->{BASE} += $purge;
347
+ $self->{POS} -= $purge;
348
+ $self->{LEN} -= $purge;
349
+ }
350
+ }
351
+ return 1;
352
+ }
314
353
 
315
354
  #------------------------------------------------------------------------------
316
- # set binary mode
355
+ # Set binary mode
317
356
  # Inputs: 0) reference to RandomAccess object
318
357
  sub BinMode($)
319
358
  {
@@ -322,7 +361,7 @@ sub BinMode($)
322
361
  }
323
362
 
324
363
  #------------------------------------------------------------------------------
325
- # close the file and free the buffer
364
+ # Close the file and free the buffer
326
365
  # Inputs: 0) reference to RandomAccess object
327
366
  sub Close($)
328
367
  {
@@ -370,6 +409,7 @@ sub Close($)
370
409
  # reset the buffer
371
410
  my $emptyBuff = '';
372
411
  $self->{BUFF_PT} = \$emptyBuff;
412
+ $self->{BASE} = 0;
373
413
  $self->{LEN} = 0;
374
414
  $self->{POS} = 0;
375
415
  }
@@ -3,7 +3,7 @@
3
3
  #
4
4
  # Description: Buffer to support random access reading of sequential file
5
5
  #
6
- # Legal: Copyright (c) 2003-2017 Phil Harvey (phil at owl.phy.queensu.ca)
6
+ # Legal: Copyright (c) 2003-2019 Phil Harvey (phil at owl.phy.queensu.ca)
7
7
  # This library is free software; you can redistribute it and/or
8
8
  # modify it under the same terms as Perl itself.
9
9
  #------------------------------------------------------------------------------
@@ -215,9 +215,28 @@ Nothing.
215
215
 
216
216
  =back
217
217
 
218
+ =head1 OPTIONS
219
+
220
+ =over 4
221
+
222
+ =item B<NoBuffer>
223
+
224
+ Avoid buffering sequential files.
225
+
226
+ $raf->{NoBuffer} = 1;
227
+
228
+ When this option is set, old data is purged from the internal buffer before
229
+ a read operation on a sequential file. In this mode, memory requirements
230
+ may be significantly reduced when reading sequential files, but seeking
231
+ backward is limited to within the size of the internal buffer (which will be
232
+ at least as large as the last returned data block), and seeking relative to
233
+ the end of file is not allowed.
234
+
235
+ =back
236
+
218
237
  =head1 AUTHOR
219
238
 
220
- Copyright 2003-2017 Phil Harvey (phil at owl.phy.queensu.ca)
239
+ Copyright 2003-2019 Phil Harvey (phil at owl.phy.queensu.ca)
221
240
 
222
241
  This library is free software; you can redistribute it and/or modify it
223
242
  under the same terms as Perl itself.
@@ -8,7 +8,7 @@
8
8
  # Revisions: Nov. 12/2003 - P. Harvey Created
9
9
  # (See html/history.html for revision history)
10
10
  #
11
- # Legal: Copyright (c) 2003-2017, Phil Harvey (phil at owl.phy.queensu.ca)
11
+ # Legal: Copyright (c) 2003-2019, Phil Harvey (phil at owl.phy.queensu.ca)
12
12
  # This library is free software; you can redistribute it and/or
13
13
  # modify it under the same terms as Perl itself.
14
14
  #------------------------------------------------------------------------------
@@ -27,7 +27,7 @@ use vars qw($VERSION $RELEASE @ISA @EXPORT_OK %EXPORT_TAGS $AUTOLOAD @fileTypes
27
27
  %mimeType $swapBytes $swapWords $currentByteOrder %unpackStd
28
28
  %jpegMarker %specialTags %fileTypeLookup);
29
29
 
30
- $VERSION = '10.65';
30
+ $VERSION = '11.41';
31
31
  $RELEASE = '';
32
32
  @ISA = qw(Exporter);
33
33
  %EXPORT_TAGS = (
@@ -41,7 +41,7 @@ $RELEASE = '';
41
41
  DataAccess => [qw(
42
42
  ReadValue GetByteOrder SetByteOrder ToggleByteOrder Get8u Get8s Get16u
43
43
  Get16s Get32u Get32s Get64u GetFloat GetDouble GetFixed32s Write
44
- WriteValue Tell Set8u Set8s Set16u Set32u
44
+ WriteValue Tell Set8u Set8s Set16u Set32u Set64u
45
45
  )],
46
46
  Utils => [qw(GetTagTable TagTableKeys GetTagInfoList AddTagToTable HexDump)],
47
47
  Vars => [qw(%allTables @tableOrder @fileTypes)],
@@ -65,7 +65,7 @@ sub SaveNewValues($);
65
65
  sub RestoreNewValues($);
66
66
  sub WriteInfo($$;$$);
67
67
  sub SetFileModifyDate($$;$$$);
68
- sub SetFileName($$;$$);
68
+ sub SetFileName($$;$$$);
69
69
  sub SetSystemTags($$);
70
70
  sub GetAllTags(;$);
71
71
  sub GetWritableTags(;$);
@@ -74,8 +74,9 @@ sub GetNewGroups($);
74
74
  sub GetDeleteGroups();
75
75
  sub AddUserDefinedTags($%);
76
76
  # non-public routines below
77
- sub InsertTagValues($$$;$);
77
+ sub InsertTagValues($$$;$$$);
78
78
  sub IsWritable($);
79
+ sub IsSameFile($$$);
79
80
  sub GetNewFileName($$);
80
81
  sub LoadAllTables();
81
82
  sub GetNewTagInfoList($;$);
@@ -83,14 +84,15 @@ sub GetNewTagInfoHash($@);
83
84
  sub GetLangInfo($$);
84
85
  sub Get64s($$);
85
86
  sub Get64u($$);
87
+ sub GetFixed64s($$);
86
88
  sub GetExtended($$);
89
+ sub Set64u(@);
87
90
  sub DecodeBits($$;$);
88
91
  sub EncodeBits($$;$$);
89
92
  sub HexDump($;$%);
90
93
  sub DumpTrailer($$);
91
94
  sub DumpUnknownTrailer($$);
92
95
  sub VerboseInfo($$$%);
93
- sub VerboseDir($$;$$);
94
96
  sub VerboseValue($$$;$);
95
97
  sub VPrint($$@);
96
98
  sub Rationalize($;$);
@@ -108,7 +110,7 @@ sub UnpackUTF8($);
108
110
  sub SetPreferredByteOrder($);
109
111
  sub CopyBlock($$$);
110
112
  sub CopyFileAttrs($$$);
111
- sub TimeNow(;$);
113
+ sub TimeNow(;$$);
112
114
  sub NewGUID();
113
115
  sub MakeTiffHeader($$$$;$$);
114
116
 
@@ -123,7 +125,7 @@ sub SetFileTime($$;$$$$);
123
125
  sub DoEscape($$);
124
126
  sub ConvertFileSize($);
125
127
  sub ParseArguments($;@); #(defined in attempt to avoid mod_perl problem)
126
- sub ReadValue($$$$$;$);
128
+ sub ReadValue($$$;$$$);
127
129
 
128
130
  # list of main tag tables to load in LoadAllTables() (sub-tables are recursed
129
131
  # automatically). Note: They will appear in this order in the documentation
@@ -131,16 +133,17 @@ sub ReadValue($$$$$;$);
131
133
  @loadAllTables = qw(
132
134
  PhotoMechanic Exif GeoTiff CanonRaw KyoceraRaw Lytro MinoltaRaw PanasonicRaw
133
135
  SigmaRaw JPEG GIMP Jpeg2000 GIF BMP BMP::OS2 BMP::Extra BPG BPG::Extensions
134
- PICT PNG MNG FLIF DjVu DPX OpenEXR MIFF PGF PSP PhotoCD Radiance PDF
136
+ PICT PNG MNG FLIF DjVu DPX OpenEXR MIFF PCX PGF PSP PhotoCD Radiance PDF
135
137
  PostScript Photoshop::Header Photoshop::Layers Photoshop::ImageData
136
138
  FujiFilm::RAF FujiFilm::IFD Samsung::Trailer Sony::SRF2 Sony::SR2SubIFD
137
139
  Sony::PMP ITC ID3 FLAC Ogg Vorbis APE APE::NewHeader APE::OldHeader Audible
138
140
  MPC MPEG::Audio MPEG::Video MPEG::Xing M2TS QuickTime QuickTime::ImageFile
139
- Matroska MOI MXF DV Flash Flash::FLV Real::Media Real::Audio Real::Metafile
140
- RIFF AIFF ASF DICOM MIE JSON HTML XMP::SVG Palm Palm::MOBI Palm::EXTH
141
- Torrent EXE EXE::PEVersion EXE::PEString EXE::MachO EXE::PEF EXE::ELF
142
- EXE::AR EXE::CHM LNK Font VCard VCard::VCalendar RSRC Rawzor ZIP ZIP::GZIP
143
- ZIP::RAR RTF OOXML iWork ISO FLIR::AFF FLIR::FPF MacOS::MDItem MacOS::XAttr
141
+ QuickTime::Stream Matroska MOI MXF DV Flash Flash::FLV Real::Media
142
+ Real::Audio Real::Metafile Red RIFF AIFF ASF WTV DICOM FITS MIE JSON HTML
143
+ XMP::SVG Palm Palm::MOBI Palm::EXTH Torrent EXE EXE::PEVersion EXE::PEString
144
+ EXE::MachO EXE::PEF EXE::ELF EXE::AR EXE::CHM LNK Font VCard
145
+ VCard::VCalendar RSRC Rawzor ZIP ZIP::GZIP ZIP::RAR RTF OOXML iWork ISO
146
+ FLIR::AFF FLIR::FPF MacOS::MDItem MacOS::XAttr FlashPix::DocTable
144
147
  );
145
148
 
146
149
  # alphabetical list of current Lang modules
@@ -175,12 +178,13 @@ $defaultLang = 'en'; # default language
175
178
  # 2) Put types with weak file signatures at end of list to avoid false matches
176
179
  # 3) PLIST must be in this list for the binary PLIST format, although it may
177
180
  # cause a file to be checked twice for XML
178
- @fileTypes = qw(JPEG CRW DR4 TIFF GIF MRW RAF X3F JP2 PNG MIE MIFF PS PDF PSD XMP
179
- BMP BPG PPM RIFF AIFF ASF MOV MPEG Real SWF PSP FLV OGG FLAC APE
180
- MPC MKV MXF DV PMP IND PGF ICC ITC FLIR FLIF FPF LFP HTML VRD
181
- RTF XCF DSS QTIF FPX PICT ZIP GZIP PLIST RAR BZ2 TAR RWZ EXE EXR
182
- HDR CHM LNK WMF AVC DEX DPX RAW Font RSRC M2TS PHP Torrent VCard
183
- AA PDB MOI ISO JSON MP3 DICOM PCD);
181
+ @fileTypes = qw(JPEG EXV CRW DR4 TIFF GIF MRW RAF X3F JP2 PNG MIE MIFF PS PDF
182
+ PSD XMP BMP BPG PPM RIFF AIFF ASF MOV MPEG Real SWF PSP FLV OGG
183
+ FLAC APE MPC MKV MXF DV PMP IND PGF ICC ITC FLIR FLIF FPF LFP
184
+ HTML VRD RTF FITS XCF DSS QTIF FPX PICT ZIP GZIP PLIST RAR BZ2
185
+ TAR RWZ EXE EXR HDR CHM LNK WMF AVC DEX DPX RAW Font RSRC M2TS
186
+ PHP PCX DCX DWF DWG WTV Torrent VCard LRI R3D AA PDB MOI ISO
187
+ JSON MP3 DICOM PCD);
184
188
 
185
189
  # file types that we can write (edit)
186
190
  my @writeTypes = qw(JPEG TIFF GIF CRW MRW ORF RAF RAW PNG MIE PSD XMP PPM EPS
@@ -190,9 +194,9 @@ my %writeTypes; # lookup for writable file types (hash filled if required)
190
194
  # file extensions that we can't write for various base types
191
195
  %noWriteFile = (
192
196
  TIFF => [ qw(3FR DCR K25 KDC SRF) ],
193
- XMP => [ 'SVG' ],
194
- JP2 => [ 'J2C', 'JPC' ],
195
- MOV => [ 'HEIC', 'HEIF' ],
197
+ XMP => [ qw(SVG INX) ],
198
+ JP2 => [ qw(J2C JPC) ],
199
+ MOV => [ qw(INSV) ],
196
200
  );
197
201
 
198
202
  # file types that we can create from scratch
@@ -210,6 +214,7 @@ my %createTypes = map { $_ => 1 } qw(XMP ICC MIE VRD DR4 EXIF EXV);
210
214
  '3GPP'=> '3GP',
211
215
  A => ['EXE', 'Static library'],
212
216
  AA => ['AA', 'Audible Audiobook'],
217
+ AAE => ['PLIST','Apple edit information'],
213
218
  AAX => ['MOV', 'Audible Enhanced Audiobook'],
214
219
  ACR => ['DICOM','American College of Radiology ACR-NEMA'],
215
220
  ACFM => ['Font', 'Adobe Composite Font Metrics'],
@@ -223,6 +228,7 @@ my %createTypes = map { $_ => 1 } qw(XMP ICC MIE VRD DR4 EXIF EXV);
223
228
  APE => ['APE', "Monkey's Audio format"],
224
229
  APNG => ['PNG', 'Animated Portable Network Graphics'],
225
230
  ARW => ['TIFF', 'Sony Alpha RAW format'],
231
+ ARQ => ['TIFF', 'Sony Alpha Pixel-Shift RAW format'],
226
232
  ASF => ['ASF', 'Microsoft Advanced Systems Format'],
227
233
  AVC => ['AVC', 'Advanced Video Connection'], # (extensions are actually _AU,_AD,_IM,_ID)
228
234
  AVI => ['RIFF', 'Audio Video Interleaved'],
@@ -236,12 +242,15 @@ my %createTypes = map { $_ => 1 } qw(XMP ICC MIE VRD DR4 EXIF EXV);
236
242
  CIFF => ['CRW', 'Camera Image File Format'],
237
243
  COS => ['COS', 'Capture One Settings'],
238
244
  CR2 => ['TIFF', 'Canon RAW 2 format'],
245
+ CR3 => ['MOV', 'Canon RAW 3 format'],
246
+ CRM => ['MOV', 'Canon RAW Movie'],
239
247
  CRW => ['CRW', 'Canon RAW format'],
240
248
  CS1 => ['PSD', 'Sinar CaptureShop 1-Shot RAW'],
241
249
  DC3 => 'DICM',
242
250
  DCM => 'DICM',
243
251
  DCP => ['TIFF', 'DNG Camera Profile'],
244
252
  DCR => ['TIFF', 'Kodak Digital Camera RAW'],
253
+ DCX => ['DCX', 'Multi-page PC Paintbrush'],
245
254
  DEX => ['DEX', 'Dalvik Executable format'],
246
255
  DFONT=> ['Font', 'Macintosh Data fork Font'],
247
256
  DIB => ['BMP', 'Device Independent Bitmap'],
@@ -267,6 +276,9 @@ my %createTypes = map { $_ => 1 } qw(XMP ICC MIE VRD DR4 EXIF EXV);
267
276
  DSS => ['DSS', 'Digital Speech Standard'],
268
277
  DV => ['DV', 'Digital Video'],
269
278
  DVB => ['MOV', 'Digital Video Broadcasting'],
279
+ 'DVR-MS'=>['ASF', 'Microsoft Digital Video recording'],
280
+ DWF => ['DWF', 'Autodesk drawing (Design Web Format)'],
281
+ DWG => ['DWG', 'AutoCAD Drawing'],
270
282
  DYLIB=> ['EXE', 'Mach-O Dynamic Link Library'],
271
283
  EIP => ['ZIP', 'Capture One Enhanced Image Package'],
272
284
  EPS => ['EPS', 'Encapsulated PostScript Format'],
@@ -284,6 +296,8 @@ my %createTypes = map { $_ => 1 } qw(XMP ICC MIE VRD DR4 EXIF EXV);
284
296
  F4P => ['MOV', 'Adobe Flash Player 9+ Protected'],
285
297
  F4V => ['MOV', 'Adobe Flash Player 9+ Video'],
286
298
  FFF => [['TIFF','FLIR'], 'Hasselblad Flexible File Format'],
299
+ FIT => 'FITS',
300
+ FITS => ['FITS', 'Flexible Image Transport System'],
287
301
  FLAC => ['FLAC', 'Free Lossless Audio Codec'],
288
302
  FLA => ['FPX', 'Macromedia/Adobe Flash project'],
289
303
  FLIF => ['FLIF', 'Free Lossless Image Format'],
@@ -292,6 +306,7 @@ my %createTypes = map { $_ => 1 } qw(XMP ICC MIE VRD DR4 EXIF EXV);
292
306
  FPF => ['FPF', 'FLIR Public image Format'],
293
307
  FPX => ['FPX', 'FlashPix'],
294
308
  GIF => ['GIF', 'Compuserve Graphics Interchange Format'],
309
+ GPR => ['TIFF', 'GoPro RAW'],
295
310
  GZ => 'GZIP',
296
311
  GZIP => ['GZIP', 'GNU ZIP compressed archive'],
297
312
  HDP => ['TIFF', 'Windows HD Photo'],
@@ -309,11 +324,12 @@ my %createTypes = map { $_ => 1 } qw(XMP ICC MIE VRD DR4 EXIF EXV);
309
324
  IND => ['IND', 'Adobe InDesign'],
310
325
  INDD => ['IND', 'Adobe InDesign Document'],
311
326
  INDT => ['IND', 'Adobe InDesign Template'],
327
+ INSV => 'MP4',
312
328
  INX => ['XMP', 'Adobe InDesign Interchange'],
313
329
  ISO => ['ISO', 'ISO 9660 disk image'],
314
330
  ITC => ['ITC', 'iTunes Cover Flow'],
315
331
  J2C => ['JP2', 'JPEG 2000 codestream'],
316
- J2K => 'JP2',
332
+ J2K => 'J2C',
317
333
  JNG => ['PNG', 'JPG Network Graphics'],
318
334
  JP2 => ['JP2', 'JPEG 2000 file'],
319
335
  # JP4? - looks like a JPEG but the image data is different
@@ -334,6 +350,8 @@ my %createTypes = map { $_ => 1 } qw(XMP ICC MIE VRD DR4 EXIF EXV);
334
350
  LFP => ['LFP', 'Lytro Light Field Picture'],
335
351
  LFR => 'LFP', # (Light Field RAW)
336
352
  LNK => ['LNK', 'Windows shortcut'],
353
+ LRI => ['LRI', 'Light RAW'],
354
+ LRV => ['MOV', 'Low-Resolution Video'],
337
355
  M2T => 'M2TS',
338
356
  M2TS => ['M2TS', 'MPEG-2 Transport Stream'],
339
357
  M2V => ['MPEG', 'MPEG-2 Video'],
@@ -391,6 +409,7 @@ my %createTypes = map { $_ => 1 } qw(XMP ICC MIE VRD DR4 EXIF EXV);
391
409
  PBM => ['PPM', 'Portable BitMap'],
392
410
  PCD => ['PCD', 'Kodak Photo CD Image Pac'],
393
411
  PCT => 'PICT',
412
+ PCX => ['PCX', 'PC Paintbrush'],
394
413
  PDB => ['PDB', 'Palm Database'],
395
414
  PDF => ['PDF', 'Adobe Portable Document Format'],
396
415
  PEF => ['TIFF', 'Pentax (RAW) Electronic Format'],
@@ -435,6 +454,7 @@ my %createTypes = map { $_ => 1 } qw(XMP ICC MIE VRD DR4 EXIF EXV);
435
454
  QT => 'MOV',
436
455
  QTI => 'QTIF',
437
456
  QTIF => ['QTIF', 'QuickTime Image File'],
457
+ R3D => ['R3D', 'Redcode RAW Video'],
438
458
  RA => ['Real', 'Real Audio'],
439
459
  RAF => ['RAF', 'FujiFilm RAW Format'],
440
460
  RAM => ['Real', 'Real Audio Metafile'],
@@ -452,6 +472,7 @@ my %createTypes = map { $_ => 1 } qw(XMP ICC MIE VRD DR4 EXIF EXV);
452
472
  RWL => ['TIFF', 'Leica RAW'],
453
473
  RWZ => ['RWZ', 'Rawzor compressed image'],
454
474
  SEQ => ['FLIR', 'FLIR image Sequence'],
475
+ SKETCH => ['ZIP', 'Sketch design file'],
455
476
  SO => ['EXE', 'Shared Object file'],
456
477
  SR2 => ['TIFF', 'Sony RAW Format 2'],
457
478
  SRF => ['TIFF', 'Sony RAW Format'],
@@ -459,7 +480,7 @@ my %createTypes = map { $_ => 1 } qw(XMP ICC MIE VRD DR4 EXIF EXV);
459
480
  SVG => ['XMP', 'Scalable Vector Graphics'],
460
481
  SWF => ['SWF', 'Shockwave Flash'],
461
482
  TAR => ['TAR', 'TAR archive'],
462
- THM => ['JPEG', 'Canon Thumbnail'],
483
+ THM => ['JPEG', 'Thumbnail'],
463
484
  THMX => [['ZIP','FPX'], 'Office Open XML Theme'],
464
485
  TIF => 'TIFF',
465
486
  TIFF => ['TIFF', 'Tagged Image File Format'],
@@ -494,6 +515,7 @@ my %createTypes = map { $_ => 1 } qw(XMP ICC MIE VRD DR4 EXIF EXV);
494
515
  XLTM => [['ZIP','FPX'], 'Office Open XML Spreadsheet Template Macro-enabled'],
495
516
  XLTX => [['ZIP','FPX'], 'Office Open XML Spreadsheet Template'],
496
517
  XMP => ['XMP', 'Extensible Metadata Platform'],
518
+ WTV => ['WTV', 'Windows recorded TV show'],
497
519
  ZIP => ['ZIP', 'ZIP archive'],
498
520
  );
499
521
 
@@ -527,6 +549,7 @@ my %fileDescription = (
527
549
  %mimeType = (
528
550
  '3FR' => 'image/x-hasselblad-3fr',
529
551
  AA => 'audio/audible',
552
+ AAE => 'application/vnd.apple.photos',
530
553
  AI => 'application/vnd.adobe.illustrator',
531
554
  AIFF => 'audio/x-aiff',
532
555
  APE => 'audio/x-monkeys-audio',
@@ -540,8 +563,11 @@ my %fileDescription = (
540
563
  'Canon 1D RAW' => 'image/x-raw', # (uses .TIF file extension)
541
564
  CHM => 'application/x-chm',
542
565
  CR2 => 'image/x-canon-cr2',
566
+ CR3 => 'image/x-canon-cr3',
567
+ CRM => 'video/x-canon-crm',
543
568
  CRW => 'image/x-canon-crw',
544
569
  DCR => 'image/x-kodak-dcr',
570
+ DCX => 'image/dcx',
545
571
  DEX => 'application/octet-stream',
546
572
  DFONT=> 'application/x-dfont',
547
573
  DICOM=> 'application/dicom',
@@ -558,12 +584,17 @@ my %fileDescription = (
558
584
  DS2 => 'audio/x-ds2',
559
585
  DSS => 'audio/x-dss',
560
586
  DV => 'video/x-dv',
587
+ 'DVR-MS' => 'video/x-ms-dvr',
588
+ DWF => 'model/vnd.dwf',
589
+ DWG => 'image/vnd.dwg',
561
590
  EIP => 'application/x-captureone', #(NC)
562
591
  EPS => 'application/postscript',
563
592
  ERF => 'image/x-epson-erf',
564
593
  EXE => 'application/octet-stream',
594
+ EXR => 'image/x-exr',
565
595
  EXV => 'image/x-exv',
566
596
  FFF => 'image/x-hasselblad-fff',
597
+ FITS => 'image/fits',
567
598
  FLA => 'application/vnd.adobe.fla',
568
599
  FLAC => 'audio/flac',
569
600
  FLIF => 'image/flif',
@@ -571,6 +602,7 @@ my %fileDescription = (
571
602
  Font => 'application/x-font-type1', # covers PFA, PFB and PFM (not sure about PFM)
572
603
  FPX => 'image/vnd.fpx',
573
604
  GIF => 'image/gif',
605
+ GPR => 'image/x-gopro-gpr',
574
606
  GZIP => 'application/x-gzip',
575
607
  HDP => 'image/vnd.ms-photo',
576
608
  HDR => 'image/vnd.radiance',
@@ -583,8 +615,8 @@ my %fileDescription = (
583
615
  INX => 'application/x-indesign-interchange', #PH (NC)
584
616
  ISO => 'application/x-iso9660-image',
585
617
  ITC => 'application/itunes',
586
- JNG => 'image/jng',
587
618
  J2C => 'image/x-j2c', #PH (NC)
619
+ JNG => 'image/jng',
588
620
  JP2 => 'image/jp2',
589
621
  JPEG => 'image/jpeg',
590
622
  JPM => 'image/jpm',
@@ -592,11 +624,13 @@ my %fileDescription = (
592
624
  JSON => 'application/json',
593
625
  K25 => 'image/x-kodak-k25',
594
626
  KDC => 'image/x-kodak-kdc',
627
+ KEY => 'application/x-iwork-keynote-sffkey',
595
628
  LFP => 'image/x-lytro-lfp', #PH (NC)
596
629
  LNK => 'application/octet-stream',
597
- MAX => 'application/x-3ds',
630
+ LRI => 'image/x-light-lri',
598
631
  M2T => 'video/mpeg',
599
632
  M2TS => 'video/m2ts',
633
+ MAX => 'application/x-3ds',
600
634
  MEF => 'image/x-mamiya-mef',
601
635
  MIE => 'application/x-mie',
602
636
  MIFF => 'application/x-magick-image',
@@ -615,6 +649,7 @@ my %fileDescription = (
615
649
  MXF => 'application/mxf',
616
650
  NEF => 'image/x-nikon-nef',
617
651
  NRW => 'image/x-nikon-nrw',
652
+ NUMBERS => 'application/x-iwork-numbers-sffnumbers',
618
653
  ODB => 'application/vnd.oasis.opendocument.database',
619
654
  ODC => 'application/vnd.oasis.opendocument.chart',
620
655
  ODF => 'application/vnd.oasis.opendocument.formula',
@@ -625,11 +660,12 @@ my %fileDescription = (
625
660
  ODT => 'application/vnd.oasis.opendocument.text',
626
661
  OGG => 'audio/ogg',
627
662
  OGV => 'video/ogg',
628
- EXR => 'image/x-exr',
629
663
  ORF => 'image/x-olympus-orf',
630
664
  OTF => 'application/x-font-otf',
665
+ PAGES=> 'application/x-iwork-pages-sffpages',
631
666
  PBM => 'image/x-portable-bitmap',
632
667
  PCD => 'image/x-photo-cd',
668
+ PCX => 'image/pcx',
633
669
  PDB => 'application/vnd.palm',
634
670
  PDF => 'application/pdf',
635
671
  PEF => 'image/x-pentax-pef',
@@ -654,6 +690,7 @@ my %fileDescription = (
654
690
  PSD => 'application/vnd.adobe.photoshop',
655
691
  PSP => 'image/x-paintshoppro', #(NC)
656
692
  QTIF => 'image/x-quicktime',
693
+ R3D => 'video/x-red-r3d', #PH (invented)
657
694
  RA => 'audio/x-pn-realaudio',
658
695
  RAF => 'image/x-fujifilm-raf',
659
696
  RAM => 'audio/x-pn-realaudio',
@@ -668,6 +705,7 @@ my %fileDescription = (
668
705
  RW2 => 'image/x-panasonic-rw2',
669
706
  RWL => 'image/x-leica-rwl',
670
707
  RWZ => 'image/x-rawzor', #(duplicated in Rawzor.pm)
708
+ SKETCH => 'application/sketch',
671
709
  SR2 => 'image/x-sony-sr2',
672
710
  SRF => 'image/x-sony-srf',
673
711
  SRW => 'image/x-samsung-srw',
@@ -686,6 +724,7 @@ my %fileDescription = (
686
724
  WMA => 'audio/x-ms-wma',
687
725
  WMF => 'application/x-wmf',
688
726
  WMV => 'video/x-ms-wmv',
727
+ WTV => 'video/x-ms-wtv',
689
728
  X3F => 'image/x-sigma-x3f',
690
729
  XCF => 'image/x-xcf',
691
730
  XLA => 'application/vnd.ms-excel',
@@ -716,8 +755,11 @@ my %moduleName = (
716
755
  COS => 'CaptureOne',
717
756
  DEX => 0,
718
757
  DOCX => 'OOXML',
758
+ DCX => 0,
719
759
  DR4 => 'CanonVRD',
720
760
  DSS => 'Olympus',
761
+ DWF => 0,
762
+ DWG => 0,
721
763
  EPS => 'PostScript',
722
764
  EXIF => '',
723
765
  EXR => 'OpenEXR',
@@ -732,6 +774,7 @@ my %moduleName = (
732
774
  JP2 => 'Jpeg2000',
733
775
  JPEG => '',
734
776
  LFP => 'Lytro',
777
+ LRI => 0,
735
778
  MOV => 'QuickTime',
736
779
  MKV => 'Matroska',
737
780
  MP3 => 'ID3',
@@ -745,6 +788,7 @@ my %moduleName = (
745
788
  PS => 'PostScript',
746
789
  PSD => 'Photoshop',
747
790
  QTIF => 'QuickTime',
791
+ R3D => 'Red',
748
792
  RAF => 'FujiFilm',
749
793
  RAR => 'ZIP',
750
794
  RAW => 'KyoceraRaw',
@@ -775,6 +819,7 @@ my %moduleName = (
775
819
  BZ2 => 'BZh[1-9]\x31\x41\x59\x26\x53\x59',
776
820
  CHM => 'ITSF.{20}\x10\xfd\x01\x7c\xaa\x7b\xd0\x11\x9e\x0c\0\xa0\xc9\x22\xe6\xec',
777
821
  CRW => '(II|MM).{4}HEAP(CCDR|JPGM)',
822
+ DCX => '\xb1\x68\xde\x3a',
778
823
  DEX => "dex\n035\0",
779
824
  DICOM=> '(.{128}DICM|\0[\x02\x04\x06\x08]\0[\0-\x20]|[\x02\x04\x06\x08]\0[\0-\x20]\0)',
780
825
  DOCX => 'PK\x03\x04',
@@ -782,11 +827,14 @@ my %moduleName = (
782
827
  DR4 => 'IIII\x04\0\x04\0',
783
828
  DSS => '(\x02dss|\x03ds2)',
784
829
  DV => '\x1f\x07\0[\x3f\xbf]', # (not tested if extension recognized)
830
+ DWF => '\(DWF V\d',
831
+ DWG => 'AC10\d{2}\0',
785
832
  EPS => '(%!PS|%!Ad|\xc5\xd0\xd3\xc6)',
786
833
  EXE => '(MZ|\xca\xfe\xba\xbe|\xfe\xed\xfa[\xce\xcf]|[\xce\xcf]\xfa\xed\xfe|Joy!peff|\x7fELF|#!\s*/\S*bin/|!<arch>\x0a)',
787
834
  EXIF => '(II\x2a\0|MM\0\x2a)',
788
835
  EXR => '\x76\x2f\x31\x01',
789
836
  EXV => '\xff\x01Exiv2',
837
+ FITS => 'SIMPLE = {20}T',
790
838
  FLAC => '(fLaC|ID3)',
791
839
  FLIF => 'FLIF[0-\x6f][0-2]',
792
840
  FLIR => '[AF]FF\0',
@@ -804,10 +852,11 @@ my %moduleName = (
804
852
  # ISO => signature is at byte 32768
805
853
  ITC => '.{4}itch',
806
854
  JP2 => '(\0\0\0\x0cjP( |\x1a\x1a)\x0d\x0a\x87\x0a|\xff\x4f\xff\x51\0)',
807
- JPEG => '\xff(\xd8\xff|\x01Exiv2)', # (includes EXV so we don't have to add EXV to @fileTypes)
808
- JSON => '\s*(\[\s*)?\{\s*"[^"]+"\s*:',
855
+ JPEG => '\xff\xd8\xff',
856
+ JSON => '(\xef\xbb\xbf)?\s*(\[\s*)?\{\s*"[^"]+"\s*:',
809
857
  LFP => '\x89LFP\x0d\x0a\x1a\x0a',
810
858
  LNK => '.{4}\x01\x14\x02\0{5}\xc0\0{6}\x46',
859
+ LRI => 'LELR \0',
811
860
  M2TS => '(....)?\x47',
812
861
  MIE => '~[\x10\x18]\x04.0MIE',
813
862
  MIFF => 'id=ImageMagick',
@@ -823,7 +872,8 @@ my %moduleName = (
823
872
  ORF => '(II|MM)',
824
873
  PDB => '.{60}(\.pdfADBE|TEXtREAd|BVokBDIC|DB99DBOS|PNRdPPrs|DataPPrs|vIMGView|PmDBPmDB|InfoINDB|ToGoToGo|SDocSilX|JbDbJBas|JfDbJFil|DATALSdb|Mdb1Mdb1|BOOKMOBI|DataPlkr|DataSprd|SM01SMem|TEXtTlDc|InfoTlIf|DataTlMl|DataTlPt|dataTDBP|TdatTide|ToRaTRPW|zTXTGPlm|BDOCWrdS)',
825
874
  # PCD => signature is at byte 2048
826
- PDF => '%PDF-\d+\.\d+',
875
+ PCX => '\x0a[\0-\x05]\x01[\x01\x02\x04\x08].{64}[\0-\x02]',
876
+ PDF => '\s*%PDF-\d+\.\d+',
827
877
  PGF => 'PGF',
828
878
  PHP => '<\?php\s',
829
879
  PICT => '(.{10}|.{522})(\x11\x01|\x00\x11)',
@@ -835,6 +885,7 @@ my %moduleName = (
835
885
  PSD => '8BPS\0[\x01\x02]',
836
886
  PSP => 'Paint Shop Pro Image File\x0a\x1a\0{5}',
837
887
  QTIF => '.{4}(idsc|idat|iicc)',
888
+ R3D => '\0\0..RED(1|2)',
838
889
  RAF => 'FUJIFILM',
839
890
  RAR => 'Rar!\x1a\x07\0',
840
891
  RAW => '(.{25}ARECOYK|II|MM)',
@@ -849,6 +900,7 @@ my %moduleName = (
849
900
  VCard=> '(?i)BEGIN:(VCARD|VCALENDAR)\r\n',
850
901
  VRD => 'CANON OPTIONAL DATA\0',
851
902
  WMF => '(\xd7\xcd\xc6\x9a\0\0|\x01\0\x09\0\0\x03)',
903
+ WTV => '\xb7\xd8\x00\x20\x37\x49\xda\x11\xa6\x4e\x00\x07\xe9\x5e\xad\x8d',
852
904
  X3F => 'FOVb',
853
905
  XCF => 'gimp xcf ',
854
906
  XMP => '\0{0,3}(\xfe\xff|\xff\xfe|\xef\xbb\xbf)?\0{0,3}\s*<',
@@ -904,7 +956,7 @@ my %allGroupsExifTool = ( 0 => 'ExifTool', 1 => 'ExifTool', 2 => 'ExifTool' );
904
956
  WRITABLE TABLE_DESC NOTES IS_OFFSET IS_SUBDIR
905
957
  EXTRACT_UNKNOWN NAMESPACE PREFERRED SRC_TABLE PRIORITY
906
958
  AVOID WRITE_GROUP LANG_INFO VARS DATAMEMBER
907
- SET_GROUP1
959
+ SET_GROUP1 PERMANENT
908
960
  );
909
961
 
910
962
  # headers for various segment types
@@ -1105,10 +1157,11 @@ my %systemTagsNotes = (
1105
1157
  FileCreateDate => {
1106
1158
  Description => 'File Creation Date/Time',
1107
1159
  Notes => q{
1108
- the filesystem creation date/time. Windows only. This tag is writable and
1109
- is preserved by default when writing if Win32API::File and Win32::API are
1110
- available. See L<MDItemFSCreationDate|MacOS.html#MDItem> for the Mac OS X
1111
- equivalent
1160
+ the filesystem creation date/time. Windows/Mac only. In Windows, the file
1161
+ creation date/time is preserved by default when writing if Win32API::File
1162
+ and Win32::API are available. On Mac, this tag is extracted only if it or
1163
+ the MacOS group is specifically requested or the RequestAll API option is
1164
+ set to 2 or higher. Requires "setfile" for writing on Mac
1112
1165
  },
1113
1166
  Groups => { 1 => 'System', 2 => 'Time' },
1114
1167
  Writable => 1,
@@ -1116,13 +1169,12 @@ my %systemTagsNotes = (
1116
1169
  DelCheck => q{"Can't delete"},
1117
1170
  Protected => 1, # all writable pseudo-tags must be protected!
1118
1171
  Shift => 'Time',
1119
- ValueConv => 'ConvertUnixTime($val,1)',
1172
+ ValueConv => '$^O eq "darwin" ? $val : ConvertUnixTime($val,1)',
1120
1173
  ValueConvInv => q{
1121
- if ($^O ne 'MSWin32') {
1122
- warn "This tag is Windows only\n";
1123
- return undef;
1124
- }
1125
- return GetUnixTime($val,1);
1174
+ return GetUnixTime($val,1) if $^O eq 'MSWin32';
1175
+ return $val if $^O eq 'darwin';
1176
+ warn "This tag is Windows/Mac only\n";
1177
+ return undef;
1126
1178
  },
1127
1179
  PrintConv => '$self->ConvertDateTime($val)',
1128
1180
  PrintConvInv => '$self->InverseDateTime($val)',
@@ -1264,12 +1316,30 @@ my %systemTagsNotes = (
1264
1316
  WritePseudo => 1,
1265
1317
  Protected => 1,
1266
1318
  Notes => q{
1267
- this write-only tag is used to create a hard link to the file. If the file
1268
- is edited, copied, renamed or moved in the same operation as writing
1269
- HardLink, then the link is made to the updated file. Note that subsequent
1270
- editing of either the linked file or the original by the exiftool
1271
- application will break the link unless the -overwrite_original_in_place
1272
- option is used
1319
+ this write-only tag is used to create a hard link with the specified name to
1320
+ the source file. If the source file is edited, copied, renamed or moved in
1321
+ the same operation as writing HardLink, then the link is made to the updated
1322
+ file. Note that subsequent editing of either hard-linked file by exiftool
1323
+ will break the link unless the -overwrite_original_in_place option is used
1324
+ },
1325
+ ValueConvInv => '$val=~tr/\\\\/\//; $val',
1326
+ },
1327
+ SymLink => {
1328
+ Writable => 1,
1329
+ DelCheck => q{"Can't delete"},
1330
+ WriteOnly => 1,
1331
+ WritePseudo => 1,
1332
+ Protected => 1,
1333
+ Notes => q{
1334
+ this write-only tag is used to create a symbolic link with the specified
1335
+ name to the source file. If the source file is edited, copied, renamed or
1336
+ moved in the same operation as writing SymLink, then the link is made to the
1337
+ updated file. The link uses an absolute path unless it is created in the
1338
+ current working directory. Valid only for file systems that support
1339
+ symbolic links. Note that subsequent editing of the file via the symbolic
1340
+ link by exiftool will cause the link to be replaced by the edited file
1341
+ without changing the original unless the -overwrite_original_in_place option
1342
+ is used
1273
1343
  },
1274
1344
  ValueConvInv => '$val=~tr/\\\\/\//; $val',
1275
1345
  },
@@ -1385,6 +1455,16 @@ my %systemTagsNotes = (
1385
1455
  # accept either scalar or scalar reference
1386
1456
  RawConv => '$self->ValidateImage(ref $val ? $val : \$val, $tag)',
1387
1457
  },
1458
+ ThumbnailImage => {
1459
+ Groups => { 2 => 'Preview' },
1460
+ Notes => 'JPEG-format embedded thumbnail image',
1461
+ RawConv => '$self->ValidateImage(ref $val ? $val : \$val, $tag)',
1462
+ },
1463
+ OtherImage => {
1464
+ Groups => { 2 => 'Preview' },
1465
+ Notes => 'other JPEG-format embedded image',
1466
+ RawConv => '$self->ValidateImage(ref $val ? $val : \$val, $tag)',
1467
+ },
1388
1468
  PreviewPNG => {
1389
1469
  Groups => { 2 => 'Preview' },
1390
1470
  Notes => 'PNG-format embedded preview image',
@@ -1400,6 +1480,11 @@ my %systemTagsNotes = (
1400
1480
  Notes => 'TIFF-format embedded preview image',
1401
1481
  Binary => 1,
1402
1482
  },
1483
+ PreviewPDF => {
1484
+ Groups => { 2 => 'Preview' },
1485
+ Notes => 'PDF-format embedded preview image',
1486
+ Binary => 1,
1487
+ },
1403
1488
  ExifByteOrder => {
1404
1489
  Writable => 1,
1405
1490
  DelCheck => q{"Can't delete"},
@@ -1421,7 +1506,8 @@ my %systemTagsNotes = (
1421
1506
  specification is particularly vague about this byte ordering, and different
1422
1507
  applications use different conventions. By default ExifTool writes Unicode
1423
1508
  text in EXIF byte order, but this write-only tag may be used to force a
1424
- specific order
1509
+ specific order. Applies to the EXIF UserComment tag when writing special
1510
+ characters
1425
1511
  },
1426
1512
  PrintConv => {
1427
1513
  II => 'Little-endian (Intel, II)',
@@ -1461,6 +1547,13 @@ my %systemTagsNotes = (
1461
1547
  higher
1462
1548
  },
1463
1549
  },
1550
+ JPEGImageLength => {
1551
+ Notes => q{
1552
+ byte length of JPEG image without metadata. For performance reasons, this
1553
+ tag is generated only if specifically requested or the RequestAll API option
1554
+ is set to 3 or higher
1555
+ },
1556
+ },
1464
1557
  # Validate (added from Validate.pm)
1465
1558
  Now => {
1466
1559
  Groups => { 0 => 'ExifTool', 1 => 'ExifTool', 2 => 'Time' },
@@ -1909,17 +2002,19 @@ sub Options($$;@)
1909
2002
  $$options{$param} = \%newParams;
1910
2003
  next;
1911
2004
  }
2005
+ my $force;
1912
2006
  # set/reset single UserParam parameter
1913
2007
  if ($newVal =~ /(.*?)=(.*)/s) {
1914
2008
  $param = lc $1;
1915
2009
  $newVal = $2;
2010
+ $force = 1 if $param =~ s/\^$//;
1916
2011
  } else {
1917
2012
  $param = lc $newVal;
1918
2013
  undef $newVal;
1919
2014
  }
1920
2015
  $oldVal = $$options{UserParam}{$param};
1921
2016
  if (defined $newVal) {
1922
- if (length $newVal) {
2017
+ if (length $newVal or $force) {
1923
2018
  $$options{UserParam}{$param} = $newVal;
1924
2019
  } else {
1925
2020
  delete $$options{UserParam}{$param};
@@ -1943,7 +2038,7 @@ sub Options($$;@)
1943
2038
  }
1944
2039
  } elsif ($param eq 'ListJoin') {
1945
2040
  $$options{$param} = $newVal;
1946
- # ListJoin just sets the List and ListSep options for backward compatibility
2041
+ # set the old List and ListSep options for backward compatibility
1947
2042
  if (defined $newVal) {
1948
2043
  $$options{List} = 0;
1949
2044
  $$options{ListSep} = $newVal;
@@ -1951,6 +2046,10 @@ sub Options($$;@)
1951
2046
  $$options{List} = 1;
1952
2047
  # (ListSep must be defined)
1953
2048
  }
2049
+ } elsif ($param eq 'List') {
2050
+ $$options{$param} = $newVal;
2051
+ # set the new ListJoin option for forward compatibility
2052
+ $$options{ListJoin} = $newVal ? undef : $$options{ListSep};
1954
2053
  } else {
1955
2054
  if ($param eq 'Escape') {
1956
2055
  # set ESCAPE_PROC
@@ -1967,6 +2066,12 @@ sub Options($$;@)
1967
2066
  $$self{BOTH} = { };
1968
2067
  } elsif ($param eq 'GlobalTimeShift') {
1969
2068
  delete $$self{GLOBAL_TIME_OFFSET}; # reset our calculated offset
2069
+ } elsif ($param eq 'TimeZone' and defined $newVal and length $newVal) {
2070
+ $ENV{TZ} = $newVal;
2071
+ eval { require POSIX; POSIX::tzset() };
2072
+ } elsif ($param eq 'Validate') {
2073
+ # load Validate module if Validate option enabled
2074
+ $newVal and require Image::ExifTool::Validate;
1970
2075
  }
1971
2076
  $$options{$param} = $newVal;
1972
2077
  }
@@ -2018,6 +2123,7 @@ sub ClearOptions($)
2018
2123
  GeoMaxHDOP => undef, # geotag maximum HDOP
2019
2124
  GeoMaxPDOP => undef, # geotag maximum PDOP
2020
2125
  GeoMinSats => undef, # geotag minimum satellites
2126
+ GeoSpeedRef => undef, # geotag GPSSpeedRef
2021
2127
  GlobalTimeShift => undef, # apply time shift to all extracted date/time values
2022
2128
  # Group# => undef, # return tags for specified groups in family #
2023
2129
  HtmlDump => 0, # HTML dump (0-3, higher # = bigger limit)
@@ -2048,6 +2154,7 @@ sub ClearOptions($)
2048
2154
  Struct => undef, # return structures as hash references
2049
2155
  SystemTags => undef, # extract additional File System tags
2050
2156
  TextOut => \*STDOUT,# file for Verbose/HtmlDump output
2157
+ TimeZone => undef, # local time zone
2051
2158
  Unknown => 0, # flag to get values of unknown tags (0-2)
2052
2159
  UserParam => { }, # user parameters for InsertTagValues()
2053
2160
  Validate => undef, # perform additional validation
@@ -2126,7 +2233,7 @@ sub ExtractInfo($;@)
2126
2233
 
2127
2234
  # return our version number
2128
2235
  $self->FoundTag('ExifToolVersion', "$VERSION$RELEASE");
2129
- $self->FoundTag('Now', TimeNow()) if $$req{now} or $reqAll;
2236
+ $self->FoundTag('Now', $self->TimeNow()) if $$req{now} or $reqAll;
2130
2237
  $self->FoundTag('NewGUID', NewGUID()) if $$req{newguid} or $reqAll;
2131
2238
  # generate sequence number if necessary
2132
2239
  $self->FoundTag('FileSequence', $$self{FILE_SEQUENCE}) if $$req{filesequence} or $reqAll;
@@ -2194,7 +2301,7 @@ sub ExtractInfo($;@)
2194
2301
  }
2195
2302
  }
2196
2303
 
2197
- if ($raf) {
2304
+ while ($raf) {
2198
2305
  my (@stat, $fileSize);
2199
2306
  if ($reEntry) {
2200
2307
  # we already set these tags
@@ -2249,10 +2356,12 @@ sub ExtractInfo($;@)
2249
2356
  # extract MDItem tags if requested (only on plain files)
2250
2357
  if ($^O eq 'darwin' and defined $filename and $filename ne '' and defined $fileSize) {
2251
2358
  my $reqMacOS = ($reqAll > 1 or $$req{'macos:'});
2359
+ my $crDate = ($reqMacOS || $$req{filecreatedate});
2252
2360
  my $mdItem = ($reqMacOS || $$options{MDItemTags} || grep /^mditem/, keys %$req);
2253
2361
  my $xattr = ($reqMacOS || $$options{XAttrTags} || grep /^xattr/, keys %$req);
2254
- if ($mdItem or $xattr) {
2362
+ if ($crDate or $mdItem or $xattr) {
2255
2363
  require Image::ExifTool::MacOS;
2364
+ Image::ExifTool::MacOS::GetFileCreateDate($self, $filename) if $crDate;
2256
2365
  Image::ExifTool::MacOS::ExtractMDItemTags($self, $filename) if $mdItem;
2257
2366
  Image::ExifTool::MacOS::ExtractXAttrTags($self, $filename) if $xattr;
2258
2367
  }
@@ -2265,6 +2374,15 @@ sub ExtractInfo($;@)
2265
2374
  $recognizedExt = $ext if defined $ext and not defined $magicNumber{$ext} and
2266
2375
  defined $moduleName{$ext} and not $moduleName{$ext};
2267
2376
  my @fileTypeList = GetFileType($realname);
2377
+ if ($fast and $fast >= 4) {
2378
+ if (@fileTypeList) {
2379
+ $type = shift @fileTypeList;
2380
+ $self->SetFileType($$self{FILE_TYPE} = $type);
2381
+ } else {
2382
+ $self->Error('Unknown file type');
2383
+ }
2384
+ last; # don't read the file
2385
+ }
2268
2386
  if (@fileTypeList) {
2269
2387
  # add remaining types to end of list so we test them all
2270
2388
  my $pat = join '|', @fileTypeList;
@@ -2286,16 +2404,21 @@ sub ExtractInfo($;@)
2286
2404
  my %dirInfo = ( RAF => $raf, Base => $pos );
2287
2405
  # loop through list of file types to test
2288
2406
  my ($buff, $seekErr);
2289
- # read first 1024 bytes of file for testing
2290
- $raf->Read($buff, 1024) or $buff = '';
2407
+ # read start of file for testing
2408
+ my $testLen = 1024;
2409
+ $raf->Read($buff, $testLen) or $buff = '';
2291
2410
  $raf->Seek($pos, 0) or $seekErr = 1;
2292
2411
  until ($seekErr) {
2293
2412
  my $unkHeader;
2294
2413
  $type = shift @fileTypeList;
2295
2414
  if ($type) {
2296
- # do quick test for this file type to avoid loading module unnecessarily
2297
- next if $magicNumber{$type} and $buff !~ /^$magicNumber{$type}/s and
2298
- not $noMagic{$type};
2415
+ if ($magicNumber{$type}) {
2416
+ # do quick test for this file type to avoid loading module unnecessarily
2417
+ next if $buff !~ /^$magicNumber{$type}/s and not $noMagic{$type};
2418
+ } else {
2419
+ # keep checking for other types if we recognize this file only by extension
2420
+ next if defined $moduleName{$type} and not $moduleName{$type};
2421
+ }
2299
2422
  next if $weakMagic{$type} and defined $recognizedExt;
2300
2423
  } elsif (not defined $type) {
2301
2424
  last;
@@ -2355,6 +2478,54 @@ sub ExtractInfo($;@)
2355
2478
  # seek back to try again from the same position in the file
2356
2479
  $raf->Seek($pos, 0) or $seekErr = 1, last;
2357
2480
  }
2481
+ if (not defined $type and not $$self{DOC_NUM}) {
2482
+ # if we were given a single image with a known type there
2483
+ # must be a format error since we couldn't read it, otherwise
2484
+ # it is likely we don't support images of this type
2485
+ my $fileType = GetFileType($realname) || '';
2486
+ my $err;
2487
+ if (not length $buff) {
2488
+ $err = 'File is empty';
2489
+ } else {
2490
+ my $ch = substr($buff, 0, 1);
2491
+ if (length $buff < 16 or $buff =~ /[^\Q$ch\E]/) {
2492
+ if ($fileType eq 'RAW') {
2493
+ $err = 'Unsupported RAW file type';
2494
+ } elsif ($fileType) {
2495
+ $err = 'File format error';
2496
+ } else {
2497
+ $err = 'Unknown file type';
2498
+ }
2499
+ } else {
2500
+ # provide some insight into the content of some corrupted files
2501
+ if ($$self{OPTIONS}{FastScan}) {
2502
+ $err = 'File header is all';
2503
+ } else {
2504
+ my $num = length $buff;
2505
+ for (;;) {
2506
+ $raf->Read($buff, 65536) or undef($num), last;
2507
+ $buff =~ /[^\Q$ch\E]/g and $num += pos($buff) - 1, last;
2508
+ $num += length($buff);
2509
+ }
2510
+ if ($num) {
2511
+ $err = 'First ' . ConvertFileSize($num) . ' of file is';
2512
+ } else {
2513
+ $err = 'Entire file is';
2514
+ }
2515
+ }
2516
+ if ($ch eq "\0") {
2517
+ $err .= ' binary zeros';
2518
+ } elsif ($ch eq ' ') {
2519
+ $err .= ' ASCII spaces';
2520
+ } elsif ($ch =~ /[a-zA-Z0-9]/) {
2521
+ $err .= " ASCII '${ch}' characters";
2522
+ } else {
2523
+ $err .= sprintf(" binary 0x%.2x's", ord $ch);
2524
+ }
2525
+ }
2526
+ }
2527
+ $self->Error($err);
2528
+ }
2358
2529
  if ($seekErr) {
2359
2530
  $self->Error('Error seeking in file');
2360
2531
  } elsif ($self->Options('ScanForXMP') and (not defined $type or
@@ -2365,21 +2536,6 @@ sub ExtractInfo($;@)
2365
2536
  require Image::ExifTool::XMP;
2366
2537
  Image::ExifTool::XMP::ScanForXMP($self, $raf) and $type = '';
2367
2538
  }
2368
- if (not defined $type and not $$self{DOC_NUM}) {
2369
- # if we were given a single image with a known type there
2370
- # must be a format error since we couldn't read it, otherwise
2371
- # it is likely we don't support images of this type
2372
- my $fileType = GetFileType($realname);
2373
- my $err;
2374
- if (not $fileType) {
2375
- $err = 'Unknown file type';
2376
- } elsif ($fileType eq 'RAW') {
2377
- $err = 'Unsupported RAW file type';
2378
- } else {
2379
- $err = 'File format error';
2380
- }
2381
- $self->Error($err);
2382
- }
2383
2539
  # extract binary EXIF data block only if requested
2384
2540
  if (defined $$self{EXIF_DATA} and length $$self{EXIF_DATA} > 16 and
2385
2541
  ($$req{exif} or
@@ -2400,6 +2556,7 @@ sub ExtractInfo($;@)
2400
2556
  $pos = ($$self{FIRST_EXIF_POS} || 0) unless defined $pos;
2401
2557
  my $dataPt = defined $$self{EXIF_DATA} ? \$$self{EXIF_DATA} : undef;
2402
2558
  undef $dataPt if defined $$self{EXIF_POS} and $pos != $$self{EXIF_POS};
2559
+ undef $dataPt if $$self{ExtendedEXIF}; # can't use EXIF block if not contiguous
2403
2560
  my $success = $$self{HTML_DUMP}->Print($raf, $dataPt, $pos,
2404
2561
  $$options{TextOut}, $$options{HtmlDump},
2405
2562
  $$self{FILENAME} ? "HTML Dump ($$self{FILENAME})" : 'HTML Dump');
@@ -2422,11 +2579,11 @@ sub ExtractInfo($;@)
2422
2579
  }
2423
2580
  }
2424
2581
  }
2582
+ last; # (loop was a cheap "goto")
2425
2583
  }
2426
2584
 
2427
2585
  # generate Validate tag if requested
2428
2586
  if ($$options{Validate} and not $reEntry) {
2429
- require Image::ExifTool::Validate;
2430
2587
  Image::ExifTool::Validate::FinishValidate($self, $$req{validate});
2431
2588
  }
2432
2589
 
@@ -2646,7 +2803,8 @@ sub GetRequestedTags($)
2646
2803
  #------------------------------------------------------------------------------
2647
2804
  # Get tag value
2648
2805
  # Inputs: 0) ExifTool object reference
2649
- # 1) tag key (or flattened tagInfo for getting field values, not part of public API)
2806
+ # 1) tag key or tag name with optional group names (case sensitive)
2807
+ # (or flattened tagInfo for getting field values, not part of public API)
2650
2808
  # 2) [optional] Value type: PrintConv, ValueConv, Both, Raw or Rational, the default
2651
2809
  # is PrintConv or ValueConv, depending on the PrintConv option setting
2652
2810
  # 3) raw field value (not part of public API)
@@ -2657,7 +2815,24 @@ sub GetValue($$;$)
2657
2815
  local $_;
2658
2816
  my ($self, $tag, $type) = @_; # plus: ($fieldValue)
2659
2817
  my (@convTypes, $tagInfo, $valueConv, $both);
2818
+ my $rawValue = $$self{VALUE};
2660
2819
 
2820
+ # get specific tag key if tag has a group name
2821
+ if ($tag =~ /^(.*):(.+)/) {
2822
+ my ($gp, $tg) = ($1, $2);
2823
+ my ($i, $key, @keys);
2824
+ # build list of tag keys in the order of priority (no index
2825
+ # is top priority, otherwise higher index is higher priority)
2826
+ for ($key=$tg, $i=$$self{DUPL_TAG}{$tg} || 0; ; --$i) {
2827
+ push @keys, $key if defined $$rawValue{$key};
2828
+ last if $i <= 0;
2829
+ $key = "$tg ($i)";
2830
+ }
2831
+ if (@keys) {
2832
+ $key = $self->GroupMatches($gp, \@keys);
2833
+ $tag = $key if $key;
2834
+ }
2835
+ }
2661
2836
  # figure out what conversions to do
2662
2837
  if ($type) {
2663
2838
  return $$self{RATIONAL}{$tag} if $type eq 'Rational';
@@ -2666,7 +2841,7 @@ sub GetValue($$;$)
2666
2841
  }
2667
2842
 
2668
2843
  # start with the raw value
2669
- my $value = $$self{VALUE}{$tag};
2844
+ my $value = $$rawValue{$tag};
2670
2845
  if (not defined $value) {
2671
2846
  return () unless ref $tag;
2672
2847
  # get the value of a structure field
@@ -2785,7 +2960,7 @@ sub GetValue($$;$)
2785
2960
  my $oldFilter = $$self{OPTIONS}{Filter};
2786
2961
  delete $$self{OPTIONS}{Filter};
2787
2962
  foreach (keys %$val) {
2788
- $raw[$_] = $$self{VALUE}{$$val{$_}};
2963
+ $raw[$_] = $$rawValue{$$val{$_}};
2789
2964
  ($val[$_], $prt[$_]) = $self->GetValue($$val{$_}, 'Both');
2790
2965
  next if defined $val[$_] or not $$tagInfo{Require}{$_};
2791
2966
  $$self{OPTIONS}{Filter} = $oldFilter if defined $oldFilter;
@@ -2832,9 +3007,8 @@ sub GetValue($$;$)
2832
3007
  $self->Warn("$convType $tag: " . CleanWarning()) if $evalWarning;
2833
3008
  }
2834
3009
  if (not defined $value) {
2835
- if (($$tagInfo{PrintHex} or
2836
- ($$tagInfo{Mask} and not defined $$tagInfo{PrintHex}))
2837
- and $val and IsInt($val) and $convType eq 'PrintConv')
3010
+ if ($$tagInfo{PrintHex} and $val and IsInt($val) and
3011
+ $convType eq 'PrintConv')
2838
3012
  {
2839
3013
  $val = sprintf('0x%x',$val);
2840
3014
  }
@@ -3108,6 +3282,7 @@ sub GetGroups($;$$)
3108
3282
  # Set priority for group where new values are written
3109
3283
  # Inputs: 0) ExifTool object reference,
3110
3284
  # 1-N) group names (reset to default if no groups specified)
3285
+ # - used when new tag values are set (ie. before files are written)
3111
3286
  sub SetNewGroups($;@)
3112
3287
  {
3113
3288
  local $_;
@@ -3137,24 +3312,19 @@ sub BuildCompositeTags($)
3137
3312
  my $self = shift;
3138
3313
 
3139
3314
  $$self{BuildingComposite} = 1;
3140
- # first, add user-defined Composite tags if necessary
3141
- if (%UserDefined and $UserDefined{'Image::ExifTool::Composite'}) {
3142
- AddCompositeTags($UserDefined{'Image::ExifTool::Composite'}, 1);
3143
- delete $UserDefined{'Image::ExifTool::Composite'};
3144
- }
3145
- my @tagList = sort keys %Image::ExifTool::Composite;
3146
- my %tagsUsed;
3147
3315
 
3316
+ my $compTable = GetTagTable('Image::ExifTool::Composite');
3317
+ my @tagList = sort keys %$compTable;
3148
3318
  my $rawValue = $$self{VALUE};
3319
+ my (%tagsUsed, %cache);
3320
+
3149
3321
  for (;;) {
3150
- my %notBuilt;
3322
+ my (%notBuilt, $tag, @deferredTags);
3151
3323
  $notBuilt{$_} = 1 foreach @tagList;
3152
- my @deferredTags;
3153
- my $tag;
3154
3324
  COMPOSITE_TAG:
3155
3325
  foreach $tag (@tagList) {
3156
3326
  next if $specialTags{$tag};
3157
- my $tagInfo = $self->GetTagInfo(\%Image::ExifTool::Composite, $tag);
3327
+ my $tagInfo = $self->GetTagInfo($compTable, $tag);
3158
3328
  next unless $tagInfo;
3159
3329
  # put required tags into array and make sure they all exist
3160
3330
  my $subDoc = ($$tagInfo{SubDoc} and $$self{DOC_COUNT});
@@ -3162,7 +3332,7 @@ COMPOSITE_TAG:
3162
3332
  my $desire = $$tagInfo{Desire} || { };
3163
3333
  my $inhibit = $$tagInfo{Inhibit} || { };
3164
3334
  # loop through sub-documents if necessary
3165
- my $doc;
3335
+ my $docNum = 0;
3166
3336
  for (;;) {
3167
3337
  my (%tagKey, $found, $index);
3168
3338
  # save Require'd and Desire'd tag values in list
@@ -3173,31 +3343,53 @@ COMPOSITE_TAG:
3173
3343
  $found = 1 if $index == 0;
3174
3344
  last;
3175
3345
  }
3176
- # add family 3 group if generating Composite tags for sub-documents
3177
- # (unless tag already begins with family 3 group name)
3178
- if ($subDoc and $reqTag !~ /^(Main|Doc\d+):/) {
3179
- $reqTag = ($doc ? "Doc$doc:" : 'Main:') . $reqTag;
3180
- }
3181
- # allow tag group to be specified
3182
- if ($reqTag =~ /^(.*):(.+)/) {
3346
+ if ($subDoc) {
3347
+ # handle SubDoc tags specially to cache tag keys for faster
3348
+ # processing when there are a large number of sub-documents
3349
+ # - get document number from the tag groups if specified,
3350
+ # otherwise we are looping through all documents for this tag
3351
+ my $doc = $reqTag =~ s/\b(Main|Doc(\d+)):// ? ($2 || 0) : $docNum;
3352
+ # make fast lookup for keys of this tag with specified groups other than doc group
3353
+ # (similar to code in InsertTagValues(), but this is case-sensitive)
3354
+ my $cacheTag = $cache{$reqTag};
3355
+ unless ($cacheTag) {
3356
+ $cacheTag = $cache{$reqTag} = [ ];
3357
+ my $reqGroup;
3358
+ $reqTag =~ s/^(.*):// and $reqGroup = $1;
3359
+ my ($i, $key, @keys);
3360
+ # build list of tag keys in order of precedence
3361
+ for ($key=$reqTag, $i=$$self{DUPL_TAG}{$reqTag} || 0; ; --$i) {
3362
+ push @keys, $key if defined $$rawValue{$key};
3363
+ last if $i <= 0;
3364
+ $key = "$reqTag ($i)";
3365
+ }
3366
+ @keys = $self->GroupMatches($reqGroup, \@keys) if defined $reqGroup;
3367
+ if (@keys) {
3368
+ my $ex = $$self{TAG_EXTRA};
3369
+ # loop through tags in reverse order of precedence so the higher
3370
+ # priority tag will win in the case of duplicates within a doc
3371
+ $$cacheTag[$$ex{$_} ? $$ex{$_}{G3} || 0 : 0] = $_ foreach reverse @keys;
3372
+ }
3373
+ }
3374
+ # (set $reqTag to a bogus key if not found)
3375
+ $reqTag = $$cacheTag[$doc] || "$reqTag (0)";
3376
+ } elsif ($reqTag =~ /^(.*):(.+)/) {
3183
3377
  my ($reqGroup, $name) = ($1, $2);
3184
3378
  if ($reqGroup eq 'Composite' and $notBuilt{$name}) {
3185
3379
  push @deferredTags, $tag;
3186
3380
  next COMPOSITE_TAG;
3187
3381
  }
3188
3382
  # (CAREFUL! keys may not be sequential if one was deleted)
3189
- my ($i, @keys);
3190
- my $key = $name;
3191
- my $last = ($$self{DUPL_TAG}{$name} || 0);
3192
- for ($i=0;;) {
3383
+ my ($i, $key, @keys);
3384
+ for ($key=$name, $i=$$self{DUPL_TAG}{$name} || 0; ; --$i) {
3193
3385
  push @keys, $key if defined $$rawValue{$key};
3194
- last if ++$i > $last;
3386
+ last if $i <= 0;
3195
3387
  $key = "$name ($i)";
3196
3388
  }
3197
3389
  # find first matching tag
3198
3390
  $key = $self->GroupMatches($reqGroup, \@keys);
3199
- $reqTag = $key if $key;
3200
- } elsif ($notBuilt{$reqTag}) {
3391
+ $reqTag = $key || "$name (0)";
3392
+ } elsif ($notBuilt{$reqTag} and not $$inhibit{$index}) {
3201
3393
  # calculate this tag later if it relies on another
3202
3394
  # Composite tag which hasn't been calculated yet
3203
3395
  push @deferredTags, $tag;
@@ -3216,13 +3408,13 @@ COMPOSITE_TAG:
3216
3408
  }
3217
3409
  $tagKey{$index} = $reqTag;
3218
3410
  }
3219
- if ($doc) {
3411
+ if ($docNum) {
3220
3412
  if ($found) {
3221
- $$self{DOC_NUM} = $doc;
3413
+ $$self{DOC_NUM} = $docNum;
3222
3414
  $self->FoundTag($tagInfo, \%tagKey);
3223
3415
  delete $$self{DOC_NUM};
3224
3416
  }
3225
- next if ++$doc <= $$self{DOC_COUNT};
3417
+ next if ++$docNum <= $$self{DOC_COUNT};
3226
3418
  last;
3227
3419
  } elsif ($found) {
3228
3420
  delete $notBuilt{$tag}; # this tag is OK to build now
@@ -3231,7 +3423,7 @@ COMPOSITE_TAG:
3231
3423
  # only tag keys with same name as a Composite tag
3232
3424
  # can be replaced (also eliminates keys with
3233
3425
  # instance numbers which can't be replaced either)
3234
- next unless $Image::ExifTool::Composite{$tagKey{$_}};
3426
+ next unless $$compTable{$tagKey{$_}};
3235
3427
  my $keyRef = \$tagKey{$_};
3236
3428
  $tagsUsed{$$keyRef} or $tagsUsed{$$keyRef} = [ ];
3237
3429
  push @{$tagsUsed{$$keyRef}}, $keyRef;
@@ -3249,7 +3441,25 @@ COMPOSITE_TAG:
3249
3441
  delete $notBuilt{$tag}; # tag can't be built anyway
3250
3442
  }
3251
3443
  last unless $subDoc;
3252
- $doc = 1; # continue to process the 1st sub-document
3444
+ # don't process sub-documents if there is no chance to build this tag
3445
+ # (can be very time-consuming if there are many docs)
3446
+ if (%$require) {
3447
+ foreach (keys %$require) {
3448
+ my $reqTag = $$require{$_};
3449
+ $reqTag =~ s/.*://;
3450
+ next COMPOSITE_TAG unless defined $$rawValue{$reqTag};
3451
+ }
3452
+ $docNum = 1; # go ahead and process the 1st sub-document
3453
+ } else {
3454
+ my @try = ref $$tagInfo{SubDoc} ? @{$$tagInfo{SubDoc}} : keys %$desire;
3455
+ # at least one of the specified desire tags must exist
3456
+ foreach (@try) {
3457
+ my $desTag = $$desire{$_} or next;
3458
+ $desTag =~ s/.*://;
3459
+ defined $$rawValue{$desTag} and $docNum = 1, last;
3460
+ }
3461
+ last unless $docNum;
3462
+ }
3253
3463
  }
3254
3464
  }
3255
3465
  last unless @deferredTags;
@@ -3416,6 +3626,7 @@ sub Init($)
3416
3626
  $$self{WARNED_ONCE}= { }; # WarnOnce() warnings already issued
3417
3627
  $$self{WRITTEN} = { }; # list of tags written (selected tags only)
3418
3628
  $$self{FORCE_WRITE}= { }; # ForceWrite lookup (set from ForceWrite tag)
3629
+ $$self{FOUND_DIR} = { }; # hash of directory names found in file
3419
3630
  $$self{PATH} = [ ]; # current subdirectory path in file when reading
3420
3631
  $$self{NUM_FOUND} = 0; # total number of tags found (incl. duplicates)
3421
3632
  $$self{CHANGED} = 0; # number of tags changed (writer only)
@@ -3655,7 +3866,7 @@ sub GetFileTime($$)
3655
3866
  # open file by name if necessary
3656
3867
  unless (ref $file) {
3657
3868
  local *FH;
3658
- $self->Open(\*FH, $file) or $self->Warn("GetFileTime error for '$file'"), return ();
3869
+ $self->Open(\*FH, $file) or $self->Warn("GetFileTime error for '${file}'"), return ();
3659
3870
  $file = *FH; # (not \*FH, so *FH will be kept open until $file goes out of scope)
3660
3871
  }
3661
3872
  # on Windows, try to work around incorrect file times when daylight saving time is in effect
@@ -3823,6 +4034,7 @@ sub GroupMatches($$$)
3823
4034
  my (@fmys, $g);
3824
4035
  for ($g=0; $g<@grps; ++$g) {
3825
4036
  $fmys[$g] = $1 if $grps[$g] =~ s/^(\d+)//;
4037
+ $grps[$g] = '' if $grps[$g] eq 'copy0'; # accept 'Copy0' for primary tag
3826
4038
  }
3827
4039
  foreach $tag (@$tagList) {
3828
4040
  my @groups = $self->GetGroup($tag, -1);
@@ -3831,9 +4043,9 @@ sub GroupMatches($$$)
3831
4043
  next if $grp eq '*' or $grp eq 'all';
3832
4044
  if (defined $fmys[$g]) {
3833
4045
  my $f = $fmys[$g];
3834
- last unless $groups[$f] and $grps[$g] eq lc $groups[$f];
4046
+ last unless defined $groups[$f] and $grp eq lc $groups[$f];
3835
4047
  } else {
3836
- last unless grep /^$grps[$g]$/i, @groups;
4048
+ last unless grep /^$grp$/i, @groups;
3837
4049
  }
3838
4050
  }
3839
4051
  if ($g == @grps) {
@@ -3843,6 +4055,7 @@ sub GroupMatches($$$)
3843
4055
  }
3844
4056
  } else {
3845
4057
  my $family = ($group =~ s/^(\d+)//) ? $1 : -1;
4058
+ $group = '' if lc $group eq 'copy0'; # accept 'Copy0' for primary tag
3846
4059
  foreach $tag (@$tagList) {
3847
4060
  my @groups = $self->GetGroup($tag, $family);
3848
4061
  if (grep(/^$group$/i, @groups)) {
@@ -3854,6 +4067,37 @@ sub GroupMatches($$$)
3854
4067
  return wantarray ? @matches : $matches[0];
3855
4068
  }
3856
4069
 
4070
+ #------------------------------------------------------------------------------
4071
+ # Remove specified tags from returned tag list, updating indices in other lists
4072
+ # Inputs: 0) tag list ref, 1) index list ref, 2) index list ref, 3) hash ref,
4073
+ # 4) true to include tags from hash instead of excluding
4074
+ # Returns: nothing, but updates input lists
4075
+ sub RemoveTagsFromList($$$$;$)
4076
+ {
4077
+ local $_;
4078
+ my ($tags, $list1, $list2, $exclude, $inv) = @_;
4079
+ my @filteredTags;
4080
+
4081
+ if (@$list1 or @$list2) {
4082
+ while (@$tags) {
4083
+ my $tag = pop @$tags;
4084
+ my $i = @$tags;
4085
+ if ($$exclude{$tag} xor $inv) {
4086
+ # remove index of excluded tag from each list
4087
+ @$list1 = map { $_ < $i ? $_ : $_ == $i ? () : $_ - 1 } @$list1;
4088
+ @$list2 = map { $_ < $i ? $_ : $_ == $i ? () : $_ - 1 } @$list2;
4089
+ } else {
4090
+ unshift @filteredTags, $tag;
4091
+ }
4092
+ }
4093
+ } else {
4094
+ foreach (@$tags) {
4095
+ push @filteredTags, $_ unless $$exclude{$_} xor $inv;
4096
+ }
4097
+ }
4098
+ $_[0] = \@filteredTags; # update tag list
4099
+ }
4100
+
3857
4101
  #------------------------------------------------------------------------------
3858
4102
  # Set list of found tags from previously requested tags
3859
4103
  # Inputs: 0) ExifTool object reference
@@ -3871,7 +4115,7 @@ sub SetFoundTags($)
3871
4115
  my $fileOrder = $$self{FILE_ORDER};
3872
4116
  my @groupOptions = sort grep /^Group/, keys %$options;
3873
4117
  my $doDups = $duplicates || $exclude || @groupOptions;
3874
- my ($tag, $rtnTags, @byValue, @wildTags, %byValue, %wildTags, $i);
4118
+ my ($tag, $rtnTags, @byValue, @wildTags);
3875
4119
 
3876
4120
  # only return requested tags if specified
3877
4121
  if (@$reqTags) {
@@ -3886,7 +4130,7 @@ sub SetFoundTags($)
3886
4130
  if ($group =~ /^(\*|all)$/i) {
3887
4131
  $allGrp = 1;
3888
4132
  } elsif ($group !~ /^[-\w:]*$/) {
3889
- $self->Warn("Invalid group name '$group'");
4133
+ $self->Warn("Invalid group name '${group}'");
3890
4134
  $group = 'invalid';
3891
4135
  }
3892
4136
  } else {
@@ -3922,7 +4166,7 @@ sub SetFoundTags($)
3922
4166
  ($matches[0]) = grep /^$tag$/i, keys %$tagHash;
3923
4167
  defined $matches[0] or undef @matches;
3924
4168
  } else {
3925
- $self->Warn("Invalid tag name '$tag'");
4169
+ $self->Warn("Invalid tag name '${tag}'");
3926
4170
  }
3927
4171
  if (defined $group and not $allGrp) {
3928
4172
  # keep only specified group
@@ -3951,14 +4195,10 @@ sub SetFoundTags($)
3951
4195
  # bogus file order entry to avoid warning if sorting in file order
3952
4196
  $$self{FILE_ORDER}{$matches[0]} = 9999;
3953
4197
  }
3954
- # save tags extracted by value
3955
- if ($byValue) {
3956
- $byValue{$_} = 1 foreach @matches;
3957
- }
3958
- # save wildcard tags
3959
- if ($allTag) {
3960
- $wildTags{$_} = 1 foreach @matches;
3961
- }
4198
+ # save indices of tags extracted by value
4199
+ push @byValue, scalar(@$rtnTags) .. (scalar(@$rtnTags)+scalar(@matches)-1) if $byValue;
4200
+ # save indices of wildcard tags
4201
+ push @wildTags, scalar(@$rtnTags) .. (scalar(@$rtnTags)+scalar(@matches)-1) if $allTag;
3962
4202
  push @$rtnTags, @matches;
3963
4203
  }
3964
4204
  } else {
@@ -3984,7 +4224,7 @@ sub SetFoundTags($)
3984
4224
  if ($group =~ /^(\*|all)$/i) {
3985
4225
  undef $group;
3986
4226
  } elsif ($group !~ /^[-\w:]*$/) {
3987
- $self->Warn("Invalid group name '$group'");
4227
+ $self->Warn("Invalid group name '${group}'");
3988
4228
  $group = 'invalid';
3989
4229
  }
3990
4230
  } else {
@@ -4003,10 +4243,9 @@ sub SetFoundTags($)
4003
4243
  $exclude{$_} = 1 foreach @matches;
4004
4244
  }
4005
4245
  if (%exclude) {
4006
- my @filteredTags;
4007
- $exclude{$_} or push @filteredTags, $_ foreach @$rtnTags;
4008
- $rtnTags = \@filteredTags; # use new filtered tag list
4009
- last unless @filteredTags; # all done if nothing left
4246
+ # remove excluded tags from return list(s)
4247
+ RemoveTagsFromList($rtnTags, \@byValue, \@wildTags, \%exclude);
4248
+ last unless @$rtnTags; # all done if nothing left
4010
4249
  }
4011
4250
  last if $duplicates and not @groupOptions;
4012
4251
  }
@@ -4055,50 +4294,33 @@ GR_TAG: foreach $tag (@$rtnTags) {
4055
4294
  $wantTag = $wanted;
4056
4295
  }
4057
4296
  next unless $wantTag;
4058
- if ($duplicates) {
4059
- push @tags, $tag;
4060
- } else {
4061
- my $tagName = GetTagName($tag);
4062
- my $bestTag = $bestTag{$tagName};
4063
- if (defined $bestTag) {
4064
- next if $wantTag > $keepTags{$bestTag};
4065
- if ($wantTag == $keepTags{$bestTag}) {
4066
- # want two tags with the same name -- keep the latest one
4067
- if ($tag =~ / \((\d+)\)$/) {
4068
- my $tagNum = $1;
4069
- next if $bestTag !~ / \((\d+)\)$/ or $1 > $tagNum;
4070
- }
4297
+ $duplicates and $keepTags{$tag} = 1, next;
4298
+ # determine which tag we want to keep
4299
+ my $tagName = GetTagName($tag);
4300
+ my $bestTag = $bestTag{$tagName};
4301
+ if (defined $bestTag) {
4302
+ next if $wantTag > $keepTags{$bestTag};
4303
+ if ($wantTag == $keepTags{$bestTag}) {
4304
+ # want two tags with the same name -- keep the latest one
4305
+ if ($tag =~ / \((\d+)\)$/) {
4306
+ my $tagNum = $1;
4307
+ next if $bestTag !~ / \((\d+)\)$/ or $1 > $tagNum;
4071
4308
  }
4072
- # this tag is better, so delete old best tag
4073
- delete $keepTags{$bestTag};
4074
4309
  }
4075
- $keepTags{$tag} = $wantTag; # keep this tag (for now...)
4076
- $bestTag{$tagName} = $tag; # this is our current best tag
4310
+ # this tag is better, so delete old best tag
4311
+ delete $keepTags{$bestTag};
4077
4312
  }
4313
+ $keepTags{$tag} = $wantTag; # keep this tag (for now...)
4314
+ $bestTag{$tagName} = $tag; # this is our current best tag
4078
4315
  }
4079
- unless ($duplicates) {
4080
- # construct new tag list with no duplicates, preserving order
4081
- foreach $tag (@$rtnTags) {
4082
- push @tags, $tag if $keepTags{$tag};
4083
- }
4084
- }
4085
- $rtnTags = \@tags;
4316
+ # include only tags we want to keep in return lists
4317
+ RemoveTagsFromList($rtnTags, \@byValue, \@wildTags, \%keepTags, 1);
4086
4318
  last;
4087
4319
  }
4088
4320
  $$self{FOUND_TAGS} = $rtnTags; # save found tags
4089
4321
 
4090
- return $rtnTags unless wantarray;
4091
-
4092
- # generate list of indices of by-value and wildcard tags
4093
- if (%byValue or %wildTags) {
4094
- for ($i=0; $i<@$rtnTags; ++$i) {
4095
- push @byValue, $i if $byValue{$$rtnTags[$i]};
4096
- push @wildTags, $i if $wildTags{$$rtnTags[$i]};
4097
- }
4098
- }
4099
-
4100
- # return reference to found tag keys and list of indices of tags to extract by value
4101
- return ($rtnTags, \@byValue, \@wildTags);
4322
+ # return reference to found tag keys (and list of indices of tags to extract by value)
4323
+ return wantarray ? ($rtnTags, \@byValue, \@wildTags) : $rtnTags;
4102
4324
  }
4103
4325
 
4104
4326
  #------------------------------------------------------------------------------
@@ -4143,13 +4365,15 @@ sub AUTOLOAD
4143
4365
  #------------------------------------------------------------------------------
4144
4366
  # Add warning tag
4145
4367
  # Inputs: 0) ExifTool object reference, 1) warning message
4146
- # 2) true if minor (2 if behaviour changes when warning is ignored)
4368
+ # 2) true if minor (2 if behaviour changes when warning is ignored,
4369
+ # or 3 if warning shouldn't be issued when Validate option is used)
4147
4370
  # Returns: true if warning tag was added
4148
4371
  sub Warn($$;$)
4149
4372
  {
4150
4373
  my ($self, $str, $ignorable) = @_;
4151
4374
  if ($ignorable) {
4152
4375
  return 0 if $$self{OPTIONS}{IgnoreMinorErrors};
4376
+ return 0 if $ignorable eq '3' and $$self{OPTIONS}{Validate};
4153
4377
  $str = $ignorable eq '2' ? "[Minor] $str" : "[minor] $str";
4154
4378
  }
4155
4379
  $self->FoundTag('Warning', $str);
@@ -4337,11 +4561,22 @@ sub AddCompositeTags($;$)
4337
4561
  # use new ID and save it so we can use it in TagLookup
4338
4562
  $$tagInfo{NewTagID} = $tag = $new unless $over;
4339
4563
  }
4340
- # convert scalar Require/Desire entries
4341
- my $type;
4342
- foreach $type ('Require','Desire') {
4564
+ # convert scalar Require/Desire/Inhibit entries
4565
+ my ($type, @hashes, @scalars, %used);
4566
+ foreach $type ('Require','Desire','Inhibit') {
4343
4567
  my $req = $$tagInfo{$type} or next;
4344
- $$tagInfo{$type} = { 0 => $req } if ref($req) ne 'HASH';
4568
+ push @{ref($req) eq 'HASH' ? \@hashes : \@scalars}, $type;
4569
+ }
4570
+ if (@scalars) {
4571
+ # make lookup for indices that are used
4572
+ foreach $type (@hashes) {
4573
+ $used{$_} = 1 foreach keys %{$$tagInfo{$type}};
4574
+ }
4575
+ my $next = 0;
4576
+ foreach $type (@scalars) {
4577
+ ++$next while $used{$next};
4578
+ $$tagInfo{$type} = { $next++ => $$tagInfo{$type} };
4579
+ }
4345
4580
  }
4346
4581
  # add this Composite tag to our main Composite table
4347
4582
  $$tagInfo{Table} = $compTable;
@@ -4419,6 +4654,12 @@ sub SetupTagTable($)
4419
4654
  $$tagInfo{Name} or $$tagInfo{Name} = MakeTagName($tagID);
4420
4655
  $$tagInfo{Flags} and ExpandFlags($tagInfo);
4421
4656
  $$tagInfo{Avoid} = $avoid if defined $avoid;
4657
+ # calculate BitShift from Mask if necessary
4658
+ if ($$tagInfo{Mask} and not defined $$tagInfo{BitShift}) {
4659
+ my ($mask, $bitShift) = ($$tagInfo{Mask}, 0);
4660
+ ++$bitShift until $mask & (1 << $bitShift);
4661
+ $$tagInfo{BitShift} = $bitShift;
4662
+ }
4422
4663
  }
4423
4664
  next unless @infoArray > 1;
4424
4665
  # add an "Index" member to each tagInfo in a list
@@ -4715,6 +4956,7 @@ my %formatSize = (
4715
4956
  fixed16u => 2,
4716
4957
  fixed32s => 4,
4717
4958
  fixed32u => 4,
4959
+ fixed64s => 8,
4718
4960
  float => 4,
4719
4961
  double => 8,
4720
4962
  extended => 10,
@@ -4746,6 +4988,7 @@ my %readValueProc = (
4746
4988
  fixed16u => \&GetFixed16u,
4747
4989
  fixed32s => \&GetFixed32s,
4748
4990
  fixed32u => \&GetFixed32u,
4991
+ fixed64s => \&GetFixed64s,
4749
4992
  float => \&GetFloat,
4750
4993
  double => \&GetDouble,
4751
4994
  extended => \&GetExtended,
@@ -4764,11 +5007,12 @@ sub FormatSize($) { return $formatSize{$_[0]}; }
4764
5007
  #------------------------------------------------------------------------------
4765
5008
  # Read value from binary data (with current byte ordering)
4766
5009
  # Inputs: 0) data reference, 1) value offset, 2) format string,
4767
- # 3) number of values (or undef to use all data)
4768
- # 4) valid data length relative to offset, 5) optional pointer to returned rational
5010
+ # 3) number of values (or undef to use all data),
5011
+ # 4) valid data length relative to offset (or undef to use all data),
5012
+ # 5) optional pointer to returned rational
4769
5013
  # Returns: converted value, or undefined if data isn't there
4770
5014
  # or list of values in list context
4771
- sub ReadValue($$$$$;$)
5015
+ sub ReadValue($$$;$$$)
4772
5016
  {
4773
5017
  my ($dataPt, $offset, $format, $count, $size, $ratPt) = @_;
4774
5018
 
@@ -4777,6 +5021,7 @@ sub ReadValue($$$$$;$)
4777
5021
  warn "Unknown format $format";
4778
5022
  $len = 1;
4779
5023
  }
5024
+ $size = length($$dataPt) - $offset unless defined $size;
4780
5025
  unless ($count) {
4781
5026
  return '' if defined $count or $size < $len;
4782
5027
  $count = int($size / $len);
@@ -5173,21 +5418,32 @@ sub TimeZoneString($;$)
5173
5418
  }
5174
5419
  my $sign = '+';
5175
5420
  $min < 0 and $sign = '-', $min = -$min;
5421
+ $min = int($min + 0.5); # round off to nearest minute
5176
5422
  my $h = int($min / 60);
5177
5423
  return sprintf('%s%.2d:%.2d', $sign, $h, $min - $h * 60);
5178
5424
  }
5179
5425
 
5180
5426
  #------------------------------------------------------------------------------
5181
5427
  # Convert Unix time to EXIF date/time string
5182
- # Inputs: 0) Unix time value, 1) non-zero to convert to local time
5428
+ # Inputs: 0) Unix time value, 1) non-zero to convert to local time,
5429
+ # 2) number of digits after the decimal for fractional seconds
5183
5430
  # Returns: EXIF date/time string (with timezone for local times)
5184
- # Notes: fractional seconds are ignored
5185
- sub ConvertUnixTime($;$)
5431
+ sub ConvertUnixTime($;$$)
5186
5432
  {
5187
- my ($time, $toLocal) = @_;
5433
+ my ($time, $toLocal, $dec) = @_;
5188
5434
  return '0000:00:00 00:00:00' if $time == 0;
5189
- $time = int($time + 1e-6) if $time != int($time); # avoid round-off errors
5190
5435
  my (@tm, $tz);
5436
+ if ($dec) {
5437
+ my $frac = $time - int($time);
5438
+ $time = int($time);
5439
+ $frac < 0 and $frac += 1, $time -= 1;
5440
+ $dec = sprintf('%.*f', $dec, $frac);
5441
+ # remove number before decimal and increment integer time if it was rounded up
5442
+ $dec =~ s/^(\d)// and $1 eq '1' and $time += 1;
5443
+ } else {
5444
+ $time = int($time + 1e-6) if $time != int($time); # avoid round-off errors
5445
+ $dec = '';
5446
+ }
5191
5447
  if ($toLocal) {
5192
5448
  @tm = localtime($time);
5193
5449
  $tz = TimeZoneString(\@tm, $time);
@@ -5195,7 +5451,7 @@ sub ConvertUnixTime($;$)
5195
5451
  @tm = gmtime($time);
5196
5452
  $tz = '';
5197
5453
  }
5198
- my $str = sprintf("%4d:%.2d:%.2d %.2d:%.2d:%.2d%s",
5454
+ my $str = sprintf("%4d:%.2d:%.2d %.2d:%.2d:%.2d$dec%s",
5199
5455
  $tm[5]+1900, $tm[4]+1, $tm[3], $tm[2], $tm[1], $tm[0], $tz);
5200
5456
  return $str;
5201
5457
  }
@@ -5208,20 +5464,20 @@ sub GetUnixTime($;$)
5208
5464
  {
5209
5465
  my ($timeStr, $isLocal) = @_;
5210
5466
  return 0 if $timeStr eq '0000:00:00 00:00:00';
5211
- my @tm = ($timeStr =~ /^(\d+):(\d+):(\d+)\s+(\d+):(\d+):(\d+)/);
5212
- return undef unless @tm == 6 and eval { require Time::Local };
5213
- my $tzsec = 0;
5467
+ my @tm = ($timeStr =~ /^(\d+):(\d+):(\d+)\s+(\d+):(\d+):(\d+)(.*)/);
5468
+ return undef unless @tm == 7 and eval { require Time::Local };
5469
+ my ($tzStr, $tzSec) = (pop(@tm), 0);
5214
5470
  # use specified timezone offset (if given) instead of local system time
5215
5471
  # if we are converting a local time value
5216
- if ($isLocal and $timeStr =~ /(?:Z|([-+])(\d+):(\d+))$/i) {
5472
+ if ($isLocal and $tzStr =~ /(?:Z|([-+])(\d+):(\d+))/i) {
5217
5473
  # use specified timezone if one exists
5218
- $tzsec = ($2 * 60 + $3) * ($1 eq '-' ? -60 : 60) if $1;
5474
+ $tzSec = ($2 * 60 + $3) * ($1 eq '-' ? -60 : 60) if $1;
5219
5475
  undef $isLocal; # convert using GMT corrected for specified timezone
5220
5476
  }
5221
5477
  $tm[0] -= 1900; # convert year
5222
5478
  $tm[1] -= 1; # convert month
5223
5479
  @tm = reverse @tm; # change to order required by timelocal()
5224
- return $isLocal ? TimeLocal(@tm) : Time::Local::timegm(@tm) - $tzsec;
5480
+ return $isLocal ? TimeLocal(@tm) : Time::Local::timegm(@tm) - $tzSec;
5225
5481
  }
5226
5482
 
5227
5483
  #------------------------------------------------------------------------------
@@ -5243,7 +5499,7 @@ sub ConvertFileSize($)
5243
5499
  #------------------------------------------------------------------------------
5244
5500
  # Convert seconds to duration string (handles negative durations)
5245
5501
  # Inputs: 0) floating point seconds
5246
- # Returns: duration string in form "S.SS s", "MM:SS" or "H:MM:SS"
5502
+ # Returns: duration string in form "S.SS s", "H:MM:SS" or "DD days HH:MM:SS"
5247
5503
  sub ConvertDuration($)
5248
5504
  {
5249
5505
  my $time = shift;
@@ -5251,6 +5507,7 @@ sub ConvertDuration($)
5251
5507
  return '0 s' if $time == 0;
5252
5508
  my $sign = ($time > 0 ? '' : (($time = -$time), '-'));
5253
5509
  return sprintf("$sign%.2f s", $time) if $time < 30;
5510
+ $time += 0.5; # to round off to nearest second
5254
5511
  my $h = int($time / 3600);
5255
5512
  $time -= $h * 3600;
5256
5513
  my $m = int($time / 60);
@@ -5309,15 +5566,30 @@ sub InverseFileName($$)
5309
5566
  #------------------------------------------------------------------------------
5310
5567
  # Save information for HTML dump
5311
5568
  # Inputs: 0) ExifTool hash ref, 1) start offset, 2) data size
5312
- # 3) comment string, 4) tool tip (or SAME), 5) flags
5313
- sub HDump($$$$;$$)
5569
+ # 3) comment string, 4) tool tip (or SAME), 5) flags, 6) IFD name
5570
+ sub HDump($$$$;$$$)
5314
5571
  {
5315
5572
  my $self = shift;
5316
- if ($$self{HTML_DUMP}) {
5317
- my $pos = shift;
5318
- $pos += $$self{BASE} if $$self{BASE};
5319
- $$self{HTML_DUMP}->Add($pos, @_);
5320
- }
5573
+ $$self{HTML_DUMP} or return;
5574
+ my ($pos, $len, $com, $tip, $flg, $ifd) = @_;
5575
+ $pos += $$self{BASE} if $$self{BASE};
5576
+ # skip structural data blocks which have been removed from the middle of this dump
5577
+ # (SkipData list contains ordered [start,end+1] offsets to skip)
5578
+ if ($$self{SkipData}) {
5579
+ my $end = $pos + $len;
5580
+ my $skip;
5581
+ foreach $skip (@{$$self{SkipData}}) {
5582
+ $end <= $$skip[0] and last;
5583
+ $pos >= $$skip[1] and $pos += $$skip[1] - $$skip[0], next;
5584
+ if ($pos != $$skip[0]) {
5585
+ $$self{HTML_DUMP}->Add($pos, $$skip[0]-$pos, $com, $tip, $flg, $ifd);
5586
+ $len -= $$skip[0] - $pos;
5587
+ $tip = 'SAME';
5588
+ }
5589
+ $pos = $$skip[1];
5590
+ }
5591
+ }
5592
+ $$self{HTML_DUMP}->Add($pos, $len, $com, $tip, $flg, $ifd);
5321
5593
  }
5322
5594
 
5323
5595
  #------------------------------------------------------------------------------
@@ -5462,7 +5734,7 @@ sub ProcessTrailers($$)
5462
5734
  0xc4 => 'DHT',
5463
5735
  0xc8 => 'JPGA',
5464
5736
  0xcc => 'DAC',
5465
- 0xd0 => 'RST0',
5737
+ 0xd0 => 'RST0', # to RST7
5466
5738
  0xd8 => 'SOI',
5467
5739
  0xd9 => 'EOI',
5468
5740
  0xda => 'SOS',
@@ -5534,13 +5806,14 @@ sub ProcessJPEG($$)
5534
5806
  local $_;
5535
5807
  my ($self, $dirInfo) = @_;
5536
5808
  my ($ch, $s, $length);
5537
- my $verbose = $$self{OPTIONS}{Verbose};
5538
- my $out = $$self{OPTIONS}{TextOut};
5539
- my $fast = $$self{OPTIONS}{FastScan};
5809
+ my $options = $$self{OPTIONS};
5810
+ my $verbose = $$options{Verbose};
5811
+ my $out = $$options{TextOut};
5812
+ my $fast = $$options{FastScan};
5540
5813
  my $raf = $$dirInfo{RAF};
5541
5814
  my $htmlDump = $$self{HTML_DUMP};
5542
5815
  my %dumpParms = ( Out => $out );
5543
- my ($success, $wantTrailer, $trailInfo, $type);
5816
+ my ($success, $wantTrailer, $trailInfo, $foundSOS);
5544
5817
  my (@iccChunk, $iccChunkCount, $iccChunksTotal, @flirChunk, $flirCount, $flirTotal);
5545
5818
  my ($preview, $scalado, @dqt, $subSampling, $dumpEnd, %extendedXMP);
5546
5819
 
@@ -5548,13 +5821,20 @@ sub ProcessJPEG($$)
5548
5821
  return 0 unless $raf->Read($s, 2) == 2 and $s =~ /^\xff[\xd8\x4f\x01]/;
5549
5822
  if ($s eq "\xff\x01") {
5550
5823
  return 0 unless $raf->Read($s, 5) == 5 and $s eq 'Exiv2';
5551
- $type = 'EXV';
5824
+ $$self{FILE_TYPE} = 'EXV';
5552
5825
  }
5553
- if (not $$self{VALUE}{FileType} or ($$self{DOC_NUM} and $$self{OPTIONS}{ExtractEmbedded})) {
5554
- $self->SetFileType($type); # set FileType tag
5826
+ my $appBytes = 0;
5827
+ my $calcImageLen = $$self{REQ_TAG_LOOKUP}{jpegimagelength};
5828
+ if ($$options{RequestAll} and $$options{RequestAll} > 2) {
5829
+ $calcImageLen = 1;
5830
+ }
5831
+ if (not $$self{VALUE}{FileType} or ($$self{DOC_NUM} and $$options{ExtractEmbedded})) {
5832
+ $self->SetFileType(); # set FileType tag
5555
5833
  return 1 if $fast and $fast == 3; # don't process file when FastScan == 3
5556
5834
  $$self{LOW_PRIORITY_DIR}{IFD1} = 1; # lower priority of IFD1 tags
5557
5835
  }
5836
+ $$raf{NoBuffer} = 1 if $self->Options('FastScan'); # disable buffering in FastScan mode
5837
+
5558
5838
  $dumpParms{MaxLen} = 128 if $verbose < 4;
5559
5839
  if ($htmlDump) {
5560
5840
  $dumpEnd = $raf->Tell();
@@ -5569,7 +5849,7 @@ sub ProcessJPEG($$)
5569
5849
  # set input record separator to 0xff (the JPEG marker) to make reading quicker
5570
5850
  local $/ = "\xff";
5571
5851
 
5572
- my ($nextMarker, $nextSegDataPt, $nextSegPos, $combinedSegData);
5852
+ my ($nextMarker, $nextSegDataPt, $nextSegPos, $combinedSegData, $firstSegPos, @skipData);
5573
5853
 
5574
5854
  # read file until we reach an end of image (EOI) or start of scan (SOS)
5575
5855
  Marker: for (;;) {
@@ -5673,7 +5953,11 @@ sub ProcessJPEG($$)
5673
5953
  $self->HDump($pos-2, 2, 'JPEG EOI', undef);
5674
5954
  $dumpEnd = 0;
5675
5955
  }
5676
- $success = 1;
5956
+ if ($foundSOS or $$self{FILE_TYPE} eq 'EXV') {
5957
+ $success = 1;
5958
+ } else {
5959
+ $self->Warn('Missing JPEG SOS');
5960
+ }
5677
5961
  # we are here because we are looking for trailer information
5678
5962
  if ($wantTrailer) {
5679
5963
  my $start = $$self{PreviewImageStart};
@@ -5735,9 +6019,11 @@ sub ProcessJPEG($$)
5735
6019
  DirLen => $endPos - $pos
5736
6020
  }) if $endPos > $pos;
5737
6021
  }
6022
+ $self->FoundTag('JPEGImageLength', $pos - $appBytes) if $calcImageLen;
5738
6023
  last; # all done parsing file
5739
6024
  } elsif ($marker == 0xda) { # SOS
5740
6025
  pop @$path;
6026
+ $foundSOS = 1;
5741
6027
  # all done with meta information unless we have a trailer
5742
6028
  $verbose and print $out "JPEG SOS\n";
5743
6029
  unless ($fast) {
@@ -5768,7 +6054,8 @@ sub ProcessJPEG($$)
5768
6054
  }
5769
6055
  next if $trailInfo or $wantTrailer or $verbose > 2 or $htmlDump;
5770
6056
  }
5771
- next if $$self{OPTIONS}{Validate}; # (validate to EOI)
6057
+ # must scan to EOI if Validate or JpegCompressionFactor used
6058
+ next if $$options{Validate} or $calcImageLen;
5772
6059
  # nothing interesting to parse after start of scan (SOS)
5773
6060
  $success = 1;
5774
6061
  last; # all done parsing file
@@ -5789,15 +6076,16 @@ sub ProcessJPEG($$)
5789
6076
  # specifically requested. The reason is that there is too much overhead involved
5790
6077
  # in the calculation of this tag to make this worth the CPU time.)
5791
6078
  ($$self{REQ_TAG_LOOKUP}{jpegdigest} or $$self{REQ_TAG_LOOKUP}{jpegqualityestimate}
5792
- or ($$self{OPTIONS}{RequestAll} and $$self{OPTIONS}{RequestAll} > 2)))
6079
+ or ($$options{RequestAll} and $$options{RequestAll} > 2)))
5793
6080
  {
5794
6081
  my $num = unpack('C',$$segDataPt) & 0x0f; # get table index
5795
6082
  $dqt[$num] = $$segDataPt if $num < 4; # save for MD5 calculation
5796
6083
  }
5797
6084
  # handle all other markers
5798
6085
  my $dumpType = '';
5799
- my ($desc, $tip);
6086
+ my ($desc, $tip, $xtra);
5800
6087
  $length = length $$segDataPt;
6088
+ $appBytes += $length + 4 if ($marker & 0xf0) == 0xe0; # total size of APP segments
5801
6089
  if ($verbose) {
5802
6090
  print $out "JPEG $markerName ($length bytes):\n";
5803
6091
  if ($verbose > 2) {
@@ -5860,14 +6148,40 @@ sub ProcessJPEG($$)
5860
6148
  } elsif ($$segDataPt !~ /^Exif\0/) {
5861
6149
  $self->Warn('Incorrect EXIF segment identifier',1);
5862
6150
  }
5863
- DirStart(\%dirInfo, $hdrLen, $hdrLen);
5864
6151
  if ($htmlDump) {
5865
6152
  $self->HDump($segPos-4, 4, 'APP1 header', "Data size: $length bytes");
5866
6153
  $self->HDump($segPos, $hdrLen, 'Exif header', 'APP1 data type: Exif');
5867
6154
  $dumpEnd = $segPos + $length;
5868
6155
  }
6156
+ my $dataPt = $segDataPt;
6157
+ if (defined $combinedSegData) {
6158
+ push @skipData, [ $segPos-4, $segPos+$hdrLen ];
6159
+ $combinedSegData .= substr($$segDataPt,$hdrLen);
6160
+ undef $$segDataPt;
6161
+ $dataPt = \$combinedSegData;
6162
+ $segPos = $firstSegPos;
6163
+ }
6164
+ # peek ahead to see if the next segment is extended EXIF
6165
+ if ($nextMarker == $marker and
6166
+ $$nextSegDataPt =~ /^$exifAPP1hdr(?!(MM\0\x2a|II\x2a\0))/)
6167
+ {
6168
+ # initialize combined data if necessary
6169
+ unless (defined $combinedSegData) {
6170
+ $combinedSegData = $$segDataPt;
6171
+ undef $$segDataPt;
6172
+ $firstSegPos = $segPos;
6173
+ $self->Warn('File contains multi-segment EXIF',1);
6174
+ $$self{ExtendedEXIF} = 1;
6175
+ }
6176
+ next;
6177
+ }
6178
+ $dirInfo{DataPt} = $dataPt;
6179
+ $dirInfo{DataPos} = $segPos;
6180
+ $dirInfo{DataLen} = $dirInfo{DirLen} = length $$dataPt;
6181
+ DirStart(\%dirInfo, $hdrLen, $hdrLen);
6182
+ $$self{SkipData} = \@skipData if @skipData;
5869
6183
  # extract the EXIF information (it is in standard TIFF format)
5870
- $self->ProcessTIFF(\%dirInfo);
6184
+ $self->ProcessTIFF(\%dirInfo) or $self->Warn('Malformed APP1 EXIF segment');
5871
6185
  # avoid looking for preview unless necessary because it really slows
5872
6186
  # us down -- only look for it if we found pointer, and preview is
5873
6187
  # outside EXIF, and PreviewImage is specifically requested
@@ -5881,12 +6195,18 @@ sub ProcessJPEG($$)
5881
6195
  $start + $plen > $$self{EXIF_POS} + length($$self{EXIF_DATA}) and
5882
6196
  ($$self{REQ_TAG_LOOKUP}{previewimage} or
5883
6197
  # (extracted normally, so check Binary option)
5884
- ($$self{OPTIONS}{Binary} and not $$self{EXCL_TAG_LOOKUP}{previewimage})))
6198
+ ($$options{Binary} and not $$self{EXCL_TAG_LOOKUP}{previewimage})))
5885
6199
  {
5886
6200
  $$self{PreviewImageStart} = $start;
5887
6201
  $$self{PreviewImageLength} = $plen;
5888
6202
  $wantTrailer = 1;
5889
6203
  }
6204
+ if (@skipData) {
6205
+ undef @skipData;
6206
+ delete $$self{SkipData};
6207
+ }
6208
+ undef $$dataPt;
6209
+ next;
5890
6210
  } elsif ($$segDataPt =~ /^$xmpExtAPP1hdr/) {
5891
6211
  # off len -- extended XMP header (75 bytes total):
5892
6212
  # 0 35 bytes - signature
@@ -6113,6 +6433,7 @@ sub ProcessJPEG($$)
6113
6433
  $dumpType = 'PreviewImage';
6114
6434
  $preview .= $$segDataPt;
6115
6435
  }
6436
+ # (also seen "QTI Debug Metadata\0" segment in some newer Samsung images)
6116
6437
  # BenQ DC E1050 continues preview in APP5
6117
6438
  if ($preview and $nextMarker ne 0xe5) {
6118
6439
  $self->FoundTag('PreviewImage', $preview);
@@ -6126,6 +6447,9 @@ sub ProcessJPEG($$)
6126
6447
  DirStart(\%dirInfo, 6, 6);
6127
6448
  my $tagTablePtr = GetTagTable('Image::ExifTool::Ricoh::RMETA');
6128
6449
  $self->ProcessDirectory(\%dirInfo, $tagTablePtr);
6450
+ } elsif ($$segDataPt =~ /^ssuniqueid\0/) {
6451
+ my $tagTablePtr = GetTagTable('Image::ExifTool::Samsung::APP5');
6452
+ $self->HandleTag($tagTablePtr, 'ssuniqueid', substr($$segDataPt, 11));
6129
6453
  } elsif ($preview) {
6130
6454
  $dumpType = 'PreviewImage';
6131
6455
  $preview .= $$segDataPt;
@@ -6155,6 +6479,12 @@ sub ProcessJPEG($$)
6155
6479
  # (ignore first TDHD element because size includes 12-byte tag header)
6156
6480
  DirStart(\%dirInfo, 12);
6157
6481
  $self->ProcessDirectory(\%dirInfo, $tagTablePtr);
6482
+ } elsif ($$segDataPt =~ /^GoPro\0/) {
6483
+ # GoPro segment
6484
+ $dumpType = 'GoPro';
6485
+ my $tagTablePtr = GetTagTable('Image::ExifTool::GoPro::GPMF');
6486
+ DirStart(\%dirInfo, 6);
6487
+ $self->ProcessDirectory(\%dirInfo, $tagTablePtr);
6158
6488
  }
6159
6489
  } elsif ($marker == 0xe7) { # APP7 (Pentax, Qualcomm)
6160
6490
  if ($$segDataPt =~ /^PENTAX \0(II|MM)/) {
@@ -6202,6 +6532,10 @@ sub ProcessJPEG($$)
6202
6532
  $dumpType = 'PhotoStudio';
6203
6533
  my $comment = $self->Decode(substr($$segDataPt,8), 'UCS2', 'MM');
6204
6534
  $self->FoundTag('Comment', $comment);
6535
+ } elsif ($$segDataPt =~ /^AROT\0/ and $length > 10) {
6536
+ # iPhone "AROT" segment containing integrated intensity per 16 scan lines
6537
+ # (with number of elements N = ImageHeight / 16 - 1, ref PH/NealKrawetz)
6538
+ $xtra = 'segment (N=' . unpack('x6N', $$segDataPt) . ')';
6205
6539
  }
6206
6540
  } elsif ($marker == 0xeb) { # APP11 (JPEG-HDR)
6207
6541
  if ($$segDataPt =~ /^HDR_RI /) {
@@ -6314,8 +6648,10 @@ sub ProcessJPEG($$)
6314
6648
  $desc = "[JPEG $markerName]"; # (other known JPEG segments)
6315
6649
  }
6316
6650
  if (defined $dumpType) {
6317
- if (not $dumpType and $$self{OPTIONS}{Unknown}) {
6318
- $self->Warn("Unknown $markerName segment", 1);
6651
+ if (not $dumpType and ($$options{Unknown} or $$options{Validate})) {
6652
+ my $str = ($$segDataPt =~ /^([\x20-\x7e]{1,20})\0/) ? " '${1}'" : '';
6653
+ $xtra = 'segment' unless $xtra;
6654
+ $self->Warn("Unknown $markerName$str $xtra", 1);
6319
6655
  }
6320
6656
  if ($htmlDump) {
6321
6657
  $desc or $desc = $markerName . ($dumpType ? " $dumpType" : '') . ' segment';
@@ -6331,7 +6667,7 @@ sub ProcessJPEG($$)
6331
6667
  # GUID indicated by the last main XMP segment
6332
6668
  my $goodGuid = $$self{VALUE}{HasExtendedXMP} || '';
6333
6669
  # GUID of the extended XMP that we will process ('2' for all)
6334
- my $readGuid = $$self{OPTIONS}{ExtendedXMP} || 0;
6670
+ my $readGuid = $$options{ExtendedXMP} || 0;
6335
6671
  $readGuid = $goodGuid if $readGuid eq '1';
6336
6672
  foreach $guid (sort keys %extendedXMP) {
6337
6673
  next unless length $guid == 32; # ignore other (internal) keys
@@ -6354,8 +6690,9 @@ sub ProcessJPEG($$)
6354
6690
  $buff .= $$extXMP{$_} foreach @offsets;
6355
6691
  my $tagTablePtr = GetTagTable('Image::ExifTool::XMP::Main');
6356
6692
  my %dirInfo = (
6357
- DataPt => \$buff,
6358
- Parent => 'APP1',
6693
+ DataPt => \$buff,
6694
+ Parent => 'APP1',
6695
+ IsExtended => 1,
6359
6696
  );
6360
6697
  $$path[$pn] = 'APP1';
6361
6698
  $self->ProcessDirectory(\%dirInfo, $tagTablePtr);
@@ -6496,6 +6833,7 @@ sub DoProcessTIFF($$;$)
6496
6833
  # return 0 unless $identifier == 0x2a;
6497
6834
 
6498
6835
  # get offset to IFD0
6836
+ return 0 if length $$dataPt < 8;
6499
6837
  my $offset = Get32u($dataPt, 4);
6500
6838
  $offset >= 8 or return 0;
6501
6839
 
@@ -6563,7 +6901,8 @@ sub DoProcessTIFF($$;$)
6563
6901
  # don't process file if FastScan == 3
6564
6902
  return 1 if not $outfile and $$self{OPTIONS}{FastScan} and $$self{OPTIONS}{FastScan} == 3;
6565
6903
  }
6566
- my $ifdName = 'IFD0';
6904
+ # (accomodate CR3 images which have a TIFF directory with ExifIFD at the top level)
6905
+ my $ifdName = ($$dirInfo{DirName} and $$dirInfo{DirName} =~ /^(ExifIFD|GPS)$/) ? $1 : 'IFD0';
6567
6906
  if (not $tagTablePtr or $$tagTablePtr{GROUPS}{0} eq 'EXIF') {
6568
6907
  $self->FoundTag('ExifByteOrder', $byteOrder) unless $outfile;
6569
6908
  } else {
@@ -6621,7 +6960,7 @@ sub DoProcessTIFF($$;$)
6621
6960
  }
6622
6961
  }
6623
6962
  # update FileType if necessary now that we know more about the file
6624
- if ($$self{DNGVersion} and $$self{VALUE}{FileType} ne 'DNG') {
6963
+ if ($$self{DNGVersion} and $$self{VALUE}{FileType} !~ /^(DNG|GPR)$/) {
6625
6964
  # override whatever FileType we set since we now know it is DNG
6626
6965
  $self->OverrideFileType($$self{TIFF_TYPE} = 'DNG');
6627
6966
  }
@@ -6810,9 +7149,13 @@ sub GetTagTable($)
6810
7149
  if ($tableName =~ /(.*)::/) {
6811
7150
  my $module = $1;
6812
7151
  if (eval "require $module") {
6813
- # load additional XMP modules if required
6814
- if (not %$tableName and $module eq 'Image::ExifTool::XMP') {
6815
- require 'Image/ExifTool/XMP2.pl';
7152
+ # load additional modules if required
7153
+ if (not %$tableName) {
7154
+ if ($module eq 'Image::ExifTool::XMP') {
7155
+ require 'Image/ExifTool/XMP2.pl';
7156
+ } elsif ($tableName eq 'Image::ExifTool::QuickTime::Stream') {
7157
+ require 'Image/ExifTool/QuickTimeStream.pl';
7158
+ }
6816
7159
  }
6817
7160
  } else {
6818
7161
  $@ and warn $@;
@@ -6863,9 +7206,7 @@ sub GetTagTable($)
6863
7206
  # set up the new table
6864
7207
  SetupTagTable($table);
6865
7208
  # add any user-defined tags (except Composite tags, which are handled specially)
6866
- if (%UserDefined and $UserDefined{$tableName} and
6867
- $tableName ne 'Image::ExifTool::Composite')
6868
- {
7209
+ if (%UserDefined and $UserDefined{$tableName} and $table ne \%Image::ExifTool::Composite) {
6869
7210
  my $tagID;
6870
7211
  foreach $tagID (TagTableKeys($UserDefined{$tableName})) {
6871
7212
  next if $specialTags{$tagID};
@@ -6878,6 +7219,17 @@ sub GetTagTable($)
6878
7219
  # insert newly loaded table into list
6879
7220
  $allTables{$tableName} = $table;
6880
7221
  }
7222
+ # must check each time to add UserDefined Composite tags because the Composite table
7223
+ # may be loaded before the UserDefined tags are available
7224
+ if ($table eq \%Image::ExifTool::Composite and not $$table{VARS}{LOADED_USERDEFINED} and
7225
+ %UserDefined and $UserDefined{$tableName})
7226
+ {
7227
+ my $userComp = $UserDefined{$tableName};
7228
+ delete $UserDefined{$tableName}; # (must delete first to avoid infinite recursion)
7229
+ AddCompositeTags($userComp, 1);
7230
+ $UserDefined{$tableName} = $userComp; # (add back again for adding writable tags later)
7231
+ $$table{VARS}{LOADED_USERDEFINED} = 1; # set flag to avoid doing this again
7232
+ }
6881
7233
  return $table;
6882
7234
  }
6883
7235
 
@@ -6900,12 +7252,13 @@ sub ProcessDirectory($$$;$)
6900
7252
  $dirName = $$tagTablePtr{GROUPS}{1} if $dirName =~ /^APP\d+$/; # (use specific APP name)
6901
7253
  $$dirInfo{DirName} = $dirName;
6902
7254
  }
7255
+
6903
7256
  # guard against cyclical recursion into the same directory
6904
7257
  if (defined $$dirInfo{DirStart} and defined $$dirInfo{DataPos} and
6905
7258
  # directories don't overlap if the length is zero
6906
7259
  ($$dirInfo{DirLen} or not defined $$dirInfo{DirLen}))
6907
7260
  {
6908
- my $addr = $$dirInfo{DirStart} + $$dirInfo{DataPos} + ($$dirInfo{Base}||0);
7261
+ my $addr = $$dirInfo{DirStart} + $$dirInfo{DataPos} + ($$dirInfo{Base}||0) + $$self{BASE};
6909
7262
  if ($$self{PROCESSED}{$addr}) {
6910
7263
  $self->Warn("$dirName pointer references previous $$self{PROCESSED}{$addr} directory");
6911
7264
  # patch for bug in Windows phone 7.5 O/S that writes incorrect InteropIFD pointer
@@ -6919,6 +7272,7 @@ sub ProcessDirectory($$$;$)
6919
7272
  $$self{INDENT} .= '| ';
6920
7273
  $$self{DIR_NAME} = $dirName;
6921
7274
  push @{$$self{PATH}}, $dirName;
7275
+ $$self{FOUND_DIR}{$dirName} = 1;
6922
7276
 
6923
7277
  # process the directory
6924
7278
  my $rtnVal = &$proc($self, $dirInfo, $tagTablePtr);
@@ -7051,10 +7405,12 @@ sub GetTagInfo($$$;$$$)
7051
7405
  #------------------------------------------------------------------------------
7052
7406
  # Add new tag to table (must use this routine to add new tags to a table)
7053
7407
  # Inputs: 0) reference to tag table, 1) tag ID
7054
- # 2) [optional] reference to tag information hash or simply tag name
7408
+ # 2) [optional] tag name or reference to tag information hash
7055
7409
  # 3) [optional] flag to avoid adding prefix when generating tag name
7410
+ # Returns: tagInfo ref
7056
7411
  # Notes: - will not override existing entry in table
7057
7412
  # - info need contain no entries when this routine is called
7413
+ # - tag name is made valid if necessary
7058
7414
  sub AddTagToTable($$;$$)
7059
7415
  {
7060
7416
  my ($tagTablePtr, $tagID, $tagInfo, $noPrefix) = @_;
@@ -7078,20 +7434,14 @@ sub AddTagToTable($$;$$)
7078
7434
  $$tagInfo{TagID} = $tagID;
7079
7435
 
7080
7436
  my $name = $$tagInfo{Name};
7081
- if (defined $name) {
7082
- $name =~ tr/-_a-zA-Z0-9//dc; # remove illegal characters
7083
- } else {
7084
- # construct a name from the tag ID
7085
- $name = $tagID;
7086
- $name =~ tr/-_a-zA-Z0-9//dc; # remove illegal characters
7087
- $name = ucfirst $name; # start with uppercase
7088
- # add prefix if specified
7089
- my $prefix = $$tagTablePtr{TAG_PREFIX};
7090
- if ($prefix and not $noPrefix) {
7091
- # make description to prevent tagID from getting mangled by MakeDescription()
7092
- $$tagInfo{Description} = MakeDescription($prefix, $name);
7093
- $name = "${prefix}_$name";
7094
- }
7437
+ $name = $tagID unless defined $name;
7438
+ $name =~ tr/-_a-zA-Z0-9//dc; # remove illegal characters
7439
+ $name = ucfirst $name; # capitalize first letter
7440
+ # add tag-name prefix if specified and tag name not provided
7441
+ unless (defined $$tagInfo{Name} or $noPrefix or not $$tagTablePtr{TAG_PREFIX}) {
7442
+ # make description to prevent tagID from getting mangled by MakeDescription()
7443
+ $$tagInfo{Description} = MakeDescription($$tagTablePtr{TAG_PREFIX}, $name);
7444
+ $name = "$$tagTablePtr{TAG_PREFIX}_$name";
7095
7445
  }
7096
7446
  # tag names must be at least 2 characters long and prefer them to start with a letter
7097
7447
  $name = "Tag$name" if length($name) < 2 or $name !~ /^[A-Z]/i;
@@ -7101,13 +7451,14 @@ sub AddTagToTable($$;$$)
7101
7451
  unless (defined $$tagTablePtr{$tagID} or $specialTags{$tagID}) {
7102
7452
  $$tagTablePtr{$tagID} = $tagInfo;
7103
7453
  }
7454
+ return $tagInfo;
7104
7455
  }
7105
7456
 
7106
7457
  #------------------------------------------------------------------------------
7107
7458
  # Handle simple extraction of new tag information
7108
7459
  # Inputs: 0) ExifTool object ref, 1) tag table reference, 2) tagID, 3) value,
7109
7460
  # 4-N) parameters hash: Index, DataPt, DataPos, Base, Start, Size, Parent,
7110
- # TagInfo, ProcessProc, RAF
7461
+ # TagInfo, ProcessProc, RAF, Format
7111
7462
  # Returns: tag key or undef if tag not found
7112
7463
  # Notes: if value is not defined, it is extracted from DataPt using TagInfo
7113
7464
  # Format and Count if provided
@@ -7243,6 +7594,7 @@ sub FoundTag($$$;@)
7243
7594
  local $_;
7244
7595
  my ($self, $tagInfo, $value, @grps) = @_;
7245
7596
  my ($tag, $noListDel);
7597
+ my $options = $$self{OPTIONS};
7246
7598
 
7247
7599
  if (ref $tagInfo eq 'HASH') {
7248
7600
  $tag = $$tagInfo{Name} or warn("No tag name\n"), return undef;
@@ -7253,7 +7605,7 @@ sub FoundTag($$$;@)
7253
7605
  # make temporary hash if tag doesn't exist in Extra
7254
7606
  # (not advised to do this since the tag won't show in list)
7255
7607
  $tagInfo or $tagInfo = { Name => $tag, Groups => \%allGroupsExifTool };
7256
- $$self{OPTIONS}{Verbose} and $self->VerboseInfo(undef, $tagInfo, Value => $value);
7608
+ $$options{Verbose} and $self->VerboseInfo(undef, $tagInfo, Value => $value);
7257
7609
  }
7258
7610
  # get tag priority
7259
7611
  my $priority = $$tagInfo{Priority};
@@ -7264,6 +7616,7 @@ sub FoundTag($$$;@)
7264
7616
  $grps[0] or $grps[0] = $$self{SET_GROUP0};
7265
7617
  $grps[1] or $grps[1] = $$self{SET_GROUP1};
7266
7618
  my $valueHash = $$self{VALUE};
7619
+
7267
7620
  if ($$tagInfo{RawConv}) {
7268
7621
  # initialize @val for use in Composite RawConv expressions
7269
7622
  my @val;
@@ -7314,10 +7667,11 @@ sub FoundTag($$$;@)
7314
7667
  # take tag with highest priority
7315
7668
  #
7316
7669
  # promote existing 0-priority tag so it takes precedence over a new 0-tag
7317
- # (unless old tag was a sub-document and new tag isn't)
7670
+ # (unless old tag was a sub-document and new tag isn't. Also, never override
7671
+ # a Warning tag because they may be added by ValueConv, which could be confusing)
7318
7672
  my $oldPriority = $$self{PRIORITY}{$tag};
7319
7673
  unless ($oldPriority) {
7320
- if ($$self{DOC_NUM} or not $$self{TAG_EXTRA}{$tag} or
7674
+ if ($$self{DOC_NUM} or not $$self{TAG_EXTRA}{$tag} or $tag eq 'Warning' or
7321
7675
  not $$self{TAG_EXTRA}{$tag}{G3})
7322
7676
  {
7323
7677
  $oldPriority = 1;
@@ -7330,7 +7684,9 @@ sub FoundTag($$$;@)
7330
7684
  # increase 0-priority tags if this is the priority directory
7331
7685
  $priority = 1 if not $priority and $$self{DIR_NAME} and
7332
7686
  $$self{DIR_NAME} eq $$self{PRIORITY_DIR};
7333
- } elsif ($$self{DIR_NAME} and $$self{LOW_PRIORITY_DIR}{$$self{DIR_NAME}}) {
7687
+ } elsif ($$self{LOW_PRIORITY_DIR}{'*'} or
7688
+ ($$self{DIR_NAME} and $$self{LOW_PRIORITY_DIR}{$$self{DIR_NAME}}))
7689
+ {
7334
7690
  $priority = 0; # default is 0 for a LOW_PRIORITY_DIR
7335
7691
  } else {
7336
7692
  $priority = 1; # the normal default
@@ -7378,7 +7734,7 @@ sub FoundTag($$$;@)
7378
7734
  }
7379
7735
  }
7380
7736
  # save path if requested
7381
- $$self{TAG_EXTRA}{$tag}{G5} = $self->MetadataPath() if $$self{OPTIONS}{SavePath};
7737
+ $$self{TAG_EXTRA}{$tag}{G5} = $self->MetadataPath() if $$options{SavePath};
7382
7738
 
7383
7739
  # remember this tagInfo if we will be accumulating values in a list
7384
7740
  # (but don't override earlier list if this may be deleted by NoListDel flag)
@@ -7386,6 +7742,13 @@ sub FoundTag($$$;@)
7386
7742
  $$self{LIST_TAGS}{$tagInfo} = $tag;
7387
7743
  }
7388
7744
 
7745
+ # validate tag if requested (but only for simple values -- could result
7746
+ # in infinite recursion if called for a Composite tag (HASH ref value)
7747
+ # because FoundTag is called in the middle of building Composite tags
7748
+ if ($$options{Validate} and not ref $value) {
7749
+ Image::ExifTool::Validate::ValidateRaw($self, $tag, $value);
7750
+ }
7751
+
7389
7752
  return $tag;
7390
7753
  }
7391
7754
 
@@ -7534,6 +7897,26 @@ sub VPrint($$@)
7534
7897
  }
7535
7898
  }
7536
7899
 
7900
+ #------------------------------------------------------------------------------
7901
+ # Print verbose directory information
7902
+ # Inputs: 0) ExifTool object reference, 1) directory name or dirInfo ref
7903
+ # 2) number of entries in directory (or 0 if unknown)
7904
+ # 3) optional size of directory in bytes
7905
+ sub VerboseDir($$;$$)
7906
+ {
7907
+ my ($self, $name, $entries, $size) = @_;
7908
+ return unless $$self{OPTIONS}{Verbose};
7909
+ if (ref $name eq 'HASH') {
7910
+ $size = $$name{DirLen} unless $size;
7911
+ $name = $$name{Name} || $$name{DirName};
7912
+ }
7913
+ my $indent = substr($$self{INDENT}, 0, -2);
7914
+ my $out = $$self{OPTIONS}{TextOut};
7915
+ my $str = $entries ? " with $entries entries" : '';
7916
+ $str .= ", $size bytes" if $size;
7917
+ print $out "$indent+ [$name directory$str]\n";
7918
+ }
7919
+
7537
7920
  #------------------------------------------------------------------------------
7538
7921
  # Verbose dump
7539
7922
  # Inputs: 0) ExifTool ref, 1) data ref, 2-N) HexDump options
@@ -7541,11 +7924,12 @@ sub VerboseDump($$;%)
7541
7924
  {
7542
7925
  my $self = shift;
7543
7926
  my $dataPt = shift;
7544
- if ($$self{OPTIONS}{Verbose} and $$self{OPTIONS}{Verbose} > 2) {
7927
+ my $verbose = $$self{OPTIONS}{Verbose};
7928
+ if ($verbose and $verbose > 2) {
7545
7929
  my %parms = (
7546
7930
  Prefix => $$self{INDENT},
7547
7931
  Out => $$self{OPTIONS}{TextOut},
7548
- MaxLen => $$self{OPTIONS}{Verbose} < 4 ? 96 : undef,
7932
+ MaxLen => $verbose < 4 ? 96 : $verbose < 5 ? 2048 : undef,
7549
7933
  );
7550
7934
  HexDump($dataPt, undef, %parms, @_);
7551
7935
  }
@@ -7802,7 +8186,7 @@ sub ProcessBinaryData($$$)
7802
8186
  unless (defined $val and not $$tagInfo{SubDirectory}) {
7803
8187
  $val = ReadValue($dataPt, $entry+$offset, $format, $count, $more, \$rational);
7804
8188
  $mask = $$tagInfo{Mask};
7805
- $val &= $mask if $mask;
8189
+ $val = ($val & $mask) >> $$tagInfo{BitShift} if $mask;
7806
8190
  }
7807
8191
  if ($verbose and not $$tagInfo{Hidden}) {
7808
8192
  if (not $$tagInfo{SubDirectory} or $$tagInfo{Format}) {
@@ -7888,19 +8272,24 @@ sub ProcessBinaryData($$$)
7888
8272
  # Load .ExifTool_config file from user's home directory
7889
8273
  # (use of noConfig is now deprecated, use configFile = '' instead)
7890
8274
  until ($Image::ExifTool::noConfig) {
7891
- my $file = $Image::ExifTool::configFile;
7892
- if (not defined $file) {
7893
- my $config = '.ExifTool_config';
8275
+ my $config = $Image::ExifTool::configFile;
8276
+ my $file;
8277
+ if (not defined $config) {
8278
+ $config = '.ExifTool_config';
7894
8279
  # get our home directory (HOMEDRIVE and HOMEPATH are used in Windows cmd shell)
7895
8280
  my $home = $ENV{EXIFTOOL_HOME} || $ENV{HOME} ||
7896
8281
  ($ENV{HOMEDRIVE} || '') . ($ENV{HOMEPATH} || '') || '.';
7897
8282
  # look for the config file in 1) the home directory, 2) the program dir
7898
8283
  $file = "$home/$config";
7899
- -r $file or $file = ($0 =~ /(.*[\\\/])/ ? $1 : './') . $config;
7900
- -r $file or last;
7901
8284
  } else {
7902
- length $file or last; # filename of "" disables configuration
7903
- -r $file or warn("Config file not found\n"), last;
8285
+ length $config or last; # filename of "" disables configuration
8286
+ $file = $config;
8287
+ }
8288
+ # also check executable directory unless path is absolute
8289
+ -r $file or $config =~ /^\// or $file = ($0 =~ /(.*[\\\/])/ ? $1 : './') . $config;
8290
+ unless (-r $file) {
8291
+ warn("Config file not found\n") if defined $Image::ExifTool::configFile;
8292
+ last;
7904
8293
  }
7905
8294
  unshift @INC, '.'; # look in current directory first
7906
8295
  eval { require $file }; # load the config file