exiftool_vendored 13.02.0 → 13.03.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -12,7 +12,7 @@ meta information extracted from or written to a file.
12
12
  =head1 TAG TABLES
13
13
 
14
14
  The tables listed below give the names of all tags recognized by ExifTool.
15
- They contain a total of 28165 tags, with 17491 unique tag names.
15
+ They contain a total of 28175 tags, with 17499 unique tag names.
16
16
 
17
17
  B<Tag ID>, B<Index#> or B<Sequence> is given in the first column of each
18
18
  table. A B<Tag ID> is the computer-readable equivalent of a tag name, and
@@ -147,8 +147,10 @@ L<https://www.w3.org/Graphics/JPEG/jfif3.pdf> for the JPEG specification.
147
147
  DJI-DBG DJI Info
148
148
  'APP8' SPIFF JPEG SPIFF
149
149
  InfiRayIsothermal InfiRay Isothermal
150
+ SEAL XMP SEAL
150
151
  'APP9' MediaJukebox JPEG MediaJukebox
151
152
  InfiRaySensor InfiRay Sensor
153
+ SEAL XMP SEAL
152
154
  'APP10' Comment no
153
155
  'APP11' JPEG-HDR JPEG HDR
154
156
  JUMBF Jpeg2000
@@ -1053,6 +1055,7 @@ for the official EXIF 2.32 specification.
1053
1055
  0xcd49 JXLDistance IFD0 float
1054
1056
  0xcd4a JXLEffort IFD0 int32u
1055
1057
  0xcd4b JXLDecodeSpeed IFD0 int32u
1058
+ 0xcea1 SEAL IFD0 XMP SEAL
1056
1059
  0xea1c Padding ExifIFD undef!
1057
1060
  0xea1d OffsetSchema ExifIFD int32s!
1058
1061
  0xfde8 OwnerName ExifIFD string/
@@ -1427,6 +1430,7 @@ L<http://www.adobe.com/devnet/xmp/> for the official XMP specification.
1427
1430
  pur XMP pur
1428
1431
  rdf XMP rdf
1429
1432
  sdc Nikon sdc
1433
+ seal XMP seal
1430
1434
  swf XMP swf
1431
1435
  tiff XMP tiff
1432
1436
  x XMP x
@@ -6152,6 +6156,37 @@ These tags belong to the ExifTool XMP-rdf family 1 group.
6152
6156
  -------- --------
6153
6157
  About string!
6154
6158
 
6159
+ =head3 XMP seal Tags
6160
+
6161
+ SEAL embedded in XMP.
6162
+
6163
+ These tags belong to the ExifTool XMP-seal family 1 group.
6164
+
6165
+ Tag Name Writable
6166
+ -------- --------
6167
+ Seal XMP SEAL
6168
+
6169
+ =head3 XMP SEAL Tags
6170
+
6171
+ These tags are used in SEAL content authentification, which is actually XML
6172
+ format, not XMP. ExifTool has read/delete support for SEAL information in
6173
+ JPG, TIFF, XMP, PNG, WEBP, HEIC, PPM, MOV and MP4 files, and read-only
6174
+ support in PDF, MKV and WAV. Use C<-seal:all=> on the command line to
6175
+ delete SEAL information in supported formats.
6176
+
6177
+ Tag ID Tag Name Writable
6178
+ ------ -------- --------
6179
+ 'b' ByteRange no
6180
+ 'copyright' Copyright no
6181
+ 'd' Domain no
6182
+ 'da' DigestAlgorithm no
6183
+ 'info' SEALComment no
6184
+ 'ka' KeyAlgorithm no
6185
+ 'kv' KeyVersion no
6186
+ 's' Signature no
6187
+ 'seal' SEALVersion no
6188
+ 'sf' SignatureFormat no
6189
+
6155
6190
  =head3 XMP swf Tags
6156
6191
 
6157
6192
  Adobe SWF namespace tags.
@@ -28129,6 +28164,7 @@ check if speed is more of a concern.
28129
28164
  'sPLT' SuggestedPalette no
28130
28165
  'sRGB' SRGBRendering yes!
28131
28166
  'sTER' StereoImage PNG StereoImage
28167
+ 'seAl' SEAL XMP SEAL
28132
28168
  'tEXt' TextualData PNG TextualData
28133
28169
  'tIME' ModifyDate yes
28134
28170
  'tRNS' Transparency no
@@ -29232,7 +29268,7 @@ C2PA JUMBF metadata extracted from "/C2PA_Manifest" file.
29232
29268
 
29233
29269
  Tag ID Tag Name Writable
29234
29270
  ------ -------- --------
29235
- '_stream' _stream Photoshop
29271
+ '_stream' PhotoshopStream Photoshop
29236
29272
 
29237
29273
  =head3 PDF Illustrator Tags
29238
29274
 
@@ -29255,7 +29291,7 @@ C2PA JUMBF metadata extracted from "/C2PA_Manifest" file.
29255
29291
 
29256
29292
  Tag ID Tag Name Writable
29257
29293
  ------ -------- --------
29258
- '_stream' _stream PostScript
29294
+ '_stream' AIStream PostScript
29259
29295
 
29260
29296
  =head3 PDF Resources Tags
29261
29297
 
@@ -29284,7 +29320,7 @@ C2PA JUMBF metadata extracted from "/C2PA_Manifest" file.
29284
29320
 
29285
29321
  Tag ID Tag Name Writable
29286
29322
  ------ -------- --------
29287
- '_stream' _stream ICC_Profile
29323
+ '_stream' ICC_Profile ICC_Profile
29288
29324
 
29289
29325
  =head3 PDF Properties Tags
29290
29326
 
@@ -29852,6 +29888,7 @@ for the official QuickTime specification.
29852
29888
  'GPS ' GPSDataList2? no
29853
29889
  'IDIT' DateTimeOriginal string
29854
29890
  'PICT' PreviewPICT no
29891
+ 'SEAL' SEAL XMP SEAL
29855
29892
  '_htc' HTCInfo QuickTime HTCInfo
29856
29893
  'ardt' ARDroneFile no
29857
29894
  'cust' CustomInfo no
@@ -31651,6 +31688,7 @@ metadata to WEBP images, but can't yet write to other RIFF-based formats.
31651
31688
  'LIST_hydt' PentaxData Pentax AVI
31652
31689
  'LIST_ncdt' NikonData Nikon AVI
31653
31690
  'LIST_pntx' PentaxData2 Pentax AVI
31691
+ 'SEAL' SEAL XMP SEAL
31654
31692
  'SGLT' BikeBroAccel QuickTime Stream
31655
31693
  'SLLT' BikeBroGPS QuickTime Stream
31656
31694
  'VP8 ' VP8Bitstream RIFF VP8
@@ -33258,6 +33296,7 @@ Matroska specification.
33258
33296
  0xb538667 SignatureSlot Matroska
33259
33297
  0xc53bb6b Cues Matroska
33260
33298
  0xf43b675 Cluster Matroska
33299
+ 0x5345414c SEAL XMP SEAL
33261
33300
 
33262
33301
  =head3 Matroska Projection Tags
33263
33302
 
@@ -34,6 +34,11 @@ my %webpMap = (
34
34
  MakerNotes => 'ExifIFD',
35
35
  );
36
36
 
37
+ my %deletableGroup = (
38
+ "XMP\0" => 'XMP', # delete incorrectly written "XMP\0" tag with XMP group
39
+ SEAL => 'SEAL', # delete SEAL tag with SEAL group
40
+ );
41
+
37
42
  #------------------------------------------------------------------------------
38
43
  # Write RIFF file (currently WebP-type only)
39
44
  # Inputs: 0) ExifTool object ref, 1) dirInfo ref
@@ -46,6 +51,7 @@ sub WriteRIFF($$)
46
51
  my $outfile = $$dirInfo{OutFile};
47
52
  my $outsize = 0;
48
53
  my $raf = $$dirInfo{RAF};
54
+ my $verbose = $et->Options('Verbose');
49
55
  my ($buff, $err, $pass, %has, %dirDat, $imageWidth, $imageHeight);
50
56
 
51
57
  # do this in 2 passes so we can set the size of the containing RIFF chunk
@@ -65,6 +71,7 @@ sub WriteRIFF($$)
65
71
  SetByteOrder('II');
66
72
 
67
73
  # determine which directories we must write for this file type
74
+ $et->Options(Verbose => 0) if $pass; # (avoid duplicate Verbose options here)
68
75
  $et->InitWriteDirs(\%webpMap);
69
76
  my $addDirs = $$et{ADD_DIRS};
70
77
  my $editDirs = $$et{EDIT_DIRS};
@@ -73,6 +80,7 @@ sub WriteRIFF($$)
73
80
 
74
81
  # write header
75
82
  if ($pass) {
83
+ $et->Options(Verbose => $verbose);
76
84
  my $needsVP8X = ($has{ANIM} or $has{'XMP '} or $has{EXIF} or
77
85
  $has{ALPH} or $has{ICCP});
78
86
  if ($has{VP8X} and not $needsVP8X and $$et{CHANGED}) {
@@ -146,13 +154,14 @@ sub WriteRIFF($$)
146
154
  # RIFF chunks are padded to an even number of bytes
147
155
  my $len2 = $len + ($len & 0x01);
148
156
  # handle incorrect "XMP\0" chunk ID written by Google software
149
- if ($tag eq "XMP\0") {
150
- if ($$et{DEL_GROUP}{XMP}) {
151
- # just ignore this chunk if deleting XMP
157
+ if ($deletableGroup{$tag}) {
158
+ if ($$et{DEL_GROUP}{$deletableGroup{$tag}}) {
159
+ # just ignore this chunk if deleting the associated group
152
160
  $raf->Seek($len2, 1) or $et->Error('Seek error'), last;
161
+ $et->VPrint(0, " Deleting $deletableGroup{$tag}\n") if $pass;
153
162
  ++$$et{CHANGED};
154
163
  next;
155
- } else {
164
+ } elsif ($tag eq "XMP\0") {
156
165
  $et->Warn('Incorrect XMP tag ID',1) if $pass;
157
166
  }
158
167
  }
@@ -43,6 +43,7 @@ my %tiffMap = (
43
43
  PrintIM => 'IFD0',
44
44
  IPTC => 'IFD0',
45
45
  Photoshop => 'IFD0',
46
+ SEAL => 'IFD0',
46
47
  InteropIFD => 'ExifIFD',
47
48
  MakerNotes => 'ExifIFD',
48
49
  CanonVRD => 'MakerNotes', # (so VRDOffset will get updated)
@@ -74,6 +75,7 @@ my %jpegMap = (
74
75
  Meta => 'APP3',
75
76
  MetaIFD => 'Meta',
76
77
  RMETA => 'APP5',
78
+ SEAL => ['APP8','APP9'], # (note: add 'IFD0' if this is a possibility)
77
79
  Ducky => 'APP12',
78
80
  Photoshop => 'APP13',
79
81
  Adobe => 'APP14',
@@ -140,7 +142,7 @@ my @delGroups = qw(
140
142
  GlobParamIFD GPS ICC_Profile IFD0 IFD1 Insta360 InteropIFD IPTC ItemList JFIF
141
143
  Jpeg2000 JUMBF Keys MakerNotes Meta MetaIFD Microsoft MIE MPF Nextbase NikonApp
142
144
  NikonCapture PDF PDF-update PhotoMechanic Photoshop PNG PNG-pHYs PrintIM
143
- QuickTime RMETA RSRC SubIFD Trailer UserData XML XML-* XMP XMP-*
145
+ QuickTime RMETA RSRC SEAL SubIFD Trailer UserData XML XML-* XMP XMP-*
144
146
  );
145
147
  # family 2 group names that we can delete
146
148
  my @delGroup2 = qw(
@@ -152,6 +154,7 @@ my %delMore = (
152
154
  QuickTime => [ qw(ItemList UserData Keys) ],
153
155
  XMP => [ 'XMP-*' ],
154
156
  XML => [ 'XML-*' ],
157
+ SEAL => [ 'XMP-SEAL' ],
155
158
  );
156
159
 
157
160
  # family 0 groups where directories should never be deleted
@@ -1311,7 +1314,7 @@ sub SetNewValuesFromFile($$;@)
1311
1314
  LimitLongValues => 10000000, # (10 MB)
1312
1315
  List => 1,
1313
1316
  MakerNotes => $$options{FastScan} && $$options{FastScan} > 1 ? undef : 1,
1314
- RequestAll => $$options{RequestAll} || 1, # (is this still necessary now that RequestTags are being set?)
1317
+ RequestAll => $$options{RequestAll} || 1, # (must request all because reqTags doesn't cover wildcards)
1315
1318
  StrictDate => defined $$options{StrictDate} ? $$options{StrictDate} : 1,
1316
1319
  Struct => $structOpt,
1317
1320
  );
@@ -1324,11 +1327,8 @@ sub SetNewValuesFromFile($$;@)
1324
1327
  $$srcExifTool{ALT_EXIFTOOL} = $$self{ALT_EXIFTOOL};
1325
1328
  foreach $tag (@setTags) {
1326
1329
  next if ref $tag;
1327
- if ($tag =~ /^-(.*)/) {
1328
- # avoid extracting tags that are excluded
1329
- push @exclude, $1;
1330
- next;
1331
- }
1330
+ # avoid extracting tags that are excluded
1331
+ $tag =~ /^-(.*)/ and push(@exclude, $1), next;
1332
1332
  # add specified tags to list of requested tags
1333
1333
  $_ = $tag;
1334
1334
  if (/(.+?)\s*(>|<)\s*(.+)/) {
@@ -2983,7 +2983,7 @@ Conv: for (;;) {
2983
2983
  $err2 = eval $$tagInfo{WriteCheck};
2984
2984
  $@ and warn($@), $err2 = 'Error evaluating WriteCheck';
2985
2985
  }
2986
- unless ($err2) {
2986
+ unless (defined $err2) {
2987
2987
  my $table = $$tagInfo{Table};
2988
2988
  if ($table and $$table{CHECK_PROC} and not $$tagInfo{RawConvInv}) {
2989
2989
  my $checkProc = $$table{CHECK_PROC};
@@ -6621,6 +6621,11 @@ sub WriteJPEG($$)
6621
6621
  $segType = 'Ricoh RMETA';
6622
6622
  $$delGroup{RMETA} and $del = 1;
6623
6623
  }
6624
+ } elsif ($marker == 0xe8 or $marker == 0xe9) { # APP8/9 (SEAL)
6625
+ if ($$segDataPt =~ /^SEAL\0/) {
6626
+ $segType = 'SEAL';
6627
+ $$delGroup{SEAL} and $del = 1;
6628
+ }
6624
6629
  } elsif ($marker == 0xeb) { # APP10 (JUMBF)
6625
6630
  if ($$segDataPt =~ /^JP/) {
6626
6631
  $segType = 'JUMBF';
@@ -6987,7 +6992,7 @@ sub SetFileTime($$;$$$$)
6987
6992
  # get Win32 handle, needed for SetFileTime
6988
6993
  my $win32Handle = eval { Win32API::File::GetOsFHandle($file) };
6989
6994
  unless ($win32Handle) {
6990
- $self->Warn('Win32API::File::GetOsFHandle returned invalid handle');
6995
+ $self->Warn('Win32API::File GetOsFHandle returned invalid handle');
6991
6996
  return 0;
6992
6997
  }
6993
6998
  # convert Unix seconds to FILETIME structs
@@ -7005,13 +7010,13 @@ sub SetFileTime($$;$$$$)
7005
7010
  return 0 if defined $k32SetFileTime;
7006
7011
  $k32SetFileTime = Win32::API->new('KERNEL32', 'SetFileTime', 'NPPP', 'I');
7007
7012
  unless ($k32SetFileTime) {
7008
- $self->Warn('Error calling Win32::API::SetFileTime');
7013
+ $self->Warn('Error loading Win32::API SetFileTime');
7009
7014
  $k32SetFileTime = 0;
7010
7015
  return 0;
7011
7016
  }
7012
7017
  }
7013
7018
  unless ($k32SetFileTime->Call($win32Handle, $ctime, $atime, $mtime)) {
7014
- $self->Warn('Win32::API::SetFileTime returned ' . Win32::GetLastError());
7019
+ $self->Warn('Win32::API SetFileTime returned ' . Win32::GetLastError());
7015
7020
  return 0;
7016
7021
  }
7017
7022
  return 1;
@@ -50,7 +50,7 @@ use Image::ExifTool::Exif;
50
50
  use Image::ExifTool::GPS;
51
51
  require Exporter;
52
52
 
53
- $VERSION = '3.68';
53
+ $VERSION = '3.69';
54
54
  @ISA = qw(Exporter);
55
55
  @EXPORT_OK = qw(EscapeXML UnescapeXML);
56
56
 
@@ -203,6 +203,7 @@ my %xmpNS = (
203
203
  hdr_metadata => 'http://ns.adobe.com/hdr-metadata/1.0/',
204
204
  hdrgm => 'http://ns.adobe.com/hdr-gain-map/1.0/',
205
205
  xmpDSA => 'http://leica-camera.com/digital-shift-assistant/1.0/',
206
+ seal => 'http://ns.seal/2024/1.0/',
206
207
  # Note: Google uses a prefix of 'Container', but this conflicts with the
207
208
  # Device Container namespace, also by Google. So call this one GContainer
208
209
  GContainer=> 'http://ns.google.com/photos/1.0/container/',
@@ -933,6 +934,10 @@ my %sRangeMask = (
933
934
  Name => 'xmpDSA',
934
935
  SubDirectory => { TagTable => 'Image::ExifTool::Panasonic::DSA' },
935
936
  },
937
+ seal => {
938
+ Name => 'seal',
939
+ SubDirectory => { TagTable => 'Image::ExifTool::XMP::seal' },
940
+ },
936
941
  GContainer => {
937
942
  Name => 'GContainer',
938
943
  SubDirectory => { TagTable => 'Image::ExifTool::XMP::GContainer' },
@@ -4142,7 +4147,7 @@ sub ParseXMPElement($$$;$$$$)
4142
4147
 
4143
4148
  #------------------------------------------------------------------------------
4144
4149
  # Process XMP data
4145
- # Inputs: 0) ExifTool object reference, 1) DirInfo reference, 2) Pointer to tag table
4150
+ # Inputs: 0) ExifTool ref, 1) dirInfo ref, 2) tag table ref
4146
4151
  # Returns: 1 on success
4147
4152
  # Notes: The following flavours of XMP files are currently recognized:
4148
4153
  # - standard XMP with xpacket, x:xmpmeta and rdf:RDF elements
@@ -25,6 +25,7 @@ use strict;
25
25
  use Image::ExifTool qw(:Utils);
26
26
  use Image::ExifTool::XMP;
27
27
 
28
+ sub ProcessSEAL($$;$);
28
29
  sub Init_crd($);
29
30
 
30
31
  #------------------------------------------------------------------------------
@@ -2290,6 +2291,65 @@ my %sACDSeeRegionStruct = (
2290
2291
  },
2291
2292
  );
2292
2293
 
2294
+ %Image::ExifTool::XMP::seal = (
2295
+ GROUPS => { 0 => 'XMP', 1 => 'XMP-seal', 2 => 'Image' },
2296
+ NAMESPACE => 'seal',
2297
+ WRITABLE => 'string',
2298
+ NOTES => 'SEAL embedded in XMP.',
2299
+ seal => {
2300
+ Name => 'Seal',
2301
+ Binary => 1,
2302
+ SubDirectory => { TagTable => 'Image::ExifTool::XMP::SEAL' },
2303
+ },
2304
+ );
2305
+
2306
+ %Image::ExifTool::XMP::SEAL = (
2307
+ GROUPS => { 0 => 'XML', 1 => 'SEAL', 2 => 'Document' },
2308
+ PROCESS_PROC => \&ProcessSEAL,
2309
+ NOTES => q{
2310
+ These tags are used in SEAL content authentification, which is actually XML
2311
+ format, not XMP. ExifTool has read/delete support for SEAL information in
2312
+ JPG, TIFF, XMP, PNG, WEBP, HEIC, PPM, MOV and MP4 files, and read-only
2313
+ support in PDF, MKV and WAV. Use C<-seal:all=> on the command line to
2314
+ delete SEAL information in supported formats.
2315
+ },
2316
+ seal=> 'SEALVersion',
2317
+ kv => 'KeyVersion',
2318
+ ka => 'KeyAlgorithm',
2319
+ da => 'DigestAlgorithm',
2320
+ sf => 'SignatureFormat',
2321
+ d => 'Domain',
2322
+ b => 'ByteRange',
2323
+ 's' => 'Signature',
2324
+ info=> 'SEALComment',
2325
+ copyright => { Name => 'Copyright', Groups => { 2 => 'Author' } },
2326
+ );
2327
+
2328
+ #------------------------------------------------------------------------------
2329
+ # We found a SEAL property name/value
2330
+ # Inputs: 0) ExifTool ref, 1) tag table ref, 2) xmp property list ref
2331
+ # 3) property value, 4) attribute hash ref
2332
+ # Returns: 1 if valid tag was found
2333
+ sub FoundSEAL($$$$;$)
2334
+ {
2335
+ my ($et, $tagTablePtr, $props, $val, $attrs) = @_;
2336
+ # remove 'seal' container property from name
2337
+ my @sealProps = @$props;
2338
+ shift @sealProps if @sealProps and $sealProps[0] eq 'seal';
2339
+ return FoundXMP($et, $tagTablePtr, \@sealProps, $val, $attrs);
2340
+ }
2341
+
2342
+ #------------------------------------------------------------------------------
2343
+ # Process SEAL XML
2344
+ # Inputs: 0) ExifTool ref, 1) dirInfo ref, 2) tag table ref
2345
+ # Returns: 1 on success
2346
+ sub ProcessSEAL($$;$)
2347
+ {
2348
+ my ($et, $dirInfo, $tagTablePtr) = @_;
2349
+ $$dirInfo{XMPParseOpts}{FoundProc} = \&FoundSEAL;
2350
+ return ProcessXMP($et, $dirInfo, $tagTablePtr);
2351
+ }
2352
+
2293
2353
  #------------------------------------------------------------------------------
2294
2354
  # Generate crd tags
2295
2355
  # Inputs: 0) tag table ref
@@ -29,7 +29,7 @@ use vars qw($VERSION $RELEASE @ISA @EXPORT_OK %EXPORT_TAGS $AUTOLOAD @fileTypes
29
29
  %jpegMarker %specialTags %fileTypeLookup $testLen $exeDir
30
30
  %static_vars $advFmtSelf);
31
31
 
32
- $VERSION = '13.02';
32
+ $VERSION = '13.03';
33
33
  $RELEASE = '';
34
34
  @ISA = qw(Exporter);
35
35
  %EXPORT_TAGS = (
@@ -4660,74 +4660,56 @@ sub EncodeFileName($$;$)
4660
4660
  # References:
4661
4661
  # - https://learn.microsoft.com/en-us/dotnet/standard/io/file-path-formats
4662
4662
  # - https://learn.microsoft.com/en-us/windows/win32/fileio/maximum-file-path-limitation
4663
+ # GetFullPathName supported by Windows XP and later. It handles:
4664
+ # full path names EG: c:\foto\sub\abc.jpg
4665
+ # relative EG: .\abc.jpg, ..\abc.jpg
4666
+ # full UNC paths EG: \\server\share\abc.jpg
4667
+ # relative UNC paths EG: .\abc.jpg, ..\abc.jpg
4668
+ # Dos device paths EG: \\.\c:\fotoabc.jpg
4669
+ # relative path on other drives EG: z:abc.jpg (working dir on z: z:\foto called from c:\foto)
4670
+ # Wide chars EG: Chars that need UTF8.
4671
+ my $k32GetFullPathName;
4663
4672
  sub WindowsLongPath($$)
4664
4673
  {
4665
4674
  my ($self, $path) = @_;
4666
4675
  my $debug = $$self{OPTIONS}{Debug};
4667
4676
  my $out = $$self{OPTIONS}{TextOut};
4668
- my @fullParts;
4669
- my $prefixLen = 0;
4670
4677
 
4671
4678
  $debug and print $out "WindowsLongPath input : $path\n";
4672
- $path =~ tr(/)(\\); # convert slashes to backslashes
4673
- my @pathParts = split /\\/, $path;
4674
4679
 
4675
- if ($path =~ /^\\\\\?\\/ or # already a device path in the format we want
4676
- $path =~ s/^\\\\\.\\/\\\\?\\/) # convert //./ to //?/
4677
- {
4678
- # path is already long-path compatible
4679
- $prefixLen = 3; # path already contains prefix of 3 parts ('', '' and '?')
4680
- } elsif ($path =~ /[*?]/) {
4681
- return $path; # do nothing because we don't support wildcards
4682
- } elsif ($path =~ /^\\\\/) {
4683
- # UNC path starts with two slashes change to "\\?\UNC\"
4684
- splice @pathParts, 2, 0, '?', 'UNC';
4685
- $prefixLen = (@pathParts > 6 ? 6 : @pathParts); # ('', '', '?', 'UNC', <server>, <share>)
4686
- } elsif ($path =~ /^[a-z]:\\/i) {
4687
- # path is already absolute but we need to add the device path prefix
4688
- unshift @pathParts, '', '', '?';
4689
- $prefixLen = 4;
4690
- } elsif ({ eval { require Cwd } }) {
4691
- my $drive;
4692
- $drive = $1 if $pathParts[0] =~ s/^([a-z]:)//;
4693
- my $cwd = Cwd::getdcwd($drive); # ($drive is undef for current working drive)
4694
- $debug and print $out "WindowsLongPath getcwd: $cwd\n";
4695
- @fullParts = split /[\\\/]/, $cwd;
4696
- # UNC path starts with "\\", so first 2 elements are empty
4697
- # --> shift and put UNC in first element.
4698
- if (@fullParts > 1 and $fullParts[0] eq '' and $fullParts[1] eq '') {
4699
- shift @fullParts;
4700
- $fullParts[0] = 'UNC';
4701
- unshift @fullParts, '', '', '?';
4702
- $prefixLen = (@fullParts > 6 ? 6 : @fullParts);
4703
- } else {
4704
- $prefixLen = 1; # drive designator only
4680
+ for (;;) { # (cheap goto)
4681
+ $path =~ tr(/)(\\); # convert slashes to backslashes
4682
+ last if $path =~ /^\\\\\?\\/; # already a device path in the format we want
4683
+
4684
+ unless ($k32GetFullPathName) { # need to import (once) GetFullPathNameW
4685
+ last if defined $k32GetFullPathName;
4686
+ unless (eval { require Win32::API }) {
4687
+ $self->WarnOnce('Install Win32::API to use WindowsLongPath option');
4688
+ last;
4689
+ }
4690
+ $k32GetFullPathName = Win32::API->new('KERNEL32', 'GetFullPathNameW', 'PNPP', 'I');
4691
+ unless ($k32GetFullPathName) {
4692
+ $k32GetFullPathName = 0;
4693
+ $self->Warn('Error loading Win32::API GetFullPathNameW');
4694
+ last;
4695
+ }
4705
4696
  }
4706
- # if absolute path on current drive starts with "\"
4707
- # just keep prefix and drop the rest of the cwd
4708
- $#fullParts = $prefixLen - 1 if $pathParts[0] eq '';
4709
- } else {
4710
- $prefixLen = @pathParts; # (nothing more we can do)
4711
- }
4712
- # remove "." and ".." from path (not handled for device paths)
4713
- my $part;
4714
- foreach $part (@pathParts) {
4715
- if ($part eq '.') {
4716
- next;
4717
- } elsif ($part eq '') {
4718
- # only allow double slashes at start of path name (max 2)
4719
- push @fullParts, $part if not @fullParts or (@fullParts == 1 and $fullParts[0] eq '');
4720
- } elsif ($part eq '..') {
4721
- # step up one directory, but not into the prefix
4722
- pop @fullParts if @fullParts > $prefixLen;
4697
+ my $enc = $$self{OPTIONS}{CharsetFileName};
4698
+ my $encPath = $self->Encode($path, 'UTF16', 'II', $enc); # need to encode to UTF16
4699
+ my $lenReq = $k32GetFullPathName->Call($encPath,0,0,0) + 1; # first pass gets length required, +1 for safety (null?)
4700
+ my $fullPath = "\0" x $lenReq x 2; # create buffer to hold full path
4701
+ $k32GetFullPathName->Call($encPath, $lenReq, $fullPath, 0); # fullPath is UTF16 now
4702
+ $path = $self->Decode($fullPath, 'UTF16', 'II', $enc);
4703
+
4704
+ last if length($path) <= 247;
4705
+
4706
+ if ($path =~ /^\\\\/) {
4707
+ $path = '\\\\?\\UNC' . substr($path, 1);
4723
4708
  } else {
4724
- push @fullParts, $part;
4709
+ $path = '\\\\?\\' . $path;
4725
4710
  }
4711
+ last;
4726
4712
  }
4727
- $path = join '\\', @fullParts;
4728
- # add device path prefix ("\\?\") if path length near the limit (the most
4729
- # conservative limit I can find is 247, which is the limit on the directory name)
4730
- $path = '\\\\?\\' . $path unless $prefixLen > 1 or length($path) <= 247;
4731
4713
  $debug and print $out "WindowsLongPath return: $path\n";
4732
4714
  return $path;
4733
4715
  }
@@ -4861,16 +4843,16 @@ sub CreateDirectory($$)
4861
4843
  my $d2 = $dir; # (must make a copy in case EncodeFileName recodes it)
4862
4844
  if ($self->EncodeFileName($d2)) {
4863
4845
  # handle Windows Unicode directory names
4864
- unless (eval { require Win32::API }) {
4865
- $err = 'Install Win32::API to create directories with Unicode names';
4866
- last;
4867
- }
4868
4846
  unless (defined $k32CreateDir) {
4847
+ unless (eval { require Win32::API }) {
4848
+ $err = 'Install Win32::API to create directories with Unicode names';
4849
+ last;
4850
+ }
4869
4851
  $k32CreateDir = Win32::API->new('KERNEL32', 'CreateDirectoryW', 'PP', 'I');
4870
4852
  unless ($k32CreateDir) {
4871
4853
  $k32CreateDir = 0;
4872
4854
  # give this error once, then just "Error creating" for subsequent attempts
4873
- return 'Error accessing Win32::API::CreateDirectoryW';
4855
+ return 'Error loading Win32::API CreateDirectoryW';
4874
4856
  }
4875
4857
  }
4876
4858
  $success = $k32CreateDir->Call($d2, 0) if $k32CreateDir;
@@ -4928,13 +4910,13 @@ sub GetFileTime($$)
4928
4910
  return () if defined $k32GetFileTime;
4929
4911
  $k32GetFileTime = Win32::API->new('KERNEL32', 'GetFileTime', 'NPPP', 'I');
4930
4912
  unless ($k32GetFileTime) {
4931
- $self->Warn('Error calling Win32::API::GetFileTime');
4913
+ $self->Warn('Error loading Win32::API GetFileTime');
4932
4914
  $k32GetFileTime = 0;
4933
4915
  return ();
4934
4916
  }
4935
4917
  }
4936
4918
  unless ($k32GetFileTime->Call($win32Handle, $ctime, $atime, $mtime)) {
4937
- $self->Warn("Win32::API::GetFileTime returned " . Win32::GetLastError());
4919
+ $self->Warn("Win32::API GetFileTime returned " . Win32::GetLastError());
4938
4920
  return ();
4939
4921
  }
4940
4922
  # convert FILETIME structs to Unix seconds
@@ -5085,7 +5067,7 @@ sub IsSameID($$)
5085
5067
 
5086
5068
  #------------------------------------------------------------------------------
5087
5069
  # Get list of tags in specified group
5088
- # Inputs: 0) ExifTool ref, 1) group spec, 2) tag key or reference to list of tag keys
5070
+ # Inputs: 0) ExifTool ref, 1) group spec (case insensitive), 2) tag key or reference to list of tag keys
5089
5071
  # Returns: list of matching tags in list context, or first match in scalar context
5090
5072
  # Notes: Group spec may contain multiple groups separated by colons, each
5091
5073
  # possibly with a leading family number
@@ -6450,9 +6432,45 @@ sub ConvertDateTime($$)
6450
6432
  my $fmt = $$self{OPTIONS}{DateFormat};
6451
6433
  my $shift = $$self{OPTIONS}{GlobalTimeShift};
6452
6434
  if ($shift) {
6453
- my $dir = ($shift =~ s/^([-+])// and $1 eq '-') ? -1 : 1;
6454
6435
  my $offset = $$self{GLOBAL_TIME_OFFSET};
6455
- $offset or $offset = $$self{GLOBAL_TIME_OFFSET} = { };
6436
+ my ($g, $t, $dir, @matches);
6437
+ if ($shift =~ s/^((\d?[A-Z][-\w]*\w:)*)([A-Z][-\w]*\w)([-+])//i) {
6438
+ ($g, $t, $dir) = ($1, $3, ($4 eq '-' ? -1 : 1));
6439
+ } else {
6440
+ $dir = ($shift =~ s/^([-+])// and $1 eq '-') ? -1 : 1;
6441
+ }
6442
+ unless ($offset) {
6443
+ $offset = $$self{GLOBAL_TIME_OFFSET} = { };
6444
+ # (see forum16692 for a discussion about why this code was added)
6445
+ if ($t) {
6446
+ # determine initial shift from specified tag
6447
+ @matches = sort grep(/^$t( \(|$)/i, keys %{$$self{VALUE}});
6448
+ if ($g and @matches) {
6449
+ $g =~ s/:$//;
6450
+ @matches = $self->GroupMatches($g, \@matches);
6451
+ }
6452
+ }
6453
+ if (not @matches and $$self{TAGS_FROM_FILE} and $$self{OPTIONS}{RequestTags}) {
6454
+ # determine initial shift from first requested date/time tag
6455
+ my @reqDate = grep /date/i, @{$$self{OPTIONS}{RequestTags}};
6456
+ while (@reqDate) {
6457
+ $t = shift @reqDate;
6458
+ @matches = sort grep(/^$t( \(|$)/i, keys %{$$self{VALUE}});
6459
+ my $ti = $$self{TAG_INFO};
6460
+ for (; @matches; shift @matches) {
6461
+ # select the first tag that calls this routine in its PrintConv
6462
+ next unless $$ti{$matches[0]}{PrintConv};
6463
+ next unless $$ti{$matches[0]}{PrintConv} =~ /ConvertDateTime/;
6464
+ undef @reqDate;
6465
+ last;
6466
+ }
6467
+ }
6468
+ }
6469
+ if (@matches) {
6470
+ my $val = $self->GetValue($matches[0], 'ValueConv');
6471
+ ShiftTime($val, $shift, $dir, $offset) if defined $val;
6472
+ }
6473
+ }
6456
6474
  ShiftTime($date, $shift, $dir, $offset);
6457
6475
  }
6458
6476
  # only convert date if a format was specified and the date is recognizable
@@ -7952,6 +7970,10 @@ sub ProcessJPEG($$;$)
7952
7970
  SetByteOrder('II');
7953
7971
  my $tagTablePtr = GetTagTable('Image::ExifTool::InfiRay::Isothermal');
7954
7972
  $self->ProcessDirectory(\%dirInfo, $tagTablePtr);
7973
+ } elsif ($$segDataPt =~ /^SEAL\0/) {
7974
+ $dumpType = 'SEAL';
7975
+ DirStart(\%dirInfo, 5);
7976
+ $self->ProcessDirectory(\%dirInfo, GetTagTable("Image::ExifTool::XMP::SEAL"));
7955
7977
  }
7956
7978
  } elsif ($marker == 0xe9) { # APP9 (InfiRay, Media Jukebox)
7957
7979
  if ($$segDataPt =~ /^Media Jukebox\0/ and $length > 22) {
@@ -7967,6 +7989,10 @@ sub ProcessJPEG($$;$)
7967
7989
  SetByteOrder('II');
7968
7990
  my $tagTablePtr = GetTagTable('Image::ExifTool::InfiRay::Sensor');
7969
7991
  $self->ProcessDirectory(\%dirInfo, $tagTablePtr);
7992
+ } elsif ($$segDataPt =~ /^SEAL\0/) {
7993
+ $dumpType = 'SEAL';
7994
+ DirStart(\%dirInfo, 5);
7995
+ $self->ProcessDirectory(\%dirInfo, GetTagTable("Image::ExifTool::XMP::SEAL"));
7970
7996
  }
7971
7997
  } elsif ($marker == 0xea) { # APP10 (PhotoStudio Unicode comments)
7972
7998
  if ($$segDataPt =~ /^UNICODE\0/) {
@@ -9102,6 +9128,7 @@ sub HandleTag($$$$;%)
9102
9128
  Base => $parms{Base},
9103
9129
  Multi => $$subdir{Multi},
9104
9130
  TagInfo => $tagInfo,
9131
+ IgnoreProp => $$subdir{IgnoreProp},
9105
9132
  RAF => $parms{RAF},
9106
9133
  );
9107
9134
  my $oldOrder = GetByteOrder();