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.
- checksums.yaml +4 -4
- data/bin/Changes +818 -19
- data/bin/MANIFEST +38 -0
- data/bin/META.json +1 -1
- data/bin/META.yml +1 -1
- data/bin/README +48 -44
- data/bin/arg_files/exif2xmp.args +4 -1
- data/bin/arg_files/gps2xmp.args +4 -1
- data/bin/arg_files/iptcCore.args +8 -0
- data/bin/arg_files/xmp2exif.args +4 -1
- data/bin/arg_files/xmp2gps.args +4 -1
- data/bin/config_files/dji.config +131 -0
- data/bin/config_files/example.config +6 -2
- data/bin/config_files/gps2utm.config +256 -256
- data/bin/config_files/nksc.config +146 -0
- data/bin/config_files/picasa_faces.config +382 -382
- data/bin/exiftool +688 -408
- data/bin/fmt_files/gpx.fmt +10 -6
- data/bin/fmt_files/gpx_wpt.fmt +10 -6
- data/bin/fmt_files/kml.fmt +8 -5
- data/bin/lib/File/RandomAccess.pm +48 -8
- data/bin/lib/File/RandomAccess.pod +21 -2
- data/bin/lib/Image/ExifTool.pm +645 -256
- data/bin/lib/Image/ExifTool.pod +219 -164
- data/bin/lib/Image/ExifTool/AES.pm +1 -1
- data/bin/lib/Image/ExifTool/AFCP.pm +3 -8
- data/bin/lib/Image/ExifTool/AIFF.pm +12 -4
- data/bin/lib/Image/ExifTool/APE.pm +1 -1
- data/bin/lib/Image/ExifTool/APP12.pm +1 -1
- data/bin/lib/Image/ExifTool/ASF.pm +19 -6
- data/bin/lib/Image/ExifTool/Apple.pm +13 -5
- data/bin/lib/Image/ExifTool/Audible.pm +1 -1
- data/bin/lib/Image/ExifTool/BMP.pm +1 -1
- data/bin/lib/Image/ExifTool/BPG.pm +17 -15
- data/bin/lib/Image/ExifTool/BZZ.pm +1 -1
- data/bin/lib/Image/ExifTool/BigTIFF.pm +30 -15
- data/bin/lib/Image/ExifTool/BuildTagLookup.pm +103 -52
- data/bin/lib/Image/ExifTool/Canon.pm +684 -112
- data/bin/lib/Image/ExifTool/CanonCustom.pm +119 -9
- data/bin/lib/Image/ExifTool/CanonRaw.pm +1 -1
- data/bin/lib/Image/ExifTool/CanonVRD.pm +13 -26
- data/bin/lib/Image/ExifTool/CaptureOne.pm +1 -1
- data/bin/lib/Image/ExifTool/Casio.pm +1 -1
- data/bin/lib/Image/ExifTool/Charset.pm +1 -1
- data/bin/lib/Image/ExifTool/DICOM.pm +12 -5
- data/bin/lib/Image/ExifTool/DJI.pm +51 -3
- data/bin/lib/Image/ExifTool/DNG.pm +15 -8
- data/bin/lib/Image/ExifTool/DPX.pm +1 -1
- data/bin/lib/Image/ExifTool/DV.pm +1 -1
- data/bin/lib/Image/ExifTool/DarwinCore.pm +63 -23
- data/bin/lib/Image/ExifTool/DjVu.pm +4 -2
- data/bin/lib/Image/ExifTool/EXE.pm +30 -6
- data/bin/lib/Image/ExifTool/Exif.pm +351 -109
- data/bin/lib/Image/ExifTool/FITS.pm +148 -0
- data/bin/lib/Image/ExifTool/FLAC.pm +2 -2
- data/bin/lib/Image/ExifTool/FLIF.pm +1 -1
- data/bin/lib/Image/ExifTool/FLIR.pm +109 -13
- data/bin/lib/Image/ExifTool/Fixup.pm +1 -1
- data/bin/lib/Image/ExifTool/Flash.pm +3 -3
- data/bin/lib/Image/ExifTool/FlashPix.pm +433 -9
- data/bin/lib/Image/ExifTool/Font.pm +2 -2
- data/bin/lib/Image/ExifTool/FotoStation.pm +1 -1
- data/bin/lib/Image/ExifTool/FujiFilm.pm +336 -16
- data/bin/lib/Image/ExifTool/GE.pm +1 -1
- data/bin/lib/Image/ExifTool/GIF.pm +5 -7
- data/bin/lib/Image/ExifTool/GIMP.pm +39 -3
- data/bin/lib/Image/ExifTool/GPS.pm +48 -22
- data/bin/lib/Image/ExifTool/GeoTiff.pm +23 -23
- data/bin/lib/Image/ExifTool/Geotag.pm +80 -45
- data/bin/lib/Image/ExifTool/GoPro.pm +709 -0
- data/bin/lib/Image/ExifTool/H264.pm +40 -18
- data/bin/lib/Image/ExifTool/HP.pm +1 -1
- data/bin/lib/Image/ExifTool/HTML.pm +19 -12
- data/bin/lib/Image/ExifTool/HtmlDump.pm +37 -26
- data/bin/lib/Image/ExifTool/ICC_Profile.pm +297 -23
- data/bin/lib/Image/ExifTool/ID3.pm +12 -7
- data/bin/lib/Image/ExifTool/IPTC.pm +48 -19
- data/bin/lib/Image/ExifTool/ISO.pm +1 -1
- data/bin/lib/Image/ExifTool/ITC.pm +1 -1
- data/bin/lib/Image/ExifTool/Import.pm +13 -9
- data/bin/lib/Image/ExifTool/InDesign.pm +3 -5
- data/bin/lib/Image/ExifTool/JPEG.pm +22 -11
- data/bin/lib/Image/ExifTool/JPEGDigest.pm +1 -1
- data/bin/lib/Image/ExifTool/JSON.pm +3 -3
- data/bin/lib/Image/ExifTool/JVC.pm +1 -1
- data/bin/lib/Image/ExifTool/Jpeg2000.pm +2 -2
- data/bin/lib/Image/ExifTool/Kodak.pm +1233 -58
- data/bin/lib/Image/ExifTool/KyoceraRaw.pm +1 -1
- data/bin/lib/Image/ExifTool/LNK.pm +1 -1
- data/bin/lib/Image/ExifTool/Lang/cs.pm +1 -1
- data/bin/lib/Image/ExifTool/Lang/de.pm +33 -24
- data/bin/lib/Image/ExifTool/Lang/en_ca.pm +64 -2
- data/bin/lib/Image/ExifTool/Lang/en_gb.pm +64 -2
- data/bin/lib/Image/ExifTool/Lang/es.pm +8 -4
- data/bin/lib/Image/ExifTool/Lang/fi.pm +46 -4
- data/bin/lib/Image/ExifTool/Lang/fr.pm +5 -3
- data/bin/lib/Image/ExifTool/Lang/it.pm +6 -3
- data/bin/lib/Image/ExifTool/Lang/ja.pm +15 -3
- data/bin/lib/Image/ExifTool/Lang/ko.pm +5 -2
- data/bin/lib/Image/ExifTool/Lang/nl.pm +6 -3
- data/bin/lib/Image/ExifTool/Lang/pl.pm +2 -2
- data/bin/lib/Image/ExifTool/Lang/ru.pm +1 -1
- data/bin/lib/Image/ExifTool/Lang/sv.pm +1 -1
- data/bin/lib/Image/ExifTool/Lang/tr.pm +4 -2
- data/bin/lib/Image/ExifTool/Lang/zh_cn.pm +1 -1
- data/bin/lib/Image/ExifTool/Lang/zh_tw.pm +1 -1
- data/bin/lib/Image/ExifTool/Leaf.pm +1 -1
- data/bin/lib/Image/ExifTool/Lytro.pm +4 -8
- data/bin/lib/Image/ExifTool/M2TS.pm +10 -9
- data/bin/lib/Image/ExifTool/MIE.pm +12 -8
- data/bin/lib/Image/ExifTool/MIEUnits.pod +1 -1
- data/bin/lib/Image/ExifTool/MIFF.pm +1 -1
- data/bin/lib/Image/ExifTool/MNG.pm +1 -1
- data/bin/lib/Image/ExifTool/MOI.pm +1 -1
- data/bin/lib/Image/ExifTool/MPC.pm +1 -1
- data/bin/lib/Image/ExifTool/MPEG.pm +2 -3
- data/bin/lib/Image/ExifTool/MPF.pm +6 -6
- data/bin/lib/Image/ExifTool/MWG.pm +4 -4
- data/bin/lib/Image/ExifTool/MXF.pm +2 -2
- data/bin/lib/Image/ExifTool/MacOS.pm +184 -34
- data/bin/lib/Image/ExifTool/MakerNotes.pm +101 -18
- data/bin/lib/Image/ExifTool/Matroska.pm +1 -1
- data/bin/lib/Image/ExifTool/Microsoft.pm +5 -3
- data/bin/lib/Image/ExifTool/Minolta.pm +89 -62
- data/bin/lib/Image/ExifTool/MinoltaRaw.pm +1 -1
- data/bin/lib/Image/ExifTool/Motorola.pm +1 -1
- data/bin/lib/Image/ExifTool/Nikon.pm +1511 -380
- data/bin/lib/Image/ExifTool/NikonCapture.pm +1 -1
- data/bin/lib/Image/ExifTool/NikonCustom.pm +2758 -2935
- data/bin/lib/Image/ExifTool/Nintendo.pm +1 -1
- data/bin/lib/Image/ExifTool/OOXML.pm +1 -1
- data/bin/lib/Image/ExifTool/Ogg.pm +1 -1
- data/bin/lib/Image/ExifTool/Olympus.pm +47 -8
- data/bin/lib/Image/ExifTool/OpenEXR.pm +1 -1
- data/bin/lib/Image/ExifTool/Opus.pm +1 -1
- data/bin/lib/Image/ExifTool/PCX.pm +138 -0
- data/bin/lib/Image/ExifTool/PDF.pm +58 -42
- data/bin/lib/Image/ExifTool/PGF.pm +1 -1
- data/bin/lib/Image/ExifTool/PICT.pm +1 -1
- data/bin/lib/Image/ExifTool/PLIST.pm +12 -5
- data/bin/lib/Image/ExifTool/PLUS.pm +1 -1
- data/bin/lib/Image/ExifTool/PNG.pm +108 -10
- data/bin/lib/Image/ExifTool/PPM.pm +3 -3
- data/bin/lib/Image/ExifTool/PSP.pm +1 -1
- data/bin/lib/Image/ExifTool/Palm.pm +1 -1
- data/bin/lib/Image/ExifTool/Panasonic.pm +299 -31
- data/bin/lib/Image/ExifTool/PanasonicRaw.pm +201 -19
- data/bin/lib/Image/ExifTool/Pentax.pm +164 -143
- data/bin/lib/Image/ExifTool/PhaseOne.pm +12 -5
- data/bin/lib/Image/ExifTool/PhotoCD.pm +9 -10
- data/bin/lib/Image/ExifTool/PhotoMechanic.pm +1 -1
- data/bin/lib/Image/ExifTool/Photoshop.pm +230 -60
- data/bin/lib/Image/ExifTool/PostScript.pm +29 -4
- data/bin/lib/Image/ExifTool/PrintIM.pm +1 -1
- data/bin/lib/Image/ExifTool/Qualcomm.pm +2 -2
- data/bin/lib/Image/ExifTool/QuickTime.pm +1539 -279
- data/bin/lib/Image/ExifTool/QuickTimeStream.pl +1857 -0
- data/bin/lib/Image/ExifTool/README +84 -46
- data/bin/lib/Image/ExifTool/RIFF.pm +116 -23
- data/bin/lib/Image/ExifTool/RSRC.pm +1 -1
- data/bin/lib/Image/ExifTool/RTF.pm +6 -4
- data/bin/lib/Image/ExifTool/Radiance.pm +1 -1
- data/bin/lib/Image/ExifTool/Rawzor.pm +3 -2
- data/bin/lib/Image/ExifTool/Real.pm +1 -1
- data/bin/lib/Image/ExifTool/Reconyx.pm +261 -7
- data/bin/lib/Image/ExifTool/Red.pm +325 -0
- data/bin/lib/Image/ExifTool/Ricoh.pm +3 -7
- data/bin/lib/Image/ExifTool/Samsung.pm +95 -25
- data/bin/lib/Image/ExifTool/Sanyo.pm +1 -1
- data/bin/lib/Image/ExifTool/Scalado.pm +1 -1
- data/bin/lib/Image/ExifTool/Shift.pl +26 -12
- data/bin/lib/Image/ExifTool/Shortcuts.pm +9 -2
- data/bin/lib/Image/ExifTool/Sigma.pm +36 -30
- data/bin/lib/Image/ExifTool/SigmaRaw.pm +3 -8
- data/bin/lib/Image/ExifTool/Sony.pm +531 -177
- data/bin/lib/Image/ExifTool/SonyIDC.pm +63 -3
- data/bin/lib/Image/ExifTool/Stim.pm +2 -2
- data/bin/lib/Image/ExifTool/TagInfoXML.pm +23 -23
- data/bin/lib/Image/ExifTool/TagLookup.pm +6352 -5062
- data/bin/lib/Image/ExifTool/TagNames.pod +3024 -565
- data/bin/lib/Image/ExifTool/Theora.pm +1 -1
- data/bin/lib/Image/ExifTool/Torrent.pm +2 -2
- data/bin/lib/Image/ExifTool/Unknown.pm +1 -1
- data/bin/lib/Image/ExifTool/VCard.pm +47 -9
- data/bin/lib/Image/ExifTool/Validate.pm +391 -99
- data/bin/lib/Image/ExifTool/Vorbis.pm +1 -1
- data/bin/lib/Image/ExifTool/WTV.pm +319 -0
- data/bin/lib/Image/ExifTool/WriteCanonRaw.pl +1 -1
- data/bin/lib/Image/ExifTool/WriteExif.pl +91 -18
- data/bin/lib/Image/ExifTool/WriteIPTC.pl +6 -6
- data/bin/lib/Image/ExifTool/WritePDF.pl +13 -12
- data/bin/lib/Image/ExifTool/WritePNG.pl +1 -1
- data/bin/lib/Image/ExifTool/WritePhotoshop.pl +1 -1
- data/bin/lib/Image/ExifTool/WritePostScript.pl +2 -2
- data/bin/lib/Image/ExifTool/WriteQuickTime.pl +764 -121
- data/bin/lib/Image/ExifTool/WriteXMP.pl +176 -67
- data/bin/lib/Image/ExifTool/Writer.pl +490 -246
- data/bin/lib/Image/ExifTool/XMP.pm +216 -76
- data/bin/lib/Image/ExifTool/XMP2.pl +54 -10
- data/bin/lib/Image/ExifTool/XMPStruct.pl +14 -11
- data/bin/lib/Image/ExifTool/ZIP.pm +60 -15
- data/bin/lib/Image/ExifTool/iWork.pm +12 -5
- data/bin/perl-Image-ExifTool.spec +46 -44
- data/lib/exiftool_vendored/version.rb +1 -1
- metadata +14 -4
data/bin/fmt_files/gpx.fmt
CHANGED
@@ -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 -
|
6
|
+
# Usage: exiftool -p gpx.fmt -ee FILE [...] > out.gpx
|
7
7
|
#
|
8
|
-
# Requires: ExifTool version
|
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)
|
13
|
-
# 2) The -
|
14
|
-
#
|
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>
|
data/bin/fmt_files/gpx_wpt.fmt
CHANGED
@@ -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 -
|
7
|
+
# Usage: exiftool -p gpx_wpt.fmt -ee FILE [...] > out.gpx
|
8
8
|
#
|
9
|
-
# Requires: ExifTool version
|
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)
|
14
|
-
# 2) The -
|
15
|
-
#
|
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>
|
data/bin/fmt_files/kml.fmt
CHANGED
@@ -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)
|
16
|
-
# 2)
|
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
|
-
#
|
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
|
-
#
|
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-
|
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.
|
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
|
-
|
75
|
-
|
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;
|
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
|
-
#
|
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
|
-
#
|
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-
|
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-
|
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.
|
data/bin/lib/Image/ExifTool.pm
CHANGED
@@ -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-
|
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 = '
|
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
|
140
|
-
RIFF AIFF ASF DICOM MIE JSON HTML
|
141
|
-
|
142
|
-
EXE::AR EXE::CHM LNK Font VCard
|
143
|
-
ZIP::RAR RTF OOXML iWork ISO
|
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
|
179
|
-
BMP BPG PPM RIFF AIFF ASF MOV MPEG Real SWF PSP FLV OGG
|
180
|
-
MPC MKV MXF DV PMP IND PGF ICC ITC FLIR FLIF FPF LFP
|
181
|
-
RTF XCF DSS QTIF FPX PICT ZIP GZIP PLIST RAR BZ2
|
182
|
-
HDR CHM LNK WMF AVC DEX DPX RAW Font RSRC M2TS
|
183
|
-
AA PDB MOI ISO
|
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 => [
|
194
|
-
JP2 => [
|
195
|
-
MOV => [
|
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 => '
|
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', '
|
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
|
-
|
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
|
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
|
-
|
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.
|
1109
|
-
is preserved by default when writing if Win32API::File
|
1110
|
-
available.
|
1111
|
-
|
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
|
1122
|
-
|
1123
|
-
|
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
|
1268
|
-
is edited, copied, renamed or moved in
|
1269
|
-
HardLink, then the link is made to the updated
|
1270
|
-
editing of either
|
1271
|
-
|
1272
|
-
|
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
|
-
#
|
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
|
-
|
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
|
2290
|
-
|
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
|
-
|
2297
|
-
|
2298
|
-
|
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
|
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 = $$
|
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[$_] = $$
|
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 (
|
2836
|
-
|
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(
|
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 $
|
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
|
-
|
3177
|
-
|
3178
|
-
|
3179
|
-
|
3180
|
-
|
3181
|
-
|
3182
|
-
|
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
|
-
|
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
|
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
|
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 ($
|
3411
|
+
if ($docNum) {
|
3220
3412
|
if ($found) {
|
3221
|
-
$$self{DOC_NUM} = $
|
3413
|
+
$$self{DOC_NUM} = $docNum;
|
3222
3414
|
$self->FoundTag($tagInfo, \%tagKey);
|
3223
3415
|
delete $$self{DOC_NUM};
|
3224
3416
|
}
|
3225
|
-
next if ++$
|
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
|
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
|
-
|
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 $
|
4046
|
+
last unless defined $groups[$f] and $grp eq lc $groups[$f];
|
3835
4047
|
} else {
|
3836
|
-
last unless grep /^$
|
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
|
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
|
3956
|
-
|
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
|
-
|
4007
|
-
$
|
4008
|
-
|
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
|
-
|
4059
|
-
|
4060
|
-
|
4061
|
-
|
4062
|
-
|
4063
|
-
if
|
4064
|
-
|
4065
|
-
|
4066
|
-
|
4067
|
-
|
4068
|
-
|
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
|
-
|
4076
|
-
$
|
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
|
-
|
4080
|
-
|
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
|
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
|
-
|
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
|
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
|
-
|
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 ==
|
5213
|
-
my $
|
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 $
|
5472
|
+
if ($isLocal and $tzStr =~ /(?:Z|([-+])(\d+):(\d+))/i) {
|
5217
5473
|
# use specified timezone if one exists
|
5218
|
-
$
|
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) - $
|
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 "
|
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
|
-
|
5317
|
-
|
5318
|
-
|
5319
|
-
|
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 $
|
5538
|
-
my $
|
5539
|
-
my $
|
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, $
|
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
|
-
|
5824
|
+
$$self{FILE_TYPE} = 'EXV';
|
5552
5825
|
}
|
5553
|
-
|
5554
|
-
|
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
|
-
$
|
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
|
-
|
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 ($$
|
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
|
-
($$
|
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 $$
|
6318
|
-
$
|
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 = $$
|
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
|
6358
|
-
Parent
|
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
|
-
|
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}
|
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
|
6814
|
-
if (not %$tableName
|
6815
|
-
|
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
|
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
|
-
|
7082
|
-
|
7083
|
-
|
7084
|
-
|
7085
|
-
|
7086
|
-
|
7087
|
-
|
7088
|
-
|
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
|
-
$$
|
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{
|
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 $$
|
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
|
-
|
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 =>
|
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
|
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 $
|
7892
|
-
|
7893
|
-
|
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 $
|
7903
|
-
|
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
|