exiftool_vendored 13.02.0 → 13.03.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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();