diff -Nru xnview-0.92/debian/changelog xnview-0.93/debian/changelog --- xnview-0.92/debian/changelog 2018-09-25 15:05:57.000000000 +0000 +++ xnview-0.93/debian/changelog 2019-02-03 18:29:00.000000000 +0000 @@ -1,3 +1,9 @@ +xnview (0.93-dhor1~xenial) xenial; urgency=medium + + * https://newsgroup.xnview.com/viewtopic.php?f=82&t=38465 + + -- Dariusz Duma Sun, 03 Feb 2019 19:29:00 +0100 + xnview (0.92-dhor1~xenial) xenial; urgency=medium * fixes diff -Nru xnview-0.92/opt64/XnView/AddOn/exiftool xnview-0.93/opt64/XnView/AddOn/exiftool --- xnview-0.92/opt64/XnView/AddOn/exiftool 2017-09-30 10:50:40.000000000 +0000 +++ xnview-0.93/opt64/XnView/AddOn/exiftool 2019-01-17 17:39:22.000000000 +0000 @@ -12,7 +12,7 @@ use strict; require 5.004; -my $version = '10.61'; +my $version = '11.26'; # add our 'lib' directory to the include list BEFORE 'use Image::ExifTool' my $exeDir; @@ -65,6 +65,7 @@ sub OpenOutputFile($;@); sub AcceptFile($); sub SlurpFile($$); +sub FilterArgfileLine($); sub ReadStayOpen($); sub PrintTagList($@); sub PrintErrors($$$); @@ -77,6 +78,7 @@ # declare all static file-scope variables my @commonArgs; # arguments common to all commands +my @condition; # conditional processing of files my @csvFiles; # list of files when reading with CSV option (in ExifTool Charset) my @csvTags; # order of tags for first file with CSV option (lower case) my @delFiles; # list of files to delete @@ -104,12 +106,14 @@ my %warnedOnce; # lookup for once-only warnings my %wext; # -W extensions to write my $allGroup; # show group name for all tags +my $altEnc; # alternate character encoding if not UTF-8 my $argFormat; # use exiftool argument-format output my $binaryOutput; # flag for binary output (undef or 1, or 0 for binary XML/PHP) my $binaryStdout; # flag set if we output binary to stdout +my $binSep; # separator used for list items in binary output +my $binTerm; # terminator used for binary output my $comma; # flag set if we need a comma in JSON output -my $condition; # conditional processing of files -my $count; # count of files scanned +my $count; # count of files scanned when reading or deleting originals my $countBad; # count of files with errors my $countBadCr; # count files not created due to errors my $countBadLink; # count bad links @@ -133,6 +137,8 @@ my $escapeHTML; # flag to escape printed values for html my $evalWarning; # warning from eval my $executeID; # -execute ID number +my $failCondition; # flag to fail -if condition +my $fastCondition; # flag for fast -if condition my $fileHeader; # header to print to output file (or console, once) my $fileTrailer; # trailer for output file my $filtered; # flag indicating file was filtered by name @@ -163,10 +169,12 @@ my $rafStdin; # File::RandomAccess for stdin (if necessary to rewind) my $recurse; # recurse into subdirectories (2=also hidden directories) my $rtnVal; # command return value (0=success) +my $rtnValPrev; # previous command return value (0=success) my $saveCount; # count the number of times we will/did call SaveNewValues() my $scanWritable; # flag to process only writable file types my $sectHeader; # current section header for -p option my $sectTrailer; # section trailer for -p option +my $seqFileBase; # sequential file number at start of directory my $seqFileNum; # sequential file number used for %C my $showGroup; # number of group to show (may be zero or '') my $showTagID; # non-zero to show tag ID's @@ -179,7 +187,6 @@ my $textOverwrite; # flag to overwrite existing text output file (2=append, 3=over+append) my $tmpFile; # temporary file to delete on exit my $tmpText; # temporary text file -my $utf8; # flag set if we are using UTF-8 encoding my $validFile; # flag indicating we processed a valid file my $verbose; # verbose setting my $vout; # verbose output file reference (\*STDOUT or \*STDERR) @@ -193,6 +200,7 @@ # 3 = waiting for -@ to switch to a new STAYOPEN argfile my $stayOpen = 0; +my $rtnValApp = 0; # app return value (0=success) my $curTitle = ''; # current window title # lookup for O/S names which may use a backslash as a directory separator @@ -224,7 +232,7 @@ '-geotag' => 1, '-globaltimeshift' => 1, '-i' => 1, '-ignore' => 1, - '-if' => 1, + '-if' => 1, '-if0' => 1, '-if1' => 1, '-if2' => 1, '-if3' => 1, '-if4' => 1, '-lang' => 0, # (optional arg; cannot begin with "-") '-listitem' => 1, '-o' => 1, '-out' => 1, @@ -241,7 +249,7 @@ '-textout' => 1, '-textout!' => 1, '-textout+' => 1, '-textout+!' => 1, '-textout!+' => 1, '-tagout' => 1, '-tagout!' => 1, '-tagout+' => 1, '-tagout+!' => 1, '-tagout!+' => 1, '-wext' => 1, - '-wm' => 1, + '-wm' => 1, '-writemode' => 1, '-x' => 1, '-exclude' => 1, '-X' => 0, ); @@ -265,6 +273,8 @@ 'POSIX::strptime' => 'Time::Piece', # (can use Time::Piece instead of POSIX::strptime) ); +my %unescapeChar = ( 't'=>"\t", 'n'=>"\n", 'r'=>"\r" ); + # exit routine sub Exit { if ($pause) { @@ -340,6 +350,10 @@ $rafStdin->Close() if $rafStdin; undef $rafStdin; +# save or previous return codes +$rtnValPrev = $rtnVal; +$rtnValApp = $rtnVal if $rtnVal; + # exit Command loop now if we are all done processing commands last unless @ARGV or not defined $rtnVal or $stayOpen >= 2 or @commonArgs; @@ -364,11 +378,10 @@ } } -$rtnVal = 0 unless defined $rtnVal; - # initialize necessary static file-scope variables # (not done: @commonArgs, @moreArgs, $critical, $binaryStdout, $helped, -# $interrupted, $mt, $pause, $rtnVal, $stayOpen, $stayOpenBuff, $stayOpenFile) +# $interrupted, $mt, $pause, $rtnValApp, $rtnValPrev, $stayOpen, $stayOpenBuff, $stayOpenFile) +undef @condition; undef @csvFiles; undef @csvTags; undef @delFiles; @@ -395,10 +408,12 @@ undef %warnedOnce; undef %wext; undef $allGroup; +undef $altEnc; undef $argFormat; undef $binaryOutput; +undef $binSep; +undef $binTerm; undef $comma; -undef $condition; undef $csv; undef $csvAdd; undef $deleteOrig; @@ -408,6 +423,8 @@ undef $escapeHTML; undef $evalWarning; undef $executeID; +undef $failCondition; +undef $fastCondition; undef $fileHeader; undef $filtered; undef $fixLen; @@ -460,11 +477,12 @@ $overwriteOrig = 0; $progStr = ''; $quiet = 0; +$rtnVal = 0; $saveCount = 0; $sectTrailer = ''; +$seqFileBase = 0; $seqFileNum = 0; $tabFormat = 0; -$utf8 = 1; $vout = \*STDOUT; $xml = 0; @@ -510,15 +528,17 @@ for (;;) { # execute the command now if no more arguments or -execute is used - if (not @ARGV or $ARGV[0] =~ /^-execute(\d*)$/i) { + if (not @ARGV or $ARGV[0] =~ /^(-|\xe2\x88\x92)execute(\d*)$/i) { if (@ARGV) { - $executeID = $1; # save -execute number for "{ready}" response + $executeID = $2; # save -execute number for "{ready}" response $helped = 1; # don't show help if we used -execute - $badCmd and shift, next Command; + $badCmd and shift, $rtnVal=1, next Command; } elsif ($stayOpen >= 2) { ReadStayOpen(\@ARGV); # read more arguments from -stay_open file next; } elsif ($badCmd) { + undef @commonArgs; # all done. Flush common arguments + $rtnVal = 1; next Command; } if ($pass == 0) { @@ -547,6 +567,13 @@ require Image::ExifTool::MWG; Image::ExifTool::MWG::Load(); } + # update necessary variables for 2nd pass + if (defined $forcePrint) { + unless (defined $mt->Options('MissingTagValue')) { + $mt->Options(MissingTagValue => '-'); + } + $forcePrint = $mt->Options('MissingTagValue'); + } } if (@nextPass) { # process arguments which were deferred to the next pass @@ -699,11 +726,8 @@ s/^\xef\xbb\xbf//; $didBOM = 1; } - next if /^#/; # ignore lines beginning with '#' - s/^\s+//; s/[\x0d\x0a]+$//s; # remove leading white space and trailing newline - # remove white space before, and single space after '=', '+=', '-=' or '<=' - s/^(-[-:\w]+#?)\s*([-+<]?=) ?/$1$2/; - push @newArgs, $_ unless $_ eq ''; + $_ = FilterArgfileLine($_); + push @newArgs, $_ if defined $_; } close ARGFILE; unshift @ARGV, @newArgs; @@ -714,9 +738,9 @@ my $opt = shift; defined $opt or Error("Expected OPT[=VAL] argument for -api option\n"), $badCmd=1, next; my $val = ($opt =~ s/=(.*)//s) ? $1 : 1; - $mt->Options($opt => (length($val) ? $val : undef)); - # update $forcePrint in case MissingTagValue was changed - $forcePrint = $mt->Options('MissingTagValue') if defined $forcePrint; + # empty string means an undefined value unless ^= is used + $val = undef unless $opt =~ s/\^$// or length $val; + $mt->Options($opt => $val); next; } /^arg(s|format)$/i and $argFormat = 1, next; @@ -737,7 +761,6 @@ $helped = 1; } elsif ($charset !~ s/^(\w+)=// or lc($1) eq 'exiftool') { $mt->Options(Charset => $charset); - $utf8 = ($mt->Options('Charset') eq 'UTF8'); } else { # set internal encoding of specified metadata type my $type = { id3 => 'ID3', iptc => 'IPTC', exif => 'EXIF', filename => 'FileName', @@ -771,7 +794,7 @@ $msg = Image::ExifTool::Import::ReadCSV(\*CSVFILE, \%database, $forcePrint); close(CSVFILE); } else { - $msg = "Error opening CSV file '$csvFile'"; + $msg = "Error opening CSV file '${csvFile}'"; } $msg and Warn("$msg\n"); $isWriting = 1; @@ -820,11 +843,7 @@ next; } if (/^f$/ or $a eq 'forceprint') { - $forcePrint = $mt->Options('MissingTagValue'); - unless (defined $forcePrint) { - $forcePrint = '-'; - $mt->Options(MissingTagValue => '-'); - } + $forcePrint = 1; next; } if (/^F([-+]?\d*)$/ or /^fixbase([-+]?\d*)$/i) { @@ -892,16 +911,15 @@ $ignore{$dir} = 1; next; } - if ($a eq 'if') { + if (/^if(\d*)$/i) { my $cond = shift; + $fastCondition = $1 if length $1; defined $cond or Error("Expecting expression for -if option\n"), $badCmd=1, next; + # prevent processing file unnecessarily for simple case of failed '$ok' or 'not $ok' + $cond =~ /^\s*(not\s*)\$ok\s*$/i and ($1 xor $rtnValPrev) and $failCondition=1; # add to list of requested tags push @requestTags, $cond =~ /\$\{?((?:[-\w]+:)*[-\w?*]+)/g; - if (defined $condition) { - $condition .= " and ($cond)"; - } else { - $condition = "($cond)"; - } + push @condition, $cond; next; } if (/^j(son)?(\+?=.*)?$/i) { @@ -926,7 +944,7 @@ $msg = Image::ExifTool::Import::ReadJSON(\*JSONFILE, \%database, $forcePrint, $chset); close(JSONFILE); } else { - $msg = "Error opening JSON file '$jsonFile'"; + $msg = "Error opening JSON file '${jsonFile}'"; } $msg and Warn("$msg\n"); $isWriting = 1; @@ -941,9 +959,9 @@ } /^(k|pause)$/i and $pause = 1, next; (/^l$/ or $a eq 'long') and --$outFormat, next; - (/^L$/ or $a eq 'latin') and $utf8 = 0, $mt->Options(Charset => 'Latin'), next; + (/^L$/ or $a eq 'latin') and $mt->Options(Charset => 'Latin'), next; if ($a eq 'lang') { - $langOpt = (@ARGV and $ARGV[0] !~ /^-/) ? shift : undef; + $langOpt = (@ARGV and $ARGV[0] !~ /^(-|\xe2\x88\x92)/) ? shift : undef; if ($langOpt) { # make lower case and use underline as a separator (eg. 'en_ca') $langOpt =~ tr/-A-Z/_a-z/; @@ -955,9 +973,9 @@ my $langs = "Available languages:\n"; $langs .= " $_ - $Image::ExifTool::langName{$_}\n" foreach @Image::ExifTool::langs; $langs =~ tr/_/-/; # display dashes instead of underlines in language codes - $langs = $mt->Decode($langs, 'UTF8'); $langs = Image::ExifTool::HTML::EscapeHTML($langs) if $escapeHTML; - $langOpt and Error("Invalid or unsupported language '$langOpt'.\n$langs"), $badCmd=1, next; + $langs = $mt->Decode($langs, 'UTF8'); + $langOpt and Error("Invalid or unsupported language '${langOpt}'.\n$langs"), $badCmd=1, next; print $langs; $helped = 1; next; @@ -972,6 +990,7 @@ /^(m|ignoreminorerrors)$/i and $mt->Options(IgnoreMinorErrors => 1), next; /^(n|-printconv)$/i and $mt->Options(PrintConv => 0), next; /^(-n|printconv)$/i and $mt->Options(PrintConv => 1), next; + $a eq 'nop' and $helped=1, next; # undocumented; no operation if (/^o(ut)?$/i) { $outOpt = shift; defined $outOpt or Error("Expected output file or directory name for -o option\n"), $badCmd=1, next; @@ -1000,7 +1019,7 @@ } (/^P$/ or $a eq 'preserve') and $preserveTime = 1, next; /^password$/i and $mt->Options(Password => shift), next; - if (/^progress(:.*)?/i) { + if (/^progress(:.*)?$/i) { if ($1) { $windowTitle = substr $1, 1; $windowTitle = 'ExifTool %p%%' unless length $windowTitle; @@ -1032,8 +1051,10 @@ /^s(hort)?(\d*)$/i and $outFormat = $2 eq '' ? $outFormat + 1 : $2, next; /^scanforxmp$/i and $mt->Options(ScanForXMP => 1), next; if (/^sep(arator)?$/i) { - $listSep = shift; + my $sep = $listSep = shift; defined $listSep or Error("Expecting list item separator for -sep option\n"), $badCmd=1, next; + $sep =~ s/\\(.)/$unescapeChar{$1}||$1/sge; # translate escape sequences + (defined $binSep ? $binTerm : $binSep) = $sep; $mt->Options(ListSep => $listSep); $joinLists = 1; # also split when writing values @@ -1083,12 +1104,7 @@ } /^t(ab)?$/ and $tabFormat = 1, next; if (/^T$/ or $a eq 'table') { - $tabFormat = 1; $outFormat+=2; ++$quiet; - $forcePrint = $mt->Options('MissingTagValue'); - unless (defined $forcePrint) { - $forcePrint = '-'; - $mt->Options(MissingTagValue => '-'); - } + $tabFormat = $forcePrint = 1; $outFormat+=2; ++$quiet; next; } if (/^(u)(nknown(2)?)?$/i) { @@ -1104,7 +1120,7 @@ local $SIG{'__WARN__'} = sub { $evalWarning = $_[0] }; unless (eval "require Image::ExifTool::$module" or eval "require $module" or - eval "require '$module'") + eval "require '${module}'") { Error("Error using module $module\n"); $badCmd = 1; @@ -1171,7 +1187,12 @@ $mt->Options(Duplicates=>1); next; } - /^z(ip)?$/i and $doUnzip = 1, $mt->Options(Compress => 1, Compact => 1), next; + if (/^z(ip)?$/i) { + $doUnzip = 1; + $mt->Options(Compress => 1, XMPShorthand => 1); + $mt->Options(Compact => 1) unless $mt->Options('Compact'); + next; + } $_ eq '' and push(@files, '-'), $srcStdin = 1, next; # read STDIN length $_ eq 1 and $_ ne '*' and Error("Unknown option -$_\n"), $badCmd=1, next; if (/^[^<]+(/) { $useMWG = 1 if /^(.*>\s*)?mwg:/si; - if (/\b(filename|directory)#?$/i) { + if (/\b(filename|directory|testname)#?$/i) { $doSetFileName = 1; } elsif (/\bgeotime#?$/i) { $addGeotime = ''; } } else { $useMWG = 1 if /^([^<]+<\s*(.*\$\{?)?)?mwg:/si; - if (/^([-\w]+:)*(filename|directory)\b/i) { + if (/^([-\w]+:)*(filename|directory|testname)\b/i) { $doSetFileName = 1; } elsif (/^([-\w]+:)*geotime\b/i) { $addGeotime = ''; @@ -1220,7 +1241,7 @@ } else { my $lst = s/^-// ? \@exclude : \@tags; unless (/^([-\w*]+:)*([-\w*?]+)#?$/) { - Warn(qq(Invalid TAG name: $_\n)); + Warn(qq(Invalid TAG name: "$_"\n)); } push @$lst, $_; # (push everything for backward compatibility) } @@ -1246,6 +1267,9 @@ } } +# set "OK" UserParam based on result of last command +$mt->Options(UserParam => 'OK=' . (not $rtnValPrev)); + # set verbose output to STDERR if output could be to console $vout = \*STDERR if $srcStdin and ($isWriting or @newValues); $mt->Options(TextOut => $vout) if $vout eq \*STDERR; @@ -1322,7 +1346,7 @@ if ($escapeHTML or $json) { # must be UTF8 for HTML conversion and JSON output - $mt->Options(Charset => 'UTF8'); + $mt->Options(Charset => 'UTF8') if $json; # use Escape option to do our HTML escaping unless XML output $mt->Options(Escape => 'HTML') if $escapeHTML and not $xml; } elsif ($escapeXML and not $xml) { @@ -1470,7 +1494,7 @@ /(.*?)=(.*)/s or next; my ($tag, $newVal) = ($1, $2); $tag =~ s/\ball\b/\*/ig; # replace 'all' with '*' in tag names - $newVal eq '' and undef $newVal; # undefined to delete tag + $newVal eq '' and undef $newVal unless $tag =~ s/\^([-+]*)$/$1/; # undefined to delete tag if ($tag =~ /^(All)?TagsFromFile$/i) { defined $newVal or Error("Need file name for -tagsFromFile\n"), next Command; ++$isWriting; @@ -1479,7 +1503,7 @@ next; # set tags from dynamic file later } unless ($mt->Exists($newVal) or $newVal eq '-') { - Warn "File '$newVal' does not exist for -tagsFromFile option\n"; + Warn "File '${newVal}' does not exist for -tagsFromFile option\n"; $rtnVal = 1; next Command; } @@ -1516,9 +1540,9 @@ push @dynamicFiles, [ $tag, $newVal, \%opts ]; ++$isWriting; } elsif (defined $result) { - Warn "Tag '$tag' is not writable\n"; + Warn "Tag '${tag}' is not writable\n"; } else { - Warn "Tag '$tag' does not exist\n"; + Warn "Tag '${tag}' does not exist\n"; } next; } @@ -1602,8 +1626,12 @@ $scanWritable = 1; } +# initialize alternate encoding flag +$altEnc = $mt->Options('Charset'); +undef $altEnc if $altEnc eq 'UTF8'; + # set flag to fix description lengths if necessary -if ($utf8 and $mt->Options('Lang') ne 'en' and eval { require Encode }) { +if (not $altEnc and $mt->Options('Lang') ne 'en' and eval { require Encode }) { # (note that Unicode::GCString is part of the Unicode::LineBreak package) $fixLen = eval { require Unicode::GCString } ? 2 : 1; } @@ -1669,7 +1697,7 @@ if (defined $absPath) { $database{$absPath} = $database{$_} unless $database{$absPath}; if ($verbose and $verbose > 1) { - print $vout "Imported entry for '$_' (full path: '$absPath')\n"; + print $vout "Imported entry for '${_}' (full path: '${absPath}')\n"; } } } @@ -1691,6 +1719,9 @@ print $sectTrailer if $sectTrailer and not $textOut; print $fileTrailer if $fileTrailer and not $textOut and not $fileHeader; +my $totWr = $countGoodWr + $countBadWr + $countSameWr + $countCopyWr + + $countGoodCr + $countBadCr; + if (defined $deleteOrig) { # print summary and delete requested files @@ -1716,7 +1747,7 @@ if ($quiet) { # no more messages } elsif ($count and not $countGoodWr and not $countBad) { - printf "%5d original files found\n", $countGoodWr; + printf "%5d original files found\n", $countGoodWr; # (this will be 0) } elsif ($deleteOrig) { printf "%5d original files deleted\n", $countGoodWr if $count; printf "%5d originals not deleted due to errors\n", $countBad if $countBad; @@ -1729,8 +1760,6 @@ # print summary my $tot = $count + $countBad; - my $totWr = $countGoodWr + $countBadWr + $countSameWr + $countCopyWr + - $countGoodCr + $countBadCr; if ($countDir or $totWr or $countFailed or $tot > 1 or $textOut or $countLink or $countBadLink) { my $o = (($html or $json or $xml or %printFmt or $csv) and not $textOut) ? \*STDERR : $vout; printf($o "%5d directories scanned\n", $countDir) if $countDir; @@ -1752,7 +1781,11 @@ } # set error status if we had any errors or if all files failed the "-if" condition -$rtnVal = 1 if $countBadWr or $countBadCr or $countBad or ($countFailed and not $count); +if ($countBadWr or $countBadCr or $countBad) { + $rtnVal = 1; +} elsif ($countFailed and not ($count or $totWr) and not $rtnVal) { + $rtnVal = 2; +} # clean up after each command Cleanup(); @@ -1761,7 +1794,7 @@ close STAYOPEN if $stayOpen >= 2; -Exit $rtnVal; # all done +Exit $rtnValApp; # all done #------------------------------------------------------------------------------ @@ -1790,6 +1823,11 @@ $title =~ s/%([%bfpr])/$lkup{$1}/eg; SetWindowTitle($title); } + unless (length $orig) { + Warn qq(Error: Zero-length file name - ""\n); + ++$countBad; + return; + } # determine the name of the source file based on the original input file name if (@srcFmt) { my ($fmt, $first); @@ -1815,37 +1853,49 @@ } } # evaluate -if expression for conditional processing - if (defined $condition) { + if (@condition) { unless ($file eq '-' or $et->Exists($file)) { - Warn "File not found: $file\n"; + Warn "Error: File not found - $file\n"; FileNotFound($file); ++$countBad; return; } - # catch run time errors as well as compile errors - undef $evalWarning; - local $SIG{'__WARN__'} = sub { $evalWarning = $_[0] }; + my $result; - my %info; - # extract information and build expression for evaluation - my $opts = { Duplicates => 1, RequestTags => \@requestTags, Verbose => 0, HtmlDump => 0 }; - # return all tags but explicitly mention tags on command line so - # requested images will generate the appropriate warnings - @foundTags = ('*', @tags) if @tags; - $info = $et->ImageInfo(Infile($pipe,$isWriting), \@foundTags, $opts); - my $cond = $et->InsertTagValues(\@foundTags, $condition, \%info); - - #### eval "-if" condition (%info) - my $result = eval $cond; - - $@ and $evalWarning = $@; - if ($evalWarning) { - # fail condition if warning is issued - undef $result; - if ($verbose) { - chomp $evalWarning; - $evalWarning =~ s/ at \(eval .*//s; - Warn "Condition: $evalWarning - $file\n"; + unless ($failCondition) { + # catch run time errors as well as compile errors + undef $evalWarning; + local $SIG{'__WARN__'} = sub { $evalWarning = $_[0] }; + + my (%info, $condition); + # extract information and build expression for evaluation + my $opts = { Duplicates => 1, RequestTags => \@requestTags, Verbose => 0, HtmlDump => 0 }; + $$opts{FastScan} = $fastCondition if defined $fastCondition; + # return all tags but explicitly mention tags on command line so + # requested images will generate the appropriate warnings + @foundTags = ('*', @tags) if @tags; + $info = $et->ImageInfo(Infile($pipe,$isWriting), \@foundTags, $opts); + foreach $condition (@condition) { + my $cond = $et->InsertTagValues(\@foundTags, $condition, \%info); + { + # set package so eval'd functions are in Image::ExifTool namespace + package Image::ExifTool; + + #### eval "-if" condition (%info) + $result = eval $cond; + + $@ and $evalWarning = $@; + } + if ($evalWarning) { + # fail condition if warning is issued + undef $result; + if ($verbose) { + chomp $evalWarning; + $evalWarning =~ s/ at \(eval .*//s; + Warn "Condition: $evalWarning - $file\n"; + } + } + last unless $result; } } unless ($result) { @@ -1855,7 +1905,7 @@ } # can't make use of $info if verbose because we must reprocess # the file anyway to generate the verbose output - undef $info if $verbose; + undef $info if $verbose or defined $fastCondition; } if (defined $deleteOrig) { print $vout "======== $file$progStr\n" if defined $verbose; @@ -1909,7 +1959,7 @@ # extract information from this file unless ($file eq '-' or $et->Exists($file)) { - Warn "File not found: $file\n"; + Warn "Error: File not found - $file\n"; FileNotFound($file); $outfile and close($fp), undef($tmpText), $et->Unlink($outfile); ++$countBad; @@ -1986,29 +2036,37 @@ # print the results for this file if (%printFmt) { # output using print format file (-p) option - my ($type, $doc, $grp); + my ($type, $doc, $grp, $lastDoc, $cache); $fileTrailer = ''; # repeat for each embedded document if necessary - my $lastDoc = $et->Options('ExtractEmbedded') ? $$et{DOC_COUNT} : 0; + if ($et->Options('ExtractEmbedded')) { + # (cache tag keys if there are sub-documents) + $lastDoc = $$et{DOC_COUNT} and $cache = { }; + } else { + $lastDoc = 0; + } for ($doc=0; $doc<=$lastDoc; ++$doc) { - foreach $type (qw(HEAD SECT BODY ENDS TAIL)) { + my $skipBody; + foreach $type (qw(HEAD SECT IF BODY ENDS TAIL)) { my $prf = $printFmt{$type} or next; + next if $type eq 'BODY' and $skipBody; if ($lastDoc) { if ($doc) { - next if $type eq 'HEAD' or $type eq 'TAIL'; # only repeat SECT/BODY/ENDS - $grp = "Doc$doc:"; + next if $type eq 'HEAD' or $type eq 'TAIL'; # only repeat SECT/IF/BODY/ENDS + $grp = "Doc$doc"; } else { - $grp = 'Main:'; + $grp = 'Main'; } - # change tag groups to print next document by adding "Main:" or "Doc#:" - # to all tags which don't already start with a family 3 group name - $prf = [ @$prf ]; - s/((^|[^\$])(\$\$)*\$\{?)((?!(Main|Doc\d+):)[\w])/$1$grp$4/ig foreach @$prf; } my @lines; + my $opt = $type eq 'IF' ? 'Silent' : 'Warn'; # silence "IF" warnings foreach (@$prf) { - my $line = $et->InsertTagValues(\@foundTags, $_, 'Warn'); - push @lines, $line if defined $line; + my $line = $et->InsertTagValues(\@foundTags, $_, $opt, $grp, $cache); + if ($type eq 'IF') { + $skipBody = 1 unless defined $line; + } elsif (defined $line) { + push @lines, $line; + } } $lineCount += scalar @lines; if ($type eq 'SECT') { @@ -2044,7 +2102,7 @@ } elsif ($xml) { my $f = $file; CleanXML(\$f); - print $fp "\n 0) { if ($structOpt and ref $val) { $val = Image::ExifTool::XMP::SerializeStruct($val); } if ($escapeHTML) { $val =~ tr/\0-\x08\x0b\x0c\x0e-\x1f/./; - Image::ExifTool::XMP::FixUTF8(\$val) if $utf8; - $val = Image::ExifTool::HTML::EscapeHTML($val); + Image::ExifTool::XMP::FixUTF8(\$val) unless $altEnc; + $val = Image::ExifTool::HTML::EscapeHTML($val, $altEnc); } else { CleanXML(\$val); } unless ($noDups{$tok}) { - print $fp "\n $tok='$val'"; + # manually un-do CR/LF conversion in Windows because output + # is in text mode, which will re-convert newlines to CR/LF + $isCRLF and $val =~ s/\x0d\x0a/\x0a/g; + print $fp "\n $tok='${val}'"; # XML does not allow duplicate attributes $noDups{$tok} = 1; } @@ -2262,20 +2321,23 @@ } else { $id = Image::ExifTool::XMP::FullEscapeXML($id); } - $xtra = " et:id='$id'"; - $xtra .= " xml:lang='$lang'" if $lang; + $xtra = " et:id='${id}'"; + $xtra .= " xml:lang='${lang}'" if $lang; } else { $xtra = ''; } if ($tabFormat) { my $table = $et->GetTableName($tag); my $index = $et->GetTagIndex($tag); - $xtra .= " et:table='$table'"; - $xtra .= " et:index='$index'" if defined $index; + $xtra .= " et:table='${table}'"; + $xtra .= " et:index='${index}'" if defined $index; } my $lastVal = $val; for ($valNum=0; $valNum<2; ++$valNum) { $val = FormatXML($val, $ind, $group); + # manually un-do CR/LF conversion in Windows because output + # is in text mode, which will re-convert newlines to CR/LF + $isCRLF and $val =~ s/\x0d\x0a/\x0a/g; if ($outFormat >= 0) { # normal output format (note: this will give # non-standard RDF/XML if there are any attributes) @@ -2510,7 +2572,7 @@ } else { $outfile = FilenameSPrintf($outOpt, $orig); if ($outfile eq '') { - Warn "Can't create file with zero-length name from $orig\n"; + Warn "Error: Can't create file with zero-length name from $orig\n"; ++$countBadCr; return 0; } @@ -2563,7 +2625,7 @@ unless ($isStdout) { $outfile = NextUnusedFilename($outfile); if ($et->Exists($outfile) and not $doSetFileName) { - Warn "Error: '$outfile' already exists - $infile\n"; + Warn "Error: '${outfile}' already exists - $infile\n"; ++$countBadWr; return 0; } @@ -2640,9 +2702,9 @@ } $et->Options(Charset => $old) if $csv eq 'JSON'; unless ($found) { - Warn("No SourceFile '$file' in imported $csv database\n"); + Warn("No SourceFile '${file}' in imported $csv database\n"); my $absPath = AbsPath($file); - Warn("(full path: '$absPath')\n") if defined $absPath and $absPath ne $file; + Warn("(full path: '${absPath}')\n") if defined $absPath and $absPath ne $file; return 0; } } @@ -2660,13 +2722,17 @@ $hardLink = $et->GetNewValues('HardLink'); $testName = $et->GetNewValues('TestName'); $hardLink = FilenameSPrintf($hardLink, $orig) if defined $hardLink; + # determine what our output file name should be + my $newFileName = $et->GetNewValues('FileName'); + my $newDir = $et->GetNewValues('Directory'); if (defined $testName) { + my $err; + $err = "You shouldn't write FileName or Directory with TestFile" if defined $newFileName or defined $newDir; + $err = "The -o option shouldn't be used with TestFile" if defined $outfile; + $err and Warn("Error: $err - $infile\n"), ++$countBadWr, return 0; $testName = FilenameSPrintf($testName, $orig); $testName = Image::ExifTool::GetNewFileName($file, $testName) if $file ne ''; } - # determine what our output file name should be - my $newFileName = $et->GetNewValues('FileName'); - my $newDir = $et->GetNewValues('Directory'); if (defined $newFileName or defined $newDir or ($doSetFileName and defined $outfile)) { if ($newFileName) { $newFileName = FilenameSPrintf($newFileName, $orig); @@ -2684,7 +2750,7 @@ $outfile = NextUnusedFilename($outfile, $infile); if ($et->Exists($outfile)) { if ($infile ne $outfile) { - Warn "Error: '$outfile' already exists - $infile\n"; + Warn "Error: '${outfile}' already exists - $infile\n"; ++$countBadWr; return 0; } @@ -2692,7 +2758,7 @@ } } if (defined $outfile) { - $verbose and print $vout "'$infile' --> '$outfile'\n"; + $verbose and print $vout "'${infile}' --> '${outfile}'\n"; # create output directory if necessary CreateDirectory($outfile); # set temporary file (automatically erased on abnormal exit) @@ -2722,7 +2788,7 @@ } } else { # file doesn't exist, and we can't create it - Warn("Error: File not found - $file\n"); + Warn "Error: File not found - $file\n"; FileNotFound($file); ++$countBadWr; return 0; @@ -2829,7 +2895,10 @@ unless ($et->Rename($newFile, $file) or ($et->Unlink($file) and $et->Rename($newFile, $file))) { - Error("Error renaming $newFile to $file\n"), return 0; + Error("Error renaming $newFile to $file\n"); + undef $critical; + SigInt() if $interrupted; + return 0; } } else { $et->SetFileModifyDate($file, $cTime, 'FileCreateDate', 1); @@ -2923,11 +2992,12 @@ } } if (defined $testName) { - $testName = NextUnusedFilename($testName, undef, 1); + $testName = NextUnusedFilename($testName, $src, 1); if ($usedFileName{$testName}) { - $et->Warn("File '$testName' would exist"); - } elsif ($et->SetFileName($src, $testName, 'Test') == 1) { + $et->Warn("File '${testName}' would exist"); + } elsif ($et->SetFileName($src, $testName, 'Test', $usedFileName{$testName}) == 1) { $usedFileName{$testName} = 1; + $usedFileName{$src} = 0; } } } @@ -2942,7 +3012,7 @@ # translate control characters that are invalid in XML $$strPt =~ tr/\0-\x08\x0b\x0c\x0e-\x1f/./; # fix malformed UTF-8 characters - Image::ExifTool::XMP::FixUTF8($strPt) if $utf8; + Image::ExifTool::XMP::FixUTF8($strPt) unless $altEnc; # escape necessary characters for XML $$strPt = Image::ExifTool::XMP::EscapeXML($$strPt); } @@ -2955,13 +3025,13 @@ { my $strPt = shift; if ($$strPt =~ /[\0-\x08\x0b\x0c\x0e-\x1f]/ or - ($utf8 and Image::ExifTool::XMP::IsUTF8($strPt) < 0)) + (not $altEnc and Image::ExifTool::XMP::IsUTF8($strPt) < 0)) { # encode binary data and non-UTF8 with special characters as base64 $$strPt = Image::ExifTool::XMP::EncodeBase64($$strPt); return 'http://www.w3.org/2001/XMLSchema#base64Binary'; #ATV } elsif ($escapeHTML) { - $$strPt = Image::ExifTool::HTML::EscapeHTML($$strPt); + $$strPt = Image::ExifTool::HTML::EscapeHTML($$strPt, $altEnc); } else { $$strPt = Image::ExifTool::XMP::EscapeXML($$strPt); } @@ -2996,7 +3066,7 @@ } else { # (note: SCALAR reference should have already been converted) my $enc = EncodeXML(\$val); - $gt = " rdf:datatype='$enc'>\n" if $enc; #ATV + $gt = " rdf:datatype='${enc}'>\n" if $enc; #ATV } return $gt . $val; } @@ -3020,13 +3090,14 @@ if ($json < 2 and defined $binaryOutput and Image::ExifTool::XMP::IsUTF8(\$str) < 0) { return '"base64:' . Image::ExifTool::XMP::EncodeBase64($str, 1) . '"'; } + $str =~ s/\0+$//; # remove trailing nulls # escape special characters $str =~ s/(["\t\n\r\\])/\\$jsonChar{$1}/sg; if ($json < 2) { # JSON # escape other control characters with \u $str =~ s/([\0-\x1f])/sprintf("\\u%.4X",ord $1)/sge; # JSON strings must be valid UTF8 - Image::ExifTool::XMP::FixUTF8(\$str) if $utf8; + Image::ExifTool::XMP::FixUTF8(\$str) unless $altEnc; } else { # PHP # must escape "$" too for PHP $str =~ s/\$/\\\$/sg; @@ -3344,7 +3415,7 @@ $filtered = 1; $verbose and print $vout "-------- $file (wrong extension)$progStr\n"; } else { - Warn "File not found: $file\n"; + Warn "Error: File not found - $file\n"; FileNotFound($file); $rtnVal = 1; } @@ -3378,17 +3449,29 @@ $utf8Name = 1; } return if $ignore{$dir}; + my $oldBase = $seqFileBase; + $seqFileBase = $seqFileNum; # use Win32::FindFile on Windows if available # (ReadDir will croak if there is a wildcard, so check for this) if ($^O eq 'MSWin32' and $dir !~ /[*?]/) { + undef $evalWarning; local $SIG{'__WARN__'} = sub { $evalWarning = $_[0] };; if (CheckUTF8($dir, $enc) >= 0) { if (eval { require Win32::FindFile }) { - @fileList = Win32::FindFile::ReadDir($dir); - $_ = $_->cFileName foreach @fileList; - $et->Options(CharsetFileName => 'UTF8'); # now using UTF8 - $utf8Name = 1; # ReadDir returns UTF-8 file names - $done = 1; + eval { + @fileList = Win32::FindFile::ReadDir($dir); + $_ = $_->cFileName foreach @fileList; + }; + $@ and $evalWarning = $@; + if ($evalWarning) { + chomp $evalWarning; + $evalWarning =~ s/ at .*//s; + Warn "Warning: [Win32::FindFile] $evalWarning - $dir\n"; + } else { + $et->Options(CharsetFileName => 'UTF8'); # now using UTF8 + $utf8Name = 1; # ReadDir returns UTF-8 file names + $done = 1; + } } else { $done = 0; } @@ -3396,7 +3479,11 @@ } unless ($done) { # use standard perl library routines to read directory - opendir(DIR_HANDLE, $dir) or Warn("Error opening directory $dir\n"), return; + unless (opendir(DIR_HANDLE, $dir)) { + Warn("Error opening directory $dir\n"); + $seqFileBase = $oldBase + ($seqFileNum - $seqFileBase); + return; + } @fileList = readdir(DIR_HANDLE); closedir(DIR_HANDLE); if (defined $done) { @@ -3449,6 +3536,8 @@ } ++$countDir; $et->Options(CharsetFileName => $enc); # restore original setting + # update sequential file base for parent directory + $seqFileBase = $oldBase + ($seqFileNum - $seqFileBase); } #------------------------------------------------------------------------------ @@ -3472,23 +3561,35 @@ return (); } CheckUTF8($wildfile, $enc) >= 0 or return (); - local $SIG{'__WARN__'} = sub { $evalWarning = $_[0] };; - my @names = Win32::FindFile::FindFile($wildfile) or return (); - # (apparently this isn't always sorted, so do a case-insensitive sort here) - @names = sort { uc($a) cmp uc($b) } @names; - my ($rname, $nm, @files); - # replace "\?" with ".", and "\*" with ".*" for regular expression - ($rname = quotemeta $wildname) =~ s/\\\?/./g; - $rname =~ s/\\\*/.*/g; - foreach $nm (@names) { - $nm = $nm->cFileName; - # make sure that FindFile behaves - # (otherwise "*.jpg" matches things like "a.jpg_original"!) - next unless $nm =~ /^$rname$/i; - next if $nm eq '.' or $nm eq '..'; # don't match "." and ".." - my $file = "$dir$nm"; # add back directory name - push @files, $file; - $utf8FileName{$file} = 1; # flag this file name as UTF-8 encoded + undef $evalWarning; + local $SIG{'__WARN__'} = sub { $evalWarning = $_[0] }; + my @files; + eval { + my @names = Win32::FindFile::FindFile($wildfile) or return; + # (apparently this isn't always sorted, so do a case-insensitive sort here) + @names = sort { uc($a) cmp uc($b) } @names; + my ($rname, $nm); + # replace "\?" with ".", and "\*" with ".*" for regular expression + ($rname = quotemeta $wildname) =~ s/\\\?/./g; + $rname =~ s/\\\*/.*/g; + foreach $nm (@names) { + $nm = $nm->cFileName; + # make sure that FindFile behaves + # (otherwise "*.jpg" matches things like "a.jpg_original"!) + next unless $nm =~ /^$rname$/i; + next if $nm eq '.' or $nm eq '..'; # don't match "." and ".." + my $file = "$dir$nm"; # add back directory name + push @files, $file; + $utf8FileName{$file} = 1; # flag this file name as UTF-8 encoded + } + }; + $@ and $evalWarning = $@; + if ($evalWarning) { + chomp $evalWarning; + $evalWarning =~ s/ at .*//s; + Warn "Error: [Win32::FindFile] $evalWarning - $wildfile\n"; + undef @files; + ++$countBad; } return @files; } @@ -3558,7 +3659,7 @@ my $expr = shift; my $type; if ($expr =~ /^#/) { - $expr =~ s/^#\[(HEAD|SECT|BODY|ENDS|TAIL)\]// or return; # ignore comments + $expr =~ s/^#\[(HEAD|SECT|IF|BODY|ENDS|TAIL)\]// or return; # ignore comments $type = $1; } else { $type = 'BODY'; @@ -3604,10 +3705,17 @@ $ext = 'icc'; } elsif ($$valPt =~ /^(MM\0\x2a|II\x2a\0)/) { $ext = 'tiff'; + } elsif ($$valPt =~ /^.{4}ftyp(3gp|mp4|f4v|qt )/s) { + my %movType = ( 'qt ' => 'mov' ); + $ext = $movType{$1} || $1; } elsif ($$valPt !~ /^.{0,4096}\0/s) { $ext = 'txt'; } elsif ($$valPt =~ /^BM/) { $ext = 'bmp'; + } elsif ($$valPt =~ /^CANON OPTIONAL DATA\0/) { + $ext = 'vrd'; + } elsif ($$valPt =~ /^IIII\x04\0\x04\0/) { + $ext = 'dr4'; } else { $ext = 'dat'; } @@ -3731,34 +3839,39 @@ $filename .= substr($fmt, $pos, pos($fmt) - $pos - length($1)); $pos = pos($fmt); my ($sign, $wid, $dec, $wid2, $mod, $tok) = ($2, $3 || 0, $4, $5 || 0, $6, $7); - my $diff = 0; + my $diff; if ($tok eq 'C') { - $diff = $wid; + $diff = $wid - ($sign eq '-' ? $seqFileBase : 0); $wid = $wid2; } else { next unless $dec or $copy; $wid = $wid2 if $wid < $wid2; + # add dash or underline separator if '-' or '+' specified + $filename .= $sep{$sign} if $sign; } - # add dash or underline separator if '-' or '+' specified - $filename .= $sep{$sign} if $sign; if ($mod and $mod ne 'n') { - my $a = $tok eq 'C' ? Num2Alpha($diff + $seq++) : $alpha; + my $a = $tok eq 'C' ? Num2Alpha($diff + $seq) : $alpha; my $str = ($wid and $wid > length $a) ? 'a' x ($wid - length($a)) : ''; $str .= $a; $str = uc $str if $mod eq 'u'; $filename .= $str; } else { - my $c = $tok eq 'C' ? ($diff + $seq++) : $copy; + my $c = $tok eq 'C' ? ($diff + $seq) : $copy; my $num = $c + ($mod ? 1 : 0); $filename .= $wid ? sprintf("%.${wid}d",$num) : $num; } } $filename .= substr($fmt, $pos); # add rest of file name # return now with filename unless file exists - return $filename unless $mt->Exists($filename) or $usedFileName{$filename}; - return $filename if defined $okfile and $filename eq $okfile; + return $filename unless ($mt->Exists($filename) and not defined $usedFileName{$filename}) or $usedFileName{$filename}; + if (defined $okfile) { + return $filename if $filename eq $okfile; + my ($fn, $ok) = (AbsPath($filename), AbsPath($okfile)); + return $okfile if defined $fn and defined $ok and $fn eq $ok; + } ++$copy; ++$alpha; + ++$seq; } } @@ -3900,6 +4013,32 @@ return 1; } + +#------------------------------------------------------------------------------ +# Filter argfile line +# Inputs: 0) line of argfile +# Returns: filtered line or undef to ignore +sub FilterArgfileLine($) +{ + my $arg = shift; + if ($arg =~ /^#/) { # comment lines begin with '#' + return undef unless $arg =~ s/^#\[CSTR\]//; + $arg =~ s/[\x0d\x0a]+$//s; # remove trailing newline + # escape double quotes, dollar signs and ampersands if they aren't already + # escaped by an odd number of backslashes, and escape a single backslash + # if it occurs at the end of the string + $arg =~ s{\\(.)|(["\$\@]|\\$)}{'\\'.($2 || $1)}sge; + $arg = eval qq{"$arg"}; # un-escape characters in C string + } else { + $arg =~ s/^\s+//; # remove leading white space + $arg =~ s/[\x0d\x0a]+$//s; # remove trailing newline + # remove white space before, and single space after '=', '+=', '-=' or '<=' + $arg =~ s/^(-[-:\w]+#?)\s*([-+<]?=) ?/$1$2/; + return undef if $arg eq ''; + } + return $arg; +} + #------------------------------------------------------------------------------ # Read arguments from -stay_open argfile # Inputs: 0) argument list ref @@ -3927,11 +4066,8 @@ my $len = pos($stayOpenBuff) - $pos; my $arg = substr($stayOpenBuff, $pos, $len); $pos += $len; - $arg =~ s/^\s+//; # remove leading white space - $arg =~ s/[\x0d\x0a]+$//s; # remove trailing newline - # remove white space before, and single space after '=', '+=', '-=' or '<=' - $arg =~ s/^(-[-:\w]+#?)\s*([-+<]?=) ?/$1$2/; - next if $arg eq '' or $arg =~ /^#/; # ignore empty/comment lines + $arg = FilterArgfileLine($arg); + next unless defined $arg; push @newArgs, $arg; if ($optArgs) { # this is an argument for the last option @@ -4055,12 +4191,12 @@ input. Metadata is read from source files and printed in readable form to the console (or written to output text files with B<-w>). -To write or delete metadata, tag values are assigned using the --I=[I] syntax, or the B<-geotag> option. To copy or move -metadata, the B<-tagsFromFile> feature is used. By default the original -files are preserved with C<_original> appended to their names -- be sure to -verify that the new files are OK before erasing the originals. Once in -write mode, exiftool will ignore any read-specific options. +To write or delete metadata, tag values are assigned using +-I=[I], and/or the B<-geotag>, B<-csv=> or B<-json=> options. +To copy or move metadata, the B<-tagsFromFile> feature is used. By default +the original files are preserved with C<_original> appended to their names +-- be sure to verify that the new files are OK before erasing the originals. +Once in write mode, exiftool will ignore any read-specific options. Note: If I is a directory name then only supported file types in the directory are processed (in write mode only writable types are processed). @@ -4075,42 +4211,45 @@ File Types ------------+-------------+-------------+-------------+------------ - 3FR r | DV r | JSON r | ODT r | RIFF r - 3G2 r/w | DVB r/w | K25 r | OFR r | RSRC r - 3GP r/w | DYLIB r | KDC r | OGG r | RTF r - A r | EIP r | KEY r | OGV r | RW2 r/w - AA r | EPS r/w | LA r | OPUS r | RWL r/w - AAX r/w | EPUB r | LFP r | ORF r/w | RWZ r - ACR r | ERF r/w | LNK r | OTF r | RM r - AFM r | EXE r | M2TS r | PAC r | SEQ r - AI r/w | EXIF r/w/c | M4A/V r/w | PAGES r | SO r - AIFF r | EXR r | MAX r | PBM r/w | SR2 r/w - APE r | EXV r/w/c | MEF r/w | PCD r | SRF r - ARW r/w | F4A/V r/w | MIE r/w/c | PDB r | SRW r/w - ASF r | FFF r/w | MIFF r | PDF r/w | SVG r - AVI r | FLA r | MKA r | PEF r/w | SWF r - AZW r | FLAC r | MKS r | PFA r | THM r/w - BMP r | FLIF r/w | MKV r | PFB r | TIFF r/w - BPG r | FLV r | MNG r/w | PFM r | TORRENT r - BTF r | FPF r | MOBI r | PGF r | TTC r - CHM r | FPX r | MODD r | PGM r/w | TTF r - COS r | GIF r/w | MOI r | PLIST r | VCF r - CR2 r/w | GZ r | MOS r/w | PICT r | VRD r/w/c - CRW r/w | HDP r/w | MOV r/w | PMP r | VSD r - CS1 r/w | HDR r | MP3 r | PNG r/w | WAV r - DCM r | HTML r | MP4 r/w | PPM r/w | WDP r/w - DCP r/w | ICC r/w/c | MPC r | PPT r | WEBP r - DCR r | ICS r | MPG r | PPTX r | WEBM r - DFONT r | IDML r | MPO r/w | PS r/w | WMA r - DIVX r | IIQ r/w | MQV r/w | PSB r/w | WMV r - DJVU r | IND r/w | MRW r/w | PSD r/w | WV r - DLL r | INX r | MXF r | PSP r | X3F r/w - DNG r/w | ISO r | NEF r/w | QTIF r/w | XCF r - DOC r | ITC r | NRW r/w | RA r | XLS r - DOCX r | J2C r | NUMBERS r | RAF r/w | XLSX r - DPX r | JNG r/w | O r | RAM r | XMP r/w/c - DR4 r/w/c | JP2 r/w | ODP r | RAR r | ZIP r - DSS r | JPEG r/w | ODS r | RAW r/w | + 3FR r | DSS r | JP2 r/w | OFR r | RTF r + 3G2 r/w | DV r | JPEG r/w | OGG r | RW2 r/w + 3GP r/w | DVB r/w | JSON r | OGV r | RWL r/w + A r | DVR-MS r | K25 r | OPUS r | RWZ r + AA r | DYLIB r | KDC r | ORF r/w | RM r + AAE r | EIP r | KEY r | OTF r | SEQ r + AAX r/w | EPS r/w | LA r | PAC r | SKETCH r + ACR r | EPUB r | LFP r | PAGES r | SO r + AFM r | ERF r/w | LNK r | PBM r/w | SR2 r/w + AI r/w | EXE r | M2TS r | PCD r | SRF r + AIFF r | EXIF r/w/c | M4A/V r/w | PCX r | SRW r/w + APE r | EXR r | MAX r | PDB r | SVG r + ARQ r/w | EXV r/w/c | MEF r/w | PDF r/w | SWF r + ARW r/w | F4A/V r/w | MIE r/w/c | PEF r/w | THM r/w + ASF r | FFF r/w | MIFF r | PFA r | TIFF r/w + AVI r | FLA r | MKA r | PFB r | TORRENT r + AZW r | FLAC r | MKS r | PFM r | TTC r + BMP r | FLIF r/w | MKV r | PGF r | TTF r + BPG r | FLV r | MNG r/w | PGM r/w | VCF r + BTF r | FPF r | MOBI r | PLIST r | VRD r/w/c + CHM r | FPX r | MODD r | PICT r | VSD r + COS r | GIF r/w | MOI r | PMP r | WAV r + CR2 r/w | GPR r/w | MOS r/w | PNG r/w | WDP r/w + CR3 r/w | GZ r | MOV r/w | PPM r/w | WEBP r + CRM r/w | HDP r/w | MP3 r | PPT r | WEBM r + CRW r/w | HDR r | MP4 r/w | PPTX r | WMA r + CS1 r/w | HEIC r | MPC r | PS r/w | WMV r + DCM r | HEIF r | MPG r | PSB r/w | WTV r + DCP r/w | HTML r | MPO r/w | PSD r/w | WV r + DCR r | ICC r/w/c | MQV r/w | PSP r | X3F r/w + DFONT r | ICS r | MRW r/w | QTIF r/w | XCF r + DIVX r | IDML r | MXF r | R3D r | XLS r + DJVU r | IIQ r/w | NEF r/w | RA r | XLSX r + DLL r | IND r/w | NRW r/w | RAF r/w | XMP r/w/c + DNG r/w | INX r | NUMBERS r | RAM r | ZIP r + DOC r | ISO r | O r | RAR r | + DOCX r | ITC r | ODP r | RAW r/w | + DPX r | J2C r | ODS r | RIFF r | + DR4 r/w/c | JNG r/w | ODT r | RSRC r | Meta Information ----------------------+----------------------+--------------------- @@ -4146,7 +4285,7 @@ L -TAG or --TAG Extract or exclude specified tag - -TAG[+-]=[VALUE] Write new value for tag + -TAG[+-^]=[VALUE] Write new value for tag -TAG[+-]<=DATFILE Write tag value from contents of file -TAG[+-] - -api OPT[=VAL] Set ExifTool API option + -api OPT[[^]=[VAL]] Set ExifTool API option -common_args Define common arguments -config CFGFILE Specify configuration file name -echo[NUM] TEXT Echo text to stdout or stderr -execute[NUM] Execute multiple commands on one line -srcfile FMT Process a different source file -stay_open FLAG Keep reading -@ argfile even after EOF - -userParam PARAM[=VAL] Set user parameter (API UserParam opt) + -userParam PARAM[[^]=[VAL]] Set user parameter (API UserParam opt) =head2 Option Details @@ -4298,7 +4437,7 @@ Instead, individual tags may be recovered using the B<-tagsFromFile> option (eg. C<-all= -tagsfromfile @ -artist>). -=item B<->I[+-]B<=>[I] +=item B<->I[+-^]B<=>[I] Write a new value for the specified tag (eg. C<-comment=wow>), or delete the tag if no I is given (eg. C<-comment=>). C<+=> and C<-=> are used to @@ -4306,7 +4445,9 @@ (see L and note 6 below for more details). C<+=> may also be used to increment numerical values (or decrement if I is negative), and C<-=> may be used to conditionally -delete or replace a tag (see L for examples). +delete or replace a tag (see L for examples). C<^=> is +used to write an empty string instead of deleting the tag when no I +is given, but otherwise it is equivalent to C<=>. I may contain one or more leading family 0, 1 or 2 group names, prefixed by optional family numbers, and separated colons. If no group name @@ -4320,11 +4461,11 @@ not written. A tag name of C is equivalent to C<*> (except that it doesn't require quoting, while arguments with wildcards do on systems with shell globbing), and is often used when deleting all metadata (ie. C<-All=>) -or an entire group (eg. C<-GROUP:All=>, see note 4 below). Note that not +or an entire group (eg. C<-XMP-dc:All=>, see note 4 below). Note that not all groups are deletable, and that the JPEG APP14 "Adobe" group is not removed by default with C<-All=> because it may affect the appearance of the -image. However, this will remove color space information, so the colors may -be affected (but this may be avoided by copying back the tags defined by the +image. However, color space information is removed, so the colors may be +affected (but this may be avoided by copying back the tags defined by the ColorSpaceTags shortcut). Use the B<-listd> option for a complete list of deletable groups, and see note 5 below regarding the "APP" groups. Also, within an image some groups may be contained within others, and these groups @@ -4420,28 +4561,30 @@ processing multiple files. Specified tags are then copied from each file in turn as it is rewritten. For advanced batch use, the source file name may also be specified using a I string in which %d, %f and %e represent the -directory, file name and extension of I. See B<-w> option for I -string examples. +directory, file name and extension of I. (eg. the current I +would be represented by C<%d%f.%e>, with the same effect as C<@>). See the +B<-w> option for I string examples. A powerful redirection feature allows a destination tag to be specified for each copied tag. With this feature, information may be written to a tag with a different name or group. This is done using -E'-IEI'E or +E'-IEI'E or E'-IEI'E on the command line after B<-tagsFromFile>, and causes the value of I to be copied from -I and written to I in I. Note that this argument -must be quoted to prevent shell redirection, and there is no C<=> sign as -when assigning new values. Source and/or destination tags may be prefixed -by a group name and/or suffixed by C<#>. Wildcards are allowed in both the -source and destination tag names. A destination group and/or tag name of -C or C<*> writes to the same family 1 group and/or tag name as the -source. If no destination group is specified, the information is written to -the preferred group. Whitespace around the C> or C> is ignored. -As a convenience, C<-tagsFromFile @> is assumed for any redirected tags -which are specified without a prior B<-tagsFromFile> option. Copied tags -may also be added or deleted from a list with arguments of the form +I and written to I in I. Has no effect unless +I exists in I. Note that this argument must be quoted to +prevent shell redirection, and there is no C<=> sign as when assigning new +values. Source and/or destination tags may be prefixed by a group name +and/or suffixed by C<#>. Wildcards are allowed in both the source and +destination tag names. A destination group and/or tag name of C or +C<*> writes to the same family 1 group and/or tag name as the source. If no +destination group is specified, the information is written to the preferred +group. Whitespace around the C> or C> is ignored. As a +convenience, C<-tagsFromFile @> is assumed for any redirected tags which are +specified without a prior B<-tagsFromFile> option. Copied tags may also be +added or deleted from a list with arguments of the form E'-I+EI'E or -E'-I-EI'E. +E'-I-EI'E (but see Note 5 below). An extension of the redirection feature allows strings involving tag names to be used on the right hand side of the C> symbol with the syntax @@ -4517,7 +4660,8 @@ matching source tag are copied individually to the destination tag (as if they were separate assignments). However, when interpolated inside a string, list items and the values of shortcut tags are concatenated (with a -separator set by the B<-sep> option), and wildcards are not allowed. +separator set by the B<-sep> option), and wildcards are not allowed. Also, +UserParam variables are available only when interpolated in a string. =item B<-x> I (B<-exclude>) @@ -4562,14 +4706,16 @@ descriptions. This option is mainly used for extracting embedded images or other binary data, but it may also be useful for some text strings since control characters (such as newlines) are not replaced by '.' as they are in -the default output. List items are separated by a newline when extracted -with the B<-b> option. May be combined with C<-j>, C<-php> or C<-X> to -extract binary data in JSON, PHP or XML format. +the default output. By default, list items are separated by a newline when +extracted with the B<-b> option, but this may be changed (see the B<-sep> +option for details). May be combined with C<-j>, C<-php> or C<-X> to +extract binary data in JSON, PHP or XML format, but note that "unsafe" tags +must be specified explicitly to be extracted as binary in these formats. =item B<-c> I (B<-coordFormat>) Set the print format for GPS coordinates. I uses the same syntax as -the C format string. The specifiers correspond to degrees, minutes +a C format string. The specifiers correspond to degrees, minutes and seconds in that order, but minutes and seconds are optional. For example, the following table gives the output for the same coordinate using various formats: @@ -4597,9 +4743,8 @@ If I is C or not specified, this option sets the ExifTool character encoding for output tag values when reading and input values when -writing. The default ExifTool encoding is C. If no I is -given, a list of available character sets is returned. Valid I -values are: +writing, with a default of C. If no I is given, a list of +available character sets is returned. Valid I values are: CHARSET Alias(es) Description ---------- --------------- ---------------------------------- @@ -4614,6 +4759,8 @@ Baltic cp1257 Windows Baltic Vietnam cp1258 Windows Vietnamese Thai cp874 Windows Thai + DOSLatinUS cp437 DOS Latin US + DOSLatin1 cp850 DOS Latin1 MacRoman cp10000, Roman Macintosh Roman MacLatin2 cp10029 Macintosh Latin2 (Central Europe) MacCyrillic cp10007 Macintosh Cyrillic @@ -4646,7 +4793,7 @@ L for more details about the B<-charset> settings. -=item B<-csv>[=I] +=item B<-csv>[[+]=I] Export information in CSV format, or import information if I is specified. When importing, the CSV file must be in exactly the same format @@ -4664,21 +4811,22 @@ # update metadata for all images in a directory from CSV file exiftool -csv=a.csv dir -Empty values are ignored when importing. Also, FileName and Directory -columns are ignored if they exist (ie. ExifTool will not attempt to write -these tags with a CSV import). To force a tag to be deleted, use the B<-f> -option and set the value to "-" in the CSV file (or to the MissingTagValue -if this API option was used). Multiple databases may be imported in a -single command. - -When exporting a CSV file, the B<-g> or B<-G> option to add group names to -the tag headings. If the B<-a> option is used to allow duplicate tag names, -the duplicate tags are only included in the CSV output if the column -headings are unique. Adding the B<-G4> option ensures a unique column -heading for each tag. When exporting specific tags, the CSV columns are -arranged in the same order as the specified tags provided the column -headings exactly match the specified tag names, otherwise the columns are -sorted in alphabetical order. +Empty values are ignored when importing (unless the B<-f> option is used and +the API MissingTagValue is set to an empty string, in which case the tag is +deleted). Also, FileName and Directory columns are ignored if they exist +(ie. ExifTool will not attempt to write these tags with a CSV import). To +force a tag to be deleted, use the B<-f> option and set the value to "-" in +the CSV file (or to the MissingTagValue if this API option was used). +Multiple databases may be imported in a single command. + +When exporting a CSV file, the B<-g> or B<-G> option adds group names to the +tag headings. If the B<-a> option is used to allow duplicate tag names, the +duplicate tags are only included in the CSV output if the column headings +are unique. Adding the B<-G4> option ensures a unique column heading for +each tag. When exporting specific tags, the CSV columns are arranged in the +same order as the specified tags provided the column headings exactly match +the specified tag names, otherwise the columns are sorted in alphabetical +order. When importing from a CSV file, only files specified on the command line are processed. Any extra entries in the CSV file are ignored. @@ -4698,11 +4846,13 @@ =item B<-d> I (B<-dateFormat>) -Set the format for date/time tag values. The specifics of the I syntax +Set the format for date/time tag values. The I string may contain +formatting codes beginning with a percent character (C<%>) to represent the +various components of a date/time value. The specifics of the I syntax are system dependent -- consult the C man page on your system for details. The default format is equivalent to "%Y:%m:%d %H:%M:%S". This option has no effect on date-only or time-only tags and ignores timezone -information if present. Only one B<-d> option may be used per command. +information if present. Only one B<-d> option may be used per command. Requires POSIX::strptime or Time::Piece for the inversion conversion when writing. @@ -4771,7 +4921,7 @@ information is dumped, but the -u option can be used to give a raw hex dump of other file formats. -=item B<-j>[=I] (B<-json>) +=item B<-j>[[+]=I] (B<-json>) Use JSON (JavaScript Object Notation) formatting for console output, or import JSON file if I is specified. This option may be combined @@ -4782,14 +4932,15 @@ preserved with the B<-struct> option (this also causes all list-type XMP tags to be output as JSON arrays, otherwise single-item lists would be output as simple strings). The B<-a> option is implied if the B<-g> or -B<-G> options are used, otherwise it is ignored and duplicate tags are -suppressed. Adding the B<-D> or B<-H> option changes tag values to JSON -objects with "val" and "id" fields, and adding B<-l> adds a "desc" field, -and a "num" field if the numerical value is different from the converted -"val". The B<-b> option may be added to output binary data, encoded in -base64 if necessary (indicated by "base64:" as the first 7 bytes of the -value), and B<-t> may be added to include tag table information (see B<-t> -for details). The JSON output is UTF-8 regardless of any B<-L> or +B<-G> options are used, otherwise it is ignored and tags with identical +JSON names are suppressed. (B<-g4> may be used to ensure that all tags have +unique JSON names.) Adding the B<-D> or B<-H> option changes tag values to +JSON objects with "val" and "id" fields, and adding B<-l> adds a "desc" +field, and a "num" field if the numerical value is different from the +converted "val". The B<-b> option may be added to output binary data, +encoded in base64 if necessary (indicated by "base64:" as the first 7 bytes +of the value), and B<-t> may be added to include tag table information (see +B<-t> for details). The JSON output is UTF-8 regardless of any B<-L> or B<-charset> option setting, but the UTF-8 validation is disabled if a character set other than UTF-8 is specified. @@ -4853,8 +5004,8 @@ For list-type tags, this causes only the item with the specified index to be extracted. I is 0 for the first item in the list. Negative indices may also be used to reference items from the end of the list. Has no effect -on single-valued tags. Also applies to tag values when copying, and in -B<-if> conditions. +on single-valued tags. Also applies to tag values when copying from a tag, +and in B<-if> conditions. =item B<-n> (B<--printConv>) @@ -4883,20 +5034,25 @@ =item B<-p> I or I (B<-printFormat>) -Print output in the format specified by the given file or string. Tag names -in the format file or string begin with a C<$> symbol and may contain a -leading group names and/or a trailing C<#>. Case is not significant. -Braces C<{}> may be used around the tag name to separate it from subsequent -text. Use C<$$> to represent a C<$> symbol, and C<$/> for a newline. -Multiple B<-p> options may be used, each contributing a line of text to the -output. Lines beginning with C<#[HEAD]> and C<#[TAIL]> are output before -the first processed file and after the last processed file respectively. -Lines beginning with C<#[SECT]> and C<#[ENDS]> are output around each -section of files. A section is defined as a group of consecutive files with -the same section header (eg. files are grouped by directory if C<#[SECT]> -contains C<$directory>). Lines beginning with C<#[BODY]> and lines not -beginning with C<#> are output for each processed file. Other lines -beginning with C<#> are ignored. For example, this format file: +Print output in the format specified by the given file or string. The +argument is interpreted as a string unless a file of that name exists, in +which case the string is loaded from the contents of the file. Tag names in +the format file or string begin with a C<$> symbol and may contain a leading +group names and/or a trailing C<#>. Case is not significant. Braces C<{}> +may be used around the tag name to separate it from subsequent text. Use +C<$$> to represent a C<$> symbol, and C<$/> for a newline. + +Multiple B<-p> options may be used, each contributing a line (or more) of +text to the output. Lines beginning with C<#[HEAD]> and C<#[TAIL]> are +output before the first processed file and after the last processed file +respectively. Lines beginning with C<#[SECT]> and C<#[ENDS]> are output +around each section of files. A section is defined as a group of +consecutive files with the same section header (eg. files are grouped by +directory if C<#[SECT]> contains C<$directory>). Lines beginning with +C<#[BODY]> and lines not beginning with C<#> are output for each processed +file. Lines beginning with C<#[IF]> are not output, but all BODY lines are +skipped if any tag on an IF line doesn't exist. Other lines beginning with +C<#> are ignored. For example, this format file: # this is a comment line #[HEAD]-- Generated by ExifTool $exifToolVersion -- @@ -4910,7 +5066,7 @@ produces output like this: - -- Generated by ExifTool 10.61 -- + -- Generated by ExifTool 11.26 -- File: a.jpg - 2003:10:31 15:44:19 (f/5.6, 1/60s, ISO 100) File: b.jpg - 2006:05:23 11:57:38 @@ -4976,6 +5132,13 @@ separator when reading, or split the value into individual characters when writing. +For pure binary output (B<-b> used without B<-j>, B<-php> or B<-X>), the +first B<-sep> option specifies a list-item separator, and subsequent B<-sep> +options specify a terminator added to the end of the list (or after each +value if not a list). In these strings, C<\n>, C<\r> and C<\t> may be used +to represent a newline, carriage return and tab respectively. By default, +binary list items are separated by a newline, and no terminator is added. + =item B<-sort>, B<--sort> Sort output by tag description, or by tag name if the B<-s> option is used. @@ -5005,8 +5168,8 @@ import). May be combined with B<-s> to print tag names instead of descriptions, or B<-S> to print tag values only, tab-delimited on a single line. The B<-t> option may be combined with B<-j>, B<-php> or B<-X> to add -tag table information (table C, decimal tag C, and C for -cases where multiple conditional tags exist with the same ID). +tag table information (C, tag C, and C for cases where +multiple conditional tags exist with the same ID). =item B<-T> (B<-table>) @@ -5041,12 +5204,13 @@ -w dir2/%d%f.txt # write to "dir2", keeping dir structure -w a%c.txt # write to "a.txt" or "a1.txt" or "a2.txt"... -Existing files will not be overwritten unless an exclamation point is added -to the option name (ie. B<-w!> or B<-textOut!>), or a plus sign to append to -the existing file (ie. B<-w+> or B<-textOut+>). Both may be used (ie. -B<-w+!> or B<-textOut+!>) to overwrite output files that didn't exist before -the command was run, and append the output from multiple source files. For -example, to write one output file for all source files in each directory: +Existing files will not be changed unless an exclamation point is added to +the option name (ie. B<-w!> or B<-textOut!>) to overwrite the file, or a +plus sign (ie. B<-w+> or B<-textOut+>) to append to the existing file. Both +may be used (ie. B<-w+!> or B<-textOut+!>) to overwrite output files that +didn't exist before the command was run, and append the output from multiple +source files. For example, to write one output file for all source files in +each directory: exiftool -filename -createdate -T -w+! %d/out.txt -r DIR @@ -5127,10 +5291,11 @@ processed file by using %C (upper case) instead of %c. This allows a sequential number to be added to output file names, even if the names are different. For %C, a copy number of zero is not omitted as it is with %c. -The number before the decimal place gives the starting index, the number -after the decimal place gives the field width. The following examples show -the output filenames when used with the command -C: +A leading '-' causes the number to be reset at the start of each new +directory, and '+' has no effect. The number before the decimal place gives +the starting index, the number after the decimal place gives the field +width. The following examples show the output filenames when used with the +command C: -w %C%f.txt # 0rose.txt, 1star.txt, 2jet.txt -w %f-%10C.txt # rose-10.txt, star-11.txt, jet-12.txt @@ -5166,7 +5331,16 @@ format codes. (For B<-w>, this would be a file extension.) This change allows a simple file name to be specified, which, when combined with the append feature, provides a method to write metadata from multiple source -files to a single output file without the need for shell redirection. +files to a single output file without the need for shell redirection. For +example, the following pairs of commands give the same result: + + # overwriting existing text file + exiftool test.jpg > out.txt # shell redirection + exiftool test.jpg -W+! out.txt # equivalent -W option + + # append to existing text file + exiftool test.jpg >> out.txt # shell redirection + exiftool test.jpg -W+ out.txt # equivalent -W option 4) Adding the B<-v> option to B<-W> generates a list of the tags and output file names instead of giving a verbose dump of the entire file. (Unless @@ -5239,16 +5413,23 @@ documents containing sub-documents are indicated with dashes in the family 3 group name. (eg. C is the 3rd sub-document of the 2nd embedded document.) Note that this option may increase processing time substantially, -especially for PDF files with many embedded images. +especially for PDF files with many embedded images or videos with streaming +metadata. + +When used with B<-ee>, the B<-p> option is evaluated for each embedded +document as if it were a separate input file. This allows, for example, +generation of GPS track logs from timed metadata in videos. See +L for +examples. =item B<-ext>[+] I, B<--ext> I (B<-extension>) Process only files with (B<-ext>) or without (B<--ext>) a specified extension. There may be multiple B<-ext> and B<--ext> options. A plus sign may be added (ie. B<-ext+>) to add the specified extension to the normally -processed files. EXT may begin with a leading '.', and case is not -significant. C<"*"> may be used to process files with any extension (or -none at all), as in the last three examples: +processed files. EXT may begin with a leading '.', which is ignored. Case +is not significant. C<"*"> may be used to process files with any extension +(or none at all), as in the last three examples: exiftool -ext JPG DIR # process only JPG files exiftool --ext cr2 --ext dng DIR # supported files but CR2/DNG @@ -5262,6 +5443,9 @@ the B<-r> option. 2) The B<-ext> option is case-insensitive, which is useful when processing files on case-sensitive filesystems. +Note that all files specified on the command line will be processed +regardless of extension unless the B<-ext> option is used. + =item B<-F>[I] (B<-fixBase>) Fix the base for maker notes offsets. A common problem with some image @@ -5285,9 +5469,11 @@ small when reading images directly from disk, but can be substantial if piping images through a network connection. For more substantial speed benefits, B<-fast2> also causes exiftool to avoid extracting any EXIF -MakerNote information. B<-fast3> avoids processing the file entirely, and -returns only an initial guess at FileType and the pseudo System tags. Has -no effect when writing. +MakerNote information. B<-fast3> avoids extracting metadata from the file, +and returns only pseudo System tags, but still reads the file header to +obtain an educated guess at FileType. B<-fast4> doesn't even read the file +header, and determines FileType based only on the file extension. Has no +effect when writing. =item B<-fileOrder> [-]I @@ -5314,7 +5500,7 @@ sensitive) may be specified to ignore symbolic links when the B<-r> option is used. -=item B<-if> I +=item B<-if>[I] I Specify a condition to be evaluated before processing each I. I is a Perl-like logic expression containing tag names prefixed by C<$> @@ -5326,7 +5512,7 @@ C<$GROUP:all> evaluates to 1 if any tag exists in the specified C, or 0 otherwise (see note 2 below). When multiple B<-if> options are used, all conditions must be satisfied to process the file. Returns an exit status of -1 if all files fail the condition. Below are a few examples: +2 if all files fail the condition. Below are a few examples: # extract shutterspeed from all Canon images in a directory exiftool -shutterspeed -if '$make eq "Canon"' dir @@ -5340,6 +5526,13 @@ # find images containing a specific keyword (case insensitive) exiftool -if '$keywords =~ /harvey/i' -filename dir +Adding I to the B<-if> option causes a separate processing pass to be +executed for evaluating I at a B<-fast> level given by I (see the +B<-fast> option documentation for details). Without I, only one +processing pass is done at the level specified by the B<-fast> option. For +example, using B<-if4> is possible if I uses only pseudo System tags, +and may significantly speed processing if enough files fail the condition. + Notes: 1) The B<-n> and B<-b> options also apply to tags used in I. @@ -5367,6 +5560,10 @@ the values of duplicate tags are accessible only by specifying a group name (such as a family 4 instance number, eg. C<$Copy1:TAG>, C<$Copy2:TAG>, etc). +6) A special "OK" UserParam is available to test the success of the previous +command when B<-execute> was used, and may be used like any other tag in the +condition (ie. "$OK"). + =item B<-m> (B<-ignoreMinorErrors>) Ignore minor errors and warnings. This enables writing to files with minor @@ -5374,9 +5571,11 @@ warnings. Generally, minor errors/warnings indicate a problem which usually won't result in loss of metadata if ignored. However, there are exceptions, so ExifTool leaves it up to you to make the final decision. Minor errors -and warnings are indicated by "[minor]" at the start of the message. +and warnings are indicated by "[minor]" at the start of the message. Warnings which affect processing when ignored are indicated by "[Minor]" -(with a capital "M"). +(with a capital "M"). Note that this causes missing values in +B<-tagsFromFile>, B<-p> and B<-if> strings to be set to an empty string +rather than an undefined value. =item B<-o> I or I (B<-out>) @@ -5455,22 +5654,23 @@ =item B<-progress>[:[I]] -Show the progress when processing files. The progress count appears in -brackets after the name of each processed file, and gives the current file -number and the total number of files to be processed. Implies the B<-v0> -option if I<TITLE> is not used, printing the name of each processed file -when writing. When combined with the B<-if> option, the total count -includes all files before the condition is applied, but files that fail the -condition will not have their names printed. +Show the progress when processing files. Without a colon, the B<-progress> +option adds a progress count in brackets after the name of each processed +file, giving the current file number and the total number of files to be +processed. Implies the B<-v0> option, causing the names of processed files +to also be printed when writing. When combined with the B<-if> option, the +total count includes all files before the condition is applied, but files +that fail the condition will not have their names printed. If followed by a colon (ie. B<-progress:>), the console window title is set according to the specified I<TITLE> string. If no I<TITLE> is given, a default I<TITLE> string of "ExifTool %p%%" is assumed. In the string, %f represents the file name, %p is the progress as a percent, %r is the -progress as a ratio, %[##]b is a progress bar of width ## (default 20), and -%% is a % character. May be combined with the normal B<-progress> option to -also show the progress count in console messages. (Note: For this feature -to function correctly on Mac/Linux, stderr must go to the console.) +progress as a ratio, %##b is a progress bar of width "##" (20 characters if +"##" is omitted), and %% is a % character. May be combined with the normal +B<-progress> option to also show the progress count in console messages. +(Note: For this feature to function correctly on Mac/Linux, stderr must go +to the console.) =item B<-q> (B<-quiet>) @@ -5485,8 +5685,9 @@ a directory name. Subdirectories with names beginning with "." are not processed unless "." is added to the option name (ie. B<-r.> or B<-recurse.>). By default, exiftool will also follow symbolic links to -directories if supported by the system, but this may be disabled with C<-i -SYMLINKS> (see the B<-i> option for details). +directories if supported by the system, but this may be disabled with +C<-i SYMLINKS> (see the B<-i> option for details). Combine this with +B<-ext> options to control the types of files processed. =item B<-scanForXMP> @@ -5511,8 +5712,7 @@ =item B<-wm> I<MODE> (B<-writeMode>) Set mode for writing/creating tags. I<MODE> is a string of one or more -characters from the list below. Write mode is C<wcg> unless otherwise -specified. +characters from the list below. The default write mode is C<wcg>. w - Write existing tags c - Create new tags @@ -5528,11 +5728,12 @@ =item B<-z> (B<-zip>) When reading, causes information to be extracted from .gz and .bz2 -compressed images. (Only one image per archive. Requires gzip and bzip2 to -be installed on the system.) When writing, causes compressed information to -be written if supported by the metadata format. (eg. PNG supports -compressed textual metadata.) This option also disables the recommended -padding in embedded XMP, saving 2424 bytes when writing XMP in a file. +compressed images (only one image per archive; requires gzip and bzip2 to be +available). When writing, causes compressed information to be written if +supported by the metadata format (eg. compressed textual metadata in PNG), +disables the recommended padding in embedded XMP (saving 2424 bytes when +writing XMP in a file), and writes XMP in shorthand format -- the equivalent +of setting the API Compress, Compact and XMPShorthand options to 1. =back @@ -5545,12 +5746,14 @@ Read command-line arguments from the specified file. The file contains one argument per line (NOT one option per line -- some options require additional arguments, and all arguments must be placed on separate lines). -Blank lines and lines beginning with C<#> are ignored. White space at the -start of a line is removed. Normal shell processing of arguments is not -performed, which among other things means that arguments should not be -quoted and spaces are treated as any other character. I<ARGFILE> may exist -relative to either the current directory or the exiftool directory unless an -absolute pathname is given. +Blank lines and lines beginning with C<#> are ignored (unless they start +with C<#[CSTR]>, in which case the rest of the line is treated as a C +string, allowing standard C escape sequences such as "\n" for a newline). +White space at the start of a line is removed. Normal shell processing of +arguments is not performed, which among other things means that arguments +should not be quoted and spaces are treated as any other character. +I<ARGFILE> may exist relative to either the current directory or the +exiftool directory unless an absolute pathname is given. For example, the following I<ARGFILE> will set the value of Copyright to "Copyright YYYY, Phil Harvey", where "YYYY" is the year of CreateDate: @@ -5749,12 +5952,11 @@ =over 5 -=item B<-api> I<OPT[=VAL]> +=item B<-api> I<OPT[[^]=[VAL]]> Set ExifTool API option. I<OPT> is an API option name. The option value is -set to 1 if I<=VAL> is omitted, or undef if just I<VAL> is omitted. An -option may not be set to an empty string ("") via the command line, but the -config file may be used to accomplish this if necessary. See +set to 1 if I<=VAL> is omitted. If I<VAL> is omitted, the option value is +set to undef if C<=> is used, or an empty string with C<^=>. See L<Image::ExifTool Options|Image::ExifTool/Options> for a list of available API options. This overrides API options set via the config file. @@ -5770,12 +5972,13 @@ Load specified configuration file instead of the default ".ExifTool_config". If used, this option must come before all other arguments on the command -line and applies to all B<-execute>'d commands. The I<CFGFILE> name may -contain a directory specification (otherwise the file must exist in the -current directory), or may be set to an empty string ("") to disable loading -of the config file. See the sample configuration file and "config.html" in -the full ExifTool distribution for more information about the ExifTool -configuration file. +line and applies to all B<-execute>'d commands. The I<CFGFILE> must exist +relative to the current working directory or the exiftool application +directory unless an absolute path is specified. Loading of the default +config file may be disabled by setting I<CFGFILE> to an empty string (ie. +""). See L<http://owl.phy.queensu.ca/~phil/exiftool/config.html> and +config_files/example.config in the full ExifTool distribution for details +about the configuration file syntax. =item B<-echo>[I<NUM>] I<TEXT> @@ -5863,7 +6066,7 @@ delay when writing arguments via a pipe with C<-@ ->, so the signal is not necessary when using this technique.) -=item B<-userParam> I<PARAM[=VAL]> +=item B<-userParam> I<PARAM[[^]=[VAL]]> Set user parameter. I<PARAM> is an arbitrary user parameter name. This is an interface to the API UserParam option (see the @@ -5872,7 +6075,8 @@ expressions (as if it were any other tag, see example below), and from PrintConv/ValueConv logic (via the ExifTool Options function). Similar to the B<-api> option, the parameter value is set to 1 if I<=VAL> is omitted, -or undef if just I<VAL> is omitted. +undef if just I<VAL> is omitted with C<=>, or an empty string if +I<VAL> is omitted with C<^=>. exiftool -p '$test from $filename' -userparam test=Hello FILE @@ -5896,16 +6100,27 @@ exiftool -p '${make;tr/ /_/;s/__+/_/g}' image.jpg +An C<@> may be added after the tag name to make the expression act on +individual list items for list-type tags, simplifying list processing. Set +C<$_> to undef to remove an item from the list. As an example, the +following command returns all subjects not containing the string "xxx": + + exiftool -p '${subject@;$_=undef if /xxx/}' image.jpg + A default expression of C<tr(/\\?*:|"E<lt>E<gt>\0)()d> is assumed if the expression is empty (ie. C<${TAG;}>). This removes the characters / \ ? * : | E<lt> E<gt> and null from the printed value. (These characters are illegal in Windows file names, so this feature is useful if tag values are used in file names.) +=head4 Helper functions + ExifTool provides a C<DateFmt> utility to simplify reformatting of individual date/time values. The function acts on a standard EXIF-formatted date/time value in C<$_> and formats it according to the specified format -string (see the B<-d> option). For example: +string (see the B<-d> option). To avoid trying to reformat an already +formatted date/time value, a C<#> must be added to the tag name (as in the +example below) if the B<-d> option is also used. For example: exiftool -p '${createdate#;DateFmt("%Y-%m-%d_%H%M%S")}' a.jpg @@ -5939,10 +6154,11 @@ C<-charset filename=CHARSET>, where C<CHARSET> is the name of a valid ExifTool character set, preferably C<UTF8> (see the B<-charset> option for a complete list). Setting this triggers the use of Windows wide-character i/o -routines, thus providing support for all Unicode file names. But note that -it is not trivial to pass properly encoded file names on the Windows command -line (see L<http://owl.phy.queensu.ca/~phil/exiftool/faq.html#Q18> for -details), so placing them in a UTF-8 encoded B<-@> argfile and using +routines, thus providing support for most Unicode file names (see note 4). +But note that it is not trivial to pass properly encoded file names on the +Windows command line (see +L<http://owl.phy.queensu.ca/~phil/exiftool/faq.html#Q18> for details), so +placing them in a UTF-8 encoded B<-@> argfile and using C<-charset filename=utf8> is recommended if possible. A warning is issued if a specified filename contains special characters and @@ -5975,6 +6191,9 @@ 3) See L</WRITING READ-ONLY FILES> below for a note about editing read-only files with Unicode names. +4) Unicode file names with surrogate pairs (code points over U+FFFF) still +cause problems. + =head1 WRITING READ-ONLY FILES In general, ExifTool may be used to write metadata to read-only files @@ -6003,7 +6222,11 @@ =item exiftool -a -u -g1 a.jpg Print all meta information in an image, including duplicate and unknown -tags, sorted by group (for family 1). +tags, sorted by group (for family 1). For performance reasons, this command +may not extract all available metadata. (Metadata in embedded documents, +metadata extracted by external utilities, and metadata requiring excessive +processing time may not be extracted). Add C<-ee> and C<-api RequestAll=3> +to the command to extract absolutely everything available. =item exiftool -common dir @@ -6357,10 +6580,9 @@ This command performs exactly the same task as the command above, except that the B<-o> option will not write to an output file that already exists. -=item exiftool -if '$jpgfromraw' -b -jpgfromraw -w %d%f_%ue.jpg -execute --if '$previewimage' -b -previewimage -w %d%f_%ue.jpg -execute --tagsfromfile @ -srcfile %d%f_%ue.jpg -overwrite_original --common_args --ext jpg DIR +=item exiftool -b -jpgfromraw -w %d%f_%ue.jpg -execute -b -previewimage -w +%d%f_%ue.jpg -execute -tagsfromfile @ -srcfile %d%f_%ue.jpg +-overwrite_original -common_args --ext jpg DIR [Advanced] Extract JpgFromRaw or PreviewImage from all but JPG files in DIR, saving them with file names like C<image_EXT.jpg>, then add all meta @@ -6549,12 +6771,12 @@ =head1 EXIT STATUS The exiftool application exits with a status of 0 on success, or 1 if an -error occurred or if all files failed the B<-if> condition (for any of the -commands if B<-execute> was used). +error occurred, or 2 if all files failed the B<-if> condition (for any of +the commands if B<-execute> was used). =head1 AUTHOR -Copyright 2003-2017, Phil Harvey +Copyright 2003-2019, Phil Harvey This is free software; you can redistribute it and/or modify it under the same terms as Perl itself. diff -Nru xnview-0.92/opt64/XnView/AddOn/lib/File/RandomAccess.pm xnview-0.93/opt64/XnView/AddOn/lib/File/RandomAccess.pm --- xnview-0.92/opt64/XnView/AddOn/lib/File/RandomAccess.pm 2017-09-30 10:50:20.000000000 +0000 +++ xnview-0.93/opt64/XnView/AddOn/lib/File/RandomAccess.pm 2019-01-10 14:15:16.000000000 +0000 @@ -16,6 +16,7 @@ # 11/26/2008 - P. Harvey Fixed bug in ReadLine when reading from a # scalar with a multi-character newline # 01/24/2009 - PH Protect against reading too much at once +# 10/04/2018 - PH Added NoBuffer option # # Notes: Calls the normal file i/o routines unless SeekTest() fails, in # which case the file is buffered in memory to allow random access. @@ -24,7 +25,7 @@ # # May also be used for string i/o (just pass a scalar reference) # -# Legal: Copyright (c) 2003-2017 Phil Harvey (phil at owl.phy.queensu.ca) +# Legal: Copyright (c) 2003-2019 Phil Harvey (phil at owl.phy.queensu.ca) # This library is free software; you can redistribute it and/or # modify it under the same terms as Perl itself. #------------------------------------------------------------------------------ @@ -36,13 +37,14 @@ require Exporter; use vars qw($VERSION @ISA @EXPORT_OK); -$VERSION = '1.10'; +$VERSION = '1.11'; @ISA = qw(Exporter); sub Read($$$); # constants my $CHUNK_SIZE = 8192; # size of chunks to read from file (must be power of 2) +my $SKIP_SIZE = 65536; # size to skip when fast-forwarding over sequential data my $SLURP_CHUNKS = 16; # read this many chunks at a time when slurping #------------------------------------------------------------------------------ @@ -60,6 +62,7 @@ # string i/o $self = { BUFF_PT => $filePt, + BASE => 0, POS => 0, LEN => length($$filePt), TESTED => -1, @@ -71,8 +74,9 @@ $self = { FILE_PT => $filePt, # file pointer BUFF_PT => \$buff, # reference to file data - POS => 0, # current position in file - LEN => 0, # data length + BASE => 0, # location of start of buffer in file + POS => 0, # current position in buffer + LEN => 0, # length of data in buffer TESTED => 0, # 0=untested, 1=passed, -1=failed (requires buffering) }; bless $self, $class; @@ -118,7 +122,7 @@ my $self = shift; my $rtnVal; if ($self->{TESTED} < 0) { - $rtnVal = $self->{POS}; + $rtnVal = $self->{POS} + $self->{BASE}; } else { $rtnVal = tell($self->{FILE_PT}); } @@ -141,9 +145,11 @@ if ($self->{TESTED} < 0) { my $newPos; if ($whence == 0) { - $newPos = $num; # from start of file + $newPos = $num - $self->{BASE}; # from start of file } elsif ($whence == 1) { $newPos = $num + $self->{POS}; # relative to current position + } elsif ($self->{NoBuffer} and $self->{FILE_PT}) { + $newPos = -1; # (can't seek relative to end if no buffering) } else { $self->Slurp(); # read whole file into buffer $newPos = $num + $self->{LEN}; # relative to end of file @@ -192,6 +198,8 @@ } # read through our buffer if necessary if ($self->{TESTED} < 0) { + # purge old data before reading in NoBuffer mode + $self->Purge() or return 0 if $self->{NoBuffer}; my $buff; my $newPos = $self->{POS} + $len; # number of bytes to read from file @@ -244,6 +252,7 @@ if ($self->{TESTED} < 0) { my ($num, $buff); + $self->Purge() or return 0 if $self->{NoBuffer}; my $pos = $self->{POS}; if ($fp) { # make sure we have some data after the current position @@ -311,9 +320,39 @@ } } +#------------------------------------------------------------------------------ +# Purge internal buffer [internal use only] +# Inputs: 0) reference to RandomAccess object +# Returns: 1 on success, or 0 if current buffer position is negative +# Notes: This is called only in NoBuffer mode +sub Purge($) +{ + my $self = shift; + return 1 unless $self->{FILE_PT}; + return 0 if $self->{POS} < 0; # error if we can't read from here + if ($self->{POS} > $CHUNK_SIZE) { + my $purge = $self->{POS} - ($self->{POS} % $CHUNK_SIZE); + if ($purge >= $self->{LEN}) { + # read up to current position in 64k chunks, discarding as we go + while ($self->{POS} > $self->{LEN}) { + $self->{BASE} += $self->{LEN}; + $self->{POS} -= $self->{LEN}; + ${$self->{BUFF_PT}} = ''; + $self->{LEN} = read($self->{FILE_PT}, ${$self->{BUFF_PT}}, $SKIP_SIZE); + last if $self->{LEN} < $SKIP_SIZE; + } + } elsif ($purge > 0) { + ${$self->{BUFF_PT}} = substr ${$self->{BUFF_PT}}, $purge; + $self->{BASE} += $purge; + $self->{POS} -= $purge; + $self->{LEN} -= $purge; + } + } + return 1; +} #------------------------------------------------------------------------------ -# set binary mode +# Set binary mode # Inputs: 0) reference to RandomAccess object sub BinMode($) { @@ -322,7 +361,7 @@ } #------------------------------------------------------------------------------ -# close the file and free the buffer +# Close the file and free the buffer # Inputs: 0) reference to RandomAccess object sub Close($) { @@ -370,6 +409,7 @@ # reset the buffer my $emptyBuff = ''; $self->{BUFF_PT} = \$emptyBuff; + $self->{BASE} = 0; $self->{LEN} = 0; $self->{POS} = 0; } diff -Nru xnview-0.92/opt64/XnView/AddOn/lib/File/RandomAccess.pod xnview-0.93/opt64/XnView/AddOn/lib/File/RandomAccess.pod --- xnview-0.92/opt64/XnView/AddOn/lib/File/RandomAccess.pod 2017-09-30 10:50:20.000000000 +0000 +++ xnview-0.93/opt64/XnView/AddOn/lib/File/RandomAccess.pod 2019-01-10 14:15:16.000000000 +0000 @@ -3,7 +3,7 @@ # # Description: Buffer to support random access reading of sequential file # -# Legal: Copyright (c) 2003-2017 Phil Harvey (phil at owl.phy.queensu.ca) +# Legal: Copyright (c) 2003-2019 Phil Harvey (phil at owl.phy.queensu.ca) # This library is free software; you can redistribute it and/or # modify it under the same terms as Perl itself. #------------------------------------------------------------------------------ @@ -215,9 +215,28 @@ =back +=head1 OPTIONS + +=over 4 + +=item B<NoBuffer> + +Avoid buffering sequential files. + + $raf->{NoBuffer} = 1; + +When this option is set, old data is purged from the internal buffer before +a read operation on a sequential file. In this mode, memory requirements +may be significantly reduced when reading sequential files, but seeking +backward is limited to within the size of the internal buffer (which will be +at least as large as the last returned data block), and seeking relative to +the end of file is not allowed. + +=back + =head1 AUTHOR -Copyright 2003-2017 Phil Harvey (phil at owl.phy.queensu.ca) +Copyright 2003-2019 Phil Harvey (phil at owl.phy.queensu.ca) This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself. diff -Nru xnview-0.92/opt64/XnView/AddOn/lib/Image/ExifTool/AES.pm xnview-0.93/opt64/XnView/AddOn/lib/Image/ExifTool/AES.pm --- xnview-0.92/opt64/XnView/AddOn/lib/Image/ExifTool/AES.pm 2017-09-30 10:50:34.000000000 +0000 +++ xnview-0.93/opt64/XnView/AddOn/lib/Image/ExifTool/AES.pm 2019-01-10 14:15:16.000000000 +0000 @@ -477,7 +477,7 @@ =head1 AUTHOR -Copyright 2003-2017, Phil Harvey (phil at owl.phy.queensu.ca) +Copyright 2003-2019, Phil Harvey (phil at owl.phy.queensu.ca) This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself. diff -Nru xnview-0.92/opt64/XnView/AddOn/lib/Image/ExifTool/AFCP.pm xnview-0.93/opt64/XnView/AddOn/lib/Image/ExifTool/AFCP.pm --- xnview-0.92/opt64/XnView/AddOn/lib/Image/ExifTool/AFCP.pm 2017-09-30 10:50:30.000000000 +0000 +++ xnview-0.93/opt64/XnView/AddOn/lib/Image/ExifTool/AFCP.pm 2019-01-10 14:15:16.000000000 +0000 @@ -14,7 +14,7 @@ use vars qw($VERSION); use Image::ExifTool qw(:DataAccess :Utils); -$VERSION = '1.07'; +$VERSION = '1.08'; sub ProcessAFCP($$); @@ -149,12 +149,7 @@ if ($verbose > 2 and not $outfile) { my $dat = $buff . $dir; print $out " AFCP Directory:\n"; - HexDump(\$dat, undef, - Addr => $$dirInfo{DataPos}, - Width => 12, - Prefix => $$et{INDENT}, - Out => $out, - ); + $et->VerboseDump(\$dat, Addr => $$dirInfo{DataPos}, Width => 12); } $fix and $et->Warn("Adjusted AFCP offsets by $fix", 1); # @@ -264,7 +259,7 @@ =head1 AUTHOR -Copyright 2003-2017, Phil Harvey (phil at owl.phy.queensu.ca) +Copyright 2003-2019, Phil Harvey (phil at owl.phy.queensu.ca) This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself. diff -Nru xnview-0.92/opt64/XnView/AddOn/lib/Image/ExifTool/AIFF.pm xnview-0.93/opt64/XnView/AddOn/lib/Image/ExifTool/AIFF.pm --- xnview-0.92/opt64/XnView/AddOn/lib/Image/ExifTool/AIFF.pm 2017-09-30 10:50:23.000000000 +0000 +++ xnview-0.93/opt64/XnView/AddOn/lib/Image/ExifTool/AIFF.pm 2019-01-10 14:15:16.000000000 +0000 @@ -18,7 +18,7 @@ use Image::ExifTool qw(:DataAccess :Utils); use Image::ExifTool::ID3; -$VERSION = '1.07'; +$VERSION = '1.09'; # information for time/date-based tags (time zero is Jan 1, 1904) my %timeInfo = ( @@ -99,6 +99,14 @@ MAC3 => 'MAC 3-to-1', MAC6 => 'MAC 6-to-1', sowt => 'Little-endian, no compression', + alaw => 'a-law', + ALAW => 'A-law', + ulaw => 'mu-law', + ULAW => 'Mu-law', + 'GSM '=> 'GSM', + G722 => 'G722', + G726 => 'G726', + G728 => 'G728', }, }, 11 => { #PH @@ -214,7 +222,7 @@ $pos += 8; my ($tag, $len) = unpack('a4N', $buff); my $tagInfo = $et->GetTagInfo($tagTablePtr, $tag); - $et->VPrint(0, "AIFF '$tag' chunk ($len bytes of data):\n"); + $et->VPrint(0, "AIFF '${tag}' chunk ($len bytes of data):\n"); # AIFF chunks are padded to an even number of bytes my $len2 = $len + ($len & 0x01); if ($tagInfo) { @@ -235,7 +243,7 @@ ); } elsif ($verbose > 2 and $len2 < 1024000) { $raf->Read($buff, $len2) == $len2 or $err = 1, last; - HexDump(\$buff, undef, MaxLen => 512); + $et->VerboseDump(\$buff); } else { $raf->Seek($len2, 1) or $err=1, last; } @@ -264,7 +272,7 @@ =head1 AUTHOR -Copyright 2003-2017, Phil Harvey (phil at owl.phy.queensu.ca) +Copyright 2003-2019, Phil Harvey (phil at owl.phy.queensu.ca) This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself. diff -Nru xnview-0.92/opt64/XnView/AddOn/lib/Image/ExifTool/APE.pm xnview-0.93/opt64/XnView/AddOn/lib/Image/ExifTool/APE.pm --- xnview-0.92/opt64/XnView/AddOn/lib/Image/ExifTool/APE.pm 2017-09-30 10:50:26.000000000 +0000 +++ xnview-0.93/opt64/XnView/AddOn/lib/Image/ExifTool/APE.pm 2019-01-10 14:15:16.000000000 +0000 @@ -263,7 +263,7 @@ =head1 AUTHOR -Copyright 2003-2017, Phil Harvey (phil at owl.phy.queensu.ca) +Copyright 2003-2019, Phil Harvey (phil at owl.phy.queensu.ca) This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself. diff -Nru xnview-0.92/opt64/XnView/AddOn/lib/Image/ExifTool/APP12.pm xnview-0.93/opt64/XnView/AddOn/lib/Image/ExifTool/APP12.pm --- xnview-0.92/opt64/XnView/AddOn/lib/Image/ExifTool/APP12.pm 2017-09-30 10:50:30.000000000 +0000 +++ xnview-0.93/opt64/XnView/AddOn/lib/Image/ExifTool/APP12.pm 2019-01-10 14:15:16.000000000 +0000 @@ -305,7 +305,7 @@ =head1 AUTHOR -Copyright 2003-2017, Phil Harvey (phil at owl.phy.queensu.ca) +Copyright 2003-2019, Phil Harvey (phil at owl.phy.queensu.ca) This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself. diff -Nru xnview-0.92/opt64/XnView/AddOn/lib/Image/ExifTool/Apple.pm xnview-0.93/opt64/XnView/AddOn/lib/Image/ExifTool/Apple.pm --- xnview-0.92/opt64/XnView/AddOn/lib/Image/ExifTool/Apple.pm 2017-09-30 10:50:22.000000000 +0000 +++ xnview-0.93/opt64/XnView/AddOn/lib/Image/ExifTool/Apple.pm 2019-01-10 14:15:16.000000000 +0000 @@ -15,7 +15,7 @@ use Image::ExifTool::Exif; use Image::ExifTool::PLIST; -$VERSION = '1.03'; +$VERSION = '1.04'; # Apple iPhone metadata (ref PH) %Image::ExifTool::Apple::Main = ( @@ -24,7 +24,7 @@ WRITABLE => 1, GROUPS => { 0 => 'MakerNotes', 2 => 'Image' }, NOTES => 'Tags extracted from the maker notes of iPhone images.', - # 0x0001 - int32s: seen 0,1,2,3,4 + # 0x0001 - int32s: seen 0,1,2,3,4,9 # 0x0002 - binary plist with a single data object of size 512 bytes (iPhone5s) 0x0003 => { Name => 'RunTime', # (includes time plugged in, but not when suspended, ref 1) @@ -67,16 +67,24 @@ Notes => 'unique ID for all images in a burst', }, # 0x000c - rational64s[2]: eg) "0.1640625 0.19921875" - # 0x000d - int32s: 0,1,6,20,40 + # 0x000d - int32s: 0,1,6,20,24,32,40 # 0x000e - int32s: 0,1,4,12 (Orienation? 0=landscape? 4=portrait? ref 1) # 0x000f - int32s: 2,3 # 0x0010 - int32s: 1 - # 0x0011 - string[37]: some type of UID, eg. "FFCBAC24-E547-4BBC-AF47-38B1A3D845E3\0" (iPhone 6s, iOS 6.1) + 0x0011 => { + Name => 'ContentIdentifier', #forum8750 + Writable => 'string', + }, # 0x0014 - int32s: 1,2,3,4,5 (iPhone 6s, iOS 6.1) 0x0015 => { Name => 'ImageUniqueID', Writable => 'string', }, + # 0x0016 - string[29]: "AXZ6pMTOh2L+acSh4Kg630XCScoO\0" + # 0x0017 - int32s: 0,8192 + # 0x0019 - int32s: 0,2,128 + # 0x001a - string[6]: "q825s\0" + # 0x001f - int32s: 0 ); # PLIST-format CMTime structure (ref PH) @@ -140,7 +148,7 @@ =head1 AUTHOR -Copyright 2003-2017, Phil Harvey (phil at owl.phy.queensu.ca) +Copyright 2003-2019, Phil Harvey (phil at owl.phy.queensu.ca) This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself. diff -Nru xnview-0.92/opt64/XnView/AddOn/lib/Image/ExifTool/ASF.pm xnview-0.93/opt64/XnView/AddOn/lib/Image/ExifTool/ASF.pm --- xnview-0.92/opt64/XnView/AddOn/lib/Image/ExifTool/ASF.pm 2017-09-30 10:50:23.000000000 +0000 +++ xnview-0.93/opt64/XnView/AddOn/lib/Image/ExifTool/ASF.pm 2019-01-10 14:15:16.000000000 +0000 @@ -17,7 +17,7 @@ use Image::ExifTool::Exif; use Image::ExifTool::RIFF; -$VERSION = '1.23'; +$VERSION = '1.25'; sub ProcessASF($$;$); sub ProcessContentDescription($$$); @@ -154,6 +154,9 @@ 3 => 'CopyrightURL', ); +# Note: Many of these tags are similar to those in Image::ExifTool::Microsoft::Xtra +# and Image::ExifTool::WTV::Metadata +# (tags in this table may have a leading "WM/" removed) %Image::ExifTool::ASF::ExtendedDescr = ( PROCESS_PROC => \&ProcessExtendedContentDescription, GROUPS => { 2 => 'Video' }, @@ -238,7 +241,7 @@ DVDID => {}, EncodedBy => {}, EncodingSettings => {}, - EncodingTime => { Groups => { 2 => 'Time' } }, + EncodingTime => { Groups => { 2 => 'Time' }, PrintConv => '$self->ConvertDateTime($val)' }, Genre => {}, GenreID => {}, InitialKey => {}, @@ -260,7 +263,11 @@ MediaIsSubtitled => {}, MediaIsTape => {}, MediaNetworkAffiliation => {}, - MediaOriginalBroadcastDateTime => { Groups => { 2 => 'Time' } }, + MediaOriginalBroadcastDateTime => { + Groups => { 2 => 'Time' }, + ValueConv => '$val=~tr/-T/: /; $val', + PrintConv => '$self->ConvertDateTime($val)', + }, MediaOriginalChannel => {}, MediaStationCallSign => {}, MediaStationName => {}, @@ -270,7 +277,11 @@ OriginalArtist => {}, OriginalFilename => 'OriginalFileName', OriginalLyricist => {}, - OriginalReleaseTime => { Groups => { 2 => 'Time' } }, + OriginalReleaseTime => { + Groups => { 2 => 'Time' }, + ValueConv => '$val=~tr/-T/: /; $val', + PrintConv => '$self->ConvertDateTime($val)', + }, OriginalReleaseYear => { Groups => { 2 => 'Time' } }, ParentalRating => {}, ParentalRatingReason => {}, @@ -380,8 +391,10 @@ }, 32 => { Name => 'DataPackets', Format => 'int64u' }, 40 => { - Name => 'PlayDuration', + Name => 'Duration', Format => 'int64u', + Notes => 'called PlayDuration by the ASF spec', + Priority => 0, ValueConv => '$val / 1e7', PrintConv => 'ConvertDuration($val)', }, @@ -863,7 +876,7 @@ =head1 AUTHOR -Copyright 2003-2017, Phil Harvey (phil at owl.phy.queensu.ca) +Copyright 2003-2019, Phil Harvey (phil at owl.phy.queensu.ca) This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself. diff -Nru xnview-0.92/opt64/XnView/AddOn/lib/Image/ExifTool/Audible.pm xnview-0.93/opt64/XnView/AddOn/lib/Image/ExifTool/Audible.pm --- xnview-0.92/opt64/XnView/AddOn/lib/Image/ExifTool/Audible.pm 2017-09-30 10:50:26.000000000 +0000 +++ xnview-0.93/opt64/XnView/AddOn/lib/Image/ExifTool/Audible.pm 2019-01-10 14:15:16.000000000 +0000 @@ -291,7 +291,7 @@ =head1 AUTHOR -Copyright 2003-2017, Phil Harvey (phil at owl.phy.queensu.ca) +Copyright 2003-2019, Phil Harvey (phil at owl.phy.queensu.ca) This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself. diff -Nru xnview-0.92/opt64/XnView/AddOn/lib/Image/ExifTool/BigTIFF.pm xnview-0.93/opt64/XnView/AddOn/lib/Image/ExifTool/BigTIFF.pm --- xnview-0.92/opt64/XnView/AddOn/lib/Image/ExifTool/BigTIFF.pm 2017-09-30 10:50:21.000000000 +0000 +++ xnview-0.93/opt64/XnView/AddOn/lib/Image/ExifTool/BigTIFF.pm 2019-01-10 14:15:16.000000000 +0000 @@ -260,7 +260,7 @@ =head1 AUTHOR -Copyright 2003-2017, Phil Harvey (phil at owl.phy.queensu.ca) +Copyright 2003-2019, Phil Harvey (phil at owl.phy.queensu.ca) This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself. diff -Nru xnview-0.92/opt64/XnView/AddOn/lib/Image/ExifTool/BMP.pm xnview-0.93/opt64/XnView/AddOn/lib/Image/ExifTool/BMP.pm --- xnview-0.92/opt64/XnView/AddOn/lib/Image/ExifTool/BMP.pm 2017-09-30 10:50:30.000000000 +0000 +++ xnview-0.93/opt64/XnView/AddOn/lib/Image/ExifTool/BMP.pm 2019-01-10 14:15:16.000000000 +0000 @@ -335,7 +335,7 @@ =head1 AUTHOR -Copyright 2003-2017, Phil Harvey (phil at owl.phy.queensu.ca) +Copyright 2003-2019, Phil Harvey (phil at owl.phy.queensu.ca) This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself. diff -Nru xnview-0.92/opt64/XnView/AddOn/lib/Image/ExifTool/BPG.pm xnview-0.93/opt64/XnView/AddOn/lib/Image/ExifTool/BPG.pm --- xnview-0.92/opt64/XnView/AddOn/lib/Image/ExifTool/BPG.pm 2017-09-30 10:50:26.000000000 +0000 +++ xnview-0.93/opt64/XnView/AddOn/lib/Image/ExifTool/BPG.pm 2019-01-10 14:15:16.000000000 +0000 @@ -14,7 +14,7 @@ use vars qw($VERSION); use Image::ExifTool qw(:DataAccess :Utils); -$VERSION = '1.00'; +$VERSION = '1.01'; # BPG information %Image::ExifTool::BPG::Main = ( @@ -29,18 +29,20 @@ Format => 'int16u', Mask => 0xe000, PrintConv => { - 0x0000 => 'Grayscale', - 0x2000 => '4:2:0 (chroma at 0.5, 0.5)', - 0x4000 => '4:2:2 (chroma at 0.5, 0)', - 0x6000 => '4:4:4', - 0x8000 => '4:2:0 (chroma at 0, 0.5)', - 0xa000 => '4:2:2 (chroma at 0, 0)', + 0 => 'Grayscale', + 1 => '4:2:0 (chroma at 0.5, 0.5)', + 2 => '4:2:2 (chroma at 0.5, 0)', + 3 => '4:4:4', + 4 => '4:2:0 (chroma at 0, 0.5)', + 5 => '4:2:2 (chroma at 0, 0)', }, }, 4.1 => { Name => 'Alpha', Format => 'int16u', Mask => 0x1004, + BitShift => 0, + PrintHex => 1, PrintConv => { 0x0000 => 'No Alpha Plane', 0x1000 => 'Alpha Exists (color not premultiplied)', @@ -52,19 +54,19 @@ Name => 'BitDepth', Format => 'int16u', Mask => 0x0f00, - ValueConv => '($val >> 8) + 8', + ValueConv => '$val + 8', }, 4.3 => { Name => 'ColorSpace', Format => 'int16u', Mask => 0x00f0, PrintConv => { - 0x0000 => 'YCbCr (BT 601)', - 0x0010 => 'RGB', - 0x0020 => 'YCgCo', - 0x0030 => 'YCbCr (BT 709)', - 0x0040 => 'YCbCr (BT 2020)', - 0x0050 => 'BT 2020 Constant Luminance', + 0 => 'YCbCr (BT 601)', + 1 => 'RGB', + 2 => 'YCgCo', + 3 => 'YCbCr (BT 709)', + 4 => 'YCbCr (BT 2020)', + 5 => 'BT 2020 Constant Luminance', }, }, 4.4 => { @@ -229,7 +231,7 @@ =head1 AUTHOR -Copyright 2003-2017, Phil Harvey (phil at owl.phy.queensu.ca) +Copyright 2003-2019, Phil Harvey (phil at owl.phy.queensu.ca) This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself. diff -Nru xnview-0.92/opt64/XnView/AddOn/lib/Image/ExifTool/BuildTagLookup.pm xnview-0.93/opt64/XnView/AddOn/lib/Image/ExifTool/BuildTagLookup.pm --- xnview-0.92/opt64/XnView/AddOn/lib/Image/ExifTool/BuildTagLookup.pm 2017-09-30 10:50:21.000000000 +0000 +++ xnview-0.93/opt64/XnView/AddOn/lib/Image/ExifTool/BuildTagLookup.pm 2019-01-10 14:15:16.000000000 +0000 @@ -34,7 +34,7 @@ use Image::ExifTool::Validate; use Image::ExifTool::MacOS; -$VERSION = '3.10'; +$VERSION = '3.19'; @ISA = qw(Exporter); sub NumbersFirst($$); @@ -99,6 +99,7 @@ 'MWG::Regions' => 'MWG::Composite', 'MWG::Keywords' => 'MWG::Regions', 'MWG::Collections' => 'MWG::Keywords', + 'GoPro::fdsc' => 'GoPro::KBAT', ); # list of all recognized Format strings @@ -162,9 +163,11 @@ B<Tag ID>, B<Index#> or B<Sequence> is given in the first column of each table. A B<Tag ID> is the computer-readable equivalent of a tag name, and is the identifier that is actually stored in the file. B<Index#> refers to -the location of a value when found at a fixed position within a data block +the offset of a value when found at a fixed position within a data block (B<#> is the multiplier for calculating a byte offset: B<1>, B<2>, B<4> or -B<8>). B<Sequence> gives the order of values for a serial data stream. +B<8>). These offsets may have a decimal part which is used only to +differentiate tags with values stored at the same position. B<Sequence> +gives the order of values for a serial data stream. A B<Tag Name> is the handle by which the information is accessed in ExifTool. In some instances, more than one name may correspond to a single @@ -200,8 +203,10 @@ taken when editing them manually since they may affect the way an image is rendered. An asterisk (C<*>) indicates a I<Protected> tag which is not writable directly, but is written automatically by ExifTool (often when a -corresponding Composite or Extra tag is written). A colon (C<:>) indicates a -I<Mandatory> tag which may be added automatically when writing. +corresponding L<Composite|Image::ExifTool::TagNames/Composite Tags> or +L<Extra|Image::ExifTool::TagNames/Extra Tags> tag is written). A colon +(C<:>) indicates a I<Mandatory> tag which may be added automatically when +writing. The HTML version of these tables also lists possible B<Values> for discrete-valued tags, as well as B<Notes> for some tags. The B<Values> are @@ -322,12 +327,13 @@ When reading, "x-default" is not specified. The XMP tags are organized according to schema B<Namespace> in the following -tables. In general, the ExifTool family 1 group names are derived from the -namespace prefixes by adding a leading "XMP-" (eg. "XMP-dc"), but a few of -the longer prefixes have been shortened for convenience (as mentioned in the -documentation below). The tags of any namespace may be deleted as a group -by specifying the family 1 group name (eg. "-XMP-dc:all=" on the command -line). This includes namespaces which are not pre-defined by ExifTool. +tables. The ExifTool family 1 group names are derived from the namespace +prefixes by adding a leading "XMP-" (eg. "XMP-dc"). A few of the longer +prefixes have been shortened (as mentioned in the documentation below) to +avoid excessively long ExifTool group names. The tags of any namespace may +be deleted as a group by specifying the family 1 group name (eg. +"-XMP-dc:all=" on the command line). This includes namespaces which are not +pre-defined by ExifTool. In cases where a tag name exists in more than one namespace, less common namespaces are avoided when writing. However, a specific namespace may be @@ -372,12 +378,12 @@ binary (C<undef>) values. These lengths are given in square brackets after the B<Writable> format name. For tags where a range of lengths is allowed, the minimum and maximum lengths are separated by a comma within the -brackets. IPTC strings are not null terminated. When writing, ExifTool -issues a minor warning and truncates the value if it is longer than allowed -by the IPTC specification. Minor errors may be ignored with the -IgnoreMinorErrors (-m) option, allowing longer values to be written, but -beware that values like this may cause problems for some other IPTC readers. -ExifTool will happily read IPTC values of any length. +brackets. When writing, ExifTool issues a minor warning and truncates the +value if it is longer than allowed by the IPTC specification. Minor errors +may be ignored with the IgnoreMinorErrors (-m) option, allowing longer +values to be written, but beware that values like this may cause problems +for some other IPTC readers. ExifTool will happily read IPTC values of any +length. Separate IPTC date and time tags may be written with a combined date/time value and ExifTool automagically takes the appropriate part of the date/time @@ -497,9 +503,10 @@ ExifTool may be used to write native PDF and XMP metadata to PDF files. It uses an incremental update technique that has the advantages of being both -fast and reversible. The original PDF can be easily recovered by deleting -the C<PDF-update> pseudo-group (with C<-PDF-update:all=> on the command -line). However, there are two main disadvantages to this technique: +fast and reversible. If ExifTool was used to modify a PDF file, the +original may be recovered by deleting the C<PDF-update> pseudo-group (with +C<-PDF-update:all=> on the command line). However, there are two main +disadvantages to this technique: 1) A linearized PDF file is no longer linearized after the update, so it must be subsequently re-linearized if this is required. @@ -526,10 +533,10 @@ and RPM). }, Extra => q{ -The extra tags represent extra information extracted or generated by -ExifTool that is not directly associated with another tag group. The -B<Group> column lists the family 1 group name when reading. Tags with a "-" -in this column are write-only. +The extra tags provide extra features or extra information extracted or +generated by ExifTool that is not directly associated with another tag +group. The B<Group> column lists the family 1 group name when reading. +Tags with a "-" in this column are write-only. Tags in the family 1 "System" group are referred to as "pseudo" tags because they don't represent real metadata in the file. Instead, this information @@ -585,7 +592,7 @@ ~head1 AUTHOR -Copyright 2003-2017, Phil Harvey (phil at owl.phy.queensu.ca) +Copyright 2003-2019, Phil Harvey (phil at owl.phy.queensu.ca) This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself. @@ -817,7 +824,7 @@ # single-character subdirectory names are allowed (not $$tagInfo{SubDirectory} or $name !~ /^[_A-Za-z]$/)) { - warn "Warning: Invalid tag name $short '$name'\n"; + warn "Warning: Invalid tag name $short '${name}'\n"; } # validate list type if ($$tagInfo{List} and $$tagInfo{List} !~ /^(1|Alt|Bag|Seq|array|string)$/) { @@ -878,8 +885,14 @@ $writable = $$table{WRITABLE}; } # validate some characteristics of obvious date/time tags + my @g = $et->GetGroup($tagInfo); + if ($$tagInfo{List} and $g[2] eq 'Time' and $writable and not $$tagInfo{Protected} and + not $$tagInfo{PrintConvInv}) + { + # (this is a problem because shifting Time:All would create a new list entry) + warn "Writable List-type Time tag $g[1]:$name has no PrintConvInv and is not Protected!\n"; + } if ($$tagInfo{PrintConv} and $$tagInfo{PrintConv} eq '$self->ConvertDateTime($val)') { - my @g = $et->GetGroup($tagInfo); warn "$short $name should be in 'Time' group!\n" unless $g[2] eq 'Time'; if ($writable and not defined $$tagInfo{Shift} and $short ne 'PostScript') { warn "$short $name is not shiftable!\n"; @@ -941,17 +954,18 @@ # remove leading/trailing spaces on each line $note =~ s/(^[ \t]+|[ \t]+$)//mg; push @values, "($note)"; - } elsif ($isXMP and lc $tagID ne lc $name) { + } + if ($isXMP and lc $tagID ne lc $name) { # add note about different XMP Tag ID - if ($$tagInfo{RootTagInfo}) { - push @values, "($tagID)"; + my $note = $$tagInfo{RootTagInfo} ? $tagID : "called $tagID by the spec"; + if ($$tagInfo{Notes}) { + $values[-1] =~ s/^\(/($note; /; } else { - push @values,"(called $tagID by the spec)"; + push @values, "($note)"; } } my $writeGroup; if ($short eq 'Extra') { - my @g = $et->GetGroup($tagInfo); $writeGroup = $$tagInfo{WriteOnly} ? '-' : $g[1]; } else { $writeGroup = $$tagInfo{WriteGroup}; @@ -991,15 +1005,15 @@ my $printConv = $$tagInfo{PrintConv}; if ($$tagInfo{Mask}) { my $val = $$tagInfo{Mask}; - push @values, sprintf('[Mask 0x%.2x]',$val); - $$tagInfo{PrintHex} = 1 unless defined $$tagInfo{PrintHex}; + my $bsh = $$tagInfo{BitShift}; + if ($bsh) { + push @values, sprintf('[val >> %d & 0x%x]',$bsh,$val>>$bsh); + } else { + push @values, sprintf('[val & 0x%x]',$val); + } # verify that all values are within the mask if (ref $printConv eq 'HASH') { - # convert mask if necessary - if ($$tagInfo{ValueConv}) { - my $v = eval $$tagInfo{ValueConv}; - $val = $v if defined $v; - } + $val >>= $$tagInfo{BitShift}; foreach (keys %$printConv) { next if $_ !~ /^\d+$/ or ($_ & $val) == $_; my $hex = sprintf '0x%.2x', $_; @@ -1079,7 +1093,12 @@ $$printConv{PrintString} = 1 if $$tagInfo{PrintString}; } else { $caseInsensitive = 0; - my @pk = sort { NumbersFirst($a,$b) } keys %$printConv; + my @pk; + if ($$tagInfo{PrintSort}) { + @pk = sort { NumbersFirst($$printConv{$a},$$printConv{$b}) } keys %$printConv; + } else { + @pk = sort { NumbersFirst($a,$b) } keys %$printConv; + } my $n = scalar @values; my ($bits, $i, $v); foreach (@pk) { @@ -1088,12 +1107,13 @@ $_ eq 'OTHER' and next; my $index; if (($$tagInfo{PrintHex} or $$printConv{BITMASK}) and /^-?\d+$/) { + my $dig = $$tagInfo{PrintHex} || 1; if ($_ >= 0) { - $index = sprintf('0x%x', $_); + $index = sprintf('0x%.*x', $dig, $_); } elsif ($format and $format =~ /int(16|32)/) { # mask off unused bits of signed integer hex value my $mask = { 16 => 0xffff, 32 => 0xffffffff }->{$1}; - $index = sprintf('0x%x', $_ & $mask); + $index = sprintf('0x%.*x', $dig, $_ & $mask); } else { $index = $_; } @@ -1105,7 +1125,7 @@ if ($index =~ s/([\x00-\x1f\x80-\xff])/sprintf("\\x%.2x",ord $1)/eg) { $index = qq{"$index"}; } else { - $index = qq{'$index'}; + $index = qq{'${index}'}; } } push @values, "$index = " . $$printConv{$_}; @@ -1328,7 +1348,7 @@ next if $tagID eq 'jP\x1a\x1a'; # ignore abnormal JP2 signature tag $tagIDstr = qq{"$tagID"}; } else { - $tagIDstr = "'$tagID'"; + $tagIDstr = "'${tagID}'"; } } my $len = length $tagIDstr; @@ -1385,6 +1405,15 @@ } } $writable .= '+' if $$tagInfo{List}; + push @vals, "($$tagInfo{Notes})" if $$tagInfo{Notes}; + # handle PrintConv lookups in Structure elements + my $printConv = $$tagInfo{PrintConv}; + if (ref $printConv eq 'HASH') { + foreach (sort keys %$printConv) { + next if /^(OTHER|BITMASK)$/; + push @vals, "$_ = $$printConv{$_}"; + } + } push @$info, [ $tag, [ $$tagInfo{Name} || ucfirst($tag) ], @@ -1441,7 +1470,7 @@ my $num = 0; foreach $tableName (@tableNames) { if ($$tableWritable{$tableName}) { - print OUTFILE "\t'$tableName',\n"; + print OUTFILE "\t'${tableName}',\n"; $wrNum{$count} = $num++; } $count++; @@ -1454,11 +1483,11 @@ foreach $tag (qw{filename directory}) { next unless $$tagLookup{$tag}; my $n = scalar keys %{$$tagLookup{$tag}}; - warn "Warning: $n writable '$tag' tags!\n" if $n > 1; + warn "Warning: $n writable '${tag}' tags!\n" if $n > 1; } print OUTFILE ");\n\n# lookup for all writable tags\nmy \%tagLookup = (\n"; foreach $tag (sort keys %$tagLookup) { - print OUTFILE "\t'$tag' => { "; + print OUTFILE "\t'${tag}' => { "; my @tableNums = sort { $a <=> $b } keys %{$$tagLookup{$tag}}; my (@entries, $tableNum); foreach $tableNum (@tableNums) { @@ -1480,12 +1509,12 @@ } # reference to root structure ID must come first in lookup # (so we can generate the flattened tags just before we need them) - unshift @tagIDs, "\\'$rootID'" if $rootID; + unshift @tagIDs, "\\'${rootID}'" if $rootID; $entry = '[' . join(',', @tagIDs) . ']'; } elsif ($tagID =~ /^\d+$/) { $entry = sprintf('0x%x',$tagID); } else { - $entry = "'$tagID'"; + $entry = "'${tagID}'"; } my $wrNum = $wrNum{$tableNum}; push @entries, "$wrNum => $entry"; @@ -1500,7 +1529,7 @@ print OUTFILE "my \%tagExists = (\n"; foreach $tag (sort keys %$tagExists) { next if $$tagLookup{$tag}; - print OUTFILE "\t'$tag' => 1,\n"; + print OUTFILE "\t'${tag}' => 1,\n"; } # # write module lookup for writable composite tags @@ -1509,7 +1538,7 @@ print OUTFILE ");\n\n# module names for writable Composite tags\n"; print OUTFILE "my \%compositeModules = (\n"; foreach (sort keys %$compositeModules) { - print OUTFILE "\t'$_' => '$$compositeModules{$_}',\n"; + print OUTFILE "\t'${_}' => '$$compositeModules{$_}',\n"; } print OUTFILE ");\n\n"; # @@ -1833,7 +1862,7 @@ $top = " class=top"; } } - $head = "<a name='$url'>$head</a>" if $url; + $head = "<a name='${url}'>$head</a>" if $url; print HTMLFILE "<h2$top>$head</h2>\n" or return 0; print HTMLFILE '<p>',Doc2Html($docs{$category}),"</p>\n" if $docs{$category}; $createdFiles{$htmlFile} = 1; @@ -1916,6 +1945,17 @@ } #------------------------------------------------------------------------------ +# Get bitmask for POD documentation +# Inputs: mask string from HTML docs +# Returns: mask string for POD, or '' +sub PodMask($) +{ + my $mask = shift; + return '' unless $mask =~ /^\[val( >> (\d+))? \& (0x[\da-f]+)\]/; + return sprintf(' & 0x%.2x', hex($3) << ($2 || 0)); +} + +#------------------------------------------------------------------------------ # Write the TagName HTML and POD documentation # Inputs: 0) BuildTagLookup object reference # 1) output pod file (eg. 'lib/Image/ExifTool/TagNames.pod') @@ -1985,7 +2025,7 @@ $short = $$shortName{$tableName}; $short = $tableName unless $short; $url = "$short.html"; - print HTMLFILE "<a href='$url'>$short</a>"; + print HTMLFILE "<a href='${url}'>$short</a>"; ++$count; } print HTMLFILE "\n</td></tr></table></td></tr></table></blockquote>\n"; @@ -2113,6 +2153,7 @@ my $ns = $Image::ExifTool::XMP::stdXlatNS{$$table{NAMESPACE}} || $$table{NAMESPACE}; my $msg = "These tags belong to the ExifTool XMP-$ns family 1 group."; if ($notes) { + $notes =~ s/\s+$//; $notes .= "\n\n" . $msg; } else { $notes = $msg; @@ -2216,6 +2257,11 @@ } elsif ($tagIDstr =~ /^-?\d+(\.\d+)?$/) { $w = $wID - 3; $idStr = sprintf " %${w}g ", $tagIDstr; + my $tooLong = length($idStr) - 6 - $w; + if ($tooLong) { + $tooLong = 3 if $tooLong > 3; + $idStr = substr($idStr, 0, -$tooLong); + } $align = " class=r"; } else { $tagIDstr =~ s/^'$prefix/'/ if $prefix; @@ -2245,16 +2291,16 @@ my @vals = @$writable; my $wrStr = shift @vals; my $subdir; - my @masks = grep /^\[Mask 0x[\da-f]+\]/, @$values; + my @masks = grep /^\[val( >> \d+)? \& 0x[\da-f]+\]/, @$values; my $tag = shift @tags; # if this is a subdirectory or structure, print subdir name (from values) instead of writable if ($wrStr =~ /^[-=]/) { $subdir = 1; if (@masks) { # combine any mask into the format string - $wrStr .= " & $1" if $masks[0] =~ /(0x[\da-f]+)/; + $wrStr .= PodMask($masks[0]); shift @masks; - @vals = grep !/^\[Mask 0x[\da-f]+\]/, @$values; + @vals = grep !/^\[val( >> \d+)? \& 0x[\da-f]+\]/, @$values; } else { @vals = @$values; } @@ -2265,7 +2311,7 @@ for ($i=0; $i<@$writable; ++$i) { $vals[$i] = $$writable[$i] unless defined $vals[$i]; if (@masks) { - $vals[$i] .= " & $1" if $masks[0] =~ /(0x[\da-f]+)/; + $vals[$i] .= PodMask($masks[0]); shift @masks; } } @@ -2288,9 +2334,9 @@ push @tags, $tag if @tags < @vals; } # add Mask to Writable column in POD doc - $wrStr .= " & $1" if $mask =~ /(0x[\da-f]+)/; + $wrStr .= PodMask($mask); } - printf PODFILE "%s%-${wTag2}s", $idStr, $tag; + my $pod = sprintf "%s%-${wTag2}s", $idStr, $tag; my $tGrp = $wGrp; if ($id and length($tag) > $wTag2) { my $madeRoom; @@ -2307,14 +2353,20 @@ } warn "Warning: Pushed $tag\n" unless $madeRoom; } - printf PODFILE " %-${tGrp}s", shift(@wGrp) || '-' if $showGrp; + $pod .= sprintf " %-${tGrp}s", shift(@wGrp) || '-' if $showGrp; if ($composite) { @reqs = @$require; $w = $wReq; # Keep writable column in line length($tag) > $wTag2 and $w -= length($tag) - $wTag2; - printf PODFILE " %-${w}s", shift(@reqs) || ''; + $pod .= sprintf " %-${w}s", shift(@reqs) || ''; + } + $pod .= " $wrStr"; + # limit line length to 82 characters (even if it means messing up column alignment) + if (length $pod > 82 and $pod !~ /\n/) { + my $remove = length($pod) - 82; + $pod =~ s/(\w)\s{$remove}/$1/; } - print PODFILE " $wrStr\n"; + print PODFILE $pod, "\n"; my $numTags = scalar @$tagNames; my $n = 0; while (@tags or @reqs or @vals) { @@ -2341,7 +2393,7 @@ if (defined $val) { $line .= " $val"; if (@masks) { - $line .= " & $1" if $masks[0] =~ /(0x[\da-f]+)/; + $line .= PodMask($masks[0]); shift @masks; } } @@ -2454,7 +2506,7 @@ } $url = (shift @names) . '.html'; @names and $url .= '#' . join '_', @names; - push @values, "--> <a href='$url'>$_$suffix</a>"; + push @values, "--> <a href='${url}'>$_$suffix</a>"; } # put small note last $smallNote and push @values, shift @values; @@ -2565,7 +2617,7 @@ =head1 AUTHOR -Copyright 2003-2017, Phil Harvey (phil at owl.phy.queensu.ca) +Copyright 2003-2019, Phil Harvey (phil at owl.phy.queensu.ca) This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself. diff -Nru xnview-0.92/opt64/XnView/AddOn/lib/Image/ExifTool/BZZ.pm xnview-0.93/opt64/XnView/AddOn/lib/Image/ExifTool/BZZ.pm --- xnview-0.92/opt64/XnView/AddOn/lib/Image/ExifTool/BZZ.pm 2017-09-30 10:50:26.000000000 +0000 +++ xnview-0.93/opt64/XnView/AddOn/lib/Image/ExifTool/BZZ.pm 2019-01-10 14:15:16.000000000 +0000 @@ -445,7 +445,7 @@ =head1 AUTHOR -Copyright 2003-2017, Phil Harvey (phil at owl.phy.queensu.ca) +Copyright 2003-2019, Phil Harvey (phil at owl.phy.queensu.ca) Copyright 2002, Leon Bottou and Yann Le Cun Copyright 2001, AT&T Copyright 1999-2001, LizardTech Inc. diff -Nru xnview-0.92/opt64/XnView/AddOn/lib/Image/ExifTool/CanonCustom.pm xnview-0.93/opt64/XnView/AddOn/lib/Image/ExifTool/CanonCustom.pm --- xnview-0.92/opt64/XnView/AddOn/lib/Image/ExifTool/CanonCustom.pm 2017-09-30 10:50:30.000000000 +0000 +++ xnview-0.93/opt64/XnView/AddOn/lib/Image/ExifTool/CanonCustom.pm 2019-01-10 14:15:16.000000000 +0000 @@ -19,7 +19,7 @@ use Image::ExifTool::Canon; use Image::ExifTool::Exif; -$VERSION = '1.54'; +$VERSION = '1.55'; sub ProcessCanonCustom($$$); sub ProcessCanonCustom2($$$); @@ -1360,8 +1360,9 @@ 4 => 'Center-weighted average', }, }, - 0x010c => { + 0x010c => [{ Name => 'ShutterSpeedRange', + Condition => '$count == 3', Count => 3, ValueConv => [ undef, @@ -1383,9 +1384,38 @@ '$val=~m{([\d./]+)} ? eval $1 : 0', '$val=~m{([\d./]+)} ? eval $1 : 0', ], - }, - 0x010d => { + },{ # (EOS R) + Name => 'ShutterSpeedRange', + Condition => '$count == 4', + Count => 4, + ValueConv => [ # (NC) + 'exp(-$val/(1600*log(2)))', + 'exp(-$val/(1600*log(2)))', + 'exp(-$val/(1600*log(2)))', + 'exp(-$val/(1600*log(2)))', + ], + ValueConvInv => [ + 'int(-log($val)*1600*log(2) + 0.5)', + 'int(-log($val)*1600*log(2) + 0.5)', + 'int(-log($val)*1600*log(2) + 0.5)', + 'int(-log($val)*1600*log(2) + 0.5)', + ], + PrintConv => [ # (NC) + '"Manual: Hi " . Image::ExifTool::Exif::PrintExposureTime($val)', + '"Lo " . Image::ExifTool::Exif::PrintExposureTime($val)', + '"Auto: Hi " . Image::ExifTool::Exif::PrintExposureTime($val)', + '"Lo " . Image::ExifTool::Exif::PrintExposureTime($val)', + ], + PrintConvInv => [ + '$val=~m{([\d./]+)} ? eval $1 : 0', + '$val=~m{([\d./]+)} ? eval $1 : 0', + '$val=~m{([\d./]+)} ? eval $1 : 0', + '$val=~m{([\d./]+)} ? eval $1 : 0', + ], + }], + 0x010d => [{ Name => 'ApertureRange', + Condition => '$count == 3', Count => 3, ValueConv => [ undef, @@ -1407,7 +1437,35 @@ '$val=~/([\d.]+)/ ? $1 : 0', '$val=~/([\d.]+)/ ? $1 : 0', ], - }, + },{ # (EOS R) + Name => 'ApertureRange', + Condition => '$count == 4', + Count => 4, + ValueConv => [ # (NC) + 'exp($val/2400)', + 'exp($val/2400)', + 'exp($val/2400)', + 'exp($val/2400)', + ], + ValueConvInv => [ + 'int(log($val)*2400) + 0.5)', + 'int(log($val)*2400) + 0.5)', + 'int(log($val)*2400) + 0.5)', + 'int(log($val)*2400) + 0.5)', + ], + PrintConv => [ # (NC) + 'sprintf("Manual: Closed %.2g",$val)', + 'sprintf("Open %.2g",$val)', + 'sprintf("Auto: Closed %.2g",$val)', + 'sprintf("Open %.2g",$val)', + ], + PrintConvInv => [ + '$val=~/([\d.]+)/ ? $1 : 0', + '$val=~/([\d.]+)/ ? $1 : 0', + '$val=~/([\d.]+)/ ? $1 : 0', + '$val=~/([\d.]+)/ ? $1 : 0', + ], + }], 0x010e => { Name => 'ApplyShootingMeteringMode', Count => 8, @@ -1481,13 +1539,36 @@ Count => 3, PrintConv => [ \%disableEnable ], }, - 0x0112 => { # (5DS) + 0x0112 => [{ # (5DS) Name => 'SameExposureForNewAperture', PrintConv => { 0 => 'Disable', 1 => 'ISO Speed', 2 => 'Shutter Speed', }, + },{ # (EOS R) + Name => 'SameExposureForNewAperture', + Notes => 'EOS R', + PrintConv => { + 0 => 'Disable', + 1 => 'ISO Speed', + 2 => 'ISO Speed/Shutter Speed', + 3 => 'Shutter Speed', + }, + }], + 0x0113 => { # (200D) + Name => 'ExposureCompAutoCancel', + PrintConv => \%enableDisable, + }, + 0x0114 => { # (R) + Name => 'AELockMeterModeAfterFocus', + # metering modes where AE lock after focus applies: + PrintConv => { BITMASK => { # (NC) + 0 => 'Evaluative', + 1 => 'Partial', + 2 => 'Spot', + 3 => 'Center-weighted', + }}, }, #### 2a) Image 0x0201 => { @@ -2258,17 +2339,42 @@ }}, ], }, - 0x710 => { # (M) + 0x0710 => { # (M) Name => 'TrashButtonFunction', PrintConv => { 0 => 'Normal (set center AF point)', 1 => 'Depth-of-field preview', }, }, - 0x711 => { # (M) + 0x0711 => { # (M) Name => 'ShutterReleaseWithoutLens', PrintConv => \%disableEnable, }, + 0x0712 => { # (R) + Name => 'ControlRingRotation', + PrintConv => { + 0 => 'Normal', + 1 => 'Reversed', + }, + }, + 0x0713 => { # (R) + Name => 'FocusRingRotation', + PrintConv => { + 0 => 'Normal', + 1 => 'Reversed', + }, + }, + 0x0714 => { # (R) + Name => 'RFLensMFFocusRingSensitivity', + PrintConv => { + 0 => 'Varies With Rotation Speed', + 1 => 'Linked To Rotation Angle', + }, + }, + 0x0715 => { # (R) + Name => 'CustomizeDials', # (NC, may be CustomizeM-FnBar) + # (too much stuff to decode) + }, #### 4b) Others 0x080b => [ { @@ -2398,6 +2504,10 @@ Name => 'RetractLensOnPowerOff', PrintConv => \%enableDisable, }, + 0x0815 => { # (R) + Name => 'AddIPTCInformation', + PrintConv => \%disableEnable, + }, ); #------------------------------------------------------------------------------ @@ -2643,7 +2753,7 @@ =head1 AUTHOR -Copyright 2003-2017, Phil Harvey (phil at owl.phy.queensu.ca) +Copyright 2003-2019, Phil Harvey (phil at owl.phy.queensu.ca) This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself. diff -Nru xnview-0.92/opt64/XnView/AddOn/lib/Image/ExifTool/Canon.pm xnview-0.93/opt64/XnView/AddOn/lib/Image/ExifTool/Canon.pm --- xnview-0.92/opt64/XnView/AddOn/lib/Image/ExifTool/Canon.pm 2017-09-30 10:50:29.000000000 +0000 +++ xnview-0.93/opt64/XnView/AddOn/lib/Image/ExifTool/Canon.pm 2019-01-10 14:15:16.000000000 +0000 @@ -83,9 +83,11 @@ sub WriteCanon($$$); sub ProcessSerialData($$$); sub ProcessFilters($$$); +sub ProcessCTMD($$$); +sub ProcessExifInfo($$$); sub SwapWords($); -$VERSION = '3.79'; +$VERSION = '4.04'; # Note: Removed 'USM' from 'L' lenses since it is redundant - PH # (or is it? Ref 32 shows 5 non-USM L-type lenses) @@ -110,6 +112,7 @@ 6.2 => 'Sigma 18-125mm f/3.5-5.6 DC IF ASP', 6.3 => 'Tokina AF 193-2 19-35mm f/3.5-4.5', 6.4 => 'Sigma 28-80mm f/3.5-5.6 II Macro', #47 + 6.5 => 'Sigma 28-300mm f/3.5-6.3 DG Macro', #IB 7 => 'Canon EF 100-300mm f/5.6L', #15 8 => 'Canon EF 100-300mm f/5.6 or Sigma or Tokina Lens', #32 8.1 => 'Sigma 70-300mm f/4-5.6 [APO] DG Macro', #15 (both APO and non-APO, ref forum2947) @@ -199,15 +202,21 @@ 53 => 'Canon EF-S 18-55mm f/3.5-5.6 III', #Jon Charnas 54 => 'Canon EF-S 55-250mm f/4-5.6 IS II', #47 60 => 'Irix 11mm f/4', #50 + 80 => 'Canon TS-E 50mm f/2.8L Macro', #42 + 81 => 'Canon TS-E 90mm f/2.8L Macro', #42 + 82 => 'Canon TS-E 135mm f/4L Macro', #42 94 => 'Canon TS-E 17mm f/4L', #42 - 95 => 'Canon TS-E 24.0mm f/3.5 L II', #43 + 95 => 'Canon TS-E 24mm f/3.5L II', #43 + 103 => 'Samyang AF 14mm f/2.8 EF or Rokinon Lens', #IB + 103.1 => 'Rokinon SP 14mm f/2.4', #IB + 103.2 => 'Rokinon AF 14mm f/2.8 EF', #IB 124 => 'Canon MP-E 65mm f/2.8 1-5x Macro Photo', #9 125 => 'Canon TS-E 24mm f/3.5L', 126 => 'Canon TS-E 45mm f/2.8', #15 127 => 'Canon TS-E 90mm f/2.8', #15 - 129 => 'Canon EF 300mm f/2.8L', #32 - 130 => 'Canon EF 50mm f/1.0L', #10/15 - 131 => 'Canon EF 28-80mm f/2.8-4L or Sigma Lens', #32 + 129 => 'Canon EF 300mm f/2.8L USM', #32 + 130 => 'Canon EF 50mm f/1.0L USM', #10/15 + 131 => 'Canon EF 28-80mm f/2.8-4L USM or Sigma Lens', #32 131.1 => 'Sigma 8mm f/3.5 EX DG Circular Fisheye', #15 131.2 => 'Sigma 17-35mm f/2.8-4 EX DG Aspherical HSM', #15 131.3 => 'Sigma 17-70mm f/2.8-4.5 DC Macro', #PH (NC) @@ -217,11 +226,11 @@ # 'Sigma APO 120-300mm f/2.8 EX DG HSM + 2x', #15 131.6 => 'Sigma 4.5mm f/2.8 EX DC HSM Circular Fisheye', #PH 131.7 => 'Sigma 70-200mm f/2.8 APO EX HSM', #PH (http://www.lensrentals.com/blog/2012/08/canon-illumination-correction-and-third-party-lenses) - 132 => 'Canon EF 1200mm f/5.6L', #32 - 134 => 'Canon EF 600mm f/4L IS', #15 - 135 => 'Canon EF 200mm f/1.8L', - 136 => 'Canon EF 300mm f/2.8L', - 137 => 'Canon EF 85mm f/1.2L or Sigma or Tamron Lens', #10 + 132 => 'Canon EF 1200mm f/5.6L USM', #32 + 134 => 'Canon EF 600mm f/4L IS USM', #15 + 135 => 'Canon EF 200mm f/1.8L USM', + 136 => 'Canon EF 300mm f/2.8L USM', + 137 => 'Canon EF 85mm f/1.2L USM or Sigma or Tamron Lens', #10 137.1 => 'Sigma 18-50mm f/2.8-4.5 DC OS HSM', #PH 137.2 => 'Sigma 50-200mm f/4-5.6 DC OS HSM', #PH 137.3 => 'Sigma 18-250mm f/3.5-6.3 DC OS HSM', #PH (also Sigma 18-250mm f/3.5-6.3 DC Macro OS HSM) @@ -239,11 +248,11 @@ '137.15' => 'Sigma 18-35mm f/1.8 DC HSM', #David Monro '137.16' => 'Sigma 12-24mm f/4.5-5.6 DG HSM II', #IB 138 => 'Canon EF 28-80mm f/2.8-4L', #32 - 139 => 'Canon EF 400mm f/2.8L', - 140 => 'Canon EF 500mm f/4.5L', #32 - 141 => 'Canon EF 500mm f/4.5L', - 142 => 'Canon EF 300mm f/2.8L IS', #15 - 143 => 'Canon EF 500mm f/4L IS or Sigma Lens', #15 + 139 => 'Canon EF 400mm f/2.8L USM', + 140 => 'Canon EF 500mm f/4.5L USM', #32 + 141 => 'Canon EF 500mm f/4.5L USM', + 142 => 'Canon EF 300mm f/2.8L IS USM', #15 + 143 => 'Canon EF 500mm f/4L IS USM or Sigma Lens', #15 143.1 => 'Sigma 17-70mm f/2.8-4 DC Macro OS HSM', #NJ (Exiv2 #1167) 144 => 'Canon EF 35-135mm f/4-5.6 USM', #26 145 => 'Canon EF 100-300mm f/4.5-5.6 USM', #32 @@ -251,18 +260,18 @@ 147 => 'Canon EF 35-135mm f/4-5.6 USM', #32 148 => 'Canon EF 28-80mm f/3.5-5.6 USM', #32 149 => 'Canon EF 100mm f/2 USM', #9 - 150 => 'Canon EF 14mm f/2.8L or Sigma Lens', #10 + 150 => 'Canon EF 14mm f/2.8L USM or Sigma Lens', #10 150.1 => 'Sigma 20mm EX f/1.8', #4 150.2 => 'Sigma 30mm f/1.4 DC HSM', #15 150.3 => 'Sigma 24mm f/1.8 DG Macro EX', #15 150.4 => 'Sigma 28mm f/1.8 DG Macro EX', #IB - 151 => 'Canon EF 200mm f/2.8L', - 152 => 'Canon EF 300mm f/4L IS or Sigma Lens', #15 + 151 => 'Canon EF 200mm f/2.8L USM', + 152 => 'Canon EF 300mm f/4L IS USM or Sigma Lens', #15 152.1 => 'Sigma 12-24mm f/4.5-5.6 EX DG ASPHERICAL HSM', #15 152.2 => 'Sigma 14mm f/2.8 EX Aspherical HSM', #15 152.3 => 'Sigma 10-20mm f/4-5.6', #14 152.4 => 'Sigma 100-300mm f/4', # (ref Bozi) - 153 => 'Canon EF 35-350mm f/3.5-5.6L or Sigma or Tamron Lens', #PH + 153 => 'Canon EF 35-350mm f/3.5-5.6L USM or Sigma or Tamron Lens', #PH 153.1 => 'Sigma 50-500mm f/4-6.3 APO HSM EX', #15 153.2 => 'Tamron AF 28-300mm f/3.5-6.3 XR LD Aspherical [IF] Macro', 153.3 => 'Tamron AF 18-200mm f/3.5-6.3 XR Di II LD Aspherical [IF] Macro Model A14', #15 @@ -271,7 +280,7 @@ 154.1 => 'Zeiss Milvus 21mm f/2.8', #IB 155 => 'Canon EF 85mm f/1.8 USM', 156 => 'Canon EF 28-105mm f/3.5-4.5 USM or Tamron Lens', - 156.1 => 'Tamron SP 70-300mm f/4.0-5.6 Di VC USD', #PH (model A005) + 156.1 => 'Tamron SP 70-300mm f/4-5.6 Di VC USD', #PH (model A005) 156.2 => 'Tamron SP AF 28-105mm f/2.8 LD Aspherical IF', #JR (Model 176D) 160 => 'Canon EF 20-35mm f/3.5-4.5 USM or Tamron or Tokina Lens', 160.1 => 'Tamron AF 19-35mm f/3.5-4.5', #44 @@ -279,7 +288,7 @@ 160.3 => 'Tokina AT-X 107 AF DX 10-17mm f/3.5-4.5 Fisheye', #PH (http://osdir.com/ml/digikam-devel/2011-04/msg00275.html) 160.4 => 'Tokina AT-X 116 AF Pro DX 11-16mm f/2.8', #forum3967 160.5 => 'Tokina AT-X 11-20 F2.8 PRO DX Aspherical 11-20mm f/2.8', #NJ (Exiv2 #1166) - 161 => 'Canon EF 28-70mm f/2.8L or Sigma or Tamron Lens', + 161 => 'Canon EF 28-70mm f/2.8L USM or Other Lens', 161.1 => 'Sigma 24-70mm f/2.8 EX', 161.2 => 'Sigma 28-70mm f/2.8 EX', #PH (http://www.breezesys.com/forum/showthread.php?t=3718) 161.3 => 'Sigma 24-60mm f/2.8 EX DG', #PH (http://www.lensrentals.com/blog/2012/08/canon-illumination-correction-and-third-party-lenses) @@ -287,15 +296,16 @@ 161.5 => 'Tamron 90mm f/2.8', 161.6 => 'Tamron SP AF 17-35mm f/2.8-4 Di LD Aspherical IF', #IB (A05) 161.7 => 'Tamron SP AF 28-75mm f/2.8 XR Di LD Aspherical [IF] Macro', #IB/NJ - 162 => 'Canon EF 200mm f/2.8L', #32 + 161.8 => 'Tokina AT-X 24-70mm f/2.8 PRO FX (IF)', #IB + 162 => 'Canon EF 200mm f/2.8L USM', #32 163 => 'Canon EF 300mm f/4L', #32 164 => 'Canon EF 400mm f/5.6L', #32 - 165 => 'Canon EF 70-200mm f/2.8 L', - 166 => 'Canon EF 70-200mm f/2.8 L + 1.4x', - 167 => 'Canon EF 70-200mm f/2.8 L + 2x', + 165 => 'Canon EF 70-200mm f/2.8L USM', + 166 => 'Canon EF 70-200mm f/2.8L USM + 1.4x', + 167 => 'Canon EF 70-200mm f/2.8L USM + 2x', 168 => 'Canon EF 28mm f/1.8 USM or Sigma Lens', #15 168.1 => 'Sigma 50-100mm f/1.8 DC HSM | A', #IB - 169 => 'Canon EF 17-35mm f/2.8L or Sigma Lens', #15 + 169 => 'Canon EF 17-35mm f/2.8L USM or Sigma Lens', #15 169.1 => 'Sigma 18-200mm f/3.5-6.3 DC OS', #23 169.2 => 'Sigma 15-30mm f/3.5-4.5 EX DG Aspherical', #4 169.3 => 'Sigma 18-50mm f/2.8 Macro', #26 @@ -303,48 +313,54 @@ 169.5 => 'Sigma 85mm f/1.4 EX DG HSM', #Rolando Ruzic 169.6 => 'Sigma 30mm f/1.4 EX DC HSM', #Rodolfo Borges 169.7 => 'Sigma 35mm f/1.4 DG HSM', #PH (also "| A" version, ref 50) - 170 => 'Canon EF 200mm f/2.8L II', #9 - 171 => 'Canon EF 300mm f/4L', #15 - 172 => 'Canon EF 400mm f/5.6L or Sigma Lens', #32 + 169.8 => 'Sigma 35mm f/1.5 FF High-Speed Prime | 017', #IB + 170 => 'Canon EF 200mm f/2.8L II USM', #9 + 171 => 'Canon EF 300mm f/4L USM', #15 + 172 => 'Canon EF 400mm f/5.6L USM or Sigma Lens', #32 172.1 =>'Sigma 150-600mm f/5-6.3 DG OS HSM | S', #50 - 173 => 'Canon EF 180mm Macro f/3.5L or Sigma Lens', #9 + 173 => 'Canon EF 180mm Macro f/3.5L USM or Sigma Lens', #9 173.1 => 'Sigma 180mm EX HSM Macro f/3.5', #14 173.2 => 'Sigma APO Macro 150mm f/2.8 EX DG HSM', #14 - 174 => 'Canon EF 135mm f/2L or Other Lens', #9 + 174 => 'Canon EF 135mm f/2L USM or Other Lens', #9 174.1 => 'Sigma 70-200mm f/2.8 EX DG APO OS HSM', #PH (probably version II of this lens) 174.2 => 'Sigma 50-500mm f/4.5-6.3 APO DG OS HSM', #forum4031 174.3 => 'Sigma 150-500mm f/5-6.3 APO DG OS HSM', #47 174.4 => 'Zeiss Milvus 100mm f/2 Makro', #IB - 175 => 'Canon EF 400mm f/2.8L', #32 + 175 => 'Canon EF 400mm f/2.8L USM', #32 176 => 'Canon EF 24-85mm f/3.5-4.5 USM', - 177 => 'Canon EF 300mm f/4L IS', #9 + 177 => 'Canon EF 300mm f/4L IS USM', #9 178 => 'Canon EF 28-135mm f/3.5-5.6 IS', - 179 => 'Canon EF 24mm f/1.4L', #20 - 180 => 'Canon EF 35mm f/1.4L or Other Lens', #9 + 179 => 'Canon EF 24mm f/1.4L USM', #20 + 180 => 'Canon EF 35mm f/1.4L USM or Other Lens', #9 180.1 => 'Sigma 50mm f/1.4 DG HSM | A', #50 180.2 => 'Sigma 24mm f/1.4 DG HSM | A', #NJ 180.3 => 'Zeiss Milvus 50mm f/1.4', #IB 180.4 => 'Zeiss Milvus 85mm f/1.4', #IB 180.5 => 'Zeiss Otus 28mm f/1.4 ZE', #PH - 181 => 'Canon EF 100-400mm f/4.5-5.6L IS + 1.4x or Sigma Lens', #15 + 180.6 => 'Sigma 24mm f/1.5 FF High-Speed Prime | 017', #IB + 180.7 => 'Sigma 50mm f/1.5 FF High-Speed Prime | 017', #IB + 180.8 => 'Sigma 85mm f/1.5 FF High-Speed Prime | 017', #IB + 181 => 'Canon EF 100-400mm f/4.5-5.6L IS USM + 1.4x or Sigma Lens', #15 181.1 => 'Sigma 150-600mm f/5-6.3 DG OS HSM | S + 1.4x', #50 - 182 => 'Canon EF 100-400mm f/4.5-5.6L IS + 2x or Sigma Lens', + 182 => 'Canon EF 100-400mm f/4.5-5.6L IS USM + 2x or Sigma Lens', 182.1 => 'Sigma 150-600mm f/5-6.3 DG OS HSM | S + 2x', #PH (NC) - 183 => 'Canon EF 100-400mm f/4.5-5.6L IS or Sigma Lens', + 183 => 'Canon EF 100-400mm f/4.5-5.6L IS USM or Sigma Lens', 183.1 => 'Sigma 150mm f/2.8 EX DG OS HSM APO Macro', #50 183.2 => 'Sigma 105mm f/2.8 EX DG OS HSM Macro', #50 183.3 => 'Sigma 180mm f/2.8 EX DG OS HSM APO Macro', #IB 183.4 => 'Sigma 150-600mm f/5-6.3 DG OS HSM | C', #47 183.5 => 'Sigma 150-600mm f/5-6.3 DG OS HSM | S', #forum7109 (Sports 014) 183.6 => 'Sigma 100-400mm f/5-6.3 DG OS HSM', #PH ("| C" ?) - 184 => 'Canon EF 400mm f/2.8L + 2x', #15 - 185 => 'Canon EF 600mm f/4L IS', #32 - 186 => 'Canon EF 70-200mm f/4L', #9 - 187 => 'Canon EF 70-200mm f/4L + 1.4x', #26 - 188 => 'Canon EF 70-200mm f/4L + 2x', #PH - 189 => 'Canon EF 70-200mm f/4L + 2.8x', #32 + 183.7 => 'Sigma 180mm f/3.5 APO Macro EX DG IF HSM', #IB + 184 => 'Canon EF 400mm f/2.8L USM + 2x', #15 + 185 => 'Canon EF 600mm f/4L IS USM', #32 + 186 => 'Canon EF 70-200mm f/4L USM', #9 + 187 => 'Canon EF 70-200mm f/4L USM + 1.4x', #26 + 188 => 'Canon EF 70-200mm f/4L USM + 2x', #PH + 189 => 'Canon EF 70-200mm f/4L USM + 2.8x', #32 190 => 'Canon EF 100mm f/2.8 Macro USM', # (+USM ref 42) - 191 => 'Canon EF 400mm f/4 DO IS', #9 + 191 => 'Canon EF 400mm f/4 DO IS or Sigma Lens', #9 + 191.1 => 'Sigma 500mm f/4 DG OS HSM', #AndrewSheih 193 => 'Canon EF 35-80mm f/4-5.6 USM', #32 194 => 'Canon EF 80-200mm f/4.5-5.6 USM', #32 195 => 'Canon EF 35-105mm f/4.5-5.6 USM', #32 @@ -354,6 +370,7 @@ 198 => 'Canon EF 50mm f/1.4 USM or Zeiss Lens', 198.1 => 'Zeiss Otus 55mm f/1.4 ZE', #JR (seen only on Sony camera) 198.2 => 'Zeiss Otus 85mm f/1.4 ZE', #JR (NC) + 198.3 => 'Zeiss Milvus 25mm f/1.4', #IB 199 => 'Canon EF 28-80mm f/3.5-5.6 USM', #32 200 => 'Canon EF 75-300mm f/4-5.6 USM', #32 201 => 'Canon EF 28-80mm f/3.5-5.6 USM', #32 @@ -371,58 +388,74 @@ 214 => 'Canon EF-S 18-55mm f/3.5-5.6 USM', #PH/34 215 => 'Canon EF 55-200mm f/4.5-5.6 II USM', 217 => 'Tamron AF 18-270mm f/3.5-6.3 Di II VC PZD', #47 - 224 => 'Canon EF 70-200mm f/2.8L IS', #11 - 225 => 'Canon EF 70-200mm f/2.8L IS + 1.4x', #11 - 226 => 'Canon EF 70-200mm f/2.8L IS + 2x', #14 - 227 => 'Canon EF 70-200mm f/2.8L IS + 2.8x', #32 + 224 => 'Canon EF 70-200mm f/2.8L IS USM', #11 + 225 => 'Canon EF 70-200mm f/2.8L IS USM + 1.4x', #11 + 226 => 'Canon EF 70-200mm f/2.8L IS USM + 2x', #14 + 227 => 'Canon EF 70-200mm f/2.8L IS USM + 2.8x', #32 228 => 'Canon EF 28-105mm f/3.5-4.5 USM', #32 - 229 => 'Canon EF 16-35mm f/2.8L', #PH - 230 => 'Canon EF 24-70mm f/2.8L', #9 - 231 => 'Canon EF 17-40mm f/4L', + 229 => 'Canon EF 16-35mm f/2.8L USM', #PH + 230 => 'Canon EF 24-70mm f/2.8L USM', #9 + 231 => 'Canon EF 17-40mm f/4L USM', 232 => 'Canon EF 70-300mm f/4.5-5.6 DO IS USM', #15 - 233 => 'Canon EF 28-300mm f/3.5-5.6L IS', #PH + 233 => 'Canon EF 28-300mm f/3.5-5.6L IS USM', #PH 234 => 'Canon EF-S 17-85mm f/4-5.6 IS USM or Tokina Lens', #19 234.1 => 'Tokina AT-X 12-28 PRO DX 12-28mm f/4', #50/NJ 235 => 'Canon EF-S 10-22mm f/3.5-4.5 USM', #15 236 => 'Canon EF-S 60mm f/2.8 Macro USM', #15 - 237 => 'Canon EF 24-105mm f/4L IS', #15 - 238 => 'Canon EF 70-300mm f/4-5.6 IS USM', #15 - 239 => 'Canon EF 85mm f/1.2L II', #15 - 240 => 'Canon EF-S 17-55mm f/2.8 IS USM', #15 - 241 => 'Canon EF 50mm f/1.2L', #15 - 242 => 'Canon EF 70-200mm f/4L IS', #PH - 243 => 'Canon EF 70-200mm f/4L IS + 1.4x', #15 - 244 => 'Canon EF 70-200mm f/4L IS + 2x', #PH - 245 => 'Canon EF 70-200mm f/4L IS + 2.8x', #32 - 246 => 'Canon EF 16-35mm f/2.8L II', #PH + 237 => 'Canon EF 24-105mm f/4L IS USM', #15 + 238 => 'Canon EF 70-300mm f/4-5.6 IS USM', #15 (and version II? ref 42) + 239 => 'Canon EF 85mm f/1.2L II USM or Rokinon Lens', #15 + 239.1 => 'Rokinon SP 85mm f/1.2', #IB + 240 => 'Canon EF-S 17-55mm f/2.8 IS USM or Sigma Lens', #15 + 240.1 => 'Sigma 17-50mm f/2.8 EX DC OS HSM', #https://github.com/Exiv2/exiv2/issues/397 + 241 => 'Canon EF 50mm f/1.2L USM', #15 + 242 => 'Canon EF 70-200mm f/4L IS USM', #PH + 243 => 'Canon EF 70-200mm f/4L IS USM + 1.4x', #15 + 244 => 'Canon EF 70-200mm f/4L IS USM + 2x', #PH + 245 => 'Canon EF 70-200mm f/4L IS USM + 2.8x', #32 + 246 => 'Canon EF 16-35mm f/2.8L II USM', #PH 247 => 'Canon EF 14mm f/2.8L II USM', #32 - 248 => 'Canon EF 200mm f/2L IS or Sigma Lens', #42 + 248 => 'Canon EF 200mm f/2L IS USM or Sigma Lens', #42 248.1 => 'Sigma 24-35mm f/2 DG HSM | A', #JR - 249 => 'Canon EF 800mm f/5.6L IS', #35 - 250 => 'Canon EF 24mm f/1.4L II or Sigma Lens', #41 + 248.2 => 'Sigma 135mm f/2 FF High-Speed Prime | 017', #IB + 248.3 => 'Sigma 24-35mm f/2.2 FF Zoom | 017', #IB + 249 => 'Canon EF 800mm f/5.6L IS USM', #35 + 250 => 'Canon EF 24mm f/1.4L II USM or Sigma Lens', #41 250.1 => 'Sigma 20mm f/1.4 DG HSM | A', #IB + 250.2 => 'Sigma 20mm f/1.5 FF High-Speed Prime | 017', #IB 251 => 'Canon EF 70-200mm f/2.8L IS II USM', 252 => 'Canon EF 70-200mm f/2.8L IS II USM + 1.4x', #50 (1.4x Mk II) 253 => 'Canon EF 70-200mm f/2.8L IS II USM + 2x', #PH (NC) + # 253.1 => 'Tamron SP 70-200mm f/2.8 Di VC USD G2 (A025) + 2x', #forum9367 254 => 'Canon EF 100mm f/2.8L Macro IS USM', #42 255 => 'Sigma 24-105mm f/4 DG OS HSM | A or Other Sigma Lens', #50 255.1 => 'Sigma 180mm f/2.8 EX DG OS HSM APO Macro', #50 + 368 => 'Sigma 14-24mm f/2.8 DG HSM | A or other Sigma Lens', #IB (A018) + 368.1 => 'Sigma 20mm f/1.4 DG HSM | A', #50 (newer firmware) + 368.2 => 'Sigma 50mm f/1.4 DG HSM | A', #50 + 368.3 => 'Sigma 40mm f/1.4 DG HSM | A', #IB (018) + 368.4 => 'Sigma 60-600mm f/4.5-6.3 DG OS HSM | S', #IB (018) # Note: LensType 488 (0x1e8) is reported as 232 (0xe8) in 7D CameraSettings 488 => 'Canon EF-S 15-85mm f/3.5-5.6 IS USM', #PH 489 => 'Canon EF 70-300mm f/4-5.6L IS USM', #Gerald Kapounek 490 => 'Canon EF 8-15mm f/4L Fisheye USM', #Klaus Reinfeld (PH added "Fisheye") 491 => 'Canon EF 300mm f/2.8L IS II USM or Tamron Lens', #42 - 491.1 => 'Tamron SP 70-200mm F/2.8 Di VC USD G2 (A025)', #IB - 491.2 => 'Tamron 18-400mm F/3.5-6.3 Di II VC HLD (B028)', #IB + 491.1 => 'Tamron SP 70-200mm f/2.8 Di VC USD G2 (A025)', #IB + 491.2 => 'Tamron 18-400mm f/3.5-6.3 Di II VC HLD (B028)', #IB + 491.3 => 'Tamron 100-400mm f/4.5-6.3 Di VC USD (A035)', #IB + 491.4 => 'Tamron 70-210mm f/4 Di VC USD (A034)', #IB + 491.5 => 'Tamron 70-210mm f/4 Di VC USD (A034) + 1.4x', #IB + 491.6 => 'Tamron SP 24-70mm f/2.8 Di VC USD G2 (A032)', 492 => 'Canon EF 400mm f/2.8L IS II USM', #PH 493 => 'Canon EF 500mm f/4L IS II USM or EF 24-105mm f4L IS USM', #PH 493.1 => 'Canon EF 24-105mm f/4L IS USM', #PH (should recheck this) - 494 => 'Canon EF 600mm f/4.0L IS II USM', #PH + 494 => 'Canon EF 600mm f/4L IS II USM', #PH 495 => 'Canon EF 24-70mm f/2.8L II USM or Sigma Lens', #PH 495.1 => 'Sigma 24-70mm F2.8 DG OS HSM | A', #IB (017) 496 => 'Canon EF 200-400mm f/4L IS USM', #PH 499 => 'Canon EF 200-400mm f/4L IS USM + 1.4x', #50 - 502 => 'Canon EF 28mm f/2.8 IS USM', #PH + 502 => 'Canon EF 28mm f/2.8 IS USM or Tamron Lens', #PH + 502.1 => 'Tamron 35mm f/1.8 Di VC USD (F012)', #forum9757 503 => 'Canon EF 24mm f/2.8 IS USM', #PH 504 => 'Canon EF 24-70mm f/4L IS USM', #PH 505 => 'Canon EF 35mm f/2 IS USM', #PH @@ -431,15 +464,24 @@ 508 => 'Canon EF 11-24mm f/4L USM or Tamron Lens', #PH 508.1 => 'Tamron 10-24mm f/3.5-4.5 Di II VC HLD', #PH (B023) 747 => 'Canon EF 100-400mm f/4.5-5.6L IS II USM or Tamron Lens', #JR - 747.1 => 'Tamron SP 150-600mm F5-6.3 Di VC USD G2', #50 - 748 => 'Canon EF 100-400mm f/4.5-5.6L IS II USM + 1.4x', #JR (1.4x Mk III) + 747.1 => 'Tamron SP 150-600mm f/5-6.3 Di VC USD G2', #50 + 748 => 'Canon EF 100-400mm f/4.5-5.6L IS II USM + 1.4x or Tamron Lens', #JR (1.4x Mk III) + 748.1 => 'Tamron 100-400mm f/4.5-6.3 Di VC USD A035E + 1.4x', #IB + 748.2 => 'Tamron 70-210mm f/4 Di VC USD (A034) + 2x', #IB + 749 => 'Tamron 100-400mm f/4.5-6.3 Di VC USD A035E + 2x', #IB 750 => 'Canon EF 35mm f/1.4L II USM', #42 751 => 'Canon EF 16-35mm f/2.8L III USM', #42 752 => 'Canon EF 24-105mm f/4L IS II USM', #42 + 753 => 'Canon EF 85mm f/1.4L IS USM', #42 + 754 => 'Canon EF 70-200mm f/4L IS II USM', #IB + 757 => 'Canon EF 400mm f/2.8L IS III USM', #IB + 758 => 'Canon EF 600mm f/4L IS III USM', #IB + + 1136 => 'Sigma 24-70mm f/2.8 DG OS HSM | Art 017', #IB # (STM lenses - 0x10xx) 4142 => 'Canon EF-S 18-135mm f/3.5-5.6 IS STM', 4143 => 'Canon EF-M 18-55mm f/3.5-5.6 IS STM or Tamron Lens', - 4143.1 => 'Tamron 18-200mm F/3.5-6.3 Di III VC', #42 + 4143.1 => 'Tamron 18-200mm f/3.5-6.3 Di III VC', #42 4144 => 'Canon EF 40mm f/2.8 STM', #50 4145 => 'Canon EF-M 22mm f/2 STM', #34 4146 => 'Canon EF-S 18-55mm f/3.5-5.6 IS STM', #PH @@ -454,12 +496,22 @@ 4156 => 'Canon EF 50mm f/1.8 STM', #42 4157 => 'Canon EF-M 18-150mm 1:3.5-6.3 IS STM', #42 4158 => 'Canon EF-S 18-55mm f/4-5.6 IS STM', #PH + 4159 => 'Canon EF-M 32mm f/1.4 STM', #42 4160 => 'Canon EF-S 35mm f/2.8 Macro IS STM', #42 # (Nano USM lenses - 0x90xx) 36910 => 'Canon EF 70-300mm f/4-5.6 IS II USM', #42 36912 => 'Canon EF-S 18-135mm f/3.5-5.6 IS USM', #42 # (CN-E lenses - 0xf0xx) + 61491 => 'Canon CN-E 14mm T3.1 L F', #PH + 61492 => 'Canon CN-E 24mm T1.5 L F', #PH + # 61493 - missing CN-E 50mm T1.3 L F ? 61494 => 'Canon CN-E 85mm T1.3 L F', #PH + 61495 => 'Canon CN-E 135mm T2.2 L F', #PH + 61496 => 'Canon CN-E 35mm T1.5 L F', #PH + 61182 => 'Canon RF 35mm F1.8 Macro IS STM or other Canon RF Lens', #IB + 61182.1 => 'Canon RF 50mm F1.2 L USM', #IB + 61182.2 => 'Canon RF 24-105mm F4 L IS USM', #IB + 61182.3 => 'Canon RF 28-70mm F2 L USM', #IB 65535 => 'n/a', ); @@ -685,6 +737,7 @@ 0x3940000 => 'EOS M5', #IB 0x3950000 => 'PowerShot G5 X', 0x3970000 => 'PowerShot G7 X Mark II', + 0x3980000 => 'EOS M100', #42 0x3990000 => 'PowerShot ELPH 360 HS / IXUS 285 HS / IXY 650', 0x4010000 => 'PowerShot SX540 HS', 0x4020000 => 'PowerShot SX420 IS', @@ -695,10 +748,14 @@ 0x4060000 => 'PowerShot SX620 HS', 0x4070000 => 'EOS M6', 0x4100000 => 'PowerShot G9 X Mark II', - 0x4150000 => 'PowerShot ELPH 185 / IXUS 185 / IXY 200', + 0x412 => 'EOS M50 / Kiss M', # (yes, no "0000") + 0x4150000 => 'PowerShot ELPH 185 / IXUS 185 / IXY 200', 0x4160000 => 'PowerShot SX430 IS', 0x4170000 => 'PowerShot SX730 HS', + 0x4180000 => 'PowerShot G1 X Mark III', #IB 0x6040000 => 'PowerShot S100 / Digital IXUS / IXY Digital', + 0x801 => 'PowerShot SX740 HS', + 0x805 => 'PowerShot SX70 HS', # (see http://cweb.canon.jp/e-support/faq/answer/digitalcamera/10447-1.html for PowerShot/IXUS/IXY names) @@ -730,8 +787,10 @@ 0x4007da8f => 'HF M30/M31/M36/M300/M306', # (LEGRIA/VIXIA) 0x4007da90 => 'HF S20/S21/S200', # (LEGRIA/VIXIA) 0x4007da92 => 'FS31/FS36/FS37/FS300/FS305/FS306/FS307', + 0x4007dca0 => 'EOS C300', 0x4007dda9 => 'HF G25', # (LEGRIA) 0x4007dfb4 => 'XC10', + 0x4007e1c3 => 'EOS C200', # NOTE: some pre-production models may have a model name of # "Canon EOS Kxxx", where "xxx" is the last 3 digits of the model ID below. @@ -777,7 +836,7 @@ 0x80000324 => 'EOS-1D C', #(NC) 0x80000325 => 'EOS 70D', 0x80000326 => 'EOS Rebel T5i / 700D / Kiss X7i', - 0x80000327 => 'EOS Rebel T5 / 1200D / Kiss X70', + 0x80000327 => 'EOS Rebel T5 / 1200D / Kiss X70 / Hi', 0x80000328 => 'EOS-1D X MARK II', #42 0x80000331 => 'EOS M', 0x80000350 => 'EOS 80D', #42 @@ -793,6 +852,9 @@ 0x80000406 => 'EOS 6D Mark II', #IB/42 0x80000408 => 'EOS 77D / 9000D', 0x80000417 => 'EOS Rebel SL2 / 200D / Kiss X9', #IB/42 + 0x80000422 => 'EOS Rebel T100 / 4000D / 3000D', #IB (3000D in China; Kiss? - PH) + 0x80000424 => 'EOR R', #IB + 0x80000432 => 'EOS Rebel T7 / 2000D / 1500D / Kiss X90', #IB ); my %canonQuality = ( @@ -802,6 +864,7 @@ 3 => 'Fine', 4 => 'RAW', 5 => 'Superfine', + 7 => 'CRAW', #42 130 => 'Normal Movie', #22 131 => 'Movie (2)', #PH (7DmkII 1920x1080) ); @@ -824,6 +887,7 @@ 130 => 'Small Movie', #22 137 => '1280x720 Movie', #PH (S95 24fps; D60 50fps) 142 => '1920x1080 Movie', #PH (D60 25fps) + 143 => '4096x2160 Movie', #PH (C200) ); my %canonWhiteBalance = ( # -1='Click", -2='Pasted' ?? - PH @@ -1474,6 +1538,13 @@ TagTable => 'Image::ExifTool::Canon::TimeInfo', }, }, + 0x38 => { #PH + Name => 'BatteryType', + Writable => 'undef', + Condition => '$count == 76', + RawConv => '$val=~/^.{4}([^\0]+)/s ? $1 : undef', + RawConvInv => 'substr("\x4c\0\0\0".$val.("\0"x72), 0, 76)', + }, 0x3c => { #PH (G1XmkII) Name => 'AFInfo3', Condition => '$$self{AFInfo3} = 1', @@ -1727,6 +1798,11 @@ Name => 'ColorData8', SubDirectory => { TagTable => 'Image::ExifTool::Canon::ColorData8' }, }, + { # (int16u[1820]) - M50 (1820) ref PH, EOS R (1824) ref IB + Condition => '$count == 1820 or $count == 1824', + Name => 'ColorData9', + SubDirectory => { TagTable => 'Image::ExifTool::Canon::ColorData9' }, + }, { Name => 'ColorDataUnknown', SubDirectory => { TagTable => 'Image::ExifTool::Canon::ColorDataUnknown' }, @@ -1801,8 +1877,8 @@ Name => 'VignettingCorrUnknown2', Condition => '$$valPt !~ /^\0\0\0\0/', SubDirectory => { - # (the size word is at byte 4 for version 3 of this structure) - Validate => 'Image::ExifTool::Canon::Validate($dirData,$subdirStart+4,$size)', + # (the size word is at byte 4 for version 3 of this structure, but not always!) + # Validate => 'Image::ExifTool::Canon::Validate($dirData,$subdirStart+4,$size)', TagTable => 'Image::ExifTool::Canon::VignettingCorrUnknown', }, }], @@ -1959,6 +2035,9 @@ 7 => 'CR2+JPEG', # (S30) 9 => 'MOV', # (S95 MOV) 10 => 'MP4', # (SX280 MP4) + 11 => 'CRM', #PH (C200 CRM) + 12 => 'CR3', #PH (EOS R) + 13 => 'CR3+JPEG', #PH (EOS R) }, }, 10 => { @@ -3295,7 +3374,7 @@ Name => 'FirmwareVersionLookAhead', Hidden => 1, # look ahead to check location of FirmwareVersion string - Format => 'undef[0x286]', + Format => 'undef[0x28b]', RawConv => q{ my $t = substr($val, 0x271, 6); # 1 = firmware 5.7.1 $t =~ /^\d+\.\d+\.\d+/ and $$self{CanonFirm} = 1, return undef; @@ -3303,6 +3382,8 @@ $t =~ /^\d+\.\d+\.\d+/ and $$self{CanonFirm} = 2, return undef; $t = substr($val, 0x280, 6); # 3 = firmware 0.0.8/1.0.2/1.1.1 $t =~ /^\d+\.\d+\.\d+/ and $$self{CanonFirm} = 3, return undef; + $t = substr($val, 0x285, 6); # 4 = firmware 2.1.0 + $t =~ /^\d+\.\d+\.\d+/ and $$self{CanonFirm} = 4, return undef; $self->Warn('Unrecognized CameraInfo1DX firmware version'); $$self{CanonFirm} = 0; return undef; # not a real tag @@ -3330,7 +3411,7 @@ 0x8e => { Name => 'FocusDistanceLower', %focusDistanceByteSwap, - Hook => '$varSize -= 4 if $$self{CanonFirm} < 3', + Hook => '$varSize -= 4 if $$self{CanonFirm} < 3; $varSize += 5 if $$self{CanonFirm} == 4', }, 0xbc => { Name => 'WhiteBalance', @@ -3730,7 +3811,7 @@ Name => 'FirmwareVersionLookAhead', Hidden => 1, # look ahead to check location of FirmwareVersion string - Format => 'undef[0x248]', + Format => 'undef[0x24d]', RawConv => q{ my $t = substr($val, 0x22c, 6); # 1 = firmware 4.5.4/4.5.6 $t =~ /^\d+\.\d+\.\d+/ and $$self{CanonFirm} = 1, return undef; @@ -3740,6 +3821,8 @@ $t =~ /^\d+\.\d+\.\d+/ and $$self{CanonFirm} = 3, return undef; $t = substr($val, 0x242, 6); # 4 = firmware 1.2.1 $t =~ /^\d+\.\d+\.\d+/ and $$self{CanonFirm} = 4, return undef; + $t = substr($val, 0x247, 6); # 5 = firmware 1.3.5 + $t =~ /^\d+\.\d+\.\d+/ and $$self{CanonFirm} = 5, return undef; $self->Warn('Unrecognized CameraInfo5DmkIII firmware version'); $$self{CanonFirm} = 0; return undef; # not a real tag @@ -3757,7 +3840,7 @@ Hook => q{ $varSize -= 3 if $$self{CanonFirm} == 1; $varSize -= 2 if $$self{CanonFirm} == 2; - $varSize += 6 if $$self{CanonFirm} == 4; + $varSize += 6 if $$self{CanonFirm} >= 4; }, }, 0x7d => { @@ -3775,7 +3858,10 @@ 0x8e => { Name => 'FocusDistanceLower', %focusDistanceByteSwap, - Hook => '$varSize -= 4 if $$self{CanonFirm} < 3', + Hook => q{ + $varSize -= 4 if $$self{CanonFirm} < 3; + $varSize += 5 if $$self{CanonFirm} > 4; + }, }, 0xbc => { Name => 'WhiteBalance', @@ -7134,6 +7220,60 @@ 0x72 => { Name => 'ColorTempUnknown13', Unknown => 1 }, ); +# color coefficients (ref PH/IB) +%Image::ExifTool::Canon::ColorCoefs2 = ( + %binaryDataAttrs, + FORMAT => 'int16s', + FIRST_ENTRY => 0, + GROUPS => { 0 => 'MakerNotes', 2 => 'Camera' }, + 0x00 => { Name => 'WB_RGGBLevelsAsShot', Format => 'int16s[4]' }, + 0x07 => 'ColorTempAsShot', + 0x08 => { Name => 'WB_RGGBLevelsAuto', Format => 'int16s[4]' }, + 0x0f => 'ColorTempAuto', + 0x10 => { Name => 'WB_RGGBLevelsMeasured', Format => 'int16s[4]' }, + 0x17 => 'ColorTempMeasured', + 0x18 => { Name => 'WB_RGGBLevelsUnknown', Format => 'int16s[4]', Unknown => 1 }, + 0x1f => { Name => 'ColorTempUnknown', Unknown => 1 }, + 0x20 => { Name => 'WB_RGGBLevelsDaylight', Format => 'int16s[4]' }, + 0x27 => 'ColorTempDaylight', + 0x28 => { Name => 'WB_RGGBLevelsShade', Format => 'int16s[4]' }, + 0x2f => 'ColorTempShade', + 0x30 => { Name => 'WB_RGGBLevelsCloudy', Format => 'int16s[4]' }, + 0x37 => 'ColorTempCloudy', + 0x38 => { Name => 'WB_RGGBLevelsTungsten', Format => 'int16s[4]' }, + 0x3f => 'ColorTempTungsten', + 0x40 => { Name => 'WB_RGGBLevelsFluorescent',Format => 'int16s[4]' }, + 0x47 => 'ColorTempFluorescent', + 0x48 => { Name => 'WB_RGGBLevelsKelvin', Format => 'int16s[4]' }, + 0x4f => 'ColorTempKelvin', + 0x50 => { Name => 'WB_RGGBLevelsFlash', Format => 'int16s[4]' }, + 0x57 => 'ColorTempFlash', + 0x58 => { Name => 'WB_RGGBLevelsUnknown2', Format => 'int16s[4]', Unknown => 1 }, + 0x5f => { Name => 'ColorTempUnknown2', Unknown => 1 }, + 0x60 => { Name => 'WB_RGGBLevelsUnknown3', Format => 'int16s[4]', Unknown => 1 }, + 0x67 => { Name => 'ColorTempUnknown3', Unknown => 1 }, + 0x68 => { Name => 'WB_RGGBLevelsUnknown4', Format => 'int16s[4]', Unknown => 1 }, + 0x6f => { Name => 'ColorTempUnknown4', Unknown => 1 }, + 0x70 => { Name => 'WB_RGGBLevelsUnknown5', Format => 'int16s[4]', Unknown => 1 }, + 0x77 => { Name => 'ColorTempUnknown5', Unknown => 1 }, + 0x78 => { Name => 'WB_RGGBLevelsUnknown6', Format => 'int16s[4]', Unknown => 1 }, + 0x7f => { Name => 'ColorTempUnknown6', Unknown => 1 }, + 0x80 => { Name => 'WB_RGGBLevelsUnknown7', Format => 'int16s[4]', Unknown => 1 }, + 0x87 => { Name => 'ColorTempUnknown7', Unknown => 1 }, + 0x88 => { Name => 'WB_RGGBLevelsUnknown8', Format => 'int16s[4]', Unknown => 1 }, + 0x8f => { Name => 'ColorTempUnknown8', Unknown => 1 }, + 0x90 => { Name => 'WB_RGGBLevelsUnknown9', Format => 'int16s[4]', Unknown => 1 }, + 0x97 => { Name => 'ColorTempUnknown9', Unknown => 1 }, + 0x98 => { Name => 'WB_RGGBLevelsUnknown10', Format => 'int16s[4]', Unknown => 1 }, + 0x9f => { Name => 'ColorTempUnknown10', Unknown => 1 }, + 0xa0 => { Name => 'WB_RGGBLevelsUnknown11', Format => 'int16s[4]', Unknown => 1 }, + 0xa7 => { Name => 'ColorTempUnknown11', Unknown => 1 }, + 0xa8 => { Name => 'WB_RGGBLevelsUnknown12', Format => 'int16s[4]', Unknown => 1 }, + 0xaf => { Name => 'ColorTempUnknown12', Unknown => 1 }, + 0xb0 => { Name => 'WB_RGGBLevelsUnknown13', Format => 'int16s[4]', Unknown => 1 }, + 0xb7 => { Name => 'ColorTempUnknown13', Unknown => 1 }, +); + # color calibration (ref 37) %Image::ExifTool::Canon::ColorCalib = ( %binaryDataAttrs, @@ -7169,37 +7309,83 @@ 0x38 => { Name => 'CameraColorCalibration15', %cameraColorCalibration }, ); +# color calibration2 +%Image::ExifTool::Canon::ColorCalib2 = ( + %binaryDataAttrs, + FORMAT => 'int16s', + FIRST_ENTRY => 0, + GROUPS => { 0 => 'MakerNotes', 2 => 'Camera' }, + NOTES => 'B, C, A, D, Temperature.', + 0x00 => { Name => 'CameraColorCalibration01', %cameraColorCalibration2 }, + 0x05 => { Name => 'CameraColorCalibration02', %cameraColorCalibration2 }, + 0x0a => { Name => 'CameraColorCalibration03', %cameraColorCalibration2 }, + 0x0f => { Name => 'CameraColorCalibration04', %cameraColorCalibration2 }, + 0x14 => { Name => 'CameraColorCalibration05', %cameraColorCalibration2 }, + 0x19 => { Name => 'CameraColorCalibration06', %cameraColorCalibration2 }, + 0x1e => { Name => 'CameraColorCalibration07', %cameraColorCalibration2 }, + 0x23 => { Name => 'CameraColorCalibration08', %cameraColorCalibration2 }, + 0x28 => { Name => 'CameraColorCalibration09', %cameraColorCalibration2 }, + 0x2d => { Name => 'CameraColorCalibration10', %cameraColorCalibration2 }, + 0x32 => { Name => 'CameraColorCalibration11', %cameraColorCalibration2 }, + 0x37 => { Name => 'CameraColorCalibration12', %cameraColorCalibration2 }, + 0x3c => { Name => 'CameraColorCalibration13', %cameraColorCalibration2 }, + 0x41 => { Name => 'CameraColorCalibration14', %cameraColorCalibration2 }, + 0x46 => { Name => 'CameraColorCalibration15', %cameraColorCalibration2 }, +); + # Color data (MakerNotes tag 0x4001, count=5120) (ref PH) %Image::ExifTool::Canon::ColorData5 = ( %binaryDataAttrs, - NOTES => 'These tags are used by the PowerShot G10.', + NOTES => 'These tags are used by many EOS M and PowerShot models.', FORMAT => 'int16s', FIRST_ENTRY => 0, GROUPS => { 0 => 'MakerNotes', 2 => 'Camera' }, - IS_SUBDIR => [ 0x47 ], - # 0x00 - oddly, this isn't ColorDataVersion (probably should have been version 8) - 0x47 => { + DATAMEMBER => [ 0x00 ], + IS_SUBDIR => [ 0x47, 0xba, 0xff ], + 0x00 => { + Name => 'ColorDataVersion', + DataMember => 'ColorDataVersion', + RawConv => '$$self{ColorDataVersion} = $val', + PrintConv => { + -3 => '-3 (M10/M3)', # (and PowerShot G1X/G1XmkII/G10/G11/G12/G15/G16/G3X/G5X/G7X/G9X/S100/S110/S120/S90/S95/SX1IS/SX50HS/SX60HS) + -4 => '-4 (M100/M5/M6)', # (and PowerShot G1XmkIII/G7XmkII/G9XmkII) + }, + }, + 0x47 => [{ Name => 'ColorCoefs', + Condition => '$$self{ColorDataVersion} == -3', Format => 'undef[230]', # ColorTempUnknown13 is last entry SubDirectory => { TagTable => 'Image::ExifTool::Canon::ColorCoefs' } + },{ + Name => 'ColorCoefs2', + Condition => '$$self{ColorDataVersion} == -4', + Format => 'undef[368]', + SubDirectory => { TagTable => 'Image::ExifTool::Canon::ColorCoefs2' } + }], + 0xba => { + Name => 'ColorCalib2', + Condition => '$$self{ColorDataVersion} == -3', + Format => 'undef[150]', + Unknown => 1, + SubDirectory => { TagTable => 'Image::ExifTool::Canon::ColorCalib2' } + }, + 0xff => { + Name => 'ColorCalib2', + Condition => '$$self{ColorDataVersion} == -4', + Format => 'undef[150]', + Unknown => 1, + SubDirectory => { TagTable => 'Image::ExifTool::Canon::ColorCalib2' } + }, + 0x108=> { #IB + Name => 'PerChannelBlackLevel', + Condition => '$$self{ColorDataVersion} == -3', + Format => 'int16s[4]', + }, + 0x14d=> { #IB + Name => 'PerChannelBlackLevel', + Condition => '$$self{ColorDataVersion} == -4', + Format => 'int16s[4]', }, - 0xba => { Name => 'CameraColorCalibration01', %cameraColorCalibration2, - Notes => 'B, C, A, D, Temperature' }, - 0xbf => { Name => 'CameraColorCalibration02', %cameraColorCalibration2 }, - 0xc4 => { Name => 'CameraColorCalibration03', %cameraColorCalibration2 }, - 0xc9 => { Name => 'CameraColorCalibration04', %cameraColorCalibration2 }, - 0xce => { Name => 'CameraColorCalibration05', %cameraColorCalibration2 }, - 0xd3 => { Name => 'CameraColorCalibration06', %cameraColorCalibration2 }, - 0xd8 => { Name => 'CameraColorCalibration07', %cameraColorCalibration2 }, - 0xdd => { Name => 'CameraColorCalibration08', %cameraColorCalibration2 }, - 0xe2 => { Name => 'CameraColorCalibration09', %cameraColorCalibration2 }, - 0xe7 => { Name => 'CameraColorCalibration10', %cameraColorCalibration2 }, - 0xec => { Name => 'CameraColorCalibration11', %cameraColorCalibration2 }, - 0xf1 => { Name => 'CameraColorCalibration12', %cameraColorCalibration2 }, - 0xf6 => { Name => 'CameraColorCalibration13', %cameraColorCalibration2 }, - 0xfb => { Name => 'CameraColorCalibration14', %cameraColorCalibration2 }, - 0x100=> { Name => 'CameraColorCalibration15', %cameraColorCalibration2 }, - 0x108=> { Name => 'PerChannelBlackLevel', Format => 'int16s[4]' }, #IB ); # Color data (MakerNotes tag 0x4001, count=1273|1275) (ref PH) @@ -7438,7 +7624,6 @@ # Color data (MakerNotes tag 0x4001, count=1560,etc) (ref IB) %Image::ExifTool::Canon::ColorData8 = ( %binaryDataAttrs, - NOTES => 'These tags are used by the EOS 5DS, 5DS R, 80D and 1300D.', FORMAT => 'int16s', FIRST_ENTRY => 0, GROUPS => { 0 => 'MakerNotes', 2 => 'Camera' }, @@ -7451,7 +7636,7 @@ PrintConv => { 12 => '12 (5DS/5DSR)', 13 => '13 (80D)', #PH - 14 => '14 (1300D)', #IB + 14 => '14 (1300D/2000D/4000D)', #IB 15 => '15 (6DmkII/77D/200D/800D)', #IB }, }, @@ -7596,6 +7781,128 @@ }, ); +# Color data (MakerNotes tag 0x4001, count=1820) (ref PH) +%Image::ExifTool::Canon::ColorData9 = ( + %binaryDataAttrs, + FORMAT => 'int16s', + FIRST_ENTRY => 0, + GROUPS => { 0 => 'MakerNotes', 2 => 'Camera' }, + DATAMEMBER => [ 0 ], + IS_SUBDIR => [ 0x10a ], + 0x00 => { + Name => 'ColorDataVersion', + DataMember => 'ColorDataVersion', + RawConv => '$$self{ColorDataVersion} = $val', + PrintConv => { + 16 => '16 (M50)', + 17 => '17 (EOS R)', + }, + }, + 0x47 => { Name => 'WB_RGGBLevelsAsShot', Format => 'int16s[4]' }, + 0x4b => 'ColorTempAsShot', + 0x4c => { Name => 'WB_RGGBLevelsAuto', Format => 'int16s[4]' }, + 0x50 => 'ColorTempAuto', + 0x51 => { Name => 'WB_RGGBLevelsMeasured', Format => 'int16s[4]' }, + 0x55 => 'ColorTempMeasured', + 0x56 => { Name => 'WB_RGGBLevelsUnknown', Format => 'int16s[4]', Unknown => 1 }, + 0x5a => { Name => 'ColorTempUnknown', Unknown => 1 }, + 0x5b => { Name => 'WB_RGGBLevelsUnknown2', Format => 'int16s[4]', Unknown => 1 }, + 0x5f => { Name => 'ColorTempUnknown2', Unknown => 1 }, + 0x60 => { Name => 'WB_RGGBLevelsUnknown3', Format => 'int16s[4]', Unknown => 1 }, + 0x64 => { Name => 'ColorTempUnknown3', Unknown => 1 }, + 0x65 => { Name => 'WB_RGGBLevelsUnknown4', Format => 'int16s[4]', Unknown => 1 }, + 0x69 => { Name => 'ColorTempUnknown4', Unknown => 1 }, + 0x6a => { Name => 'WB_RGGBLevelsUnknown5', Format => 'int16s[4]', Unknown => 1 }, + 0x6e => { Name => 'ColorTempUnknown5', Unknown => 1 }, + 0x6f => { Name => 'WB_RGGBLevelsUnknown6', Format => 'int16s[4]', Unknown => 1 }, + 0x73 => { Name => 'ColorTempUnknown6', Unknown => 1 }, + 0x74 => { Name => 'WB_RGGBLevelsUnknown7', Format => 'int16s[4]', Unknown => 1 }, + 0x78 => { Name => 'ColorTempUnknown7', Unknown => 1 }, + 0x79 => { Name => 'WB_RGGBLevelsUnknown8', Format => 'int16s[4]', Unknown => 1 }, + 0x7d => { Name => 'ColorTempUnknown8', Unknown => 1 }, + 0x7e => { Name => 'WB_RGGBLevelsUnknown9', Format => 'int16s[4]', Unknown => 1 }, + 0x82 => { Name => 'ColorTempUnknown9', Unknown => 1 }, + 0x83 => { Name => 'WB_RGGBLevelsUnknown10', Format => 'int16s[4]', Unknown => 1 }, + 0x87 => { Name => 'ColorTempUnknown10', Unknown => 1 }, + 0x88 => { Name => 'WB_RGGBLevelsDaylight', Format => 'int16s[4]' }, + 0x8c => 'ColorTempDaylight', + 0x8d => { Name => 'WB_RGGBLevelsShade', Format => 'int16s[4]' }, + 0x91 => 'ColorTempShade', + 0x92 => { Name => 'WB_RGGBLevelsCloudy', Format => 'int16s[4]' }, + 0x96 => 'ColorTempCloudy', + 0x97 => { Name => 'WB_RGGBLevelsTungsten', Format => 'int16s[4]' }, + 0x9b => 'ColorTempTungsten', + 0x9c => { Name => 'WB_RGGBLevelsFluorescent',Format => 'int16s[4]' }, + 0xa0 => 'ColorTempFluorescent', + 0xa1 => { Name => 'WB_RGGBLevelsKelvin', Format => 'int16s[4]' }, + 0xa5 => 'ColorTempKelvin', + 0xa6 => { Name => 'WB_RGGBLevelsFlash', Format => 'int16s[4]' }, + 0xaa => 'ColorTempFlash', + 0xab => { Name => 'WB_RGGBLevelsUnknown11', Format => 'int16s[4]', Unknown => 1 }, + 0xaf => { Name => 'ColorTempUnknown11', Unknown => 1 }, + 0xb0 => { Name => 'WB_RGGBLevelsUnknown12', Format => 'int16s[4]', Unknown => 1 }, + 0xb4 => { Name => 'ColorTempUnknown12', Unknown => 1 }, + 0xb5 => { Name => 'WB_RGGBLevelsUnknown13', Format => 'int16s[4]', Unknown => 1 }, + 0xb9 => { Name => 'ColorTempUnknown13', Unknown => 1 }, + 0xba => { Name => 'WB_RGGBLevelsUnknown14', Format => 'int16s[4]', Unknown => 1 }, + 0xbe => { Name => 'ColorTempUnknown14', Unknown => 1 }, + 0xbf => { Name => 'WB_RGGBLevelsUnknown15', Format => 'int16s[4]', Unknown => 1 }, + 0xc3 => { Name => 'ColorTempUnknown15', Unknown => 1 }, + 0xc4 => { Name => 'WB_RGGBLevelsUnknown16', Format => 'int16s[4]', Unknown => 1 }, + 0xc8 => { Name => 'ColorTempUnknown16', Unknown => 1 }, + 0xc9 => { Name => 'WB_RGGBLevelsUnknown17', Format => 'int16s[4]', Unknown => 1 }, + 0xcd => { Name => 'ColorTempUnknown17', Unknown => 1 }, + 0xce => { Name => 'WB_RGGBLevelsUnknown18', Format => 'int16s[4]', Unknown => 1 }, + 0xd2 => { Name => 'ColorTempUnknown18', Unknown => 1 }, + 0xd3 => { Name => 'WB_RGGBLevelsUnknown19', Format => 'int16s[4]', Unknown => 1 }, + 0xd7 => { Name => 'ColorTempUnknown19', Unknown => 1 }, + 0xd8 => { Name => 'WB_RGGBLevelsUnknown20', Format => 'int16s[4]', Unknown => 1 }, + 0xdc => { Name => 'ColorTempUnknown20', Unknown => 1 }, + 0xdd => { Name => 'WB_RGGBLevelsUnknown21', Format => 'int16s[4]', Unknown => 1 }, + 0xe1 => { Name => 'ColorTempUnknown21', Unknown => 1 }, + 0xe2 => { Name => 'WB_RGGBLevelsUnknown22', Format => 'int16s[4]', Unknown => 1 }, + 0xe6 => { Name => 'ColorTempUnknown22', Unknown => 1 }, + 0xe7 => { Name => 'WB_RGGBLevelsUnknown23', Format => 'int16s[4]', Unknown => 1 }, + 0xeb => { Name => 'ColorTempUnknown23', Unknown => 1 }, + 0xec => { Name => 'WB_RGGBLevelsUnknown24', Format => 'int16s[4]', Unknown => 1 }, + 0xf0 => { Name => 'ColorTempUnknown24', Unknown => 1 }, + 0xf1 => { Name => 'WB_RGGBLevelsUnknown25', Format => 'int16s[4]', Unknown => 1 }, + 0xf5 => { Name => 'ColorTempUnknown25', Unknown => 1 }, + 0xf6 => { Name => 'WB_RGGBLevelsUnknown26', Format => 'int16s[4]', Unknown => 1 }, + 0xfa => { Name => 'ColorTempUnknown26', Unknown => 1 }, + 0xfb => { Name => 'WB_RGGBLevelsUnknown27', Format => 'int16s[4]', Unknown => 1 }, + 0xff => { Name => 'ColorTempUnknown27', Unknown => 1 }, + 0x100=> { Name => 'WB_RGGBLevelsUnknown28', Format => 'int16s[4]', Unknown => 1 }, + 0x104=> { Name => 'ColorTempUnknown28', Unknown => 1 }, + 0x105=> { Name => 'WB_RGGBLevelsUnknown29', Format => 'int16s[4]', Unknown => 1 }, + 0x109=> { Name => 'ColorTempUnknown29', Unknown => 1 }, + 0x10a => { #IB + Name => 'ColorCalib', + Format => 'undef[120]', + Unknown => 1, + SubDirectory => { TagTable => 'Image::ExifTool::Canon::ColorCalib' } + }, + 0x149 => { #IB + Name => 'PerChannelBlackLevel', + Format => 'int16u[4]', + Notes => '1300D', + }, + # 0x318 - PerChannelBlackLevel again (ref IB) + 0x31c => { #IB + Name => 'NormalWhiteLevel', + Format => 'int16u', + RawConv => '$val || undef', + }, + 0x31d => { #IB + Name => 'SpecularWhiteLevel', + Format => 'int16u', + }, + 0x31e => { #IB + Name => 'LinearityUpperMargin', + Format => 'int16u', + }, +); + # Unknown color data (MakerNotes tag 0x4001) %Image::ExifTool::Canon::ColorDataUnknown = ( PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData, @@ -8042,17 +8349,183 @@ GROUPS => { 0 => 'MakerNotes', 1 => 'Canon', 2 => 'Video' }, NOTES => q{ Tags extracted from the uuid atom of MP4 videos from cameras such as the - SX280. + SX280, and CR3 images from cameras such as the EOS M50. + }, + CNCV => { + Name => 'CompressorVersion', + # use this to recognize the specific type of Canon RAW (CR3 or CRM) + RawConv => '$self->OverrideFileType($1) if $val =~ /^Canon(\w{3})/i; $val', }, - CNCV => 'CompressorVersion', # CNDM - 4 bytes - 0xff,0xd8,0xff,0xd9 CNTH => { Name => 'CanonCNTH', SubDirectory => { TagTable => 'Image::ExifTool::Canon::CNTH' }, }, + CCTP => { # (CR3 files) + Name => 'CanonCCTP', + SubDirectory => { + TagTable => 'Image::ExifTool::Canon::CCTP', + Start => '12', + }, + }, + # CTBO - (CR3 files) int32u entry count N, N x (int32u index, int64u offset, int64u size) + # index 1=XMP, 2=PRVW, 3=mdat + CMT1 => { # (CR3 files) + Name => 'IFD0', + SubDirectory => { + TagTable => 'Image::ExifTool::Exif::Main', + ProcessProc => \&Image::ExifTool::ProcessTIFF, + }, + }, + CMT2 => { # (CR3 files) + Name => 'ExifIFD', + SubDirectory => { + TagTable => 'Image::ExifTool::Exif::Main', + ProcessProc => \&Image::ExifTool::ProcessTIFF, + }, + }, + CMT3 => { # (CR3 files) + Name => 'MakerNoteCanon', + SubDirectory => { + TagTable => 'Image::ExifTool::Canon::Main', + ProcessProc => \&Image::ExifTool::ProcessTIFF, + }, + }, + CMT4 => { # (CR3 files) + Name => 'GPSInfo', + SubDirectory => { + TagTable => 'Image::ExifTool::GPS::Main', + ProcessProc => \&Image::ExifTool::ProcessTIFF, + DirName => 'GPS', + }, + }, + THMB => { + Name => 'ThumbnailImage', + Groups => { 2 => 'Preview' }, + RawConv => 'substr($val, 16)', + Binary => 1, + }, + CNOP => { #PH (M50) + Name => 'CanonCNOP', + SubDirectory => { TagTable => 'Image::ExifTool::Canon::CNOP' }, + }, +); + +%Image::ExifTool::Canon::UnknownIFD = ( + GROUPS => { 0 => 'MakerNotes', 2 => 'Camera' }, +); + +# Canon CCTP atoms (ref PH, CR3 files) +%Image::ExifTool::Canon::CCTP = ( + GROUPS => { 0 => 'MakerNotes', 1 => 'Canon', 2 => 'Video' }, + # CCDT - int32u[3]: 0. 0, 1. decoder type?, 2. 0, 3. index +); + +# 'CDI1' atom information (ref PH, CR3 files) +%Image::ExifTool::Canon::CDI1 = ( + GROUPS => { 0 => 'QuickTime', 1 => 'Canon', 2 => 'Image' }, + # IAD1 - 32/48 bytes +); + +# Canon Timed MetaData (ref PH, CR3 files) +%Image::ExifTool::Canon::CTMD = ( + GROUPS => { 0 => 'MakerNotes', 1 => 'Canon', 2 => 'Image' }, + PROCESS_PROC => \&ProcessCTMD, + NOTES => q{ + Canon Timed MetaData tags found in CR3 images. The ExtractEmbedded option + is automatically applied when reading CR3 files to be able to extract this + information. + }, + 1 => { + Name => 'TimeStamp', + Groups => { 2 => 'Time' }, + RawConv => q{ + my $fmt = GetByteOrder() eq 'MM' ? 'x2nCCCCCC' : 'x2vCCCCCC'; + sprintf('%.4d:%.2d:%.2d %.2d:%.2d:%.2d.%.2d', unpack($fmt, $val)); + }, + PrintConv => '$self->ConvertDateTime($val)', + }, + # 3 - 4 bytes, seen: ff ff ff ff + 4 => { + Name => 'FocalInfo', + SubDirectory => { TagTable => 'Image::ExifTool::Canon::FocalInfo' }, + }, + 5 => { + Name => 'ExposureInfo', + SubDirectory => { TagTable => 'Image::ExifTool::Canon::ExposureInfo' }, + }, + 7 => { + Name => 'ExifInfo7', + SubDirectory => { TagTable => 'Image::ExifTool::Canon::ExifInfo' }, + }, + 8 => { + Name => 'ExifInfo8', + SubDirectory => { TagTable => 'Image::ExifTool::Canon::ExifInfo' }, + }, + 9 => { + Name => 'ExifInfo9', + SubDirectory => { TagTable => 'Image::ExifTool::Canon::ExifInfo' }, + }, + # 10 - 60 bytes: all zeros with a pair of 0xff's at offset 0x02 (C200 CRM) + # 11 - 612 bytes: all zero with pairs of 0xff's at offset 0x6e and 0x116 (C200 CRM) +); + +# Canon Timed MetaData (ref PH, CR3 files) +%Image::ExifTool::Canon::ExifInfo = ( + GROUPS => { 0 => 'MakerNotes', 1 => 'Canon', 2 => 'Image' }, + PROCESS_PROC => \&ProcessExifInfo, + 0x8769 => { + Name => 'ExifIFD', + SubDirectory => { + TagTable => 'Image::ExifTool::Exif::Main', + ProcessProc => \&Image::ExifTool::ProcessTIFF, + }, + }, + 0x927c => { + Name => 'MakerNoteCanon', + SubDirectory => { + TagTable => 'Image::ExifTool::Canon::Main', + ProcessProc => \&Image::ExifTool::ProcessTIFF, + }, + }, +); + +# timed focal length information (ref PH, CR3 files) +%Image::ExifTool::Canon::FocalInfo = ( + PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData, + GROUPS => { 0 => 'MakerNotes', 1 => 'Canon', 2 => 'Image' }, + FORMAT => 'int32u', + FIRST_ENTRY => 0, + 0 => { + Name => 'FocalLength', + Format => 'rational32u', + PrintConv => 'sprintf("%.1f mm",$val)', + }, +); + +# timed exposure information (ref PH, CR3 files) +%Image::ExifTool::Canon::ExposureInfo = ( + PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData, + GROUPS => { 0 => 'MakerNotes', 1 => 'Canon', 2 => 'Image' }, + FORMAT => 'int32u', + FIRST_ENTRY => 0, + 0 => { + Name => 'FNumber', + Format => 'rational32u', + PrintConv => 'Image::ExifTool::Exif::PrintFNumber($val)', + }, + 1 => { + Name => 'ExposureTime', + Format => 'rational32u', + PrintConv => 'Image::ExifTool::Exif::PrintExposureTime($val)', + }, + 2 => { + Name => 'ISO', + Format => 'int32u', + ValueConv => '$val & 0x7fffffff', # (not sure what high bit indicates) + }, ); -# Canon CNTH atoms (ref PH) %Image::ExifTool::Canon::CNTH = ( GROUPS => { 0 => 'MakerNotes', 1 => 'Canon', 2 => 'Video' }, NOTES => q{ @@ -8077,7 +8550,9 @@ # Canon CNOP atoms (ref PH) %Image::ExifTool::Canon::CNOP = ( GROUPS => { 0 => 'MakerNotes', 1 => 'Canon', 2 => 'Video' }, - # CNFB - 52 bytes (7DmkII) + # CNFB - 52 bytes (7DmkII,M50,C200) + # CNMI - 4 bytes: "0x20000001" (C200) + # CNCM - 48 bytes: original file name in bytes 24-31 (C200) ); # 'skip' atom of Canon MOV videos (ref PH) @@ -8821,6 +9296,87 @@ } #------------------------------------------------------------------------------ +# Process CTMD EXIF information +# Inputs: 0) ExifTool object ref, 1) dirInfo ref, 2) tag table ref +# Returns: 1 on success +sub ProcessExifInfo($$$) +{ + my ($et, $dirInfo, $tagTablePtr) = @_; + my $dataPt = $$dirInfo{DataPt}; + my $start = $$dirInfo{DirStart} || 0; + my $dirLen = $$dirInfo{DirLen} || (length($$dataPt) - $start); + my $dirEnd = $start + $dirLen; + # loop through TIFF-format EXIF/MakerNote records + my ($pos, $len, $tag); + for ($pos = $start; $pos + 8 < $dirEnd; $pos += $len) { + $len = Get32u($dataPt, $pos); + $tag = Get32u($dataPt, $pos + 4); + # test size/tag for valid ExifInfo (not EXIF in CRM files) + last if $len < 8 or $pos + $len > $dirEnd or not $$tagTablePtr{$tag}; + $et->VerboseDir('ExifInfo', undef, $dirLen) if $pos == $start; + $et->HandleTag($tagTablePtr, $tag, undef, + DataPt => $dataPt, + Base => $$dirInfo{Base} + $pos + 8, # base for TIFF pointers + DataPos => -($pos + 8), # (relative to Base) + Start => $pos + 8, + Size => $len - 8, + ); + } + return 1; +} + +#------------------------------------------------------------------------------ +# Process Canon Timed MetaData (ref PH) +# Inputs: 0) ExifTool object ref, 1) dirInfo ref, 2) tag table ref +# Returns: 1 on success +sub ProcessCTMD($$$) +{ + my ($et, $dirInfo, $tagTablePtr) = @_; + my $dataPt = $$dirInfo{DataPt}; + my $verbose = $et->Options('Verbose'); + my $dirLen = length $$dataPt; + my $pos = 0; + SetByteOrder('II'); + while ($pos + 6 < $dirLen) { + my $size = Get32u($dataPt, $pos); + my $type = Get16u($dataPt, $pos + 4); + # what is the meaning of the 6-byte header of these records?: + # type 1 - 00 00 00 01 zz zz - TimeStamp(CR3/CRM); zz=00(CR3),ff(CRM) + # type 3 - 00 00 00 01 zz zz - ? "ff ff ff ff"; zz=00(CR3),ff(CRM) + # type 4 - 00 00 00 01 ff ff - FocalInfo(CR3/CRM) + # type 5 - 00 00 00 01 ff ff - ExposureInfo(CR3/CRM) + # type 6 - 00 04 00 01 ff ff - ? "03 04 00 80 e0 15 ff ff"(CRM) [0x15e0 = ColorTemperature?] + # type 7 - xx yy 00 01 ff ff - ExifIFD + MakerNotes(CR3), ?(CRM); xxyy=0101(CR3),0004(CRM) + # type 8 - 01 yy 00 01 ff ff - MakerNotes(CR3), ?(CRM); yy=01(CR3),04(CRM) + # type 9 - 01 yy 00 01 ff ff - MakerNotes(CR3), ?(CRM); yy=01(CR3),00(CRM) + # type 10- 01 00 00 01 ff ff - ? (CRM) + # type 11- 01 00 00 01 ff ff - ? (CRM) + # --> maybe yy == 01 for ExifInfo? + $size < 12 and $et->Warn('Short CTMD record'), last; + $pos + $size > $dirLen and $et->Warn('Truncated CTMD record'), last; + $et->VerboseDir("CTMD type $type", undef, $size - 6); + HexDump($dataPt, 6, # dump 6-byte header + Start => $pos + 6, + Addr => $$dirInfo{Base} + $pos + 6, + Prefix => $$et{INDENT}, + ) if $verbose > 2; + if ($$tagTablePtr{$type}) { + $et->HandleTag($tagTablePtr, $type, undef, + DataPt => $dataPt, + Base => $$dirInfo{Base}, + Start => $pos + 12, + Size => $size - 12, + ); + } elsif ($verbose) { + $et->VerboseDump($dataPt, Len=>$size-12, Start=>$pos+12, DataPos=>$$dirInfo{Base}); + } + $pos += $size; + } + $et->Warn('Error parsing Canon CTMD data', 1) if $pos != $dirLen; + return 1; +} + +#------------------------------------------------------------------------------ # Process a creative filter data # Inputs: 0) ExifTool object ref, 1) dirInfo ref, 2) tag table ref # Returns: 1 on success @@ -8913,7 +9469,7 @@ =head1 AUTHOR -Copyright 2003-2017, Phil Harvey (phil at owl.phy.queensu.ca) +Copyright 2003-2019, Phil Harvey (phil at owl.phy.queensu.ca) This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself. diff -Nru xnview-0.92/opt64/XnView/AddOn/lib/Image/ExifTool/CanonRaw.pm xnview-0.93/opt64/XnView/AddOn/lib/Image/ExifTool/CanonRaw.pm --- xnview-0.92/opt64/XnView/AddOn/lib/Image/ExifTool/CanonRaw.pm 2017-09-30 10:50:25.000000000 +0000 +++ xnview-0.93/opt64/XnView/AddOn/lib/Image/ExifTool/CanonRaw.pm 2019-01-10 14:15:16.000000000 +0000 @@ -877,7 +877,7 @@ =head1 AUTHOR -Copyright 2003-2017, Phil Harvey (phil at owl.phy.queensu.ca) +Copyright 2003-2019, Phil Harvey (phil at owl.phy.queensu.ca) This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself. diff -Nru xnview-0.92/opt64/XnView/AddOn/lib/Image/ExifTool/CanonVRD.pm xnview-0.93/opt64/XnView/AddOn/lib/Image/ExifTool/CanonVRD.pm --- xnview-0.92/opt64/XnView/AddOn/lib/Image/ExifTool/CanonVRD.pm 2017-09-30 10:50:27.000000000 +0000 +++ xnview-0.93/opt64/XnView/AddOn/lib/Image/ExifTool/CanonVRD.pm 2019-01-10 14:15:16.000000000 +0000 @@ -10,6 +10,7 @@ # 2010/06/18 - PH Support variable-length CustomPictureStyle data # 2010/09/14 - PH Added r/w support for XMP in VRD # 2015/05/16 - PH Added DR4 support (DPP 4.1.50.0) +# 2018/03/13 - PH Update to DPP 4.8.20 # # References: 1) Bogdan private communication (Canon DPP v3.4.1.1) # 2) Gert Kello private communiation (DPP 3.8) @@ -22,7 +23,7 @@ use Image::ExifTool qw(:DataAccess :Utils); use Image::ExifTool::Canon; -$VERSION = '1.28'; +$VERSION = '1.32'; sub ProcessCanonVRD($$;$); sub WriteCanonVRD($$;$); @@ -1144,6 +1145,8 @@ 0x20600 => 'LuminanceNoiseReduction', 0x20601 => 'ChrominanceNoiseReduction', # 0x20650 - fmt=9: 0 (JPG images) + 0x20670 => 'ColorMoireReduction', + '0x20670.0' => { Name => 'ColorMoireReductionOn', %noYes }, 0x20701 => { Name => 'ShootingDistance', Notes => '100% = infinity', @@ -1195,13 +1198,14 @@ }, # 0x20800 - fmt=1: 0 # 0x20801 - fmt=1: 0 + 0x2070b => { Name => 'DiffractionCorrectionOn', %noYes }, 0x20900 => 'ColorHue', 0x20901 => 'SaturationAdj', 0x20910 => 'RedHSL', 0x20911 => 'OrangeHSL', - 0x20912 => 'GreenHSL', - 0x20913 => 'AquaHSL', - 0x20914 => 'BlueHSL', + 0x20912 => 'YellowHSL', + 0x20913 => 'GreenHSL', + 0x20914 => 'AquaHSL', 0x20915 => 'BlueHSL', 0x20916 => 'PurpleHSL', 0x20917 => 'MagentaHSL', @@ -1280,7 +1284,7 @@ Name => 'DR4CameraModel', Writable => 'int32u', PrintHex => 1, - SeparateTable => 'Canon CameraModelID', + SeparateTable => 'Canon CanonModelID', PrintConv => \%Image::ExifTool::Canon::canonModelID, }, # 4 - value: 3 @@ -1517,6 +1521,7 @@ # make a copy for editing in place my $buff = substr($$dataPt, $pos, $dirLen); $dataPt = $$dirInfo{DataPt} = \$buff; + $dataPos += $pos; $pos = $$dirInfo{DirStart} = 0; } my $dirEnd = $pos + $dirLen; @@ -1542,16 +1547,7 @@ if ($verbose > 1 and not $outfile) { printf $out "$$et{INDENT}CanonVRD Edit record ($recLen bytes at offset 0x%x)\n", $pos + $dataPos; - if ($recNum and $verbose > 2) { - my %parms = ( - Start => $pos, - Addr => $pos + $dataPos, - Out => $out, - Prefix => $$et{INDENT}, - ); - $parms{MaxLen} = $verbose == 3 ? 96 : 2048 if $verbose < 5; - HexDump($dataPt, $recLen, %parms); - } + $et->VerboseDump($dataPt, Len => $recLen, Start => $pos, Addr => $pos + $dataPos) if $recNum; } # our edit information is the 0th record, so don't process the others @@ -2125,16 +2121,7 @@ if ($verbose > 1 and not $outfile) { printf $out " CanonVRD block 0x%.8x ($blockLen bytes at offset 0x%x)\n", $blockType, $pos + $$dirInfo{DataPos}; - if ($verbose > 2) { - my %parms = ( - Start => $pos, - Addr => $pos + $$dirInfo{DataPos}, - Out => $out, - Prefix => $$et{INDENT}, - ); - $parms{MaxLen} = $verbose == 3 ? 96 : 2048 if $verbose < 5; - HexDump($dataPt, $blockLen, %parms); - } + $et->VerboseDump($dataPt, Len => $blockLen, Start => $pos, Addr => $pos + $$dirInfo{DataPos}); } my $tagInfo = $$tagTablePtr{$blockType}; unless ($tagInfo) { @@ -2255,7 +2242,7 @@ =head1 AUTHOR -Copyright 2003-2017, Phil Harvey (phil at owl.phy.queensu.ca) +Copyright 2003-2019, Phil Harvey (phil at owl.phy.queensu.ca) This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself. diff -Nru xnview-0.92/opt64/XnView/AddOn/lib/Image/ExifTool/CaptureOne.pm xnview-0.93/opt64/XnView/AddOn/lib/Image/ExifTool/CaptureOne.pm --- xnview-0.92/opt64/XnView/AddOn/lib/Image/ExifTool/CaptureOne.pm 2017-09-30 10:50:28.000000000 +0000 +++ xnview-0.93/opt64/XnView/AddOn/lib/Image/ExifTool/CaptureOne.pm 2019-01-10 14:15:16.000000000 +0000 @@ -221,7 +221,7 @@ =head1 AUTHOR -Copyright 2003-2017, Phil Harvey (phil at owl.phy.queensu.ca) +Copyright 2003-2019, Phil Harvey (phil at owl.phy.queensu.ca) This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself. diff -Nru xnview-0.92/opt64/XnView/AddOn/lib/Image/ExifTool/Casio.pm xnview-0.93/opt64/XnView/AddOn/lib/Image/ExifTool/Casio.pm --- xnview-0.92/opt64/XnView/AddOn/lib/Image/ExifTool/Casio.pm 2017-09-30 10:50:25.000000000 +0000 +++ xnview-0.93/opt64/XnView/AddOn/lib/Image/ExifTool/Casio.pm 2019-01-10 14:15:16.000000000 +0000 @@ -2034,7 +2034,7 @@ =head1 AUTHOR -Copyright 2003-2017, Phil Harvey (phil at owl.phy.queensu.ca) +Copyright 2003-2019, Phil Harvey (phil at owl.phy.queensu.ca) This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself. diff -Nru xnview-0.92/opt64/XnView/AddOn/lib/Image/ExifTool/Charset/DOSLatin1.pm xnview-0.93/opt64/XnView/AddOn/lib/Image/ExifTool/Charset/DOSLatin1.pm --- xnview-0.92/opt64/XnView/AddOn/lib/Image/ExifTool/Charset/DOSLatin1.pm 1970-01-01 00:00:00.000000000 +0000 +++ xnview-0.93/opt64/XnView/AddOn/lib/Image/ExifTool/Charset/DOSLatin1.pm 2017-10-31 13:27:24.000000000 +0000 @@ -0,0 +1,49 @@ +#------------------------------------------------------------------------------ +# File: DOSLatin1.pm +# +# Description: cp850 to Unicode +# +# Revisions: 2017/10/31- P. Harvey created +# +# References: 1) http://unicode.org/Public/MAPPINGS/VENDORS/MICSFT/PC/CP850.TXT +# +# Notes: The table omits 1-byte characters with the same values as Unicode +#------------------------------------------------------------------------------ +use strict; + +%Image::ExifTool::Charset::DOSLatin1 = ( + 0x80 => 0x00c7, 0x81 => 0x00fc, 0x82 => 0x00e9, 0x83 => 0x00e2, + 0x84 => 0x00e4, 0x85 => 0x00e0, 0x86 => 0x00e5, 0x87 => 0x00e7, + 0x88 => 0x00ea, 0x89 => 0x00eb, 0x8a => 0x00e8, 0x8b => 0x00ef, + 0x8c => 0x00ee, 0x8d => 0x00ec, 0x8e => 0x00c4, 0x8f => 0x00c5, + 0x90 => 0x00c9, 0x91 => 0x00e6, 0x92 => 0x00c6, 0x93 => 0x00f4, + 0x94 => 0x00f6, 0x95 => 0x00f2, 0x96 => 0x00fb, 0x97 => 0x00f9, + 0x98 => 0x00ff, 0x99 => 0x00d6, 0x9a => 0x00dc, 0x9b => 0x00f8, + 0x9c => 0x00a3, 0x9d => 0x00d8, 0x9e => 0x00d7, 0x9f => 0x0192, + 0xa0 => 0x00e1, 0xa1 => 0x00ed, 0xa2 => 0x00f3, 0xa3 => 0x00fa, + 0xa4 => 0x00f1, 0xa5 => 0x00d1, 0xa6 => 0x00aa, 0xa7 => 0x00ba, + 0xa8 => 0x00bf, 0xa9 => 0x00ae, 0xaa => 0x00ac, 0xab => 0x00bd, + 0xac => 0x00bc, 0xad => 0x00a1, 0xae => 0x00ab, 0xaf => 0x00bb, + 0xb0 => 0x2591, 0xb1 => 0x2592, 0xb2 => 0x2593, 0xb3 => 0x2502, + 0xb4 => 0x2524, 0xb5 => 0x00c1, 0xb6 => 0x00c2, 0xb7 => 0x00c0, + 0xb8 => 0x00a9, 0xb9 => 0x2563, 0xba => 0x2551, 0xbb => 0x2557, + 0xbc => 0x255d, 0xbd => 0x00a2, 0xbe => 0x00a5, 0xbf => 0x2510, + 0xc0 => 0x2514, 0xc1 => 0x2534, 0xc2 => 0x252c, 0xc3 => 0x251c, + 0xc4 => 0x2500, 0xc5 => 0x253c, 0xc6 => 0x00e3, 0xc7 => 0x00c3, + 0xc8 => 0x255a, 0xc9 => 0x2554, 0xca => 0x2569, 0xcb => 0x2566, + 0xcc => 0x2560, 0xcd => 0x2550, 0xce => 0x256c, 0xcf => 0x00a4, + 0xd0 => 0x00f0, 0xd1 => 0x00d0, 0xd2 => 0x00ca, 0xd3 => 0x00cb, + 0xd4 => 0x00c8, 0xd5 => 0x0131, 0xd6 => 0x00cd, 0xd7 => 0x00ce, + 0xd8 => 0x00cf, 0xd9 => 0x2518, 0xda => 0x250c, 0xdb => 0x2588, + 0xdc => 0x2584, 0xdd => 0x00a6, 0xde => 0x00cc, 0xdf => 0x2580, + 0xe0 => 0x00d3, 0xe1 => 0x00df, 0xe2 => 0x00d4, 0xe3 => 0x00d2, + 0xe4 => 0x00f5, 0xe5 => 0x00d5, 0xe6 => 0x00b5, 0xe7 => 0x00fe, + 0xe8 => 0x00de, 0xe9 => 0x00da, 0xea => 0x00db, 0xeb => 0x00d9, + 0xec => 0x00fd, 0xed => 0x00dd, 0xee => 0x00af, 0xef => 0x00b4, + 0xf0 => 0x00ad, 0xf1 => 0x00b1, 0xf2 => 0x2017, 0xf3 => 0x00be, + 0xf4 => 0x00b6, 0xf5 => 0x00a7, 0xf6 => 0x00f7, 0xf7 => 0x00b8, + 0xf8 => 0x00b0, 0xf9 => 0x00a8, 0xfa => 0x00b7, 0xfb => 0x00b9, + 0xfc => 0x00b3, 0xfd => 0x00b2, 0xfe => 0x25a0, 0xff => 0x00a0, +); + +1; # end diff -Nru xnview-0.92/opt64/XnView/AddOn/lib/Image/ExifTool/Charset/DOSLatinUS.pm xnview-0.93/opt64/XnView/AddOn/lib/Image/ExifTool/Charset/DOSLatinUS.pm --- xnview-0.92/opt64/XnView/AddOn/lib/Image/ExifTool/Charset/DOSLatinUS.pm 1970-01-01 00:00:00.000000000 +0000 +++ xnview-0.93/opt64/XnView/AddOn/lib/Image/ExifTool/Charset/DOSLatinUS.pm 2017-10-31 13:27:21.000000000 +0000 @@ -0,0 +1,49 @@ +#------------------------------------------------------------------------------ +# File: DOSLatinUS.pm +# +# Description: cp437 to Unicode +# +# Revisions: 2017/10/31- P. Harvey created +# +# References: 1) http://unicode.org/Public/MAPPINGS/VENDORS/MICSFT/PC/CP437.TXT +# +# Notes: The table omits 1-byte characters with the same values as Unicode +#------------------------------------------------------------------------------ +use strict; + +%Image::ExifTool::Charset::DOSLatinUS = ( + 0x80 => 0x00c7, 0x81 => 0x00fc, 0x82 => 0x00e9, 0x83 => 0x00e2, + 0x84 => 0x00e4, 0x85 => 0x00e0, 0x86 => 0x00e5, 0x87 => 0x00e7, + 0x88 => 0x00ea, 0x89 => 0x00eb, 0x8a => 0x00e8, 0x8b => 0x00ef, + 0x8c => 0x00ee, 0x8d => 0x00ec, 0x8e => 0x00c4, 0x8f => 0x00c5, + 0x90 => 0x00c9, 0x91 => 0x00e6, 0x92 => 0x00c6, 0x93 => 0x00f4, + 0x94 => 0x00f6, 0x95 => 0x00f2, 0x96 => 0x00fb, 0x97 => 0x00f9, + 0x98 => 0x00ff, 0x99 => 0x00d6, 0x9a => 0x00dc, 0x9b => 0x00a2, + 0x9c => 0x00a3, 0x9d => 0x00a5, 0x9e => 0x20a7, 0x9f => 0x0192, + 0xa0 => 0x00e1, 0xa1 => 0x00ed, 0xa2 => 0x00f3, 0xa3 => 0x00fa, + 0xa4 => 0x00f1, 0xa5 => 0x00d1, 0xa6 => 0x00aa, 0xa7 => 0x00ba, + 0xa8 => 0x00bf, 0xa9 => 0x2310, 0xaa => 0x00ac, 0xab => 0x00bd, + 0xac => 0x00bc, 0xad => 0x00a1, 0xae => 0x00ab, 0xaf => 0x00bb, + 0xb0 => 0x2591, 0xb1 => 0x2592, 0xb2 => 0x2593, 0xb3 => 0x2502, + 0xb4 => 0x2524, 0xb5 => 0x2561, 0xb6 => 0x2562, 0xb7 => 0x2556, + 0xb8 => 0x2555, 0xb9 => 0x2563, 0xba => 0x2551, 0xbb => 0x2557, + 0xbc => 0x255d, 0xbd => 0x255c, 0xbe => 0x255b, 0xbf => 0x2510, + 0xc0 => 0x2514, 0xc1 => 0x2534, 0xc2 => 0x252c, 0xc3 => 0x251c, + 0xc4 => 0x2500, 0xc5 => 0x253c, 0xc6 => 0x255e, 0xc7 => 0x255f, + 0xc8 => 0x255a, 0xc9 => 0x2554, 0xca => 0x2569, 0xcb => 0x2566, + 0xcc => 0x2560, 0xcd => 0x2550, 0xce => 0x256c, 0xcf => 0x2567, + 0xd0 => 0x2568, 0xd1 => 0x2564, 0xd2 => 0x2565, 0xd3 => 0x2559, + 0xd4 => 0x2558, 0xd5 => 0x2552, 0xd6 => 0x2553, 0xd7 => 0x256b, + 0xd8 => 0x256a, 0xd9 => 0x2518, 0xda => 0x250c, 0xdb => 0x2588, + 0xdc => 0x2584, 0xdd => 0x258c, 0xde => 0x2590, 0xdf => 0x2580, + 0xe0 => 0x03b1, 0xe1 => 0x00df, 0xe2 => 0x0393, 0xe3 => 0x03c0, + 0xe4 => 0x03a3, 0xe5 => 0x03c3, 0xe6 => 0x00b5, 0xe7 => 0x03c4, + 0xe8 => 0x03a6, 0xe9 => 0x0398, 0xea => 0x03a9, 0xeb => 0x03b4, + 0xec => 0x221e, 0xed => 0x03c6, 0xee => 0x03b5, 0xef => 0x2229, + 0xf0 => 0x2261, 0xf1 => 0x00b1, 0xf2 => 0x2265, 0xf3 => 0x2264, + 0xf4 => 0x2320, 0xf5 => 0x2321, 0xf6 => 0x00f7, 0xf7 => 0x2248, + 0xf8 => 0x00b0, 0xf9 => 0x2219, 0xfa => 0x00b7, 0xfb => 0x221a, + 0xfc => 0x207f, 0xfd => 0x00b2, 0xfe => 0x25a0, 0xff => 0x00a0, +); + +1; # end diff -Nru xnview-0.92/opt64/XnView/AddOn/lib/Image/ExifTool/Charset.pm xnview-0.93/opt64/XnView/AddOn/lib/Image/ExifTool/Charset.pm --- xnview-0.92/opt64/XnView/AddOn/lib/Image/ExifTool/Charset.pm 2017-09-30 10:50:21.000000000 +0000 +++ xnview-0.93/opt64/XnView/AddOn/lib/Image/ExifTool/Charset.pm 2019-01-10 14:15:16.000000000 +0000 @@ -14,7 +14,7 @@ use vars qw($VERSION %csType); use Image::ExifTool qw(:DataAccess :Utils); -$VERSION = '1.10'; +$VERSION = '1.11'; my %charsetTable; # character set tables we've loaded @@ -57,6 +57,8 @@ Hebrew => 0x101, Latin => 0x101, Latin2 => 0x101, + DOSLatinUS => 0x101, + DOSLatin1 => 0x101, MacCroatian => 0x101, MacCyrillic => 0x101, MacGreek => 0x101, @@ -405,10 +407,11 @@ character sets. Currently, the following character sets are supported: UTF8, UTF16, UCS2, UCS4, Arabic, Baltic, Cyrillic, Greek, Hebrew, JIS, - Latin, Latin2, MacArabic, MacChineseCN, MacChineseTW, MacCroatian, - MacCyrillic, MacGreek, MacHebrew, MacIceland, MacJapanese, MacKorean, - MacLatin2, MacRSymbol, MacRoman, MacRomanian, MacThai, MacTurkish, - PDFDoc, RSymbol, ShiftJIS, Symbol, Thai, Turkish, Vietnam + Latin, Latin2, DOSLatinUS, DOSLatin1, MacArabic, MacChineseCN, + MacChineseTW, MacCroatian, MacCyrillic, MacGreek, MacHebrew, MacIceland, + MacJapanese, MacKorean, MacLatin2, MacRSymbol, MacRoman, MacRomanian, + MacThai, MacTurkish, PDFDoc, RSymbol, ShiftJIS, Symbol, Thai, Turkish, + Vietnam However, only some of these character sets are available to the user via ExifTool options -- the multi-byte character sets are used only internally @@ -416,7 +419,7 @@ =head1 AUTHOR -Copyright 2003-2017, Phil Harvey (phil at owl.phy.queensu.ca) +Copyright 2003-2019, Phil Harvey (phil at owl.phy.queensu.ca) This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself. diff -Nru xnview-0.92/opt64/XnView/AddOn/lib/Image/ExifTool/DarwinCore.pm xnview-0.93/opt64/XnView/AddOn/lib/Image/ExifTool/DarwinCore.pm --- xnview-0.92/opt64/XnView/AddOn/lib/Image/ExifTool/DarwinCore.pm 2017-09-30 10:50:30.000000000 +0000 +++ xnview-0.93/opt64/XnView/AddOn/lib/Image/ExifTool/DarwinCore.pm 2019-01-10 14:15:16.000000000 +0000 @@ -6,6 +6,7 @@ # Revisions: 2013-01-28 - P. Harvey Created # # References: 1) http://rs.tdwg.org/dwc/index.htm +# 2) http://u88.n24.queensu.ca/exiftool/forum/index.php/topic,4442.0/all.html #------------------------------------------------------------------------------ package Image::ExifTool::DarwinCore; @@ -14,7 +15,7 @@ use vars qw($VERSION); use Image::ExifTool::XMP; -$VERSION = '1.01'; +$VERSION = '1.02'; my %dateTimeInfo = ( # NOTE: Do NOT put "Groups" here because Groups hash must not be common! @@ -24,6 +25,37 @@ PrintConvInv => '$self->InverseDateTime($val,undef,1)', ); +my %materialSample = ( + STRUCT_NAME => 'DarwinCore MaterialSample', + NAMESPACE => 'dwc', + materialSampleID => { }, +); + +my %event = ( + STRUCT_NAME => 'DarwinCore Event', + NAMESPACE => 'dwc', + day => { Writable => 'integer', Groups => { 2 => 'Time' } }, + earliestDate => { %dateTimeInfo, Groups => { 2 => 'Time' } }, + endDayOfYear => { Writable => 'integer', Groups => { 2 => 'Time' } }, + eventDate => { %dateTimeInfo, Groups => { 2 => 'Time' } }, + eventID => { }, + eventRemarks => { Writable => 'lang-alt' }, + eventTime => { %dateTimeInfo, Groups => { 2 => 'Time' } }, + fieldNotes => { }, + fieldNumber => { }, + habitat => { }, + latestDate => { %dateTimeInfo, Groups => { 2 => 'Time' } }, + month => { Writable => 'integer', Groups => { 2 => 'Time' } }, + parentEventID => { }, + samplingEffort => { }, + samplingProtocol => { }, + sampleSizeValue => { }, + sampleSizeUnit => { }, + startDayOfYear => { Writable => 'integer', Groups => { 2 => 'Time' } }, + verbatimEventDate => { Groups => { 2 => 'Time' } }, + year => { Writable => 'integer', Groups => { 2 => 'Time' } }, +); + # Darwin Core tags %Image::ExifTool::DarwinCore::Main = ( GROUPS => { 0 => 'XMP', 1 => 'XMP-dwc', 2 => 'Other' }, @@ -36,31 +68,14 @@ Event => { Name => 'DCEvent', # (avoid conflict with XMP-iptcExt:Event) FlatName => 'Event', - Struct => { - STRUCT_NAME => 'DarwinCore Event', - NAMESPACE => 'dwc', - day => { Writable => 'integer', Groups => { 2 => 'Time' } }, - earliestDate => { %dateTimeInfo, Groups => { 2 => 'Time' } }, - endDayOfYear => { Writable => 'integer', Groups => { 2 => 'Time' } }, - eventID => { }, - eventRemarks => { Writable => 'lang-alt' }, - eventTime => { %dateTimeInfo, Groups => { 2 => 'Time' } }, - fieldNotes => { }, - fieldNumber => { }, - habitat => { }, - latestDate => { %dateTimeInfo, Groups => { 2 => 'Time' } }, - month => { Writable => 'integer', Groups => { 2 => 'Time' } }, - samplingEffort => { }, - samplingProtocol => { }, - startDayOfYear => { Writable => 'integer', Groups => { 2 => 'Time' } }, - verbatimEventDate => { Groups => { 2 => 'Time' } }, - year => { Writable => 'integer', Groups => { 2 => 'Time' } }, - }, + Struct => \%event, }, # tweak a few of the flattened tag names + EventEventDate => { Name => 'EventDate', Flat => 1 }, EventEventID => { Name => 'EventID', Flat => 1 }, EventEventRemarks => { Name => 'EventRemarks', Flat => 1 }, EventEventTime => { Name => 'EventTime', Flat => 1 }, + FossilSpecimen => { Struct => \%materialSample }, GeologicalContext => { FlatName => '', # ('GeologicalContext' is too long) Struct => { @@ -90,6 +105,7 @@ GeologicalContextFormation => { Name => 'GeologicalContextFormation', Flat => 1 }, GeologicalContextGroup => { Name => 'GeologicalContextGroup', Flat => 1 }, GeologicalContextMember => { Name => 'GeologicalContextMember', Flat => 1 }, + HumanObservation => { Struct => \%event }, Identification => { FlatName => '', # ('Identification' is redundant) Struct => { @@ -105,6 +121,10 @@ typeStatus => { }, }, }, + LivingSpecimen => { Struct => \%materialSample }, + MachineObservation => { Struct => \%event }, + MaterialSample => { Struct => \%materialSample }, + MaterialSampleMaterialSampleID => { Name => 'MaterialSampleID', Flat => 1 }, MeasurementOrFact => { FlatName => '', # ('MeasurementOrFact' is redundant and too long) Struct => { @@ -141,6 +161,8 @@ occurrenceID => { }, occurrenceRemarks => { }, occurrenceStatus => { }, + organismQuantity => { }, + organismQuantityType => { }, otherCatalogNumbers => { }, preparations => { }, previousIdentifications => { }, @@ -150,10 +172,28 @@ sex => { }, }, }, - OccurrenceOccurrenceRemarks => { Name => 'OccurrenceRemarks', Flat => 1 }, OccurrenceOccurrenceDetails => { Name => 'OccurrenceDetails', Flat => 1 }, OccurrenceOccurrenceID => { Name => 'OccurrenceID', Flat => 1 }, + OccurrenceOccurrenceRemarks => { Name => 'OccurrenceRemarks', Flat => 1 }, OccurrenceOccurrenceStatus => { Name => 'OccurrenceStatus', Flat => 1 }, + Organism => { + Struct => { + STRUCT_NAME => 'DarwinCore Organism', + NAMESPACE => 'dwc', + associatedOccurrences => { }, + associatedOrganisms => { }, + organismID => { }, + organismName => { }, + organismRemarks => { }, + organismScope => { }, + previousIdentifications => { }, + }, + }, + OrganismOrganismID => { Name => 'OrganismID', Flat => 1 }, + OrganismOrganismName => { Name => 'OrganismName', Flat => 1 }, + OrganismOrganismRemarks => { Name => 'OrganismRemarks', Flat => 1 }, + OrganismOrganismScope => { Name => 'OrganismScope', Flat => 1 }, + PreservedSpecimen => { Struct => \%materialSample }, Record => { Struct => { STRUCT_NAME => 'DarwinCore Record', @@ -301,7 +341,7 @@ =head1 AUTHOR -Copyright 2003-2017, Phil Harvey (phil at owl.phy.queensu.ca) +Copyright 2003-2019, Phil Harvey (phil at owl.phy.queensu.ca) This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself. diff -Nru xnview-0.92/opt64/XnView/AddOn/lib/Image/ExifTool/DICOM.pm xnview-0.93/opt64/XnView/AddOn/lib/Image/ExifTool/DICOM.pm --- xnview-0.92/opt64/XnView/AddOn/lib/Image/ExifTool/DICOM.pm 2017-09-30 10:50:31.000000000 +0000 +++ xnview-0.93/opt64/XnView/AddOn/lib/Image/ExifTool/DICOM.pm 2019-01-10 14:15:16.000000000 +0000 @@ -3846,7 +3846,7 @@ =head1 AUTHOR -Copyright 2003-2017, Phil Harvey (phil at owl.phy.queensu.ca) +Copyright 2003-2019, Phil Harvey (phil at owl.phy.queensu.ca) This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself. diff -Nru xnview-0.92/opt64/XnView/AddOn/lib/Image/ExifTool/DJI.pm xnview-0.93/opt64/XnView/AddOn/lib/Image/ExifTool/DJI.pm --- xnview-0.92/opt64/XnView/AddOn/lib/Image/ExifTool/DJI.pm 2017-09-30 10:50:29.000000000 +0000 +++ xnview-0.93/opt64/XnView/AddOn/lib/Image/ExifTool/DJI.pm 2019-01-10 14:15:16.000000000 +0000 @@ -77,7 +77,7 @@ =head1 AUTHOR -Copyright 2003-2017, Phil Harvey (phil at owl.phy.queensu.ca) +Copyright 2003-2019, Phil Harvey (phil at owl.phy.queensu.ca) This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself. diff -Nru xnview-0.92/opt64/XnView/AddOn/lib/Image/ExifTool/DjVu.pm xnview-0.93/opt64/XnView/AddOn/lib/Image/ExifTool/DjVu.pm --- xnview-0.92/opt64/XnView/AddOn/lib/Image/ExifTool/DjVu.pm 2017-09-30 10:50:27.000000000 +0000 +++ xnview-0.93/opt64/XnView/AddOn/lib/Image/ExifTool/DjVu.pm 2019-01-10 14:15:16.000000000 +0000 @@ -18,7 +18,7 @@ use vars qw($VERSION); use Image::ExifTool qw(:DataAccess :Utils); -$VERSION = '1.05'; +$VERSION = '1.06'; sub ParseAnt($); sub ProcessAnt($$$); @@ -227,6 +227,8 @@ last unless $tok =~ /(\\+)$/ and length($1) & 0x01; $tok .= '"'; # quote is part of the string } + # must protect unescaped "$" and "@" symbols, and "\" at end of string + $tok =~ s{\\(.)|([\$\@]|\\$)}{'\\'.($2 || $1)}sge; # convert C escape sequences (allowed in quoted text) $tok = eval qq{"$tok"}; } else { # key name @@ -351,7 +353,7 @@ =head1 AUTHOR -Copyright 2003-2017, Phil Harvey (phil at owl.phy.queensu.ca) +Copyright 2003-2019, Phil Harvey (phil at owl.phy.queensu.ca) This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself. diff -Nru xnview-0.92/opt64/XnView/AddOn/lib/Image/ExifTool/DNG.pm xnview-0.93/opt64/XnView/AddOn/lib/Image/ExifTool/DNG.pm --- xnview-0.92/opt64/XnView/AddOn/lib/Image/ExifTool/DNG.pm 2017-09-30 10:50:20.000000000 +0000 +++ xnview-0.93/opt64/XnView/AddOn/lib/Image/ExifTool/DNG.pm 2019-01-10 14:15:16.000000000 +0000 @@ -17,7 +17,7 @@ use Image::ExifTool::MakerNotes; use Image::ExifTool::CanonRaw; -$VERSION = '1.22'; +$VERSION = '1.23'; sub ProcessOriginalRaw($$$); sub ProcessAdobeData($$$); @@ -662,6 +662,7 @@ # Inputs: 0) ExifTool object ref, 1) dirInfo ref, 2) tag table ref # Returns: 1 on success, otherwise returns 0 and sets a Warning # Notes: data has 6 byte header (2 for byte order and 4 for original offset) +# --> or 18 bytes for DNG converted from JPG by Adobe Camera Raw! sub ProcessAdobeMakN($$$) { my ($et, $dirInfo, $tagTablePtr) = @_; @@ -677,8 +678,14 @@ $et->VerboseDir($dirInfo) unless $outfile; my $dataPos = $$dirInfo{DataPos}; - my $dirStart = $start + 6; # pointer to maker note directory - my $dirLen = $len - 6; + my $hdrLen = 6; + + # hack for extra 12 bytes in MakN header of JPEG converted to DNG by Adobe Camera Raw + # (4 bytes "00 00 00 01" followed by 8 unknown bytes) + $hdrLen += 12 if $len >= 18 and substr($$dataPt, $start+6, 4) eq "\0\0\0\x01"; + + my $dirStart = $start + $hdrLen; # pointer to maker note directory + my $dirLen = $len - $hdrLen; my $hdr = substr($$dataPt, $dirStart, $dirLen < 48 ? $dirLen : 48); my $tagInfo = $et->GetTagInfo($tagTablePtr, 'MakN', \$hdr); @@ -707,7 +714,7 @@ return 0; } if ($et->Options('HtmlDump')) { - $et->HDump($dataPos + $start, 6, 'Adobe MakN data'); + $et->HDump($dataPos + $start, $hdrLen, 'Adobe MakN data'); $et->HDump($dataPos + $dirStart, $loc, "$$tagInfo{Name} header") if $loc; } @@ -746,8 +753,8 @@ $loc = 0 if $subdirInfo{BlockWrite}; $fixup->{Shift} += $loc; # adjust for makernotes header $fixup->ApplyFixup(\$buff); # fix up pointer offsets - # get copy of original Adobe header (6) and makernotes header ($loc) - my $header = substr($$dataPt, $start, 6 + $loc); + # get copy of original Adobe header (6 or 18) and makernotes header ($loc) + my $header = substr($$dataPt, $start, $hdrLen + $loc); # add Adobe and makernotes headers to new directory $$outfile = $header . $buff; } else { @@ -764,7 +771,7 @@ $subdirInfo{DirStart} = $dirStart; $subdirInfo{DirLen} = $dirLen; # rebuild the maker notes to identify all offsets that require fixing up - $val = Image::ExifTool::Exif::RebuildMakerNotes($et, $subTable, \%subdirInfo); + $val = Image::ExifTool::Exif::RebuildMakerNotes($et, \%subdirInfo, $subTable); if (not defined $val and $dirLen > 4) { $et->Warn('Error rebuilding maker notes (may be corrupt)'); } @@ -813,7 +820,7 @@ =head1 AUTHOR -Copyright 2003-2017, Phil Harvey (phil at owl.phy.queensu.ca) +Copyright 2003-2019, Phil Harvey (phil at owl.phy.queensu.ca) This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself. diff -Nru xnview-0.92/opt64/XnView/AddOn/lib/Image/ExifTool/DPX.pm xnview-0.93/opt64/XnView/AddOn/lib/Image/ExifTool/DPX.pm --- xnview-0.92/opt64/XnView/AddOn/lib/Image/ExifTool/DPX.pm 2017-09-30 10:50:27.000000000 +0000 +++ xnview-0.93/opt64/XnView/AddOn/lib/Image/ExifTool/DPX.pm 2019-01-10 14:15:16.000000000 +0000 @@ -160,7 +160,7 @@ =head1 AUTHOR -Copyright 2003-2017, Phil Harvey (phil at owl.phy.queensu.ca) +Copyright 2003-2019, Phil Harvey (phil at owl.phy.queensu.ca) This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself. diff -Nru xnview-0.92/opt64/XnView/AddOn/lib/Image/ExifTool/DV.pm xnview-0.93/opt64/XnView/AddOn/lib/Image/ExifTool/DV.pm --- xnview-0.92/opt64/XnView/AddOn/lib/Image/ExifTool/DV.pm 2017-09-30 10:50:30.000000000 +0000 +++ xnview-0.93/opt64/XnView/AddOn/lib/Image/ExifTool/DV.pm 2019-01-10 14:15:16.000000000 +0000 @@ -295,7 +295,7 @@ =head1 AUTHOR -Copyright 2003-2017, Phil Harvey (phil at owl.phy.queensu.ca) +Copyright 2003-2019, Phil Harvey (phil at owl.phy.queensu.ca) This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself. diff -Nru xnview-0.92/opt64/XnView/AddOn/lib/Image/ExifTool/EXE.pm xnview-0.93/opt64/XnView/AddOn/lib/Image/ExifTool/EXE.pm --- xnview-0.92/opt64/XnView/AddOn/lib/Image/ExifTool/EXE.pm 2017-09-30 10:50:24.000000000 +0000 +++ xnview-0.93/opt64/XnView/AddOn/lib/Image/ExifTool/EXE.pm 2019-01-10 14:15:16.000000000 +0000 @@ -21,7 +21,7 @@ use vars qw($VERSION); use Image::ExifTool qw(:DataAccess :Utils); -$VERSION = '1.14'; +$VERSION = '1.15'; sub ProcessPEResources($$); sub ProcessPEVersion($$); @@ -218,10 +218,32 @@ ValueConv => 'ConvertUnixTime($val,1)', PrintConv => '$self->ConvertDateTime($val)', }, + 9 => { + Name => 'ImageFileCharacteristics', + # ref https://docs.microsoft.com/en-us/windows/desktop/api/winnt/ns-winnt-_image_file_header + PrintConv => { BITMASK => { + 0 => 'No relocs', + 1 => 'Executable', + 2 => 'No line numbers', + 3 => 'No symbols', + 4 => 'Aggressive working-set trim', + 5 => 'Large address aware', + 7 => 'Bytes reversed lo', + 8 => '32-bit', + 9 => 'No debug', + 10 => 'Removable run from swap', + 11 => 'Net run from swap', + 12 => 'System file', + 13 => 'DLL', + 14 => 'Uniprocessor only', + 15 => 'Bytes reversed hi', + }}, + }, 10 => { Name => 'PEType', PrintHex => 1, PrintConv => { + 0x107 => 'ROM Image', 0x10b => 'PE32', 0x20b => 'PE32+', }, @@ -1141,9 +1163,10 @@ # 20 int16u SizeOfOptionalHeader # 22 int16u Characteristics if ($size >= 24) { # PE header is 24 bytes (plus optional header) - my $machine = $Image::ExifTool::EXE::Main{0}{PrintConv}{Get16u(\$buff, 4)} || ''; + my $mach = Get16u(\$buff, 4); # MachineType + my $flags = Get16u(\$buff, 22); # ImageFileCharacteristics + my $machine = $Image::ExifTool::EXE::Main{0}{PrintConv}{$mach} || ''; my $winType = $machine =~ /64/ ? 'Win64' : 'Win32'; - my $flags = Get16u(\$buff, 22); $ext = $flags & 0x2000 ? 'DLL' : 'EXE'; $et->SetFileType("$winType $ext", undef, $ext); return 1 if $fast3; @@ -1155,11 +1178,12 @@ $buff .= $buf2; $size += $more; my $magic = Get16u(\$buff, 24); - # verify PE32/PE32+ magic number - unless ($magic == 0x10b or $magic == 0x20b) { + # verify PE magic number + unless ($magic == 0x107 or $magic == 0x10b or $magic == 0x20b) { $et->Warn('Unknown PE magic number'); return 1; } + # --> 64-bit if $magic is 0x20b ???? } else { $et->Warn('Error reading optional header'); } @@ -1367,7 +1391,7 @@ =head1 AUTHOR -Copyright 2003-2017, Phil Harvey (phil at owl.phy.queensu.ca) +Copyright 2003-2019, Phil Harvey (phil at owl.phy.queensu.ca) This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself. diff -Nru xnview-0.92/opt64/XnView/AddOn/lib/Image/ExifTool/Exif.pm xnview-0.93/opt64/XnView/AddOn/lib/Image/ExifTool/Exif.pm --- xnview-0.92/opt64/XnView/AddOn/lib/Image/ExifTool/Exif.pm 2017-09-30 10:50:28.000000000 +0000 +++ xnview-0.93/opt64/XnView/AddOn/lib/Image/ExifTool/Exif.pm 2019-01-21 15:10:28.000000000 +0000 @@ -40,6 +40,8 @@ # 27) Gregg Lee private communication # 28) http://wwwimages.adobe.com/www.adobe.com/content/dam/Adobe/en/devnet/cinemadng/pdfs/CinemaDNG_Format_Specification_v1_1.pdf # 29) http://www.libtiff.org +# 30) http://geotiff.maptools.org/spec/geotiffhome.html +# 31) https://android.googlesource.com/platform/external/dng_sdk/+/refs/heads/master/source/dng_tag_codes.h # IB) Iliah Borg private communication (LibRaw) # JD) Jens Duttke private communication #------------------------------------------------------------------------------ @@ -49,11 +51,11 @@ use strict; use vars qw($VERSION $AUTOLOAD @formatSize @formatName %formatNumber %intFormat %lightSource %flash %compression %photometricInterpretation %orientation - %subfileType); + %subfileType %saveForValidate); use Image::ExifTool qw(:DataAccess :Utils); use Image::ExifTool::MakerNotes; -$VERSION = '3.93'; +$VERSION = '4.10'; sub ProcessExif($$$); sub WriteExif($$$); @@ -61,6 +63,7 @@ sub RebuildMakerNotes($$$); sub EncodeExifText($$); sub ValidateIFD($;$); +sub ValidateImageData($$$;$); sub ProcessTiffIFD($$$); sub PrintParameter($$$); sub GetOffList($$$$$); @@ -217,8 +220,11 @@ 32898 => 'IT8BL', #3 32908 => 'PixarFilm', #3 32909 => 'PixarLog', #3 + # 32910,32911 - Pixar reserved 32946 => 'Deflate', #3 32947 => 'DCS', #3 + 33003 => 'Aperio JPEG 2000 YCbCr', #https://openslide.org/formats/aperio/ + 33005 => 'Aperio JPEG 2000 RGB', #https://openslide.org/formats/aperio/ 34661 => 'JBIG', #3 34676 => 'SGILog', #3 34677 => 'SGILog24', #3 @@ -228,7 +234,14 @@ 34718 => 'Microsoft Document Imaging (MDI) Binary Level Codec', #18 34719 => 'Microsoft Document Imaging (MDI) Progressive Transform Codec', #18 34720 => 'Microsoft Document Imaging (MDI) Vector', #18 + 34887 => 'ESRI Lerc', #LibTiff + # 34888,34889 - ESRI reserved 34892 => 'Lossy JPEG', # (DNG 1.4) + 34925 => 'LZMA2', #LibTiff + 34926 => 'Zstd', #LibTiff + 34927 => 'WebP', #LibTiff + 34933 => 'PNG', # (TIFF mail list) + 34934 => 'JPEG XR', # (TIFF mail list) 65000 => 'Kodak DCR Compressed', #PH 65535 => 'Pentax PEF Compressed', #Jens ); @@ -247,6 +260,7 @@ 32803 => 'Color Filter Array', #2 32844 => 'Pixar LogL', #3 32845 => 'Pixar LogLuv', #3 + 32892 => 'Sequential Color Filter', #JR (Sony ARQ) 34892 => 'Linear Raw', #2 ); @@ -313,6 +327,15 @@ 6 => 'Complex float', # complex IEEE floating point (ref 3) ); +# save the values of these tags for additional validation checks +%saveForValidate = ( + 0x100 => 1, # ImageWidth + 0x101 => 1, # ImageHeight + 0x102 => 1, # BitsPerSample + 0x103 => 1, # Compression + 0x115 => 1, # SamplesPerPixel +); + # main EXIF tag table %Image::ExifTool::Exif::Main = ( GROUPS => { 0 => 'EXIF', 1 => 'IFD0', 2 => 'Image'}, @@ -349,6 +372,7 @@ }, 0xfe => { Name => 'SubfileType', + Notes => 'called NewSubfileType by the TIFF specification', Protected => 1, Writable => 'int32u', WriteGroup => 'IFD0', @@ -359,6 +383,7 @@ }, 0xff => { Name => 'OldSubfileType', + Notes => 'called SubfileType by the TIFF specification', Protected => 1, Writable => 'int16u', WriteGroup => 'IFD0', @@ -502,9 +527,11 @@ ByteOrder => 'LittleEndian', }, { + # (APP1 IFD2 is for Leica JPEG preview) Condition => q[ not ($$self{TIFF_TYPE} eq 'CR2' and $$self{DIR_NAME} eq 'IFD0') and - not ($$self{TIFF_TYPE} eq 'DNG' and $$self{Compression} eq '7' and $$self{SubfileType} ne '0') + not ($$self{TIFF_TYPE} eq 'DNG' and $$self{Compression} eq '7' and $$self{SubfileType} ne '0') and + not ($$self{TIFF_TYPE} eq 'APP1' and $$self{DIR_NAME} eq 'IFD2') ], Name => 'StripOffsets', IsOffset => 1, @@ -586,9 +613,11 @@ ByteOrder => 'LittleEndian', }, { + # (APP1 IFD2 is for Leica JPEG preview) Condition => q[ not ($$self{TIFF_TYPE} eq 'CR2' and $$self{DIR_NAME} eq 'IFD0') and - not ($$self{TIFF_TYPE} eq 'DNG' and $$self{Compression} eq '7' and $$self{SubfileType} ne '0') + not ($$self{TIFF_TYPE} eq 'DNG' and $$self{Compression} eq '7' and $$self{SubfileType} ne '0') and + not ($$self{TIFF_TYPE} eq 'APP1' and $$self{DIR_NAME} eq 'IFD2') ], Name => 'StripByteCounts', OffsetPair => 0x111, # point to associated offset @@ -766,6 +795,7 @@ Writable => 'string', Shift => 'Time', WriteGroup => 'IFD0', + Validate => 'ValidateExifDate($val)', PrintConv => '$self->ConvertDateTime($val)', PrintConvInv => '$self->InverseDateTime($val,0)', }, @@ -1380,10 +1410,26 @@ }, }, # 0x7001 - int16u[1] (in SubIFD of Sony ARW images) - values: 0,1 - # 0x7010 - int16u[4] (in SubIFD of Sony ARW images) - values: "0 9824 11512 16362","8000 10400 12900 14100" + 0x7010 => { #IB + Name => 'SonyToneCurve', + # int16u[4] (in SubIFD of Sony ARW images -- don't allow writes for now) + # - only the middle 4 points are stored (lower comes from black level, + # and upper from data maximum) + }, # 0x7011 - int16u[4] (in SubIFD of Sony ARW images) - values: "0 4912 8212 12287","4000 7200 10050 12075" # 0x7020 - int32u[1] (in SubIFD of Sony ARW images) - values: 0,3 - # 0x7031 - int16u[1] (in SubIFD of Sony ARW images) - values: 256,257 + 0x7031 => { + Name => 'VignettingCorrection', + Notes => 'found in Sony ARW images', + Protected => 1, + Writable => 'int16s', + WriteGroup => 'SubIFD', + PrintConv => { + 256 => 'Off', + 257 => 'Auto', + 511 => 'No correction params available', + }, + }, 0x7032 => { Name => 'VignettingCorrParams', #forum7640 Notes => 'found in Sony ARW images', @@ -1392,7 +1438,18 @@ WriteGroup => 'SubIFD', Count => 17, }, - # 0x7034 - int16u[1] (in SubIFD of Sony ARW images) - values: 1 + 0x7034 => { + Name => 'ChromaticAberrationCorrection', + Notes => 'found in Sony ARW images', + Protected => 1, + Writable => 'int16s', + WriteGroup => 'SubIFD', + PrintConv => { + 0 => 'Off', + 1 => 'Auto', + 255 => 'No correction params available', + }, + }, 0x7035 => { Name => 'ChromaticAberrationCorrParams', #forum6509 Notes => 'found in Sony ARW images', @@ -1401,7 +1458,19 @@ WriteGroup => 'SubIFD', Count => 33, }, - # 0x7036 - int16u[1] (in SubIFD of Sony ARW images) - values: 0,1,17 + 0x7036 => { + Name => 'DistortionCorrection', + Notes => 'found in Sony ARW images', + Protected => 1, + Writable => 'int16s', + WriteGroup => 'SubIFD', + PrintConv => { + 0 => 'Off', + 1 => 'Auto', + 17 => 'Auto fixed by lens', + 255 => 'No correction params available', + }, + }, 0x7037 => { Name => 'DistortionCorrParams', #forum6509 Notes => 'found in Sony ARW images', @@ -1531,7 +1600,11 @@ 0x82aa => 'MDPrepDate', #3 0x82ab => 'MDPrepTime', #3 0x82ac => 'MDFileUnits', #3 - 0x830e => 'PixelScale', + 0x830e => { #30 + Name => 'PixelScale', + Writable => 'double', + Count => 3, + }, 0x8335 => 'AdventScale', #20 0x8336 => 'AdventRevision', #20 0x835c => 'UIC1Tag', #23 @@ -1558,11 +1631,17 @@ }, 0x847e => 'IntergraphPacketData', #3 0x847f => 'IntergraphFlagRegisters', #3 - 0x8480 => 'IntergraphMatrix', + 0x8480 => { #30 (obsolete) + Name => 'IntergraphMatrix', + Writable => 'double', + Count => -1, + }, 0x8481 => 'INGRReserved', #20 - 0x8482 => { + 0x8482 => { #30 Name => 'ModelTiePoint', Groups => { 2 => 'Location' }, + Writable => 'double', + Count => -1, }, 0x84e0 => 'Site', #9 0x84e1 => 'ColorSequence', #9 @@ -1625,9 +1704,11 @@ }, 0x85b8 => 'PixelMagicJBIGOptions', #20 0x85d7 => 'JPLCartoIFD', #exifprobe (NC) - 0x85d8 => { + 0x85d8 => { #30 Name => 'ModelTransform', Groups => { 2 => 'Location' }, + Writable => 'double', + Count => 16, }, 0x8602 => { #16 Name => 'WB_GRGBLevels', @@ -1645,6 +1726,7 @@ 0x8649 => { #19 Name => 'PhotoshopSettings', Format => 'binary', + WriteGroup => 'IFD0', # (only for Validate) SubDirectory => { DirName => 'Photoshop', TagTable => 'Image::ExifTool::Photoshop::Main', @@ -1703,7 +1785,7 @@ }, 0x8782 => 'T88Options', #20 0x87ac => 'ImageLayer', - 0x87af => { + 0x87af => { #30 Name => 'GeoTiffDirectory', Format => 'undef', Binary => 1, @@ -1725,7 +1807,7 @@ return pack('v*',unpack('n*',$val)); }, }, - 0x87b0 => { + 0x87b0 => { #30 Name => 'GeoTiffDoubleParams', Format => 'undef', Binary => 1, @@ -1743,7 +1825,7 @@ return pack('V*',unpack('N*',$val)); }, }, - 0x87b1 => { + 0x87b1 => { #30 Name => 'GeoTiffAsciiParams', Writable => 'string', WriteGroup => 'IFD0', @@ -1881,6 +1963,7 @@ Notes => 'date/time when original image was taken', Writable => 'string', Shift => 'Time', + Validate => 'ValidateExifDate($val)', PrintConv => '$self->ConvertDateTime($val)', PrintConvInv => '$self->InverseDateTime($val,0)', }, @@ -1890,6 +1973,7 @@ Notes => 'called DateTimeDigitized by the EXIF spec.', Writable => 'string', Shift => 'Time', + Validate => 'ValidateExifDate($val)', PrintConv => '$self->ConvertDateTime($val)', PrintConvInv => '$self->InverseDateTime($val,0)', }, @@ -2115,14 +2199,14 @@ 0x9217 => { #12 Name => 'SensingMethod', Groups => { 2 => 'Camera' }, - Notes => 'values 1 and 6 are not standard EXIF', PrintConv => { - 1 => 'Monochrome area', #12 (not standard EXIF) + # (values 1 and 6 are not used by corresponding EXIF tag 0xa217) + 1 => 'Monochrome area', 2 => 'One-chip color area', 3 => 'Two-chip color area', 4 => 'Three-chip color area', 5 => 'Color sequential area', - 6 => 'Monochrome linear', #12 (not standard EXIF) + 6 => 'Monochrome linear', 7 => 'Trilinear', 8 => 'Color sequential linear', }, @@ -2184,12 +2268,13 @@ Binary => 1, # (just in case -- don't know what format this is) }, 0x935c => { #3/19 - Name => 'ImageSourceData', + Name => 'ImageSourceData', # (writable directory!) Writable => 'undef', WriteGroup => 'IFD0', - Protected => 1, + SubDirectory => { TagTable => 'Image::ExifTool::Photoshop::DocumentData' }, Binary => 1, - Protected => 1, # (because this can be hundreds of megabytes) + Protected => 1, # (because this can be hundreds of megabytes) + ReadFromRAF => 1, # don't load into memory when reading }, 0x9400 => { Name => 'AmbientTemperature', @@ -2438,11 +2523,20 @@ 0xa401 => { Name => 'CustomRendered', Writable => 'int16u', + Notes => q{ + only 0 and 1 are standard EXIF, but other values are used by Apple iOS + devices + }, PrintConv => { 0 => 'Normal', 1 => 'Custom', - # 4 - Apple iPhone5c horizontal orientation - # 6 - Apple iPhone5c panorama + # 2 - also seen (Apple iOS) + 3 => 'HDR', # non-standard (Apple iOS) + # 4 - also seen (Apple iOS) - normal image from iOS Camera app (ref http://regex.info/blog/lightroom-goodies/metadata-presets) + 6 => 'Panorama', # non-standard (Apple iOS, horizontal or vertical) + # 7 - also seen (Apple iOS) + 8 => 'Portrait', # non-standard (Apple iOS, blurred background) + # 9 - also seen (Apple iOS) (HDR Portrait?) }, }, 0xa402 => { @@ -2577,7 +2671,7 @@ Writable => 'rational64u', Count => 4, # convert to the form "12-20mm f/3.8-4.5" or "50mm f/1.4" - PrintConv => \&Image::ExifTool::Exif::PrintLensInfo, + PrintConv => \&PrintLensInfo, PrintConvInv => \&ConvertLensInfo, }, 0xa433 => { Name => 'LensMake', Writable => 'string' }, #24 @@ -3052,6 +3146,7 @@ # - Adobe Camera Raw 5.3 gives an error # - Apple Preview 10.5.8 gets the wrong white balance FixFormat => 'int8u', # (stupid Sony) + WriteGroup => 'IFD0', # (for Validate) SubDirectory => { DirName => 'SR2Private', TagTable => 'Image::ExifTool::Sony::SR2Private', @@ -3066,7 +3161,7 @@ WriteGroup => 'IFD0', NestedHtmlDump => 1, SubDirectory => { TagTable => 'Image::ExifTool::DNG::AdobeData' }, - Format => 'undef', # written incorrectly as int8u (change to undef for speed) + Format => 'undef', # but written as int8u (change to undef for speed) }, { # Pentax/Samsung models that write AOC maker notes in JPG images: @@ -3079,6 +3174,7 @@ Name => 'MakerNotePentax', MakerNotes => 1, # (causes "MakerNotes header" to be identified in HtmlDump output) Binary => 1, + WriteGroup => 'IFD0', # (for Validate) # Note: Don't make this block-writable for a few reasons: # 1) It would be dangerous (possibly confusing Pentax software) # 2) It is a different format from the JPEG version of MakerNotePentax @@ -3089,7 +3185,7 @@ Base => '$start - 10', ByteOrder => 'Unknown', # easier to do this than read byteorder word }, - Format => 'undef', # written incorrectly as int8u (change to undef for speed) + Format => 'undef', # but written as int8u (change to undef for speed) }, { # must duplicate the above tag with a different name for more recent @@ -3099,6 +3195,7 @@ Name => 'MakerNotePentax5', MakerNotes => 1, Binary => 1, + WriteGroup => 'IFD0', # (for Validate) SubDirectory => { TagTable => 'Image::ExifTool::Pentax::Main', Start => '$valuePtr + 10', @@ -3111,7 +3208,7 @@ Name => 'DNGPrivateData', Flags => [ 'Binary', 'Protected' ], Format => 'undef', - Writable => 'undef', + Writable => 'int8u', WriteGroup => 'IFD0', }, ], @@ -3248,7 +3345,7 @@ WriteGroup => 'IFD0', Protected => 1, }, - 0xc6c5 => { Name => 'SRawType', Description => 'SRaw Type' }, #exifprobe (CR2 proprietary) + 0xc6c5 => { Name => 'SRawType', Description => 'SRaw Type', WriteGroup => 'IFD0' }, #exifprobe (CR2 proprietary) 0xc6d2 => { #JD (Panasonic DMC-TZ5) # this text is UTF-8 encoded (hooray!) - PH (TZ5) Name => 'PanasonicTitle', @@ -3275,6 +3372,7 @@ ValueConvInv => '$self->Encode($val,"UTF8")', }, # 0xc6dc - int32u[4]: found in CR2 images (PH, 7DmkIII) + # 0xc6dd - int16u[256]: found in CR2 images (PH, 5DmkIV) 0xc6f3 => { Name => 'CameraCalibrationSig', WriteGroup => 'IFD0', @@ -3719,7 +3817,17 @@ WriteGroup => 'IFD0', Protected => 1, }, - # 0xc7aa - undocumented DNG tag written by LR4 (int32u[1] - val=256, related to fast load data?) + # 0xc7a9 - CacheBlob (ref 31) + 0xc7aa => { #31 undocumented DNG tag written by LR4 (val=256, related to fast load data?) + Name => 'CacheVersion', + Writable => 'int32u', + WriteGroup => 'SubIFD2', + Format => 'int8u', + Count => 4, + Protected => 1, + PrintConv => '$val =~ tr/ /./; $val', + PrintConvInv => '$val =~ tr/./ /; $val', + }, 0xc7b5 => { # DNG 1.4 Name => 'DefaultUserCrop', Writable => 'rational64u', @@ -3727,9 +3835,21 @@ Count => 4, Protected => 1, }, + 0xc7d5 => { #PH (in SubIFD1 of Nikon Z6/Z7 NEF images) + Name => 'NikonNEFInfo', + Condition => '$$valPt =~ /^Nikon\0/', + SubDirectory => { + TagTable => 'Image::ExifTool::Nikon::NEFInfo', + Start => '$valuePtr + 18', + Base => '$start - 8', + ByteOrder => 'Unknown', + }, + }, + # 0xc7d6 - int8u: 1 (SubIFD1 of Nikon Z6/Z7 NEF) 0xea1c => { #13 Name => 'Padding', Binary => 1, + Protected => 1, Writable => 'undef', # must start with 0x1c 0xea by the WM Photo specification # (not sure what should happen if padding is only 1 byte) @@ -3739,6 +3859,7 @@ 0xea1d => { Name => 'OffsetSchema', Notes => "Microsoft's ill-conceived maker note offset difference", + Protected => 1, Writable => 'int32s', # From the Microsoft documentation: # @@ -4061,7 +4182,7 @@ 3 => 'FocusDistance', # focus distance in metres (0 is infinity) 4 => 'SubjectDistance', 5 => 'ObjectDistance', - 6 => 'ApproximateFocusDistance ', + 6 => 'ApproximateFocusDistance', 7 => 'FocusDistanceLower', 8 => 'FocusDistanceUpper', }, @@ -4407,7 +4528,8 @@ 0 => 'GPSLatitude', 1 => 'GPSLongitude', }, - ValueConv => '"$val[0] $val[1]"', + Priority => 0, + ValueConv => '(length($val[0]) or length($val[1])) ? "$val[0] $val[1]" : undef', PrintConv => '"$prt[0], $prt[1]"', }, LensID => { @@ -5246,6 +5368,17 @@ } #------------------------------------------------------------------------------ +# Utility routine to return tag ID string for warnings +# Inputs: 0) Tag ID, 1) [optional] TagInfo ref +# Returns: "tag 0xXXXX NAME" +sub TagName($;$) +{ + my ($tagID, $tagInfo) = @_; + my $tagName = $tagInfo ? ' '.$$tagInfo{Name} : ''; + return sprintf('tag 0x%.4x%s', $tagID, $tagName); +} + +#------------------------------------------------------------------------------ # Process EXIF directory # Inputs: 0) ExifTool object reference # 1) Reference to directory information hash @@ -5267,18 +5400,16 @@ my $validate = $et->Options('Validate'); my $htmlDump = $$et{HTML_DUMP}; my $success = 1; - my ($tagKey, $dirSize, $makerAddr, $strEnc); + my ($tagKey, $dirSize, $makerAddr, $strEnc, %offsetInfo); my $inMakerNotes = $$tagTablePtr{GROUPS}{0} eq 'MakerNotes'; - - require Image::ExifTool::Validate if $validate; + my $isExif = ($tagTablePtr eq \%Image::ExifTool::Exif::Main); # set encoding to assume for strings $strEnc = $et->Options('CharsetEXIF') if $$tagTablePtr{GROUPS}{0} eq 'EXIF'; # ignore non-standard EXIF while in strict MWG compatibility mode if (($validate or $Image::ExifTool::MWG::strict) and $dirName eq 'IFD0' and - $tagTablePtr eq \%Image::ExifTool::Exif::Main and - $$et{FILE_TYPE} =~ /^(JPEG|TIFF|PSD)$/) + $isExif and $$et{FILE_TYPE} =~ /^(JPEG|TIFF|PSD)$/) { my $path = $et->MetadataPath(); unless ($path =~ /^(JPEG-APP1-IFD0|TIFF-IFD0|PSD-EXIFInfo-IFD0)$/) { @@ -5296,8 +5427,8 @@ $dirName eq 'EXIF' and $dirName = $$dirInfo{DirName} = 'IFD0'; $$dirInfo{Multi} = 1 if $dirName =~ /^(IFD0|SubIFD)$/ and not defined $$dirInfo{Multi}; # get a more descriptive name for MakerNote sub-directories - my $name = $$dirInfo{Name}; - $name = $dirName unless $name and $inMakerNotes and $name !~ /^MakerNote/; + my $dir = $$dirInfo{Name}; + $dir = $dirName unless $dir and $inMakerNotes and $dir !~ /^MakerNote/; my ($numEntries, $dirEnd); if ($dirStart >= 0 and $dirStart <= $dataLen-2) { @@ -5309,7 +5440,7 @@ if (($verbose > 0 or $validate) and not $$dirInfo{SubIFD}) { my $short = $dirSize - $dirLen; $$et{INDENT} =~ s/..$//; # keep indent the same - $et->Warn("Short directory size for $name (missing $short bytes)"); + $et->Warn("Short directory size for $dir (missing $short bytes)"); $$et{INDENT} .= '| '; } undef $dirSize if $dirEnd > $dataLen; # read from file if necessary @@ -5341,7 +5472,7 @@ } } unless ($success) { - $et->Warn("Bad $name directory"); + $et->Warn("Bad $dir directory"); return 0; } $numEntries = Get16u($dataPt, $dirStart); @@ -5352,7 +5483,7 @@ my $bytesFromEnd = $dataLen - $dirEnd; if ($bytesFromEnd < 4) { unless ($bytesFromEnd==2 or $bytesFromEnd==0) { - $et->Warn("Illegal $name directory size ($numEntries entries)"); + $et->Warn("Illegal $dir directory size ($numEntries entries)"); return 0; } } @@ -5366,21 +5497,23 @@ } } if ($htmlDump) { - my $longName = $name eq 'MakerNotes' ? ($$dirInfo{Name} || $name) : $name; + my $longName = $dir eq 'MakerNotes' ? ($$dirInfo{Name} || $dir) : $dir; if (defined $makerAddr) { my $hdrLen = $dirStart + $dataPos + $base - $makerAddr; $et->HDump($makerAddr, $hdrLen, "MakerNotes header", $longName) if $hdrLen > 0; } unless ($$dirInfo{NoDumpEntryCount}) { $et->HDump($dirStart + $dataPos + $base, 2, "$longName entries", - "Entry count: $numEntries"); + "Entry count: $numEntries", undef, $dirName); } - my $tip; + my ($tip, $nxt); if ($bytesFromEnd >= 4) { - my $nxt = ($name =~ /^(.*?)(\d+)$/) ? $1 . ($2 + 1) : 'Next IFD'; + $nxt = ($dir =~ /^(.*?)(\d+)$/) ? $1 . ($2 + 1) : 'Next IFD'; $tip = sprintf("$nxt offset: 0x%.4x", Get32u($dataPt, $dirEnd)); + $nxt = $nxt eq 'Next IFD' ? undef : " Offset_$nxt"; } - $et->HDump($dirEnd + $dataPos + $base, 4, "Next IFD", $tip, 0); + $nxt = '' unless defined $nxt; + $et->HDump($dirEnd + $dataPos + $base, 4, "Next IFD", $tip, 0, "$dirName$nxt"); } # patch for Canon EOS 40D firmware 1.0.4 bug (incorrect directory counts) @@ -5407,7 +5540,7 @@ my ($warnCount, $lastID) = (0, -1); for ($index=0; $index<$numEntries; ++$index) { if ($warnCount > 10) { - $et->Warn("Too many warnings -- $name parsing aborted", 2) and return 0; + $et->Warn("Too many warnings -- $dir parsing aborted", 2) and return 0; } my $entry = $dirStart + 2 + 12 * $index; my $tagID = Get16u($dataPt, $entry); @@ -5421,7 +5554,7 @@ "Bad format type: $format", 1); # warn unless the IFD was just padded with zeros if ($format or $validate) { - $et->Warn("Bad format ($format) for $name entry $index", $inMakerNotes); + $et->Warn("Bad format ($format) for $dir entry $index", $inMakerNotes); ++$warnCount; } # assume corrupted IFD if this is our first entry (except Sony ILCE-7M2 firmware 1.21) @@ -5448,25 +5581,27 @@ } } $validate and not $inMakerNotes and Image::ExifTool::Validate::ValidateExif( - $et, $tagTablePtr, $tagID, $tagInfo, $lastID, $name, $count, $formatStr); + $et, $tagTablePtr, $tagID, $tagInfo, $lastID, $dir, $count, $formatStr); my $size = $count * $formatSize[$format]; my $readSize = $size; if ($size > 4) { if ($size > 0x7fffffff) { - $et->Warn(sprintf("Invalid size (%u) for %s tag 0x%.4x", $size, $name, $tagID), $inMakerNotes); + $et->Warn(sprintf("Invalid size (%u) for %s %s",$size,$dir,TagName($tagID,$tagInfo)), $inMakerNotes); ++$warnCount; next; } $valuePtr = Get32u($dataPt, $valuePtr); if ($validate and not $inMakerNotes) { - $et->Warn(sprintf('Odd offset for %s tag 0x%.4x', $name, $tagID), 1) if $valuePtr & 0x01; - if ($valuePtr < 8 || $valuePtr + $size > ($$et{VALUE}{FileSize} || length($$dataPt))) { - $et->Warn(sprintf("Invalid offset for %s tag 0x%.4x", $name, $tagID)); + $et->Warn(sprintf('Odd offset for %s %s',$dir,TagName($tagID,$tagInfo)), 1) if $valuePtr & 0x01; + if ($valuePtr < 8 || ($valuePtr + $size > length($$dataPt) and + $valuePtr + $size > $$et{VALUE}{FileSize})) + { + $et->Warn(sprintf('Invalid offset for %s %s',$dir,TagName($tagID,$tagInfo))); ++$warnCount; next; } if ($valuePtr + $size > $dirStart + $dataPos and $valuePtr < $dirEnd + $dataPos + 4) { - $et->Warn(sprintf("Value for %s tag 0x%.4x overlaps IFD", $name, $tagID)); + $et->Warn(sprintf('Value for %s %s overlaps IFD', $dir, TagName($tagID,$tagInfo))); } } # fix valuePtr if necessary @@ -5495,13 +5630,12 @@ my $buff; if ($raf) { # avoid loading large binary data unless necessary - # (eg. ImageSourceData -- layers in Photoshop TIFF image) while ($size > BINARY_DATA_LIMIT) { if ($tagInfo) { # make large unknown blocks binary data $$tagInfo{Binary} = 1 if $$tagInfo{Unknown}; last unless $$tagInfo{Binary}; # must read non-binary data - last if $$tagInfo{SubDirectory}; # must read SubDirectory data + last if $$tagInfo{SubDirectory}; my $lcTag = lc($$tagInfo{Name}); if ($$et{OPTIONS}{Binary} and not $$et{EXCL_TAG_LOOKUP}{$lcTag}) @@ -5523,16 +5657,30 @@ last; } # read from file if necessary - unless (defined $buff or - ($raf->Seek($base + $valuePtr + $dataPos,0) and - $raf->Read($buff,$size) == $size)) - { - $et->Warn("Error reading value for $name entry $index", $inMakerNotes); - return 0 unless $inMakerNotes or $htmlDump; - ++$warnCount; - $buff = '' unless defined $buff; - $readSize = length $buff; - $bad = 1; + unless (defined $buff) { + my $wrn; + my $readFromRAF = ($tagInfo and $$tagInfo{ReadFromRAF}); + if (not $raf->Seek($base + $valuePtr + $dataPos, 0)) { + $wrn = "Invalid offset for $dir entry $index"; + } elsif ($readFromRAF and $size > BINARY_DATA_LIMIT and + not $$et{REQ_TAG_LOOKUP}{lc $$tagInfo{Name}}) + { + $buff = "$$tagInfo{Name} data $size bytes"; + $readSize = length $buff; + } elsif ($raf->Read($buff,$size) != $size) { + $wrn = "Error reading value for $dir entry $index"; + } elsif ($readFromRAF) { + # seek back to the start of the value + $raf->Seek($base + $valuePtr + $dataPos, 0); + } + if ($wrn) { + $et->Warn($wrn, $inMakerNotes); + return 0 unless $inMakerNotes or $htmlDump; + ++$warnCount; + $buff = '' unless defined $buff; + $readSize = length $buff; + $bad = 1; + } } $valueDataLen = length $buff; $valueDataPt = \$buff; @@ -5582,7 +5730,7 @@ }; } } else { - $et->Warn("Bad offset for $name $tagStr", $inMakerNotes); + $et->Warn("Bad offset for $dir $tagStr", $inMakerNotes); ++$warnCount; } unless (defined $buff) { @@ -5597,7 +5745,7 @@ # warn about suspect offsets if they didn't already cause another warning if (defined $suspect and $suspect == $warnCount) { my $tagStr = $tagInfo ? $$tagInfo{Name} : sprintf('tag 0x%.4x', $tagID); - if ($et->Warn("Suspicious $name offset for $tagStr", $inMakerNotes)) { + if ($et->Warn("Suspicious $dir offset for $tagStr", $inMakerNotes)) { ++$warnCount; next unless $verbose; } @@ -5607,7 +5755,7 @@ $formatStr = 'int8u' if $format == 7 and $count == 1; my ($val, $subdir, $wrongFormat); - if ($tagID > 0xf000 and $tagTablePtr eq \%Image::ExifTool::Exif::Main) { + if ($tagID > 0xf000 and $isExif) { my $oldInfo = $$tagTablePtr{$tagID}; if ((not $oldInfo or (ref $oldInfo eq 'HASH' and $$oldInfo{Condition} and not $$oldInfo{PSRaw})) and not $bad) @@ -5670,7 +5818,11 @@ } # verify that offset-type values are integral if (($$tagInfo{IsOffset} or $$tagInfo{SubIFD}) and not $intFormat{$formatStr}) { - $et->Warn("Wrong format ($formatStr) for $name $$tagInfo{Name}"); + $et->Warn(sprintf('Wrong format (%s) for %s 0x%.4x %s',$formatStr,$dir,$tagID,$$tagInfo{Name})); + if ($validate) { + $$et{WrongFormat}{"$dir:$$tagInfo{Name}"} = 1; + $offsetInfo{$tagID} = [ $tagInfo, '' ]; + } next unless $verbose; $wrongFormat = 1; } @@ -5708,7 +5860,7 @@ } else { $tagName = sprintf("Tag 0x%.4x",$tagID); } - my $dname = sprintf("${name}-%.2d", $index); + my $dname = sprintf("${dir}-%.2d", $index); # build our tool tip $size < 0 and $size = $count * $formatSize[$format]; my $fstr = "$formatName[$format]\[$count]"; @@ -5758,7 +5910,11 @@ } } $tip .= "Value: $tval"; - $et->HDump($entry+$dataPos+$base, 12, "$dname $colName", $tip, 1); + my $id = $dirName; + if ($tagInfo and $$tagInfo{SubIFD} and $$tagInfo{Groups}{1}) { + $id .= " Offset_$$tagInfo{Groups}{1}"; + } + $et->HDump($entry+$dataPos+$base, 12, "$dname $colName", $tip, 1, $id); next if $valueDataLen < 0; # don't process bad pointer entry if ($size > 4) { my $exifDumpPos = $valuePtr + $valueDataPos + $base; @@ -5775,7 +5931,12 @@ } } else { if ($tagID <= $lastID and not $inMakerNotes) { - $et->Warn(sprintf('Tag ID 0x%.4x out of sequence in %s', $tagID, $dirName)); + my $str = $tagInfo ? ' '.$$tagInfo{Name} : ''; + if ($tagID == $lastID) { + $et->Warn(sprintf('Duplicate tag 0x%.4x%s in %s', $tagID, $str, $dirName)); + } else { + $et->Warn(sprintf('Tag ID 0x%.4x%s out of sequence in %s', $tagID, $str, $dirName)); + } } $lastID = $tagID; if ($verbose > 0) { @@ -5915,7 +6076,7 @@ $msg .= " (directory end is $end but EXIF size is only $subdirDataLen)"; } } - $et->Warn($msg); + $et->Warn($msg, $inMakerNotes); last; } } @@ -5996,8 +6157,8 @@ if ($doMaker and $doMaker eq '2') { # extract maker notes without rebuilding (no fixup information) delete $$et{MAKER_NOTE_FIXUP}; - } elsif (not $$tagInfo{NotIFD}) { - # this is a pain, but we must rebuild EXIF-typemaker notes to + } elsif (not $$tagInfo{NotIFD} or $$tagInfo{IsPhaseOne}) { + # this is a pain, but we must rebuild EXIF-type maker notes to # include all the value data if data was outside the maker notes my %makerDirInfo = ( Name => $tagStr, @@ -6013,9 +6174,16 @@ FixOffsets => $$subdir{FixOffsets}, TagInfo => $tagInfo, ); - $makerDirInfo{FixBase} = 1 if $$subdir{FixBase}; - # rebuild maker notes (creates $$et{MAKER_NOTE_FIXUP}) - my $val2 = RebuildMakerNotes($et, $newTagTable, \%makerDirInfo); + my $val2; + if ($$tagInfo{IsPhaseOne}) { + $$et{DropTags} = 1; + $val2 = Image::ExifTool::PhaseOne::WritePhaseOne($et, \%makerDirInfo, $newTagTable); + delete $$et{DropTags}; + } else { + $makerDirInfo{FixBase} = 1 if $$subdir{FixBase}; + # rebuild maker notes (creates $$et{MAKER_NOTE_FIXUP}) + $val2 = RebuildMakerNotes($et, \%makerDirInfo, $newTagTable); + } if (defined $val2) { $val = $val2; } elsif ($size > 4) { @@ -6045,6 +6213,13 @@ } $val = join(' ', @vals); } + if ($validate) { + if ($$tagInfo{OffsetPair}) { + $offsetInfo{$tagID} = [ $tagInfo, $val ]; + } elsif ($saveForValidate{$tagID} and $isExif) { + $offsetInfo{$tagID} = $val; + } + } # save the value of this tag $tagKey = $et->FoundTag($tagInfo, $val); if (defined $tagKey) { @@ -6055,6 +6230,11 @@ } } + # validate image data offsets for this IFD + if ($validate and %offsetInfo) { + Image::ExifTool::Validate::ValidateOffsetInfo($et, \%offsetInfo, $$dirInfo{DirName}, $inMakerNotes) + } + # scan for subsequent IFD's if specified if ($$dirInfo{Multi} and $bytesFromEnd >= 4) { my $offset = Get32u($dataPt, $dirEnd); @@ -6101,7 +6281,7 @@ =head1 AUTHOR -Copyright 2003-2017, Phil Harvey (phil at owl.phy.queensu.ca) +Copyright 2003-2019, Phil Harvey (phil at owl.phy.queensu.ca) This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself. @@ -6150,6 +6330,8 @@ =item L<http://wwwimages.adobe.com/www.adobe.com/content/dam/Adobe/en/devnet/cinemadng/pdfs/CinemaDNG_Format_Specification_v1_1.pdf> +=item L<http://geotiff.maptools.org/spec/geotiffhome.html> + =back =head1 ACKNOWLEDGEMENTS diff -Nru xnview-0.92/opt64/XnView/AddOn/lib/Image/ExifTool/Fixup.pm xnview-0.93/opt64/XnView/AddOn/lib/Image/ExifTool/Fixup.pm --- xnview-0.92/opt64/XnView/AddOn/lib/Image/ExifTool/Fixup.pm 2017-09-30 10:50:29.000000000 +0000 +++ xnview-0.93/opt64/XnView/AddOn/lib/Image/ExifTool/Fixup.pm 2019-01-10 14:15:16.000000000 +0000 @@ -354,7 +354,7 @@ =head1 AUTHOR -Copyright 2003-2017, Phil Harvey (phil at owl.phy.queensu.ca) +Copyright 2003-2019, Phil Harvey (phil at owl.phy.queensu.ca) This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself. diff -Nru xnview-0.92/opt64/XnView/AddOn/lib/Image/ExifTool/FLAC.pm xnview-0.93/opt64/XnView/AddOn/lib/Image/ExifTool/FLAC.pm --- xnview-0.92/opt64/XnView/AddOn/lib/Image/ExifTool/FLAC.pm 2017-09-30 10:50:26.000000000 +0000 +++ xnview-0.93/opt64/XnView/AddOn/lib/Image/ExifTool/FLAC.pm 2019-01-10 14:15:16.000000000 +0000 @@ -14,7 +14,7 @@ use vars qw($VERSION); use Image::ExifTool qw(:DataAccess :Utils); -$VERSION = '1.07'; +$VERSION = '1.08'; sub ProcessBitStream($$$); @@ -62,6 +62,11 @@ ValueConv => '$val + 1', }, 'Bit108-143' => 'TotalSamples', + 'Bit144-271' => { #Tim Eliseo + Name => 'MD5Signature', + Format => 'undef', + ValueConv => 'unpack("H*",$val)', + }, ); %Image::ExifTool::FLAC::Picture = ( @@ -150,7 +155,7 @@ my $tag; if ($verbose) { - $et->VPrint(0, " + [BitStream directory, $dirLen bytes, '$byteOrder' order]\n"); + $et->VPrint(0, " + [BitStream directory, $dirLen bytes, '${byteOrder}' order]\n"); } foreach $tag (sort keys %$tagTablePtr) { next unless $tag =~ /^Bit(\d+)-?(\d+)?/; @@ -158,47 +163,52 @@ my ($i1, $i2) = (int($b1 / 8), int($b2 / 8)); # start/end byte numbers my ($f1, $f2) = ($b1 % 8, $b2 % 8); # start/end bit numbers within each byte last if $i2 >= $dirLen; - my $val = 0; - my ($i, $mask, $extra); - $extra = ', Mask=0x' if $verbose and ($f1 != 0 or $f2 != 7); - if ($byteOrder eq 'MM') { - # loop from high byte to low byte - for ($i=$i1; $i<=$i2; ++$i) { - $mask = 0xff; - if ($i == $i1 and $f1) { - # mask off high bits in first word (0 is high bit) - foreach ((8-$f1) .. 7) { $mask ^= (1 << $_) } + my ($val, $extra); + # if Format is unspecified, convert the specified number of bits to an unsigned integer, + # otherwise allow HandleTag to convert whole bytes the normal way (via undefined $val) + if (ref $$tagTablePtr{$tag} ne 'HASH' or not $$tagTablePtr{$tag}{Format}) { + my ($i, $mask); + $val = 0; + $extra = ', Mask=0x' if $verbose and ($f1 != 0 or $f2 != 7); + if ($byteOrder eq 'MM') { + # loop from high byte to low byte + for ($i=$i1; $i<=$i2; ++$i) { + $mask = 0xff; + if ($i == $i1 and $f1) { + # mask off high bits in first word (0 is high bit) + foreach ((8-$f1) .. 7) { $mask ^= (1 << $_) } + } + if ($i == $i2 and $f2 < 7) { + # mask off low bits in last word (7 is low bit) + foreach (0 .. (6-$f2)) { $mask ^= (1 << $_) } + } + $val = $val * 256 + ($mask & Get8u($dataPt, $i + $dirStart)); + $extra .= sprintf('%.2x', $mask) if $extra; } - if ($i == $i2 and $f2 < 7) { - # mask off low bits in last word (7 is low bit) - foreach (0 .. (6-$f2)) { $mask ^= (1 << $_) } + } else { + # (FLAC is big-endian, but support little-endian bit streams + # so this routine can be used by other modules) + # loop from high byte to low byte + for ($i=$i2; $i>=$i1; --$i) { + $mask = 0xff; + if ($i == $i1 and $f1) { + # mask off low bits in first word (0 is low bit) + foreach (0 .. ($f1-1)) { $mask ^= (1 << $_) } + } + if ($i == $i2 and $f2 < 7) { + # mask off high bits in last word (7 is high bit) + foreach (($f2+1) .. 7) { $mask ^= (1 << $_) } + } + $val = $val * 256 + ($mask & Get8u($dataPt, $i + $dirStart)); + $extra .= sprintf('%.2x', $mask) if $extra; } - $val = $val * 256 + ($mask & Get8u($dataPt, $i + $dirStart)); - $extra .= sprintf('%.2x', $mask) if $extra; } - } else { - # (FLAC is big-endian, but support little-endian bit streams - # so this routine can be used by other modules) - # loop from high byte to low byte - for ($i=$i2; $i>=$i1; --$i) { - $mask = 0xff; - if ($i == $i1 and $f1) { - # mask off low bits in first word (0 is low bit) - foreach (0 .. ($f1-1)) { $mask ^= (1 << $_) } - } - if ($i == $i2 and $f2 < 7) { - # mask off high bits in last word (7 is high bit) - foreach (($f2+1) .. 7) { $mask ^= (1 << $_) } - } - $val = $val * 256 + ($mask & Get8u($dataPt, $i + $dirStart)); - $extra .= sprintf('%.2x', $mask) if $extra; + # shift word down until low bit is in position 0 + until ($mask & 0x01) { + $val /= 2; + $mask >>= 1; } } - # shift word down until low bit is in position 0 - until ($mask & 0x01) { - $val /= 2; - $mask >>= 1; - } $et->HandleTag($tagTablePtr, $tag, $val, DataPt => $dataPt, DataPos => $dataPos, @@ -274,7 +284,7 @@ =head1 AUTHOR -Copyright 2003-2017, Phil Harvey (phil at owl.phy.queensu.ca) +Copyright 2003-2019, Phil Harvey (phil at owl.phy.queensu.ca) This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself. diff -Nru xnview-0.92/opt64/XnView/AddOn/lib/Image/ExifTool/FlashPix.pm xnview-0.93/opt64/XnView/AddOn/lib/Image/ExifTool/FlashPix.pm --- xnview-0.92/opt64/XnView/AddOn/lib/Image/ExifTool/FlashPix.pm 2017-09-30 10:50:27.000000000 +0000 +++ xnview-0.93/opt64/XnView/AddOn/lib/Image/ExifTool/FlashPix.pm 2019-01-10 14:15:16.000000000 +0000 @@ -9,6 +9,8 @@ # 2) http://www.graphcomp.com/info/specs/livepicture/fpx.pdf # 3) http://search.cpan.org/~jdb/libwin32/ # 4) http://msdn.microsoft.com/en-us/library/aa380374.aspx +# 5) http://www.cpan.org/modules/by-authors/id/H/HC/HCARVEY/File-MSWord-0.1.zip +# 6) https://msdn.microsoft.com/en-us/library/cc313153(v=office.12).aspx #------------------------------------------------------------------------------ package Image::ExifTool::FlashPix; @@ -19,7 +21,7 @@ use Image::ExifTool::Exif; use Image::ExifTool::ASF; # for GetGUID() -$VERSION = '1.29'; +$VERSION = '1.36'; sub ProcessFPX($$); sub ProcessFPXR($$$); @@ -27,7 +29,12 @@ sub ReadFPXValue($$$$$;$$); sub ProcessHyperlinks($$); sub ProcessContents($$$); +sub ProcessWordDocument($$$); +sub ProcessDocumentTable($); +sub ProcessCommentBy($$$); +sub ProcessLastSavedBy($$$); sub SetDocNum($$;$$$); +sub ConvertDTTM($); # sector type constants sub HDR_SIZE () { 512; } @@ -425,6 +432,21 @@ return substr($val, 8 + $pos, $len); }, }, + 'WordDocument' => { + Name => 'WordDocument', + SubDirectory => { TagTable => 'Image::ExifTool::FlashPix::WordDocument' }, + }, + # save these tables until after the WordDocument was processed + '0Table' => { + Name => 'Table0', + Hidden => 1, # (used only as temporary storage until table is processed) + Binary => 1, + }, + '1Table' => { + Name => 'Table1', + Hidden => 1, # (used only as temporary storage until table is processed) + Binary => 1, + }, Preview => { Name => 'PreviewImage', Groups => { 2 => 'Preview' }, @@ -1031,6 +1053,216 @@ }, ); +# decode Word document FIB header (ref [MS-DOC].pdf) +%Image::ExifTool::FlashPix::WordDocument = ( + PROCESS_PROC => \&ProcessWordDocument, + GROUPS => { 2 => 'Other' }, + FORMAT => 'int16u', + NOTES => 'Tags extracted from the Microsoft Word document stream.', + 0 => { + Name => 'Identification', + PrintHex => 1, + PrintConv => { + 0x6a62 => 'MS Word 97', + 0x626a => 'Word 98 Mac', + 0xa5dc => 'Word 6.0/7.0', + 0xa5ec => 'Word 8.0', + }, + }, + 3 => { + Name => 'LanguageCode', + PrintHex => 1, + PrintConv => { + 0x0400 => 'None', + 0x0401 => 'Arabic', + 0x0402 => 'Bulgarian', + 0x0403 => 'Catalan', + 0x0404 => 'Traditional Chinese', + 0x0804 => 'Simplified Chinese', + 0x0405 => 'Czech', + 0x0406 => 'Danish', + 0x0407 => 'German', + 0x0807 => 'German (Swiss)', + 0x0408 => 'Greek', + 0x0409 => 'English (US)', + 0x0809 => 'English (British)', + 0x0c09 => 'English (Australian)', + 0x040a => 'Spanish (Castilian)', + 0x080a => 'Spanish (Mexican)', + 0x040b => 'Finnish', + 0x040c => 'French', + 0x080c => 'French (Belgian)', + 0x0c0c => 'French (Canadian)', + 0x100c => 'French (Swiss)', + 0x040d => 'Hebrew', + 0x040e => 'Hungarian', + 0x040f => 'Icelandic', + 0x0410 => 'Italian', + 0x0810 => 'Italian (Swiss)', + 0x0411 => 'Japanese', + 0x0412 => 'Korean', + 0x0413 => 'Dutch', + 0x0813 => 'Dutch (Belgian)', + 0x0414 => 'Norwegian (Bokmal)', + 0x0814 => 'Norwegian (Nynorsk)', + 0x0415 => 'Polish', + 0x0416 => 'Portuguese (Brazilian)', + 0x0816 => 'Portuguese', + 0x0417 => 'Rhaeto-Romanic', + 0x0418 => 'Romanian', + 0x0419 => 'Russian', + 0x041a => 'Croato-Serbian (Latin)', + 0x081a => 'Serbo-Croatian (Cyrillic)', + 0x041b => 'Slovak', + 0x041c => 'Albanian', + 0x041d => 'Swedish', + 0x041e => 'Thai', + 0x041f => 'Turkish', + 0x0420 => 'Urdu', + 0x0421 => 'Bahasa', + 0x0422 => 'Ukrainian', + 0x0423 => 'Byelorussian', + 0x0424 => 'Slovenian', + 0x0425 => 'Estonian', + 0x0426 => 'Latvian', + 0x0427 => 'Lithuanian', + 0x0429 => 'Farsi', + 0x042d => 'Basque', + 0x042f => 'Macedonian', + 0x0436 => 'Afrikaans', + 0x043e => 'Malaysian', + }, + }, + 5 => { + Name => 'DocFlags', + Mask => 0xff0f, # ignore save count + RawConv => '$$self{DocFlags} = $val', + PrintConv => { BITMASK => { + 0 => 'Template', + 1 => 'AutoText only', + 2 => 'Complex', + 3 => 'Has picture', + # 4-7 = number of incremental saves + 8 => 'Encrypted', + 9 => '1Table', + 10 => 'Read only', + 11 => 'Passworded', + 12 => 'ExtChar', + 13 => 'Load override', + 14 => 'Far east', + 15 => 'Obfuscated', + }}, + }, + 9.1 => { + Name => 'System', + Mask => 0x0001, + PrintConv => { + 0x0000 => 'Windows', + 0x0001 => 'Macintosh', + }, + }, + 9.2 => { + Name => 'Word97', + Mask => 0x0010, + PrintConv => { 0 => 'No', 1 => 'Yes' }, + }, +); + +# tags decoded from Word document table +%Image::ExifTool::FlashPix::DocTable = ( + GROUPS => { 1 => 'MS-DOC', 2 => 'Document' }, + NOTES => 'Tags extracted from the Microsoft Word document table.', + VARS => { NO_ID => 1 }, + CommentBy => { + Groups => { 2 => 'Author' }, + Notes => 'enable Duplicates option to extract all entries', + }, + LastSavedBy => { + Groups => { 2 => 'Author' }, + Notes => 'enable Duplicates option to extract history of up to 10 entries', + }, + DOP => { SubDirectory => { TagTable => 'Image::ExifTool::FlashPix::DOP' } }, + ModifyDate => { + Groups => { 2 => 'Time' }, + Format => 'int64u', + Priority => 0, + RawConv => q{ + $val = $val * 1e-7 - 11644473600; # convert to seconds since 1970 + return $val > 0 ? $val : undef; + }, + ValueConv => 'ConvertUnixTime($val)', + PrintConv => '$self->ConvertDateTime($val)', + }, +# +# tags below are used internally in intermediate steps to extract the tags above +# + TableOffsets => { Hidden => 1 }, # stores offsets to extract data from document table + CommentByBlock => { # entire block of CommentBy entries + SubDirectory => { + TagTable => 'Image::ExifTool::FlashPix::DocTable', + ProcessProc => \&ProcessCommentBy, + }, + Hidden => 1, + }, + LastSavedByBlock => { # entire block of LastSavedBy entries + SubDirectory => { + TagTable => 'Image::ExifTool::FlashPix::DocTable', + ProcessProc => \&ProcessLastSavedBy, + }, + Hidden => 1, + }, +); + +# Microsoft Office Document Properties (ref [MS-DOC].pdf) +%Image::ExifTool::FlashPix::DOP = ( + PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData, + GROUPS => { 1 => 'MS-DOC', 2 => 'Document' }, + NOTES => 'Microsoft office document properties.', + 20 => { + Name => 'CreateDate', + Format => 'int32u', + Groups => { 2 => 'Time' }, + Priority => 0, + RawConv => \&ConvertDTTM, + PrintConv => '$self->ConvertDateTime($val)', + }, + 24 => { + Name => 'ModifyDate', + Format => 'int32u', + Groups => { 2 => 'Time' }, + Priority => 0, + RawConv => \&ConvertDTTM, + PrintConv => '$self->ConvertDateTime($val)', + }, + 28 => { + Name => 'LastPrinted', + Format => 'int32u', + Groups => { 2 => 'Time' }, + RawConv => \&ConvertDTTM, + PrintConv => '$self->ConvertDateTime($val)', + }, + 32 => { Name => 'RevisionNumber', Format => 'int16u' }, + 34 => { + Name => 'TotalEditTime', + Format => 'int32u', + PrintConv => 'ConvertTimeSpan($val,60)', + }, + # (according to the MS-DOC specification, the following are accurate only if + # flag 'X' is set, and flag 'u' specifies whether the main or subdoc tags are + # used, but in my tests it seems that both are filled in with reasonable values, + # so just extract the main counts and ignore the subdoc counts for now - PH) + 38 => { Name => 'Words', Format => 'int32u' }, + 42 => { Name => 'Characters', Format => 'int32u' }, + 46 => { Name => 'Pages', Format => 'int16u' }, + 48 => { Name => 'Paragraphs', Format => 'int32u' }, + 56 => { Name => 'Lines', Format => 'int32u' }, + #60 => { Name => 'WordsWithSubdocs', Format => 'int32u' }, + #64 => { Name => 'CharactersWithSubdocs', Format => 'int32u' }, + #68 => { Name => 'PagesWithSubdocs', Format => 'int16u' }, + #70 => { Name => 'ParagraphsWithSubdocs', Format => 'int32u' }, + #74 => { Name => 'LinesWithSubdocs', Format => 'int32u' }, +); + # FujiFilm "Property" information (ref PH) %Image::ExifTool::FlashPix::PreviewInfo = ( PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData, @@ -1073,6 +1305,22 @@ Image::ExifTool::AddCompositeTags('Image::ExifTool::FlashPix'); #------------------------------------------------------------------------------ +# Convert Microsoft DTTM structure to date/time +# Inputs: 0) DTTM value +# Returns: EXIF-format date/time string ("0000:00:00 00:00:00" for zero date/time) +sub ConvertDTTM($) +{ + my $val = shift; + my $yr = ($val >> 20) & 0x1ff; + my $mon = ($val >> 16) & 0x0f; + my $day = ($val >> 11) & 0x1f; + my $hr = ($val >> 6) & 0x1f; + my $min = ($val & 0x3f); + $yr += 1900 if $val; + return sprintf("%.4d:%.2d:%.2d %.2d:%.2d:00%s",$yr,$mon,$day,$hr,$min,$val ? 'Z' : ''); +} + +#------------------------------------------------------------------------------ # Process hyperlinks from PID_HYPERLINKS array # (ref http://msdn.microsoft.com/archive/default.asp?url=/archive/en-us/dnaro97ta/html/msdn_hyper97.asp) # Inputs: 0) value, 1) ExifTool ref @@ -1264,6 +1512,181 @@ } #------------------------------------------------------------------------------ +# Process WordDocument stream of MSWord doc file (ref 6) +# Inputs: 0) ExifTool object ref, 1) dirInfo ref, 2) tag table ref +# Returns: 1 on success +sub ProcessWordDocument($$$) +{ + my ($et, $dirInfo, $tagTablePtr) = @_; + my $dataPt = $$dirInfo{DataPt} or return 0; + my $dirLen = length $$dataPt; + # validate the FIB signature + unless ($dirLen > 2 and Get16u($dataPt,0) == 0xa5ec) { + $et->WarnOnce('Invalid FIB signature', 1); + return 0; + } + $et->ProcessBinaryData($dirInfo, $tagTablePtr); # process FIB + # continue parsing the WordDocument stream until we find the FibRgFcLcb + my $pos = 32; + return 0 if $pos + 2 > $dirLen; + my $n = Get16u($dataPt, $pos); # read csw + $pos += 2 + $n * 2; # skip fibRgW + return 0 if $pos + 2 > $dirLen; + $n = Get16u($dataPt, $pos); # read cslw + $pos += 2 + $n * 4; # skip fibRgLw + return 0 if $pos + 2 > $dirLen; + $n = Get16u($dataPt, $pos); # read cbRgFcLcb + $pos += 2; # point to start of fibRgFcLcbBlob + return 0 if $pos + $n * 8 > $dirLen; + my ($off, @tableOffsets); + # save necessary entries for later processing of document table + # (DOP, CommentBy, LastSavedBy) + foreach $off (0xf8, 0x120, 0x238) { + last if $off + 8 > $n * 8; + push @tableOffsets, Get32u($dataPt, $pos + $off); + push @tableOffsets, Get32u($dataPt, $pos + $off + 4); + } + my $tbl = GetTagTable('Image::ExifTool::FlashPix::DocTable'); + # extract ModifyDate if it exists + $et->HandleTag($tbl, 'ModifyDate', undef, + DataPt => $dataPt, + Start => $pos + 0x2b8, + Size => 8, + ); + $et->HandleTag($tbl, TableOffsets => \@tableOffsets); # save for later + # $pos += $n * 8; # skip fibRgFcLcbBlob + # return 0 if $pos + 2 > $dirLen; + # $n = Get16u($dataPt, $pos); # read cswNew + # return 0 if $pos + 2 + $n * 2 > $dirLen; + # my $nFib = Get16u($dataPt, 2 + ($n ? $pos : 0)); + # $pos += 2 + $n * 2; # skip fibRgCswNew + return 1; +} + +#------------------------------------------------------------------------------ +# Process Microsoft Word Document Table +# Inputs: 0) ExifTool object ref +sub ProcessDocumentTable($) +{ + my $et = shift; + my $value = $$et{VALUE}; + my $extra = $$et{TAG_EXTRA}; + my ($i, $j, $tag); + # loop through TableOffsets for each sub-document + for ($i=0; ; ++$i) { + my $key = 'TableOffsets' . ($i ? " ($i)" : ''); + my $offsets = $$value{$key}; + last unless defined $offsets; + my $doc = $$extra{$key}{G3} if $$extra{$key}; + $doc = '' unless $doc; + # get DocFlags for this sub-document + my ($docFlags, $docTable); + for ($j=0; ; ++$j) { + my $key = 'DocFlags' . ($j ? " ($j)" : ''); + last unless defined $$value{$key}; + my $tmp = $$extra{$key}{G3} if $$extra{$key}; + $tmp = '' unless $tmp; + if ($tmp eq $doc) { + $docFlags = $$value{$key}; + last; + } + } + next unless defined $docFlags; + $tag = $docFlags & 0x200 ? 'Table1' : 'Table0'; + # get table for this sub-document + for ($j=0; ; ++$j) { + my $key = $tag . ($j ? " ($j)" : ''); + last unless defined $$value{$key}; + my $tmp = $$extra{$key}{G3} if $$extra{$key}; + $tmp = '' unless $tmp; + if ($tmp eq $doc) { + $docTable = \$$value{$key}; + last; + } + } + next unless defined $docTable; + # extract DOP and LastSavedBy information from document table + $$et{DOC_NUM} = $doc; # use same document number + my $tagTablePtr = GetTagTable('Image::ExifTool::FlashPix::DocTable'); + foreach $tag (qw(DOP CommentByBlock LastSavedByBlock)) { + last unless @$offsets; + my $off = shift @$offsets; + my $len = shift @$offsets; + next unless $len and $off + $len <= length $$docTable; + $et->HandleTag($tagTablePtr, $tag, undef, + DataPt => $docTable, + Start => $off, + Size => $len, + ); + } + delete $$et{DOC_NUM}; + } + # delete intermediate tags + foreach $tag (qw(TableOffsets Table0 Table1)) { + for ($i=0; ; ++$i) { + my $key = $tag . ($i ? " ($i)" : ''); + last unless defined $$value{$key}; + $et->DeleteTag($key); + } + } +} + +#------------------------------------------------------------------------------ +# Extract names of comment authors (ref 6) +# Inputs: 0) ExifTool object ref, 1) dirInfo ref, 2) tag table ref +# Returns: 1 on success +sub ProcessCommentBy($$$) +{ + my ($et, $dirInfo, $tagTablePtr) = @_; + my $dataPt = $$dirInfo{DataPt}; + my $pos = $$dirInfo{DirStart}; + my $end = $$dirInfo{DirLen} + $pos; + $et->VerboseDir($$dirInfo{DirName}); + while ($pos + 2 < $end) { + my $len = Get16u($dataPt, $pos); + $pos += 2; + last if $pos + $len * 2 > $end; + my $author = $et->Decode(substr($$dataPt, $pos, $len*2), 'UCS2'); + $pos += $len * 2; + $et->HandleTag($tagTablePtr, CommentBy => $author); + } + return 1; +} + +#------------------------------------------------------------------------------ +# Extract last-saved-by names (ref 5) +# Inputs: 0) ExifTool object ref, 1) dirInfo ref, 2) tag table ref +# Returns: 1 on success +sub ProcessLastSavedBy($$$) +{ + my ($et, $dirInfo, $tagTablePtr) = @_; + my $dataPt = $$dirInfo{DataPt}; + my $pos = $$dirInfo{DirStart}; + my $end = $$dirInfo{DirLen} + $pos; + return 0 if $pos + 6 > $end; + $et->VerboseDir($$dirInfo{DirName}); + my $num = Get16u($dataPt, $pos+2); + $pos += 6; + while ($num >= 2) { + last if $pos + 2 > $end; + my $len = Get16u($dataPt, $pos); + $pos += 2; + last if $pos + $len * 2 > $end; + my $author = $et->Decode(substr($$dataPt, $pos, $len*2), 'UCS2'); + $pos += $len * 2; + last if $pos + 2 > $end; + $len = Get16u($dataPt, $pos); + $pos += 2; + last if $pos + $len * 2 > $end; + my $path = $et->Decode(substr($$dataPt, $pos, $len*2), 'UCS2'); + $pos += $len * 2; + $et->HandleTag($tagTablePtr, LastSavedBy => "$author ($path)"); + $num -= 2; + } + return 1; +} + +#------------------------------------------------------------------------------ # Check FPX byte order mark (BOM) and set byte order appropriately # Inputs: 0) data ref, 1) offset to BOM # Returns: true on success @@ -1482,7 +1905,7 @@ my $name = Image::ExifTool::Decode(undef, $1, 'UCS2', 'II', 'Latin'); if ($verbose) { my $psize = ($size == 0xffffffff) ? 'storage' : "$size bytes"; - $et->VPrint(0," | $entry) Name: '$name' [$psize]\n"); + $et->VPrint(0," | $entry) Name: '${name}' [$psize]\n"); } # remove directory specification $name =~ s{.*/}{}s; @@ -1637,7 +2060,7 @@ { my ($et, $dirInfo) = @_; my $raf = $$dirInfo{RAF}; - my ($buff, $out, %dumpParms, $oldIndent, $miniStreamBuff); + my ($buff, $out, $oldIndent, $miniStreamBuff); my ($tag, %hier, %objIndex, %loadedDifSect); # read header @@ -1667,8 +2090,6 @@ if ($verbose) { $out = $et->Options('TextOut'); - $dumpParms{Out} = $out; - $dumpParms{MaxLen} = 96 if $verbose == 3; print $out " Sector size=$sectSize\n FAT: Count=$fatCount\n"; print $out " DIR: Start=$dirStart\n"; print $out " MiniFAT: Mini-sector size=$miniSize Start=$miniStart Count=$miniCount Cutoff=$miniCutoff\n"; @@ -1736,11 +2157,11 @@ } if ($verbose) { print $out " FAT [",length($fat)," bytes]:\n"; - HexDump(\$fat, undef, %dumpParms) if $verbose > 2; + $et->VerboseDump(\$fat); print $out " Mini-FAT [",length($miniFat)," bytes]:\n"; - HexDump(\$miniFat, undef, %dumpParms) if $verbose > 2; + $et->VerboseDump(\$miniFat); print $out " Directory [",length($dir)," bytes]:\n"; - HexDump(\$dir, undef, %dumpParms) if $verbose > 2; + $et->VerboseDump(\$dir); } # # process the directory @@ -1919,6 +2340,9 @@ } } } + # process Word document table + ProcessDocumentTable($et); + return 1; } @@ -1942,7 +2366,7 @@ =head1 AUTHOR -Copyright 2003-2017, Phil Harvey (phil at owl.phy.queensu.ca) +Copyright 2003-2019, Phil Harvey (phil at owl.phy.queensu.ca) This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself. diff -Nru xnview-0.92/opt64/XnView/AddOn/lib/Image/ExifTool/Flash.pm xnview-0.93/opt64/XnView/AddOn/lib/Image/ExifTool/Flash.pm --- xnview-0.92/opt64/XnView/AddOn/lib/Image/ExifTool/Flash.pm 2017-09-30 10:50:31.000000000 +0000 +++ xnview-0.93/opt64/XnView/AddOn/lib/Image/ExifTool/Flash.pm 2019-01-10 14:15:16.000000000 +0000 @@ -363,7 +363,7 @@ $pos += 2 + $len; # first string of a typed object is the object name if ($getName) { - $et->VPrint(1," | (object name '$tag')\n"); + $et->VPrint(1," | (object name '${tag}')\n"); undef $getName; next; # (ignore name for now) } @@ -454,7 +454,7 @@ } else { # give verbose indication if we ignore a lone value my $t = $amfType[$type] || sprintf('type 0x%x',$type); - $et->VPrint(1, " | (ignored lone $t value '$val')\n"); + $et->VPrint(1, " | (ignored lone $t value '${val}')\n"); } } } @@ -721,7 +721,7 @@ =head1 AUTHOR -Copyright 2003-2017, Phil Harvey (phil at owl.phy.queensu.ca) +Copyright 2003-2019, Phil Harvey (phil at owl.phy.queensu.ca) This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself. diff -Nru xnview-0.92/opt64/XnView/AddOn/lib/Image/ExifTool/FLIF.pm xnview-0.93/opt64/XnView/AddOn/lib/Image/ExifTool/FLIF.pm --- xnview-0.92/opt64/XnView/AddOn/lib/Image/ExifTool/FLIF.pm 2017-09-30 10:50:26.000000000 +0000 +++ xnview-0.93/opt64/XnView/AddOn/lib/Image/ExifTool/FLIF.pm 2019-01-10 14:15:16.000000000 +0000 @@ -329,7 +329,7 @@ =head1 AUTHOR -Copyright 2003-2017, Phil Harvey (phil at owl.phy.queensu.ca) +Copyright 2003-2019, Phil Harvey (phil at owl.phy.queensu.ca) This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself. diff -Nru xnview-0.92/opt64/XnView/AddOn/lib/Image/ExifTool/FLIR.pm xnview-0.93/opt64/XnView/AddOn/lib/Image/ExifTool/FLIR.pm --- xnview-0.92/opt64/XnView/AddOn/lib/Image/ExifTool/FLIR.pm 2017-09-30 10:50:23.000000000 +0000 +++ xnview-0.93/opt64/XnView/AddOn/lib/Image/ExifTool/FLIR.pm 2019-01-10 14:15:16.000000000 +0000 @@ -24,7 +24,7 @@ use Image::ExifTool::Exif; use Image::ExifTool::GPS; -$VERSION = '1.15'; +$VERSION = '1.17'; sub ProcessFLIR($$;$); sub ProcessFLIRText($$$); @@ -97,7 +97,7 @@ PROCESS_PROC => \&ProcessFLIR, VARS => { ALPHA_FIRST => 1 }, NOTES => q{ - Information extracted from FLIR FFF images and the FLIR APP1 segment of JPEG + Information extracted from FLIR FFF images and the APP1 FLIR segment of JPEG images. These tags may also be extracted from the first frame of an FLIR SEQ file. }, @@ -165,7 +165,10 @@ }, 0x2b => { Name => 'GPSInfo', - SubDirectory => { TagTable => 'Image::ExifTool::FLIR::GPSInfo' }, + SubDirectory => { + TagTable => 'Image::ExifTool::FLIR::GPSInfo', + ByteOrder => 'LittleEndian', + }, }, 0x2c => { Name => 'MeterLink', @@ -461,6 +464,7 @@ 0x390 => { Name => 'FocusStepCount', Format => 'int16u' }, 0x45c => { Name => 'FocusDistance', Format => 'float', PrintConv => 'sprintf("%.1f m",$val)' }, # 0x43c - string: either "Live" or the file name + 0x464 => { Name => 'FrameRate', Format => 'int16u' }, #SebastianHani ); # FLIR measurement tools record (ref 6) @@ -601,15 +605,111 @@ 7 => { Name => 'PiPY2', Description => 'PiP Y2' }, ); -# FLIR GPS record (ref PH/JD) +# FLIR GPS record (ref PH/JD/forum9615) %Image::ExifTool::FLIR::GPSInfo = ( - GROUPS => { 0 => 'APP1', 2 => 'Image' }, + GROUPS => { 0 => 'APP1', 2 => 'Location' }, PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData, FIRST_ENTRY => 0, + 0x00 => { + Name => 'GPSValid', + Format => 'int32u', + RawConv => '$$self{GPSValid} = $val', + PrintConv => { 0 => 'No', 1 => 'Yes' }, + }, + 0x04 => { + Name => 'GPSVersionID', + Format => 'undef[4]', + RawConv => '$val eq "\0\0\0\0" ? undef : $val', + PrintConv => 'join ".", split //, $val', + }, + 0x08 => { + Name => 'GPSLatitudeRef', + Format => 'string[2]', + RawConv => 'length($val) ? $val : undef', + PrintConv => { + N => 'North', + S => 'South', + }, + }, + 0x0a => { + Name => 'GPSLongitudeRef', + Format => 'string[2]', + RawConv => 'length($val) ? $val : undef', + PrintConv => { + E => 'East', + W => 'West', + }, + }, + # 0x0c - 4 unknown bytes + 0x10 => { + Name => 'GPSLatitude', + Condition => '$$self{GPSValid}', # valid only if GPSValid is 1 + Format => 'double', # (signed) + PrintConv => 'Image::ExifTool::GPS::ToDMS($self, $val, 1, "N")', + }, + 0x18 => { + Name => 'GPSLongitude', + Condition => '$$self{GPSValid}', # valid only if GPSValid is 1 + Format => 'double', # (signed) + PrintConv => 'Image::ExifTool::GPS::ToDMS($self, $val, 1, "E")', + }, + 0x20 => { + Name => 'GPSAltitude', + Condition => '$$self{GPSValid}', # valid only if GPSValid is 1 + Format => 'float', + # (have seen likely invalid value of -1 when GPSValid is 1) + PrintConv => 'sprintf("%.2f m", $val)', + }, + # 0x24 - 28 unknown bytes: + # 0x28 - int8u: seen 0,49,51,55,57 (ASCII "1","3","7","9") + # 0x29 - int8u: seen 0,48 (ASCII "0") + 0x40 => { + Name => 'GPSDOP', + Description => 'GPS Dilution Of Precision', + Format => 'float', + RawConv => '$val > 0 ? $val : undef', # (have also seen likely invalid value of 1) + PrintConv => 'sprintf("%.2f", $val)', + }, + 0x44 => { + Name => 'GPSSpeedRef', + Format => 'string[2]', + RawConv => 'length($val) ? $val : undef', + PrintConv => { + K => 'km/h', + M => 'mph', + N => 'knots', + }, + }, + 0x46 => { + Name => 'GPSTrackRef', + Format => 'string[2]', + RawConv => 'length($val) ? $val : undef', + PrintConv => { + M => 'Magnetic North', + T => 'True North', + }, + }, + # 0x48 - int32u: seen 0,77 + 0x4c => { + Name => 'GPSSpeed', + %float2f, + RawConv => '$val < 0 ? undef : $val', + }, + 0x50 => { + Name => 'GPSTrack', + %float2f, + RawConv => '$val < 0 ? undef : $val', + }, + # 0x54 - float: seen 0,-1 0x58 => { Name => 'GPSMapDatum', Format => 'string[16]', + RawConv => 'length($val) ? $val : undef', }, + # 0xa4 - string[6]: seen 000208,081210,020409,000608,010408,020808,091011 + # 0x78 - double[2]: seen "-1 -1","0 0" + # 0x78 - float[2]: seen "-1 -1","0 0" + # 0xb2 - string[2]?: seen "5\0" ); # humidity meter information @@ -1310,10 +1410,7 @@ last if $recLen < 0x28 or $pos + $recLen > $dirEnd; my $pre = 'Meas' . $i; $et->VerboseDir("MeasInfo $i", undef, $recLen); - if ($verbose > 2) { - HexDump($dataPt, $recLen, - Start=>$pos, Prefix=>$$et{INDENT}, DataPos=>$dataPos); - } + $et->VerboseDump($dataPt, Len => $recLen, Start=>$pos, DataPos=>$dataPos); my $coordLen = Get16u($dataPt, $pos+4); # generate tag table entries for this tool if necessary foreach $t ('Type', 'Params', 'Label') { @@ -1447,9 +1544,7 @@ Size => $recLen, ); } elsif ($verbose > 2) { - my %parms = ( DataPos => $recPos, Prefix => $$et{INDENT} ); - $parms{MaxLen} = 96 if $verbose < 4; - HexDump(\$rec, $recLen, %parms); + $et->VerboseDump(\$rec, Len => $recLen, DataPos => $recPos); } } delete $$et{SET_GROUP0}; @@ -1498,7 +1593,7 @@ =head1 AUTHOR -Copyright 2003-2017, Phil Harvey (phil at owl.phy.queensu.ca) +Copyright 2003-2019, Phil Harvey (phil at owl.phy.queensu.ca) This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself. diff -Nru xnview-0.92/opt64/XnView/AddOn/lib/Image/ExifTool/Font.pm xnview-0.93/opt64/XnView/AddOn/lib/Image/ExifTool/Font.pm --- xnview-0.92/opt64/XnView/AddOn/lib/Image/ExifTool/Font.pm 2017-09-30 10:50:25.000000000 +0000 +++ xnview-0.93/opt64/XnView/AddOn/lib/Image/ExifTool/Font.pm 2019-01-10 14:15:16.000000000 +0000 @@ -396,7 +396,7 @@ my $offset = Get32u(\$tbl, $pos + 8); my $size = Get32u(\$tbl, $pos + 12); unless ($raf->Seek($offset+$base, 0) and $raf->Read($buff, $size) == $size) { - $et->Warn("Error reading '$tag' data"); + $et->Warn("Error reading '${tag}' data"); next; } if ($verbose) { @@ -615,7 +615,7 @@ =head1 AUTHOR -Copyright 2003-2017, Phil Harvey (phil at owl.phy.queensu.ca) +Copyright 2003-2019, Phil Harvey (phil at owl.phy.queensu.ca) This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself. diff -Nru xnview-0.92/opt64/XnView/AddOn/lib/Image/ExifTool/FotoStation.pm xnview-0.93/opt64/XnView/AddOn/lib/Image/ExifTool/FotoStation.pm --- xnview-0.92/opt64/XnView/AddOn/lib/Image/ExifTool/FotoStation.pm 2017-09-30 10:50:36.000000000 +0000 +++ xnview-0.93/opt64/XnView/AddOn/lib/Image/ExifTool/FotoStation.pm 2019-01-10 14:15:16.000000000 +0000 @@ -243,7 +243,7 @@ =head1 AUTHOR -Copyright 2003-2017, Phil Harvey (phil at owl.phy.queensu.ca) +Copyright 2003-2019, Phil Harvey (phil at owl.phy.queensu.ca) This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself. diff -Nru xnview-0.92/opt64/XnView/AddOn/lib/Image/ExifTool/FujiFilm.pm xnview-0.93/opt64/XnView/AddOn/lib/Image/ExifTool/FujiFilm.pm --- xnview-0.92/opt64/XnView/AddOn/lib/Image/ExifTool/FujiFilm.pm 2017-09-30 10:50:33.000000000 +0000 +++ xnview-0.93/opt64/XnView/AddOn/lib/Image/ExifTool/FujiFilm.pm 2019-01-10 14:15:16.000000000 +0000 @@ -18,6 +18,7 @@ # 9) Zilvinas Brobliauskas private communication # 10) Albert Shan private communication # 11) http://u88.n24.queensu.ca/exiftool/forum/index.php/topic,8377.0.html +# 12) http://u88.n24.queensu.ca/exiftool/forum/index.php/topic,9607.0.html # IB) Iliah Borg private communication (LibRaw) # JD) Jens Duttke private communication #------------------------------------------------------------------------------ @@ -29,21 +30,23 @@ use Image::ExifTool qw(:DataAccess :Utils); use Image::ExifTool::Exif; -$VERSION = '1.59'; +$VERSION = '1.66'; sub ProcessFujiDir($$$); sub ProcessFaceRec($$$); # the following RAF version numbers have been tested for writing: my %testedRAF = ( - '0100' => 'E550, E900, F770, S5600, S6000fd, S6500fd, HS10/HS11, HS30, S200EXR, X100, XF1, X-Pro1, X-S1, XQ2 Ver1.00', - '0101' => 'X-E1, X20 Ver1.01', + '0100' => 'E550, E900, F770, S5600, S6000fd, S6500fd, HS10/HS11, HS30, S200EXR, X100, XF1, X-Pro1, X-S1, XQ2 Ver1.00, X-T100, GFX 50R, XF10', + '0101' => 'X-E1, X20 Ver1.01, X-T3', '0102' => 'S100FS, X10 Ver1.02', '0103' => 'IS Pro Ver1.03', '0104' => 'S5Pro Ver1.04', '0106' => 'S5Pro Ver1.06', '0111' => 'S5Pro Ver1.11', '0114' => 'S9600 Ver1.00', + '0132' => 'X-T2 Ver1.32', + '0144' => 'X100T Ver1.44', '0159' => 'S2Pro Ver1.00', '0200' => 'X10 Ver2.00', '0212' => 'S3Pro Ver2.12', @@ -54,8 +57,11 @@ '0269' => 'S9500 Ver1.02', '0271' => 'S3Pro Ver2.71', # UV/IR model? '0300' => 'X-E2', + # 0400 - expect to see this for X-T1 + '0540' => 'X-T1 Ver5.40', '0712' => 'S5000 Ver3.00', '0716' => 'S5000 Ver3.00', # (yes, 2 RAF versions with the same Software version) + '0Dgi' => 'X-A10 Ver1.01 and X-A3 Ver1.02', # (yes, non-digits in the firmware number) ); my %faceCategories = ( @@ -77,22 +83,34 @@ Name => 'Version', Writable => 'undef', }, - 0x0010 => { #PH (how does this compare to actual serial number?) + 0x0010 => { #PH/IB Name => 'InternalSerialNumber', Writable => 'string', Notes => q{ - this number is unique, and contains the date of manufacture, but doesn't - necessarily correspond to the camera body number -- this needs to be checked + this number is unique for most models, and contains the camera model ID and + the date of manufacture }, # eg) "FPX20017035 592D31313034060427796060110384" # "FPX 20495643 592D313335310701318AD010110047" (F40fd) - # yymmdd + # HHHHHHHHHHHHyymmdd + # HHHHHHHHHHHH = camera body number in hex + # yymmdd = date of manufacture PrintConv => q{ - return $val unless $val=~/^(.*)(\d{2})(\d{2})(\d{2})(.{12})$/; - my $yr = $2 + ($2 < 70 ? 2000 : 1900); - return "$1 $yr:$3:$4 $5"; + if ($val =~ /^(.*?\s*)([0-9a-fA-F]*)(\d{2})(\d{2})(\d{2})(.{12})\s*\0*$/s + and $4 >= 1 and $4 <= 12 and $5 >= 1 and $5 <= 31) + { + my $yr = $3 + ($3 < 70 ? 2000 : 1900); + my $sn = pack 'H*', $2; + return "$1$sn $yr:$4:$5 $6"; + } else { + # handle a couple of models which use a slightly different format + $val =~ s/\b(592D(3[0-9])+)/pack("H*",$1).' '/e; + } + return $val; }, - PrintConvInv => '$_=$val; s/ (19|20)(\d{2}):(\d{2}):(\d{2}) /$2$3$4/; $_', + # (this inverse conversion doesn't work in all cases, so it is best to write + # the ValueConv value if an authentic internal serial number is required) + PrintConvInv => '$_=$val; s/(\S+) (19|20)(\d{2}):(\d{2}):(\d{2}) /unpack("H*",$1)."$3$4$5"/e; $_', }, 0x1000 => { Name => 'Quality', @@ -206,6 +224,7 @@ Name => 'NoiseReduction', Flags => 'PrintHex', Writable => 'int16u', + RawConv => '$val == 0x100 ? undef : $val', PrintConv => { 0x40 => 'Low', 0x80 => 'Normal', @@ -213,7 +232,7 @@ }, }, 0x100e => { #PH (X100) - Name => 'HighISONoiseReduction', + Name => 'NoiseReduction', Flags => 'PrintHex', Writable => 'int16u', PrintConv => { @@ -279,6 +298,18 @@ 512 => 'Wide/Tracking', }, }, + 0x102b => { + Name => 'PrioritySettings', + SubDirectory => { TagTable => 'Image::ExifTool::FujiFilm::PrioritySettings' }, + }, + 0x102d => { + Name => 'FocusSettings', + SubDirectory => { TagTable => 'Image::ExifTool::FujiFilm::FocusSettings' }, + }, + 0x102e => { + Name => 'AFCSettings', + SubDirectory => { TagTable => 'Image::ExifTool::FujiFilm::AFCSettings' }, + }, 0x1023 => { #2 Name => 'FocusPixel', Writable => 'int16u', @@ -386,27 +417,83 @@ ValueConv => '$val / 8', ValueConvInv => '$val * 8', }, + 0x1045 => { #12 + Name => 'LensModulationOptimizer', + Writable => 'int32u', + PrintConv => { 0 => 'Off', 1 => 'On' }, + }, + 0x1047 => { #12 + Name => 'GrainEffect', + Writable => 'int32s', + PrintConv => { + 0 => 'Off', + 32 => 'Weak', + 64 => 'Strong', + }, + }, + 0x1048 => { #12 + Name => 'ColorChromeEffect', + Writable => 'int32s', + PrintConv => { + 0 => 'Off', + 32 => 'Weak', + 64 => 'Strong', + }, + }, + 0x1049 => { #12 + Name => 'BWAdjustment', + Notes => 'positive values are warm, negative values are cool', + Format => 'int8s', + PrintConv => '$val > 0 ? "+$val" : $val', + PrintConvInv => '$val + 0', + }, + 0x104d => { #forum9634 + Name => 'CropMode', + Writable => 'int16u', + PrintConv => { + 0 => 'n/a', + 2 => 'Sports Finder Mode', # (mechanical shutter) + 4 => 'Electronic Shutter 1.25x Crop', # (continuous high) + }, + }, 0x1050 => { #forum6109 Name => 'ShutterType', Writable => 'int16u', PrintConv => { 0 => 'Mechanical', 1 => 'Electronic', + 2 => 'Electronic (long shutter speed)', #12 + 3 => 'Electronic Front Curtain', #10 }, }, - 0x1100 => { + 0x1100 => [{ Name => 'AutoBracketing', + Condition => '$$self{Model} eq "X-T3"', + Notes => 'X-T3 only', + Writable => 'int16u', + PrintConv => { + 0 => 'Off', + 1 => 'On', + 2 => 'Pre-shot', #12 (Electronic Shutter and Continuous High drive mode only) + }, + },{ + Name => 'AutoBracketing', + Notes => 'other models', Writable => 'int16u', PrintConv => { 0 => 'Off', 1 => 'On', 2 => 'No flash & flash', #3 }, - }, + }], 0x1101 => { Name => 'SequenceNumber', Writable => 'int16u', }, + 0x1103 => { + Name => 'DriveSettings', + SubDirectory => { TagTable => 'Image::ExifTool::FujiFilm::DriveSettings' }, + }, # (0x1150-0x1152 exist only for Pro Low-light and Pro Focus PictureModes) # 0x1150 - Pro Low-light - val=1; Pro Focus - val=2 (ref 7) # 0x1151 - Pro Low-light - val=4 (number of pictures taken?); Pro Focus - val=2,3 (ref 7) @@ -510,6 +597,7 @@ 0x500 => 'Pro Neg. Std', #PH (X-Pro1) 0x501 => 'Pro Neg. Hi', #PH (X-Pro1) 0x600 => 'Classic Chrome', #forum6109 + 0x700 => 'Eterna', #12 }, }, 0x1402 => { #2 @@ -602,6 +690,30 @@ ValueConv => '$val & 0x7fff', ValueConvInv => '$val | 0x8000', }, + 0x1443 => { #12 (X-T3) + Name => 'DRangePriority', + Writable => 'int16u', + PrintConv => { 0 => 'Auto', 1 => 'Fixed' }, + }, + 0x1444 => { #12 (X-T3, only exists if DRangePriority is 'Auto') + Name => 'DRangePriorityAuto', + Writable => 'int16u', + PrintConv => { 1 => 'Weak', 2 => 'Strong' }, + }, + 0x1445 => { #12 (X-T3, only exists if DRangePriority is 'Fixed') + Name => 'DRangePriorityFixed', + Writable => 'int16u', + PrintConv => { 1 => 'Weak', 2 => 'Strong' }, + }, + 0x1446 => { #12 + Name => 'FlickerReduction', + Writable => 'int32u', + PrintConv => q{ + my $on = ((($val >> 12) & 0xff) == 3) ? 'On' : 'Off'; + return sprintf('%s (0x%.4x)', $on, $val); + }, + PrintConvInv => '$val=~/(0x[0-9a-f]+)/i; hex $1', + }, 0x3820 => { #PH (HS20EXR MOV) Name => 'FrameRate', Writable => 'int16u', @@ -617,6 +729,11 @@ Writable => 'int16u', Groups => { 2 => 'Video' }, }, + 0x4005 => { #forum9634 + Name => 'FaceElementSelected', # (could be face or eye) + Writable => 'int16u', + Count => 4, + }, 0x4100 => { #PH Name => 'FacesDetected', Writable => 'int16u', @@ -684,6 +801,159 @@ # 0xb212 - also found in MPIMage2 images - PH ); +# Focus Priority settings, tag 0x102b (X-T3, ref forum 9607) +%Image::ExifTool::FujiFilm::PrioritySettings = ( + PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData, + WRITE_PROC => \&Image::ExifTool::WriteBinaryData, + CHECK_PROC => \&Image::ExifTool::CheckBinaryData, + GROUPS => { 0 => 'MakerNotes', 2 => 'Camera' }, + FORMAT => 'int16u', + WRITABLE => 1, + 0.1 => { + Name => 'AF-SPriority', + Mask => 0x000f, + PrintConv => { + 1 => 'Release', + 2 => 'Focus', + }, + }, + 0.2 => { + Name => 'AF-CPriority', + Mask => 0x00f0, + PrintConv => { + 1 => 'Release', + 2 => 'Focus', + }, + }, +); + +# Focus settings, tag 0x102d (X-T3, ref forum 9607) +%Image::ExifTool::FujiFilm::FocusSettings = ( + PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData, + WRITE_PROC => \&Image::ExifTool::WriteBinaryData, + CHECK_PROC => \&Image::ExifTool::CheckBinaryData, + GROUPS => { 0 => 'MakerNotes', 2 => 'Camera' }, + FORMAT => 'int32u', + WRITABLE => 1, + 0.1 => { + Name => 'FocusMode2', + Mask => 0x000000ff, + PrintConv => { + 0 => 'AF-M', + 1 => 'AF-S', + 2 => 'AF-C', + }, + }, + 0.2 => { + Name => 'AFAreaMode', + Mask => 0x0f00, + PrintConv => { + 0 => 'Single Point', + 1 => 'Zone', + 2 => 'Wide/Tracking', + }, + }, + 0.3 => { + Name => 'AFAreaPointSize', + Mask => 0xf000, + PrintConv => { + 0 => 'n/a', + OTHER => sub { return $_[0] }, + }, + }, + 0.4 => { + Name => 'AFAreaZoneSize', + Mask => 0xf0000, + PrintConv => { + 0 => 'n/a', + OTHER => sub { + my ($val, $inv) = @_; + return "$val x $val" unless $inv; + $val =~ s/ ?x.*//; + return $val; + }, + }, + }, +); + +# AF-C settings, tag 0x102e (ref forum 9607) +%Image::ExifTool::FujiFilm::AFCSettings = ( + PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData, + WRITE_PROC => \&Image::ExifTool::WriteBinaryData, + CHECK_PROC => \&Image::ExifTool::CheckBinaryData, + GROUPS => { 0 => 'MakerNotes', 2 => 'Camera' }, + FORMAT => 'int32u', + WRITABLE => 1, + 0 => { + Name => 'AF-CSetting', + PrintHex => 3, + PrintSort => 1, # sort PrintConv by value + # decode in-camera preset values (X-T3) + PrintConv => { + 0x102 => 'Set 1 (multi-purpose)', # (2,0,Auto) + 0x203 => 'Set 2 (ignore obstacles)', # (3,0,Center) + 0x122 => 'Set 3 (accelerating subject)', # (2,2,Auto) + 0x010 => 'Set 4 (suddenly appearing subject)', # (0,1,Front) + 0x123 => 'Set 5 (erratic motion)', # (3,2,Auto) + OTHER => sub { + my ($val, $inv) = @_; + return $val =~ /(0x\w+)/ ? hex $1 : undef if $inv; + return sprintf 'Set 6 (custom 0x%.3x)', $val; + }, + }, + }, + 0.1 => { + Name => 'AF-CTrackingSensitivity', + Mask => 0x000f, # (values 0-4) + }, + 0.2 => { + Name => 'AF-CSpeedTrackingSensitivity', + Mask => 0x00f0, + # (values 0-2) + }, + 0.3 => { + Name => 'AF-CZoneAreaSwitching', + Mask => 0x0f00, + PrintConv => { + 0 => 'Front', + 1 => 'Auto', + 2 => 'Center', + }, + }, +); + +# DriveMode settings, tag 0x1103 (X-T3, ref forum 9607) +%Image::ExifTool::FujiFilm::DriveSettings = ( + PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData, + WRITE_PROC => \&Image::ExifTool::WriteBinaryData, + CHECK_PROC => \&Image::ExifTool::CheckBinaryData, + GROUPS => { 0 => 'MakerNotes', 2 => 'Camera' }, + FORMAT => 'int32u', + WRITABLE => 1, + 0.1 => { + Name => 'DriveMode', + Mask => 0x000000ff, + PrintConv => { + 0 => 'Single', + 1 => 'Continuous Low', + 2 => 'Continuous High', + }, + }, + 0.2 => { + Name => 'DriveSpeed', + Mask => 0xff000000, + PrintConv => { + 0 => 'n/a', + OTHER => sub { + my ($val, $inv) = @_; + return "$val fps" unless $inv; + $val =~ s/ ?fps$//; + return $val; + }, + }, + }, +); + # Face recognition information from FinePix F550EXR (ref PH) %Image::ExifTool::FujiFilm::FaceRecInfo = ( PROCESS_PROC => \&ProcessFaceRec, @@ -1113,7 +1383,7 @@ $raf->Read($hdr,0x94) == 0x94 or return 0; $hdr =~ /^FUJIFILM/ or return 0; my $ver = substr($hdr, 0x3c, 4); - $ver =~ /^\d{4}$/ or return 0; + $ver =~ /^\d{4}$/ or $testedRAF{$ver} or return 0; # get the position and size of embedded JPEG my ($jpos, $jlen) = unpack('x84NN', $hdr); @@ -1288,7 +1558,7 @@ =head1 AUTHOR -Copyright 2003-2017, Phil Harvey (phil at owl.phy.queensu.ca) +Copyright 2003-2019, Phil Harvey (phil at owl.phy.queensu.ca) This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself. diff -Nru xnview-0.92/opt64/XnView/AddOn/lib/Image/ExifTool/Geotag.pm xnview-0.93/opt64/XnView/AddOn/lib/Image/ExifTool/Geotag.pm --- xnview-0.92/opt64/XnView/AddOn/lib/Image/ExifTool/Geotag.pm 2017-09-30 10:50:30.000000000 +0000 +++ xnview-0.93/opt64/XnView/AddOn/lib/Image/ExifTool/Geotag.pm 2019-01-10 14:15:16.000000000 +0000 @@ -24,7 +24,7 @@ use vars qw($VERSION); use Image::ExifTool qw(:Public); -$VERSION = '1.51'; +$VERSION = '1.56'; sub JITTER() { return 2 } # maximum time jitter @@ -81,6 +81,16 @@ my %isOrient = ( dir => 1, pitch => 1, roll => 1 ); # test for orientation key +# conversion factors for GPSSpeed +my %speedConv = ( + 'K' => 1.852, # km/h per knot + 'M' => 1.150779448, # mph per knot + 'k' => 'K', # (allow lower case) + 'm' => 'M', + 'km/h' => 'K', # (allow other formats) + 'mph' => 'M', +); + my $secPerDay = 24 * 3600; # a useful constant #------------------------------------------------------------------------------ @@ -147,7 +157,7 @@ $raf = new File::RandomAccess(\*EXIFTOOL_TRKFILE); unless ($raf->Read($_, 256)) { close EXIFTOOL_TRKFILE; - return "Empty track file '$val'"; + return "Empty track file '${val}'"; } # look for XML or GPX header (might as well allow UTF-8 BOM) if (/^(\xef\xbb\xbf)?<(\?xml|gpx)[\s>]/) { @@ -157,17 +167,17 @@ $/ = $1; } else { close EXIFTOOL_TRKFILE; - return "Invalid track file '$val'"; + return "Invalid track file '${val}'"; } $raf->Seek(0,0); - $from = "file '$val'"; + $from = "file '${val}'"; } elsif ($val eq 'DATETIMEONLY') { $$geotag{DateTimeOnly} = 1; $$geotag{IsDate} = 1; $et->VPrint(0, 'Geotagging date/time only'); return $geotag; } else { - return "Error opening GPS file '$val'"; + return "Error opening GPS file '${val}'"; } } unless ($from) { @@ -195,7 +205,7 @@ $format = 'XML'; # check for NMEA sentence # (must ONLY start with ones that have timestamps! eg. not GSA or PTNTHPR!) - } elsif (/^\$(GP(RMC|GGA|GLL|ZDA)|PMGNTRK),/) { + } elsif (/^\$([A-Z]{2}(RMC|GGA|GLL|ZDA)|PMGNTRK),/) { $format = 'NMEA'; $nmeaStart = $2 || $1; # save type of first sentence } elsif (/^A(FLA|XSY|FIL)/) { @@ -237,7 +247,13 @@ my $tag = $xmlTag{lc $2}; if ($tag) { $$fix{$tag} = $4; - $$has{orient} = 1 if $isOrient{$tag}; + if ($isOrient{$tag}) { + $$has{orient} = 1; + } elsif ($tag eq 'alt') { + # validate altitude + undef $$fix{alt} if defined $$fix{alt} and $$fix{alt} !~ /^[+-]?\d+\.?\d*/; + $$has{alt} = 1 if $$fix{alt}; # set "has altitude" flag if appropriate + } } } # loop through XML elements @@ -270,7 +286,13 @@ @$fix{'lon','lat','alt'} = split ',', $1; } else { $$fix{$tag} = $1; - $$has{orient} = 1 if $isOrient{$tag}; + if ($isOrient{$tag}) { + $$has{orient} = 1; + } elsif ($tag eq 'alt') { + # validate altitude + undef $$fix{alt} if defined $$fix{alt} and $$fix{alt} !~ /^[+-]?\d+\.?\d*/; + $$has{alt} = 1 if $$fix{alt}; # set "has altitude" flag if appropriate + } } } next; @@ -287,11 +309,8 @@ $e1 or $et->VPrint(0, "Timestamp format error in $from\n"), $e1 = 1; next; } - # validate altitude - undef $$fix{alt} if defined $$fix{alt} and $$fix{alt} !~ /^[+-]?\d+\.?\d*/; $isDate = 1; $canCut= 1 if defined $$fix{pdop} or defined $$fix{hdop} or defined $$fix{nsats}; - $$has{alt} = 1 if $$fix{alt}; # set "has altitude" flag if appropriate # generate extra fixes assuming an equally spaced track if ($$fix{begin}) { my $begin = GetTime($$fix{begin}); @@ -361,7 +380,8 @@ my (%fix, $secs, $date, $nmea); if ($format eq 'NMEA') { # ignore unrecognized NMEA sentences - next unless /^\$(GP(RMC|GGA|GLL|GSA|ZDA)|PMGNTRK|PTNTHPR),/; + # (first 2 characters: GP=GPS, GL=GLONASS, GA=Gallileo, GN=combined, BD=Beidou) + next unless /^\$([A-Z]{2}(RMC|GGA|GLL|GSA|ZDA)|PMGNTRK|PTNTHPR),/; $nmea = $2 || $1; } # @@ -390,7 +410,7 @@ # $GPRMC,092204.999,A,4250.5589,S,14718.5084,E,0.00,89.68,211200,,*25 # $GPRMC,093657.007,,3652.835020,N,01053.104094,E,1.642,,290913,,,A*0F # $GPRMC,hhmmss.sss,A/V,ddmm.mmmm,N/S,ddmmm.mmmm,E/W,spd(knots),dir(deg),DDMMYY,,*cs - /^\$GPRMC,(\d{2})(\d{2})(\d+(\.\d*)?),A?,(\d*?)(\d{1,2}\.\d+),([NS]),(\d*?)(\d{1,2}\.\d+),([EW]),(\d*\.?\d*),(\d*\.?\d*),(\d{2})(\d{2})(\d+)/ or next; + /^\$[A-Z]{2}RMC,(\d{2})(\d{2})(\d+(\.\d*)?),A?,(\d*?)(\d{1,2}\.\d+),([NS]),(\d*?)(\d{1,2}\.\d+),([EW]),(\d*\.?\d*),(\d*\.?\d*),(\d{2})(\d{2})(\d+)/ or next; next if $13 > 31 or $14 > 12 or $15 > 99; # validate day/month/year $fix{lat} = (($5 || 0) + $6/60) * ($7 eq 'N' ? 1 : -1); $fix{lon} = (($8 || 0) + $9/60) * ($10 eq 'E' ? 1 : -1); @@ -406,7 +426,7 @@ # $GPGGA,092204.999,4250.5589,S,14718.5084,E,1,04,24.4,19.7,M,,,,0000*1F # $GPGGA,093657.000,3652.835020,N,01053.104094,E,,8,,166.924,M,40.9,M,,*77 # $GPGGA,hhmmss.sss,ddmm.mmmm,N/S,dddmm.mmmm,E/W,0=invalid,sats,hdop,alt,M,... - /^\$GPGGA,(\d{2})(\d{2})(\d+(\.\d*)?),(\d*?)(\d{1,2}\.\d+),([NS]),(\d*?)(\d{1,2}\.\d+),([EW]),[1-6]?,(\d+)?,(\.\d+|\d+\.?\d*)?,(-?\d+\.?\d*)?,M?/ or next; + /^\$[A-Z]{2}GGA,(\d{2})(\d{2})(\d+(\.\d*)?),(\d*?)(\d{1,2}\.\d+),([NS]),(\d*?)(\d{1,2}\.\d+),([EW]),[1-6]?,(\d+)?,(\.\d+|\d+\.?\d*)?,(-?\d+\.?\d*)?,M?/ or next; $fix{lat} = (($5 || 0) + $6/60) * ($7 eq 'N' ? 1 : -1); $fix{lon} = (($8 || 0) + $9/60) * ($10 eq 'E' ? 1 : -1); @fix{qw(nsats hdop alt)} = ($11,$12,$13); @@ -418,7 +438,7 @@ } elsif ($nmea eq 'GLL') { # $GPGLL,4250.5589,S,14718.5084,E,092204.999,A*2D # $GPGLL,ddmm.mmmm,N/S,dddmm.mmmm,E/W,hhmmss.sss,A/V*cs - /^\$GPGLL,(\d*?)(\d{1,2}\.\d+),([NS]),(\d*?)(\d{1,2}\.\d+),([EW]),(\d{2})(\d{2})(\d+(\.\d*)?),A/ or next; + /^\$[A-Z]{2}GLL,(\d*?)(\d{1,2}\.\d+),([NS]),(\d*?)(\d{1,2}\.\d+),([EW]),(\d{2})(\d{2})(\d+(\.\d*)?),A/ or next; $fix{lat} = (($1 || 0) + $2/60) * ($3 eq 'N' ? 1 : -1); $fix{lon} = (($4 || 0) + $5/60) * ($6 eq 'E' ? 1 : -1); $secs = (($7 * 60) + $8) * 60 + $9; @@ -427,7 +447,7 @@ # } elsif ($nmea eq 'GSA') { # $GPGSA,A,3,04,05,,,,,,,,,,,pdop,hdop,vdop*HH - /^\$GPGSA,[AM],([23]),((?:\d*,){11}(?:\d*)),(\d+\.?\d*|\.\d+)?,(\d+\.?\d*|\.\d+)?,(\d+\.?\d*|\.\d+)?\*/ or next; + /^\$[A-Z]{2}GSA,[AM],([23]),((?:\d*,){11}(?:\d*)),(\d+\.?\d*|\.\d+)?,(\d+\.?\d*|\.\d+)?,(\d+\.?\d*|\.\d+)?\*/ or next; @fix{qw(fixtype sats pdop hdop vdop)} = ($1.'d',$2,$3,$4,$5); # count the number of acquired satellites my @a = ($fix{sats} =~ /\d+/g); @@ -439,7 +459,7 @@ } elsif ($nmea eq 'ZDA') { # $GPZDA,093655.000,29,09,2013,,*58 # $GPZDA,hhmmss.ss,DD,MM,YYYY,tzh,tzm (hhmmss in UTC) - /^\$GPZDA,(\d{2})(\d{2})(\d{2}(\.\d*)?),(\d+),(\d+),(\d+)/ or next; + /^\$[A-Z]{2}ZDA,(\d{2})(\d{2})(\d{2}(\.\d*)?),(\d+),(\d+),(\d+)/ or next; $secs = (($1 * 60) + $2) * 60 + $3; $date = Time::Local::timegm(0,0,0,$5,$6-1,$7-1900); # @@ -469,7 +489,7 @@ # status: L=low alarm, M=low warning, N=normal, O=high warning # P=high alarm, C=tuning analog circuit # (ignore this information on any alarm status) - /^\$PTNTHPR,(-?[\d.]+),[MNO],(-?[\d.]+),[MNO],(-?[\d.]+),[MNO],/ or next; + /^\$PTNTHPR,(-?[\d.]+),[MNO],(-?[\d.]+),[MNO],(-?[\d.]+),[MNO]/ or next; @fix{qw(dir pitch roll)} = ($1,$2,$3); } else { @@ -675,11 +695,7 @@ my ($i0, $i1) = (0, scalar(@$syncTimes) - 1); while ($i1 > $i0 + 1) { my $pt = int(($i0 + $i1) / 2); - if ($time < $$syncTimes[$pt]) { - $i1 = $pt; - } else { - $i0 = $pt; - } + ($time < $$syncTimes[$pt] ? $i1 : $i0) = $pt; } my ($t0, $t1) = ($$syncTimes[$i0], $$syncTimes[$i1]); # interpolate/extrapolate to account for linear camera clock drift @@ -829,7 +845,8 @@ $time += $fs if $fs and $fs ne '.'; # bring UTC time back to Jan. 1 if no date is given - $time %= $secPerDay if $noDate; + # (don't use '%' operator here because it drops fractional seconds) + $time -= int($time / $secPerDay) * $secPerDay if $noDate; # apply time synchronization if available my $sync = ApplySyncCorr($et, $time); @@ -877,11 +894,7 @@ my ($i0, $i1) = (0, scalar(@$times) - 1); while ($i1 > $i0 + 1) { my $pt = int(($i0 + $i1) / 2); - if ($time < $$times[$pt]) { - $i1 = $pt; - } else { - $i0 = $pt; - } + ($time < $$times[$pt] ? $i1 : $i0) = $pt; } # do linear interpolation for position my $t0 = $$times[$i0]; @@ -1021,8 +1034,18 @@ } @r = $et->SetNewValue(GPSTrack => $$tFix{track}, %opts); @r = $et->SetNewValue(GPSTrackRef => (defined $$tFix{track} ? 'T' : undef), %opts); - @r = $et->SetNewValue(GPSSpeed => $$tFix{speed}, %opts); - @r = $et->SetNewValue(GPSSpeedRef => (defined $$tFix{speed} ? 'N' : undef), %opts); + my ($spd, $ref); + if (defined($spd = $$tFix{speed})) { + $ref = $$et{OPTIONS}{GeoSpeedRef}; + if ($ref and defined $speedConv{$ref}) { + $ref = $speedConv{$ref} if $speedConv{$speedConv{$ref}}; + $spd *= $speedConv{$ref}; + } else { + $ref = 'N'; # knots by default + } + } + @r = $et->SetNewValue(GPSSpeed => $spd, %opts); + @r = $et->SetNewValue(GPSSpeedRef => $ref, %opts); } if ($$has{orient}) { my $tFix = $fix; @@ -1076,7 +1099,7 @@ # Returns: geosync hash: # Offset = Offset in seconds for latest synchronization (GPS - image time) # Points = hash of all sync offsets keyed by image times in seconds -# Times = sorted list of image synchronization times (keys in Points hash) +# Times = sorted list of image synchronization times (keys in Points hash) # Notes: calling this routine with more than one geosync'd file causes time drift # correction to be implemented sub ConvertGeosync($$) @@ -1087,11 +1110,7 @@ if ($val =~ /(.*?)\@(.*)/) { $gpsTime = $1; - if (-f $2) { - $syncFile = $2; - } else { - $imgTime = $2; - } + (-f $2 ? $syncFile : $imgTime) = $2; # (take care because "-f '1:30'" crashes ActivePerl 5.10) } elsif ($val !~ /^\d/ or $val !~ /:/) { $syncFile = $val if -f $val; @@ -1114,11 +1133,11 @@ foreach $tag (@timeTags) { if ($$info{$tag}) { $imgTime = $$info{$tag}; - $et->VPrint(2, "Geosyncing with $tag from '$syncFile'\n"); + $et->VPrint(2, "Geosyncing with $tag from '${syncFile}'\n"); last; } } - $imgTime or warn("No image timestamp in '$syncFile'\n"), return undef; + $imgTime or warn("No image timestamp in '${syncFile}'\n"), return undef; } # add date to date-less timestamps my ($imgDateTime, $gpsDateTime, $noDate); @@ -1143,9 +1162,9 @@ } # calculate Unix seconds since the epoch my $imgSecs = Image::ExifTool::GetUnixTime($imgDateTime, 1); - defined $imgSecs or warn("Invalid image time '$imgTime'\n"), return undef; + defined $imgSecs or warn("Invalid image time '${imgTime}'\n"), return undef; my $gpsSecs = Image::ExifTool::GetUnixTime($gpsDateTime, 1); - defined $gpsSecs or warn("Invalid GPS time '$gpsTime'\n"), return undef; + defined $gpsSecs or warn("Invalid GPS time '${gpsTime}'\n"), return undef; # add fractional seconds $gpsSecs += $1 if $gpsTime =~ /(\.\d+)/; $imgSecs += $1 if $imgTime =~ /(\.\d+)/; @@ -1262,7 +1281,7 @@ =head1 AUTHOR -Copyright 2003-2017, Phil Harvey (phil at owl.phy.queensu.ca) +Copyright 2003-2019, Phil Harvey (phil at owl.phy.queensu.ca) This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself. diff -Nru xnview-0.92/opt64/XnView/AddOn/lib/Image/ExifTool/GeoTiff.pm xnview-0.93/opt64/XnView/AddOn/lib/Image/ExifTool/GeoTiff.pm --- xnview-0.92/opt64/XnView/AddOn/lib/Image/ExifTool/GeoTiff.pm 2017-09-30 10:50:23.000000000 +0000 +++ xnview-0.93/opt64/XnView/AddOn/lib/Image/ExifTool/GeoTiff.pm 2019-01-10 14:15:16.000000000 +0000 @@ -19,11 +19,12 @@ use vars qw($VERSION); use Image::ExifTool qw(:DataAccess :Utils); -$VERSION = '1.11'; +$VERSION = '1.12'; # format codes for geoTiff directory entries my %geoTiffFormat = ( - 0 => 'int16u', + 0 => 'int16u', # (value is stored in offset, and count is 1) + 0x87af => 'int16u', # (value is stored after directory) 0x87b0 => 'double', 0x87b1 => 'string', ); @@ -2167,31 +2168,30 @@ my $offset = Get16u($dirData, $pt+6); my $format = $geoTiffFormat{$loc}; my ($val, $dataPt); - if ($format eq 'double') { # in the double parms - if (not $doubleData or length($$doubleData) < 8*($offset+$count)) { - $et->Warn("Missing double data for $$tagInfo{Name}"); - next; - } + if (not $format) { + $et->Warn("Unknown GeoTiff location ($loc) for $$tagInfo{Name}"); + next; + } elsif ($format eq 'double') { # in the double parms $dataPt = $doubleData; - $offset *= 8; - $val = Image::ExifTool::ReadValue($dataPt, $offset, $format, - $count, length($$doubleData)-$offset); } elsif ($format eq 'string') { # in the ASCII parms - if (not $asciiData or length($$asciiData) < $offset+$count) { - $et->Warn("Missing string data for $$tagInfo{Name}"); - next; - } $dataPt = $asciiData; - $val = substr($$dataPt, $offset, $count); - $val =~ s/(\0|\|)$//; # remove trailing terminator (NULL or '|') - } elsif ($format eq 'int16u') { # use the offset as the value + } elsif ($format eq 'int16u') { # in the GeoTiffDirectory data $dataPt = $dirData; - $val = $offset; - $offset = $pt+6; - } else { - $et->Warn("Unknown GeoTiff location: $loc"); + unless ($loc) { # is value is stored in offset? + $count = 1; # (implied by location of 0) + $offset = ($pt + 6) / 2; # offset of the "offset" value + } + } + my $size = Image::ExifTool::FormatSize($format); + if (not $dataPt or length($$dataPt) < $size*($offset+$count)) { + $et->Warn("Missing $format data for $$tagInfo{Name}"); next; } + $offset *= $size; + $val = Image::ExifTool::ReadValue($dataPt, $offset, $format, + $count, length($$dataPt)-$offset); + # remove trailing terminator (NULL or '|') from string value + $val =~ s/(\0|\|)$// if $format eq 'string'; $verbose and $et->VerboseInfo($tag, $tagInfo, 'Table' => $tagTable, 'Index' => $i, @@ -2200,7 +2200,7 @@ 'Start' => $offset, 'Format' => $format, 'Count' => $count, - 'Size' => $count * Image::ExifTool::FormatSize($format), + 'Size' => $count * $size, ); $et->FoundTag($tagInfo, $val); } @@ -2240,7 +2240,7 @@ =head1 AUTHOR -Copyright 2003-2017, Phil Harvey (phil at owl.phy.queensu.ca) +Copyright 2003-2019, Phil Harvey (phil at owl.phy.queensu.ca) This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself. diff -Nru xnview-0.92/opt64/XnView/AddOn/lib/Image/ExifTool/GE.pm xnview-0.93/opt64/XnView/AddOn/lib/Image/ExifTool/GE.pm --- xnview-0.92/opt64/XnView/AddOn/lib/Image/ExifTool/GE.pm 2017-09-30 10:50:37.000000000 +0000 +++ xnview-0.93/opt64/XnView/AddOn/lib/Image/ExifTool/GE.pm 2019-01-10 14:15:16.000000000 +0000 @@ -68,7 +68,7 @@ =head1 AUTHOR -Copyright 2003-2017, Phil Harvey (phil at owl.phy.queensu.ca) +Copyright 2003-2019, Phil Harvey (phil at owl.phy.queensu.ca) This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself. diff -Nru xnview-0.92/opt64/XnView/AddOn/lib/Image/ExifTool/GIF.pm xnview-0.93/opt64/XnView/AddOn/lib/Image/ExifTool/GIF.pm --- xnview-0.92/opt64/XnView/AddOn/lib/Image/ExifTool/GIF.pm 2017-09-30 10:50:26.000000000 +0000 +++ xnview-0.93/opt64/XnView/AddOn/lib/Image/ExifTool/GIF.pm 2019-01-10 14:15:16.000000000 +0000 @@ -20,7 +20,7 @@ use vars qw($VERSION); use Image::ExifTool qw(:DataAccess :Utils); -$VERSION = '1.14'; +$VERSION = '1.16'; # road map of directory locations in GIF images my %gifMap = ( @@ -102,12 +102,12 @@ 4.1 => { Name => 'HasColorMap', Mask => 0x80, - PrintConv => { 0x00 => 'No', 0x80 => 'Yes' }, + PrintConv => { 0 => 'No', 1 => 'Yes' }, }, 4.2 => { Name => 'ColorResolutionDepth', Mask => 0x70, - ValueConv => '($val >> 4) + 1', + ValueConv => '$val + 1', }, 4.3 => { Name => 'BitsPerPixel', @@ -321,9 +321,7 @@ my $comment = ''; while ($length) { last unless $raf->Read($buff, $length) == $length; - if ($verbose > 2 and not $outfile) { - HexDump(\$buff, undef, Out => $out); - } + $et->VerboseDump(\$buff) unless $outfile; # add buffer to comment string $comment .= $buff; last unless $raf->Read($ch, 1); # read next block header @@ -534,7 +532,7 @@ =head1 AUTHOR -Copyright 2003-2017, Phil Harvey (phil at owl.phy.queensu.ca) +Copyright 2003-2019, Phil Harvey (phil at owl.phy.queensu.ca) This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself. diff -Nru xnview-0.92/opt64/XnView/AddOn/lib/Image/ExifTool/GIMP.pm xnview-0.93/opt64/XnView/AddOn/lib/Image/ExifTool/GIMP.pm --- xnview-0.92/opt64/XnView/AddOn/lib/Image/ExifTool/GIMP.pm 2017-09-30 10:50:20.000000000 +0000 +++ xnview-0.93/opt64/XnView/AddOn/lib/Image/ExifTool/GIMP.pm 2019-01-10 14:15:16.000000000 +0000 @@ -4,9 +4,10 @@ # Description: Read meta information from GIMP XCF images # # Revisions: 2010/10/05 - P. Harvey Created +# 2018/08/21 - PH Updated to current XCF specification (v013) # # References: 1) GIMP source code -# 2) http://svn.gnome.org/viewvc/gimp/trunk/devel-docs/xcf.txt?view=markup +# 2) https://gitlab.gnome.org/GNOME/gimp/blob/master/devel-docs/xcf.txt #------------------------------------------------------------------------------ package Image::ExifTool::GIMP; @@ -15,7 +16,7 @@ use vars qw($VERSION); use Image::ExifTool qw(:DataAccess :Utils); -$VERSION = '1.02'; +$VERSION = '1.03'; sub ProcessParasites($$$); @@ -28,6 +29,9 @@ XCF (eXperimental Computing Facilty) images. }, header => { SubDirectory => { TagTable => 'Image::ExifTool::GIMP::Header' } }, + # recognized properties + # 1 - ColorMap + # 17 - SamplePoints? (doc says 17 is also "PROP_SAMPLE_POINTS"??) 17 => { Name => 'Compression', Format => 'int8u', @@ -38,14 +42,32 @@ 3 => 'Fractal', }, }, + # 18 - Guides 19 => { Name => 'Resolution', SubDirectory => { TagTable => 'Image::ExifTool::GIMP::Resolution' }, }, + 20 => { + Name => 'Tattoo', + Format => 'int32u', + }, 21 => { Name => 'Parasites', SubDirectory => { TagTable => 'Image::ExifTool::GIMP::Parasite' }, }, + 22 => { + Name => 'Units', + Format => 'int32u', + PrintConv => { + 1 => 'Inches', + 2 => 'mm', + 3 => 'Points', + 4 => 'Picas', + }, + }, + # 23 Paths + # 24 UserUnit + # 25 Vectors ); # information extracted from the XCF file header (ref 2) @@ -55,10 +77,13 @@ 9 => { Name => 'XCFVersion', Format => 'string[5]', + DataMember => 'XCFVersion', + RawConv => '$$self{XCFVersion} = $val', PrintConv => { 'file' => '0', 'v001' => '1', 'v002' => '2', + OTHER => sub { my $val = shift; $val =~ s/^v0*//; return $val }, }, }, 14 => { Name => 'ImageWidth', Format => 'int32u' }, @@ -72,6 +97,7 @@ 2 => 'Indexed Color', }, }, + # 26 - [XCF 4 or later] Precision ); # XCF resolution data (property type 19) (ref 2) @@ -126,6 +152,13 @@ Start => 10, # starts after "GIMP_XMP_1" header }, }, + 'gimp-image-metadata' => { + Name => 'XML', + SubDirectory => { TagTable => 'Image::ExifTool::XMP::XML' }, + }, + # Seen, but not yet decoded: + # gimp-image-grid + # jpeg-settings ); #------------------------------------------------------------------------------ @@ -193,6 +226,9 @@ # process the XCF header $et->HandleTag($tagTablePtr, 'header', $buff); + # skip over precision for XCV version 4 or later + $raf->Seek(4, 1) if $$et{XCFVersion} =~ /^v0*(\d+)/ and $1 >= 4; + # loop through image properties for (;;) { $raf->Read($buff, 8) == 8 or last; @@ -234,7 +270,7 @@ =head1 AUTHOR -Copyright 2003-2017, Phil Harvey (phil at owl.phy.queensu.ca) +Copyright 2003-2019, Phil Harvey (phil at owl.phy.queensu.ca) This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself. diff -Nru xnview-0.92/opt64/XnView/AddOn/lib/Image/ExifTool/GoPro.pm xnview-0.93/opt64/XnView/AddOn/lib/Image/ExifTool/GoPro.pm --- xnview-0.92/opt64/XnView/AddOn/lib/Image/ExifTool/GoPro.pm 1970-01-01 00:00:00.000000000 +0000 +++ xnview-0.93/opt64/XnView/AddOn/lib/Image/ExifTool/GoPro.pm 2019-01-10 14:15:16.000000000 +0000 @@ -0,0 +1,709 @@ +#------------------------------------------------------------------------------ +# File: GoPro.pm +# +# Description: Read information from GoPro videos +# +# Revisions: 2018/01/12 - P. Harvey Created +# +# References: 1) https://github.com/gopro/gpmf-parser +# 2) https://github.com/stilldavid/gopro-utils +#------------------------------------------------------------------------------ + +package Image::ExifTool::GoPro; + +use strict; +use vars qw($VERSION); +use Image::ExifTool qw(:DataAccess :Utils); +use Image::ExifTool::QuickTime; + +$VERSION = '1.02'; + +sub ProcessGoPro($$$); +sub ProcessString($$$); +sub ScaleValues($$); +sub AddUnits($$$); +sub ConvertSystemTime($$); + +# GoPro data types that have ExifTool equivalents (ref 1) +my %goProFmt = ( # format codes + # 0x00 - container (subdirectory) + 0x62 => 'int8s', # 'b' + 0x42 => 'int8u', # 'B' + 0x63 => 'string', # 'c' (possibly null terminated) + 0x73 => 'int16s', # 's' + 0x53 => 'int16u', # 'S' + 0x6c => 'int32s', # 'l' + 0x4c => 'int32u', # 'L' + 0x66 => 'float', # 'f' + 0x64 => 'double', # 'd' + 0x46 => 'undef', # 'F' (4-char ID) + 0x47 => 'undef', # 'G' (16-byte uuid) + 0x6a => 'int64s', # 'j' + 0x4a => 'int64u', # 'J' + 0x71 => 'fixed32s', # 'q' + 0x51 => 'fixed64s', # 'Q' + 0x55 => 'undef', # 'U' (16-byte date) + 0x3f => 'undef', # '?' (complex structure) +); + +# sizes of format codes if different than what FormatSize() would return +my %goProSize = ( + 0x46 => 4, + 0x47 => 16, + 0x55 => 16, +); + +# tagInfo elements to add units to PrintConv value +my %addUnits = ( + AddUnits => 1, + PrintConv => 'Image::ExifTool::GoPro::AddUnits($self, $val, $tag)', +); + +# Tags found in the GPMF box of Hero6 mp4 videos (ref PH), and +# the gpmd-format timed metadata of Hero5 and Hero6 videos (ref 1) +%Image::ExifTool::GoPro::GPMF = ( + PROCESS_PROC => \&ProcessGoPro, + GROUPS => { 2 => 'Camera' }, + NOTES => q{ + Tags extracted from the GPMF box of GoPro MP4 videos, the APP6 "GoPro" segment + of JPEG files, and from the "gpmd" timed metadata if the ExtractEmbedded option + is enabled. Many more tags exist, but are currently unknown and extracted only + with the -u option. Please let me know if you discover the meaning of any of + these unknown tags. See L<https://github.com/gopro/gpmf-parser> for details + about this format. + }, + ACCL => { #2 (gpmd) + Name => 'Accelerometer', + Notes => 'accelerator readings in m/s', + Binary => 1, + }, + ALLD => 'AutoLowLightDuration', #1 (gpmd) (untested) + # APTO (GPMF) - seen: 'RAW' (fmt c) + ATTD => { #PH (Karma) + Name => 'Attitude', + # UNIT=s,rad,rad,rad,rad/s,rad/s,rad/s, + # TYPE=LffffffB + # SCAL=1000 1 1 1 1 1 1 1 + Binary => 1, + }, + ATTR => { #PH (Karma) + Name => 'AttitudeTarget', + # UNIT=s,rad,rad,rad, + # TYPE=Jffff + # SCAL=1000 1 1 1 1 + Binary => 1, + }, + AUDO => 'AudioSetting', #PH (GPMF - seen: 'WIND', fmt c) + # AUPT (GPMF) - seen: 'N' (fmt c) + BPOS => { #PH (Karma) + Name => 'Controller', + Unknown => 1, + # UNIT=deg,deg,m,deg,deg,m,m,m + # TYPE=lllfffff + # SCAL=10000000 10000000 1000 1 1 1 1 1 + %addUnits, + }, + # BRID (GPMF) - seen: 0 (fmt B) + # BROD (GPMF) - seen: 'ASK' (fmt c) + CASN => 'CameraSerialNumber', #PH (GPMF - seen: 'C3221324545448', fmt c) + # CINF (GPMF) - seen: 0x67376be7709bc8876a8baf3940908618 (fmt B) + # CMOD (GPMF) - seen: 12,13,17 [13 time-laps video, 17 JPEG] (fmt B) + CYTS => { #PH (Karma) + Name => 'CoyoteStatus', + # UNIT=s,,,,,rad,rad,rad,, + # TYPE=LLLLLfffBB + # SCAL=1000 1 1 1 1 1 1 1 1 1 + Binary => 1, + }, + CSEN => { #PH (Karma) + Name => 'CoyoteSense', + # UNIT=s,rad/s,rad/s,rad/s,g,g,g,,,, + # TYPE=LffffffLLLL + # SCAL=1000 1 1 1 1 1 1 1 1 1 1 + Binary => 1, + }, + DEVC => { #PH (gpmd,GPMF, fmt \0) + Name => 'DeviceContainer', + SubDirectory => { TagTable => 'Image::ExifTool::GoPro::GPMF' }, + }, + # DVID (GPMF) - DeviceID; seen: 1 (fmt L), HLMT (fmt F) + DVID => { Name => 'DeviceID', Unknown => 1 }, #2 (gpmd) + # DVNM (GPMF) seen: 'Video Global Settings' (fmt c), 'Highlights' (fmt c) + # DVNM (gpmd) seen: 'Camera' (Hero5), 'Hero6 Black' (Hero6), 'GoPro Karma v1.0' (Karma) + DVNM => 'DeviceName', #PH + DZOM => { #PH (GPMF - seen: 'Y', fmt c) + Name => 'DigitalZoom', + PrintConv => { N => 'No', Y => 'Yes' }, + }, + # DZST (GPMF) - seen: 0 (fmt L) (something to do with digital zoom maybe?) + # EISA (GPMF) - seen: 'Y','N' [N was for a time-lapse video] (fmt c) + # EISE (GPMF) - seen: 'Y' (fmt c) + EMPT => { Name => 'Empty', Unknown => 1 }, #2 (gpmd) + ESCS => { #PH (Karma) + Name => 'EscapeStatus', + # UNIT=s,rpm,rpm,rpm,rpm,rpm,rpm,rpm,rpm,degC,degC,degC,degC,V,V,V,V,A,A,A,A,,,,,,,,, + # TYPE=JSSSSSSSSssssSSSSSSSSSSSSSSSSB + # (no SCAL!) + Unknown => 1, + %addUnits, + }, + # EXPT (GPMF) - seen: '' (fmt c) + FACE => 'FaceDetected', #PH (gpmd) + FCNM => 'FaceNumbers', #PH (gpmd) (faces counted per frame, ref 1) + FMWR => 'FirmwareVersion', #PH (GPMF - seen: HD6.01.01.51.00, fmt c) + FWVS => 'OtherFirmware', #PH (NC) (gpmd - seen: '1.1.11.0', Karma) + GLPI => { #PH (gpmd, Karma) + Name => 'GPSPos', + # UNIT=s,deg,deg,m,m,m/s,m/s,m/s,deg + # TYPE=LllllsssS + # SCAL=1000 10000000 10000000 1000 1000 100 100 100 100 + RawConv => '$val', # necessary to use scaled value instead of raw data as subdir data + SubDirectory => { TagTable => 'Image::ExifTool::GoPro::GLPI' }, + }, + GPRI => { #PH (gpmd, Karma) + Name => 'GPSRaw', + # UNIT=s,deg,deg,m,m,m,m/s,deg,, + # TYPE=JlllSSSSBB + # SCAL=1000000,10000000,10000000,1000,100,100,100,100,1,1 + Unknown => 1, + RawConv => '$val', # necessary to use scaled value instead of raw data as subdir data + SubDirectory => { TagTable => 'Image::ExifTool::GoPro::GPRI' }, + }, + GPS5 => { #2 (gpmd) + Name => 'GPSInfo', + # SCAL=10000000,10000000,1000,1000,100 + RawConv => '$val', # necessary to use scaled value instead of raw data as subdir data + SubDirectory => { TagTable => 'Image::ExifTool::GoPro::GPS5' }, + }, + GPSF => { #2 (gpmd) + Name => 'GPSMeasureMode', + PrintConv => { + 2 => '2-Dimensional Measurement', + 3 => '3-Dimensional Measurement', + }, + }, + GPSP => { #2 (gpmd) + Name => 'GPSHPositioningError', + Description => 'GPS Horizontal Positioning Error', + ValueConv => '$val / 100', # convert from cm to m + }, + GPSU => { #2 (gpmd) + Name => 'GPSDateTime', + Groups => { 2 => 'Time' }, + # (HERO5 writes this in 'c' format, HERO6 writes 'U') + ValueConv => '$val =~ s/^(\d{2})(\d{2})(\d{2})(\d{2})(\d{2})/20$1:$2:$3 $4:$5:/; $val', + PrintConv => '$self->ConvertDateTime($val)', + }, + GYRO => { #2 (gpmd) + Name => 'Gyroscope', + Notes => 'gyroscope readings in rad/s', + Binary => 1, + }, + # HFLG (APP6) - seen: 0 + ISOE => 'ISOSpeeds', #PH (gpmd) + ISOG => { #2 (gpmd) + Name => 'ImageSensorGain', + Binary => 1, + }, + KBAT => { #PH (gpmd) (Karma) + Name => 'BatteryStatus', + # UNIT=A,Ah,J,degC,V,V,V,V,s,%,,,,,% + # TYPE=lLlsSSSSSSSBBBb + # SCAL=1000,1000,0.00999999977648258,100,1000,1000,1000,1000,0.0166666675359011,1,1,1,1,1,1 + RawConv => '$val', # necessary to use scaled value instead of raw data as subdir data + SubDirectory => { TagTable => 'Image::ExifTool::GoPro::KBAT' }, + }, + # LINF (GPMF) - seen: LAJ7061916601668 (fmt c) + LNED => { #PH (Karma) + Name => 'LocalPositionNED', + # UNIT=s,m,m,m,m/s,m/s,m/s + # TYPE=Lffffff + # SCAL=1000 1 1 1 1 1 1 + Binary => 1, + }, + MAGN => 'Magnetometer', #1 (gpmd) (units of uT) + MINF => { #PH (GPMF - seen: HERO6 Black, fmt c) + Name => 'Model', + Groups => { 2 => 'Camera' }, + Description => 'Camera Model Name', + }, + # MTYP (GPMF) - seen: 0,1,11 [1 for time-lapse video, 11 for JPEG] (fmt B) + # MUID (GPMF) - seen: 3882563431 2278071152 967805802 411471936 0 0 0 0 (fmt L) + OREN => { #PH (GPMF - seen: 'U', fmt c) + Name => 'AutoRotation', + PrintConv => { + U => 'Up', + D => 'Down', # (NC) + A => 'Auto', # (NC) + }, + }, + # (most of the "P" tags are ProTune settings - PH) + PHDR => 'HDRSetting', #PH (APP6 - seen: 0) + PIMN => 'AutoISOMin', #PH (GPMF - seen: 100, fmt L) + PIMX => 'AutoISOMax', #PH (GPMF - seen: 1600, fmt L) + # PRAW (APP6) - seen: 0 + PRES => 'PhotoResolution', #PH (APP6 - seen: '12MP_W') + PRTN => { #PH (GPMF - seen: 'N', fmt c) + Name => 'ProTune', + PrintConv => { + N => 'Off', + Y => 'On', # (NC) + }, + }, + PTCL => 'ColorMode', #PH (GPMF - seen: 'GOPRO', fmt c' APP6: 'FLAT') + PTEV => 'ExposureCompensation', #PH (GPMF - seen: '0.0', fmt c) + PTSH => 'Sharpness', #PH (GPMF - seen: 'HIGH', fmt c) + PTWB => 'WhiteBalance', #PH (GPMF - seen: 'AUTO', fmt c) + RATE => 'Rate', #PH (GPMF - seen: '0_5SEC', fmt c; APP6 - seen: '4_1SEC') + RMRK => { #2 (gpmd) + Name => 'Comments', + ValueConv => '$self->Decode($val, "Latin")', + }, + SCAL => { #2 (gpmd) scale factor for subsequent data + Name => 'ScaleFactor', + Unknown => 1, + }, + SCPR => { #PH (Karma) [stream was empty] + Name => 'ScaledPressure', + # UNIT=s,Pa,Pa,degC + # TYPE=Lffs + # SCAL=1000 0.00999999977648258 0.00999999977648258 100 + %addUnits, + }, + SHUT => { #2 (gpmd) + Name => 'ExposureTimes', + PrintConv => q{ + my @a = split ' ', $val; + $_ = Image::ExifTool::Exif::PrintExposureTime($_) foreach @a; + return join ' ', @a; + }, + }, + SIMU => { #PH (Karma) + Name => 'ScaledIMU', + # UNIT=s,g,g,g,rad/s,rad/s,rad/s,T,T,T + # TYPE=Lsssssssss + # SCAL=1000 1000 1000 1000 1000 1000 1000 1000 1000 1000 + %addUnits, + }, + SIUN => { #2 (gpmd - seen : 'm/s2','rad/s') + Name => 'SIUnits', + Unknown => 1, + ValueConv => '$self->Decode($val, "Latin")', + }, + # SMTR (GPMF) - seen: 'N' (fmt c) + STMP => { #1 (gpmd) + Name => 'TimeStamp', + ValueConv => '$val / 1e6', + }, + STRM => { #2 (gpmd,GPMF, fmt \0) + Name => 'NestedSignalStream', + SubDirectory => { TagTable => 'Image::ExifTool::GoPro::GPMF' }, + }, + STNM => { #2 (gpmd) + Name => 'StreamName', + Unknown => 1, + ValueConv => '$self->Decode($val, "Latin")', + }, + SYST => { #PH (Karma) + Name => 'SystemTime', + # UNIT=s,s + # TYPE=JJ + # SCAL=1000000 1000 + # save system time calibrations for later + RawConv => q{ + my @v = split ' ', $val; + if (@v == 2) { + my $s = $$self{SystemTimeList}; + $s or $s = $$self{SystemTimeList} = [ ]; + push @$s, \@v; + } + return $val; + }, + }, + # TICK => { Name => 'InTime', Unknown => 1, ValueConv => '$val/1000' }, #1 (gpmd) + TMPC => { #2 (gpmd) + Name => 'CameraTemperature', + PrintConv => '"$val C"', + }, + # TOCK => { Name => 'OutTime', Unknown => 1, ValueConv => '$val/1000' }, #1 (gpmd) + TSMP => { Name => 'TotalSamples', Unknown => 1 }, #2 (gpmd) + TYPE => { Name => 'StructureType', Unknown => 1 }, #2 (gpmd,GPMF - eg 'LLLllfFff', fmt c) + UNIT => { #2 (gpmd) alternative units + Name => 'Units', + Unknown => 1, + ValueConv => '$self->Decode($val, "Latin")', + }, + VFOV => { #PH (GPMF - seen: 'W', fmt c) + Name => 'FieldOfView', + PrintConv => { + W => 'Wide', + S => 'Super View', # (NC, not seen) + L => 'Linear', # (NC, not seen) + }, + }, + # VLTA (GPMF) - seen: 78 ('N') (fmt B -- wrong format?) + VFRH => { #PH (Karma) + Name => 'VisualFlightRulesHUD', + BinaryData => 1, + # UNIT=m/s,m/s,m,m/s,deg,% + # TYPE=ffffsS + }, + # VLTE (GPMF) - seen: 'Y' (fmt c) + WBAL => 'ColorTemperatures', #PH (gpmd) + WRGB => { #PH (gpmd) + Name => 'WhiteBalanceRGB', + Binary => 1, + }, +); + +# GoPro GPS5 tags (ref 2) (Hero5,Hero6) +%Image::ExifTool::GoPro::GPS5 = ( + PROCESS_PROC => \&ProcessString, + GROUPS => { 1 => 'GoPro', 2 => 'Location' }, + VARS => { HEX_ID => 0, ID_LABEL => 'Index' }, + 0 => { # (unit='deg') + Name => 'GPSLatitude', + PrintConv => 'Image::ExifTool::GPS::ToDMS($self, $val, 1, "N")', + }, + 1 => { # (unit='deg') + Name => 'GPSLongitude', + PrintConv => 'Image::ExifTool::GPS::ToDMS($self, $val, 1, "E")', + }, + 2 => { # (unit='m') + Name => 'GPSAltitude', + PrintConv => '"$val m"', + }, + 3 => 'GPSSpeed', # (unit='m/s') + 4 => 'GPSSpeed3D', # (unit='m/s') +); + +# GoPro GPRI tags (ref PH) (Karma) +%Image::ExifTool::GoPro::GPRI = ( + PROCESS_PROC => \&ProcessString, + GROUPS => { 1 => 'GoPro', 2 => 'Location' }, + VARS => { HEX_ID => 0, ID_LABEL => 'Index' }, + 0 => { # (unit='s') + Name => 'GPSDateTimeRaw', + Groups => { 2 => 'Time' }, + ValueConv => \&ConvertSystemTime, # convert to date/time based on SystemTime clock + PrintConv => '$self->ConvertDateTime($val)', + }, + 1 => { # (unit='deg') + Name => 'GPSLatitudeRaw', + PrintConv => 'Image::ExifTool::GPS::ToDMS($self, $val, 1, "N")', + }, + 2 => { # (unit='deg') + Name => 'GPSLongitudeRaw', + PrintConv => 'Image::ExifTool::GPS::ToDMS($self, $val, 1, "E")', + }, + 3 => { + Name => 'GPSAltitudeRaw', # (NC) + PrintConv => '"$val m"', + }, + # (unknown tags must be defined so that ProcessString() will iterate through all values) + 4 => { Name => 'GPRI_Unknown4', Unknown => 1, Hidden => 1, PrintConv => '"$val m"' }, + 5 => { Name => 'GPRI_Unknown5', Unknown => 1, Hidden => 1, PrintConv => '"$val m"' }, + 6 => 'GPSSpeedRaw', # (NC) # (unit='m/s' -- should convert to other units?) + 7 => 'GPSTrackRaw', # (NC) # (unit='deg') + 8 => { Name => 'GPRI_Unknown8', Unknown => 1, Hidden => 1 }, # (no units) + 9 => { Name => 'GPRI_Unknown9', Unknown => 1, Hidden => 1 }, # (no units) +); + +# GoPro GLPI tags (ref PH) (Karma) +%Image::ExifTool::GoPro::GLPI = ( + PROCESS_PROC => \&ProcessString, + GROUPS => { 1 => 'GoPro', 2 => 'Location' }, + VARS => { HEX_ID => 0, ID_LABEL => 'Index' }, + 0 => { # (unit='s') + Name => 'GPSDateTime', + Groups => { 2 => 'Time' }, + ValueConv => \&ConvertSystemTime, # convert to date/time based on SystemTime clock + PrintConv => '$self->ConvertDateTime($val)', + }, + 1 => { # (unit='deg') + Name => 'GPSLatitude', + PrintConv => 'Image::ExifTool::GPS::ToDMS($self, $val, 1, "N")', + }, + 2 => { # (unit='deg') + Name => 'GPSLongitude', + PrintConv => 'Image::ExifTool::GPS::ToDMS($self, $val, 1, "E")', + }, + 3 => { # (unit='m') + Name => 'GPSAltitude', # (NC) + PrintConv => '"$val m"', + }, + # (unknown tags must be defined so that ProcessString() will iterate through all values) + 4 => { Name => 'GLPI_Unknown4', Unknown => 1, Hidden => 1, PrintConv => '"$val m"' }, + 5 => { Name => 'GPSSpeedX', PrintConv => '"$val m/s"' }, # (NC) + 6 => { Name => 'GPSSpeedY', PrintConv => '"$val m/s"' }, # (NC) + 7 => { Name => 'GPSSpeedZ', PrintConv => '"$val m/s"' }, # (NC) + 8 => { Name => 'GPSTrack' }, # (unit='deg') +); + +# GoPro KBAT tags (ref PH) +%Image::ExifTool::GoPro::KBAT = ( + PROCESS_PROC => \&ProcessString, + GROUPS => { 1 => 'GoPro', 2 => 'Camera' }, + VARS => { HEX_ID => 0, ID_LABEL => 'Index' }, + NOTES => 'Battery status information found in GoPro Karma videos.', + 0 => { Name => 'BatteryCurrent', PrintConv => '"$val A"' }, + 1 => { Name => 'BatteryCapacity', PrintConv => '"$val Ah"' }, + 2 => { Name => 'KBAT_Unknown2', PrintConv => '"$val J"', Unknown => 1, Hidden => 1 }, + 3 => { Name => 'BatteryTemperature', PrintConv => '"$val C"' }, + 4 => { Name => 'BatteryVoltage1', PrintConv => '"$val V"' }, + 5 => { Name => 'BatteryVoltage2', PrintConv => '"$val V"' }, + 6 => { Name => 'BatteryVoltage3', PrintConv => '"$val V"' }, + 7 => { Name => 'BatteryVoltage4', PrintConv => '"$val V"' }, + 8 => { Name => 'BatteryTime', PrintConv => 'ConvertDuration(int($val + 0.5))' }, # (NC) + 9 => { Name => 'KBAT_Unknown9', PrintConv => '"$val %"', Unknown => 1, Hidden => 1, }, + 10 => { Name => 'KBAT_Unknown10', Unknown => 1, Hidden => 1 }, # (no units) + 11 => { Name => 'KBAT_Unknown11', Unknown => 1, Hidden => 1 }, # (no units) + 12 => { Name => 'KBAT_Unknown12', Unknown => 1, Hidden => 1 }, # (no units) + 13 => { Name => 'KBAT_Unknown13', Unknown => 1, Hidden => 1 }, # (no units) + 14 => { Name => 'BatteryLevel', PrintConv => '"$val %"' }, +); + +# GoPro fdsc tags written by the Hero5 and Hero6 (ref PH) +%Image::ExifTool::GoPro::fdsc = ( + GROUPS => { 2 => 'Camera' }, + PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData, + NOTES => q{ + Tags extracted from the MP4 "fdsc" timed metadata when the ExtractEmbedded + option is used. + }, + 0x08 => { Name => 'FirmwareVersion', Format => 'string[15]' }, + 0x17 => { Name => 'SerialNumber', Format => 'string[16]' }, + 0x57 => { Name => 'OtherSerialNumber', Format => 'string[15]' }, # (NC) + 0x66 => { + Name => 'Model', + Description => 'Camera Model Name', + Format => 'string[16]', + }, + # ... + # after this there are lots of interesting values also found in the GPMF box, + # but this block is lacking tag ID's and any directory structure, so the + # value offsets are therefore presumably firmware dependent :( +); + +#------------------------------------------------------------------------------ +# Convert system time to date/time string +# Inputs: 0) system time value, 1) ExifTool ref +# Returns: EXIF-format date/time string with milliseconds +sub ConvertSystemTime($$) +{ + my ($val, $et) = @_; + my $s = $$et{SystemTimeList} or return '<uncalibrated>'; + unless ($$et{SystemTimeListSorted}) { + $s = $$et{SystemTimeList} = [ sort { $$a[0] <=> $$b[0] } @$s ]; + $$et{SystemTimeListSorted} = 1; + } + my ($i, $j) = (0, $#$s); + # perform binary search to find this system time value + while ($j - $i > 1) { + my $t = int(($i + $j) / 2); + ($val < $$s[$t][0] ? $j : $i) = $t; + } + if ($i == $j or $$s[$j][0] == $$s[$i][0]) { + $val = $$s[$i][1]; + } else { + # interpolate between values + $val = $$s[$i][1] + ($$s[$j][1] - $$s[$i][1]) * ($val - $$s[$i][0]) / ($$s[$j][0] - $$s[$i][0]); + } + # (a bit tricky to remove fractional seconds then add them back again after + # the date/time conversion while avoiding round-off errors which could + # put the seconds out by 1...) + my ($t, $f) = ("$val" =~ /^(\d+)(\.\d+)/); + return Image::ExifTool::ConvertUnixTime($t, $$et{OPTIONS}{QuickTimeUTC}) . $f; +} + +#------------------------------------------------------------------------------ +# Scale values by last 'SCAL' constants +# Inputs: 0) value or list of values, 1) string of scale factors +# Returns: nothing, but updates values +sub ScaleValues($$) +{ + my ($val, $scl) = @_; + return unless $val and $scl; + my @scl = split ' ', $scl or return; + my @scaled; + my $v = (ref $val eq 'ARRAY') ? $val : [ $val ]; + foreach $val (@$v) { + my @a = split ' ', $val; + $a[$_] /= $scl[$_ % @scl] foreach 0..$#a; + push @scaled, join(' ', @a); + } + $_[0] = @scaled > 1 ? \@scaled : $scaled[0]; +} + +#------------------------------------------------------------------------------ +# Add units to values for human-readable output +# Inputs: 0) ExifTool ref, 1) value, 2) tag key +# Returns: converted value +sub AddUnits($$$) +{ + my ($et, $val, $tag) = @_; + if ($et and $$et{TAG_EXTRA}{$tag} and $$et{TAG_EXTRA}{$tag}{Units}) { + my $u = $$et{TAG_EXTRA}{$tag}{Units}; + $u = [ $u ] unless ref $u eq 'ARRAY'; + my @a = split ' ', $val; + if (@$u == @a) { + my $i; + for ($i=0; $i<@a; ++$i) { + $a[$i] .= ' ' . $$u[$i] if $$u[$i]; + } + $val = join ' ', @a; + } + } + return $val; +} + +#------------------------------------------------------------------------------ +# Process string of values (or array of strings) to extract as separate tags +# Inputs: 0) ExifTool object ref, 1) directory information ref, 2) tag table ref +# Returns: 1 on success +sub ProcessString($$$) +{ + my ($et, $dirInfo, $tagTablePtr) = @_; + my $dataPt = $$dirInfo{DataPt}; + my @list = ref $$dataPt eq 'ARRAY' ? @{$$dataPt} : ( $$dataPt ); + my ($string, $val); + $et->VerboseDir('GoPro structure'); + foreach $string (@list) { + my @val = split ' ', $string; + my $i = 0; + foreach $val (@val) { + $et->HandleTag($tagTablePtr, $i, $val); + $$tagTablePtr{++$i} or $i = 0; + } + } + return 1; +} + +#------------------------------------------------------------------------------ +# Process GoPro metadata (gpmd samples, GPMF box, or APP6) (ref PH/1/2) +# Inputs: 0) ExifTool object ref, 1) dirInfo ref, 2) tag table ref +# Returns: 1 on success +sub ProcessGoPro($$$) +{ + my ($et, $dirInfo, $tagTablePtr) = @_; + my $dataPt = $$dirInfo{DataPt}; + my $base = $$dirInfo{Base}; + my $pos = $$dirInfo{DirStart} || 0; + my $dirEnd = $pos + ($$dirInfo{DirLen} || (length($$dataPt) - $pos)); + my $verbose = $et->Options('Verbose'); + my $unknown = $verbose || $et->Options('Unknown'); + my ($size, $type, $unit, $scal, $setGroup0); + + $et->VerboseDir($$dirInfo{DirName} || 'GPMF', undef, $dirEnd-$pos) if $verbose; + if ($pos) { + my $parent = $$dirInfo{Parent}; + $setGroup0 = $$et{SET_GROUP0} = 'APP6' if $parent and $parent eq 'APP6'; + } else { + # set group0 to "QuickTime" unless group1 is being changed (to Track#) + $setGroup0 = $$et{SET_GROUP0} = 'QuickTime' unless $$et{SET_GROUP1}; + } + + for (; $pos+8<=$dirEnd; $pos+=($size+3)&0xfffffffc) { + my ($tag,$fmt,$len,$count) = unpack("x${pos}a4CCn", $$dataPt); + $size = $len * $count; + $pos += 8; + last if $pos + $size > $dirEnd; + my $tagInfo = $et->GetTagInfo($tagTablePtr, $tag); + last if $tag eq "\0\0\0\0"; # stop at null tag + next unless $size or $verbose; # don't save empty values unless verbose + my $format = $goProFmt{$fmt} || 'undef'; + my ($val, $i, $j, $p, @v); + if ($fmt eq 0x3f and defined $type) { + # decode structure with format given by previous 'TYPE' + for ($i=0; $i<$count; ++$i) { + my (@s, $l); + for ($j=0, $p=0; $j<length($type); ++$j, $p+=$l) { + my $b = Get8u(\$type, $j); + my $f = $goProFmt{$b} or last; + $l = $goProSize{$b} || Image::ExifTool::FormatSize($f) or last; + last if $p + $l > $len; + my $s = ReadValue($dataPt, $pos+$i*$len+$p, $f, undef, $l); + last unless defined $s; + push @s, $s; + } + push @v, join ' ', @s if @s; + } + $val = @v > 1 ? \@v : $v[0]; + } elsif (($format eq 'undef' or $format eq 'string') and $count > 1 and $len > 1) { + # unpack multiple undef/string values as a list + my $a = $format eq 'undef' ? 'a' : 'A'; + $val = [ unpack("x${pos}".("$a$len" x $count), $$dataPt) ]; + } else { + $val = ReadValue($dataPt, $pos, $format, undef, $size); + } + # save TYPE, UNIT/SIUN and SCAL values for later + $type = $val if $tag eq 'TYPE'; + $unit = $val if $tag eq 'UNIT' or $tag eq 'SIUN'; + $scal = $val if $tag eq 'SCAL'; + + unless ($tagInfo) { + next unless $unknown; + my $name = Image::ExifTool::QuickTime::PrintableTagID($tag); + $tagInfo = { Name => "GoPro_$name", Description => "GoPro $name", Unknown => 1 }; + $$tagInfo{SubDirectory} = { TagTable => 'Image::ExifTool::GoPro::GPMF' } if not $fmt; + AddTagToTable($tagTablePtr, $tag, $tagInfo); + } + # apply scaling if available to last tag in this container + ScaleValues($val, $scal) if $scal and $tag ne 'SCAL' and $pos+$size+3>=$dirEnd; + my $key = $et->HandleTag($tagTablePtr, $tag, $val, + DataPt => $dataPt, + Base => $base, + Start => $pos, + Size => $size, + TagInfo => $tagInfo, + Format => $format, + Extra => $verbose ? ", type='".($fmt ? chr($fmt) : '\0')."' size=$len count=$count" : undef, + ); + # save units for adding in print conversion if specified + $$et{TAG_EXTRA}{$key}{Units} = $unit if $$tagInfo{AddUnits} and $key; + } + delete $$et{SET_GROUP0} if $setGroup0; + return 1; +} + +1; # end + +__END__ + +=head1 NAME + +Image::ExifTool::GoPro - Read information from GoPro videos + +=head1 SYNOPSIS + +This module is used by Image::ExifTool + +=head1 DESCRIPTION + +This module contains definitions required by Image::ExifTool to decode +metadata from GoPro MP4 videos. + +=head1 AUTHOR + +Copyright 2003-2019, Phil Harvey (phil at owl.phy.queensu.ca) + +This library is free software; you can redistribute it and/or modify it +under the same terms as Perl itself. + +=head1 REFERENCES + +=over 4 + +=item L<https://github.com/gopro/gpmf-parser> + +=item L<https://github.com/stilldavid/gopro-utils> + +=back + +=head1 SEE ALSO + +L<Image::ExifTool::TagNames/GoPro Tags>, +L<Image::ExifTool(3pm)|Image::ExifTool> + +=cut + diff -Nru xnview-0.92/opt64/XnView/AddOn/lib/Image/ExifTool/GPS.pm xnview-0.93/opt64/XnView/AddOn/lib/Image/ExifTool/GPS.pm --- xnview-0.92/opt64/XnView/AddOn/lib/Image/ExifTool/GPS.pm 2017-09-30 10:50:30.000000000 +0000 +++ xnview-0.93/opt64/XnView/AddOn/lib/Image/ExifTool/GPS.pm 2019-01-10 14:15:16.000000000 +0000 @@ -12,7 +12,7 @@ use vars qw($VERSION); use Image::ExifTool::Exif; -$VERSION = '1.47'; +$VERSION = '1.50'; my %coordConv = ( ValueConv => 'Image::ExifTool::GPS::ToDegrees($val)', @@ -133,7 +133,8 @@ # pull time out of any format date/time string # (converting to UTC if a timezone is given) PrintConvInv => sub { - my $v = shift; + my ($v, $et) = @_; + $v = $et->TimeNow() if lc($v) eq 'now'; my @tz; if ($v =~ s/([-+])(.*)//s) { # remove timezone my $s = $1 eq '-' ? 1 : -1; # opposite sign to convert back to UTC @@ -230,10 +231,7 @@ Writable => 'string', Notes => 'tags 0x0013-0x001a used for subject location according to MWG 2.0', Count => 2, - PrintConv => { - N => 'North', - S => 'South', - }, + PrintConv => { N => 'North', S => 'South' }, }, 0x0014 => { Name => 'GPSDestLatitude', @@ -245,10 +243,7 @@ Name => 'GPSDestLongitudeRef', Writable => 'string', Count => 2, - PrintConv => { - E => 'East', - W => 'West', - }, + PrintConv => { E => 'East', W => 'West' }, }, 0x0016 => { Name => 'GPSDestLongitude', @@ -314,6 +309,7 @@ # (and adjust to UTC if this is a full date/time/timezone value) PrintConvInv => q{ my $secs; + $val = $self->TimeNow() if lc($val) eq 'now'; if ($val =~ /[-+]/ and ($secs = Image::ExifTool::GetUnixTime($val, 1))) { $val = Image::ExifTool::ConvertUnixTime($secs); } @@ -374,7 +370,7 @@ PrintConv => 'Image::ExifTool::GPS::ToDMS($self, $val, 1, "E")', }, GPSAltitude => { - SubDoc => 1, # generate for all sub-documents + SubDoc => [1,3], # generate for sub-documents if Desire 1 or 3 has a chance to exist Desire => { 0 => 'GPS:GPSAltitude', 1 => 'GPS:GPSAltitudeRef', @@ -386,7 +382,7 @@ ValueConv => q{ my $alt = $val[0]; $alt = $val[2] unless defined $alt; - return undef unless defined $alt; + return undef unless defined $alt and IsFloat($alt); return ($val[1] || $val[3]) ? -$alt : $alt; }, PrintConv => q{ @@ -394,6 +390,23 @@ return ($val =~ s/^-// ? "$val m Below" : "$val m Above") . " Sea Level"; }, }, + GPSDestLatitude => { + Require => { + 0 => 'GPS:GPSDestLatitude', + 1 => 'GPS:GPSDestLatitudeRef', + }, + ValueConv => '$val[1] =~ /^S/i ? -$val[0] : $val[0]', + PrintConv => 'Image::ExifTool::GPS::ToDMS($self, $val, 1, "N")', + }, + GPSDestLongitude => { + SubDoc => 1, # generate for all sub-documents + Require => { + 0 => 'GPS:GPSDestLongitude', + 1 => 'GPS:GPSDestLongitudeRef', + }, + ValueConv => '$val[1] =~ /^W/i ? -$val[0] : $val[0]', + PrintConv => 'Image::ExifTool::GPS::ToDMS($self, $val, 1, "E")', + }, ); # add our composite tags @@ -410,14 +423,14 @@ my $f = (($h || 0) * 60 + ($m || 0)) * 60 + ($s || 0); $h = int($f / 3600); $f -= $h * 3600; $m = int($f / 60); $f -= $m * 60; - $s = int($f); $f -= $s; - $f = int($f * 1000000000 + 0.5); - if ($f) { - ($f = sprintf(".%.9d", $f)) =~ s/0+$//; + my $ss = sprintf('%012.9f', $f); + if ($ss >= 60) { + $ss = '00'; + ++$m >= 60 and $m -= 60, ++$h; } else { - $f = '' + $ss =~ s/\.?0+$//; # trim trailing zeros + decimal } - return sprintf("%.2d:%.2d:%.2d%s",$h,$m,$s,$f); + return sprintf("%.2d:%.2d:%s",$h,$m,$ss); } #------------------------------------------------------------------------------ @@ -442,8 +455,13 @@ sub ToDMS($$;$$) { my ($et, $val, $doPrintConv, $ref) = @_; - my ($fmt, @fmt, $num, $sign); + my ($fmt, @fmt, $num, $sign, $rtnVal); + unless (length $val) { + # don't convert an empty value + return $val if $doPrintConv and $doPrintConv eq 1; # avoid hiding existing tag when extracting + return undef; # avoid writing empty value + } if ($ref) { if ($val < 0) { $val = -$val; @@ -469,7 +487,7 @@ $fmt =~ s/%\+/%/g; # don't know sign, so don't print it } } else { - $fmt = "%d,%.6f$ref"; # use XMP standard format + $fmt = "%d,%.8f$ref"; # use XMP format with 8 decimal minutes } # count (and capture) the format specifiers (max 3) while ($fmt =~ /(%(%|[^%]*?[diouxXDOUeEfFgGcs]))/g) { @@ -498,7 +516,14 @@ ($c[-2] += 1) >= 60 and $num > 2 and $c[-2] -= 60, $c[-3] += 1; } } - return $doPrintConv ? sprintf($fmt, @c) : "@c$ref"; + if ($doPrintConv) { + $rtnVal = sprintf($fmt, @c); + # trim trailing zeros in XMP + $rtnVal =~ s/(\d)0+$ref$/$1$ref/ if $doPrintConv eq '2'; + } else { + $rtnVal = "@c$ref"; + } + return $rtnVal; } #------------------------------------------------------------------------------ @@ -511,7 +536,8 @@ my ($val, $doSign) = @_; # extract decimal or floating point values out of any other garbage my ($d, $m, $s) = ($val =~ /((?:[+-]?)(?=\d|\.\d)\d*(?:\.\d*)?(?:[Ee][+-]\d+)?)/g); - my $deg = ($d || 0) + (($m || 0) + ($s || 0)/60) / 60; + return '' unless defined $d; + my $deg = $d + (($m || 0) + ($s || 0)/60) / 60; # make negative if S or W coordinate $deg = -$deg if $doSign ? $val =~ /[^A-Z](S|W)$/i : $deg < 0; return $deg; @@ -537,7 +563,7 @@ =head1 AUTHOR -Copyright 2003-2017, Phil Harvey (phil at owl.phy.queensu.ca) +Copyright 2003-2019, Phil Harvey (phil at owl.phy.queensu.ca) This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself. diff -Nru xnview-0.92/opt64/XnView/AddOn/lib/Image/ExifTool/H264.pm xnview-0.93/opt64/XnView/AddOn/lib/Image/ExifTool/H264.pm --- xnview-0.92/opt64/XnView/AddOn/lib/Image/ExifTool/H264.pm 2017-09-30 10:50:26.000000000 +0000 +++ xnview-0.93/opt64/XnView/AddOn/lib/Image/ExifTool/H264.pm 2019-01-10 14:15:17.000000000 +0000 @@ -25,7 +25,7 @@ use Image::ExifTool::Exif; use Image::ExifTool::GPS; -$VERSION = '1.14'; +$VERSION = '1.17'; sub ProcessSEI($$); @@ -433,24 +433,24 @@ 1.1 => { Name => 'ExposureProgram', Mask => 0xf0, - ValueConv => '$val == 0xf0 ? undef : $val', + ValueConv => '$val == 15 ? undef : $val', PrintConv => { - 0x00 => 'Program AE', - 0x10 => 'Gain', #? - 0x20 => 'Shutter speed priority AE', - 0x30 => 'Aperture-priority AE', - 0x40 => 'Manual', + 0 => 'Program AE', + 1 => 'Gain', #? + 2 => 'Shutter speed priority AE', + 3 => 'Aperture-priority AE', + 4 => 'Manual', }, }, 2.1 => { Name => 'WhiteBalance', Mask => 0xe0, - ValueConv => '$val == 0xe0 ? undef : $val', + ValueConv => '$val == 7 ? undef : $val', PrintConv => { - 0x00 => 'Auto', - 0x20 => 'Hold', - 0x40 => '1-Push', - 0x60 => 'Daylight', + 0 => 'Auto', + 1 => 'Hold', + 2 => '1-Push', + 3 => 'Daylight', }, }, 3 => { @@ -521,6 +521,8 @@ # 0x0345 - Panasonic HC-V7272 # 0x0414 - Panasonic AG-AF100 # 0x0591 - various Panasonic DMC models + # 0x0802 - Panasonic DMC-TZ60 with GPS information off + # 0x0803 - Panasonic DMC-TZ60 with GPS information on # 0x3001 - various Sony DSC, HDR, NEX and SLT models # 0x3003 - various Sony DSC models # 0x3100 - various Sony DSC, ILCE, NEX and SLT models @@ -708,7 +710,8 @@ #------------------------------------------------------------------------------ # Parse H.264 sequence parameter set RBSP (ref 1) -# Inputs) 0) ExifTool ref, 1) tag table ref, 2) data ref +# Inputs: 0) ExifTool ref, 1) tag table ref, 2) data ref +# Notes: All this just to get the image size! sub ParseSeqParamSet($$$) { my ($et, $tagTablePtr, $dataPt) = @_; @@ -830,7 +833,9 @@ #------------------------------------------------------------------------------ # Parse H.264 picture timing SEI message (payload type 1) (ref 1) -# Inputs) 0) ExifTool ref, 1) data ref +# Inputs: 0) ExifTool ref, 1) data ref +# Notes: this routine is for test purposes only, and not called unless the +# $parsePictureTiming flag is set sub ParsePictureTiming($$) { my ($et, $dataPt) = @_; @@ -887,6 +892,26 @@ # Process H.264 Supplementary Enhancement Information (ref 1/PH) # Inputs: 0) Exiftool ref, 1) dirInfo ref, 2) tag table ref # Returns: 1 if we processed payload type 5 +# Payload types: +# 0 - buffer period +# 1 - pic timing +# 2 - pan scan rect +# 3 - filler payload +# 4 - user data registered itu t t35 +# 5 - user data unregistered +# 6 - recovery point +# 7 - dec ref pic marking repetition +# 8 - spare pic +# 9 - sene info +# 10 - sub seq info +# 11 - sub seq layer characteristics +# 12 - sub seq characteristics +# 13 - full frame freeze +# 14 - full frame freeze release +# 15 - full frame snapshot +# 16 - progressive refinement segment start +# 17 - progressive refinement segment end +# 18 - motion constrained slice group set sub ProcessSEI($$) { my ($et, $dirInfo) = @_; @@ -913,6 +938,7 @@ last unless $t == 255; } return 0 if $pos + $size > $end; + $et->VPrint(1," (SEI type $type)\n"); if ($type == 1) { # picture timing information if ($parsePictureTiming) { my $buff = substr($$dataPt, $pos, $size); @@ -1029,9 +1055,7 @@ $buff .= substr($$dataPt, $pos, $end - $pos); if ($verbose > 1) { printf $out " NAL Unit Type: 0x%x (%d bytes)\n",$nal_unit_type, length $buff; - my %parms = ( Out => $out ); - $parms{MaxLen} = 96 if $verbose < 4; - HexDump(\$buff, undef, %parms) if $verbose > 2; + $et->VerboseDump(\$buff); } pos($$dataPt) = $pos = $nextPos; @@ -1084,7 +1108,7 @@ =head1 AUTHOR -Copyright 2003-2017, Phil Harvey (phil at owl.phy.queensu.ca) +Copyright 2003-2019, Phil Harvey (phil at owl.phy.queensu.ca) This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself. diff -Nru xnview-0.92/opt64/XnView/AddOn/lib/Image/ExifTool/HP.pm xnview-0.93/opt64/XnView/AddOn/lib/Image/ExifTool/HP.pm --- xnview-0.92/opt64/XnView/AddOn/lib/Image/ExifTool/HP.pm 2017-09-30 10:50:25.000000000 +0000 +++ xnview-0.93/opt64/XnView/AddOn/lib/Image/ExifTool/HP.pm 2019-01-10 14:15:17.000000000 +0000 @@ -250,7 +250,7 @@ =head1 AUTHOR -Copyright 2003-2017, Phil Harvey (phil at owl.phy.queensu.ca) +Copyright 2003-2019, Phil Harvey (phil at owl.phy.queensu.ca) This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself. diff -Nru xnview-0.92/opt64/XnView/AddOn/lib/Image/ExifTool/HtmlDump.pm xnview-0.93/opt64/XnView/AddOn/lib/Image/ExifTool/HtmlDump.pm --- xnview-0.92/opt64/XnView/AddOn/lib/Image/ExifTool/HtmlDump.pm 2017-09-30 10:50:31.000000000 +0000 +++ xnview-0.93/opt64/XnView/AddOn/lib/Image/ExifTool/HtmlDump.pm 2019-01-21 15:06:11.000000000 +0000 @@ -13,9 +13,9 @@ use Image::ExifTool; # only for FinishTiffDump() use Image::ExifTool::HTML qw(EscapeHTML); -$VERSION = '1.34'; +$VERSION = '1.35'; -sub DumpTable($$$;$$$$$); +sub DumpTable($$$;$$$$$$); sub Open($$$;@); sub Write($@); @@ -32,6 +32,7 @@ # Note: Don't change font-weight style because it can affect line height my $htmlHeader2 = <<_END_PART_2_; +