use
vars
qw($VERSION %exifSpec)
;
$VERSION
=
'1.24'
;
%exifSpec
= (
0x1
=> 210,
0x100
=> 1,
0x8298
=> 1,
0x9207
=> 1,
0xa217
=> 1,
0x101
=> 1,
0x829a
=> 1,
0x9208
=> 1,
0xa300
=> 1,
0x102
=> 1,
0x829d
=> 1,
0x9209
=> 1,
0xa301
=> 1,
0x103
=> 1,
0x8769
=> 1,
0x920a
=> 1,
0xa302
=> 1,
0x106
=> 1,
0x8822
=> 1,
0x9214
=> 220,
0xa401
=> 220,
0x10e
=> 1,
0x8824
=> 1,
0x927c
=> 1,
0xa402
=> 220,
0x10f
=> 1,
0x8825
=> 200,
0x9286
=> 1,
0xa403
=> 220,
0x110
=> 1,
0x8827
=> 1,
0x9290
=> 1,
0xa404
=> 220,
0x111
=> 1,
0x8828
=> 1,
0x9291
=> 1,
0xa405
=> 220,
0x112
=> 1,
0x8830
=> 230,
0x9292
=> 1,
0xa406
=> 220,
0x115
=> 1,
0x8831
=> 230,
0x9400
=> 231,
0xa407
=> 220,
0x116
=> 1,
0x8832
=> 230,
0x9401
=> 231,
0xa408
=> 220,
0x117
=> 1,
0x8833
=> 230,
0x9402
=> 231,
0xa409
=> 220,
0x11a
=> 1,
0x8834
=> 230,
0x9403
=> 231,
0xa40a
=> 220,
0x11b
=> 1,
0x8835
=> 230,
0x9404
=> 231,
0xa40b
=> 220,
0x11c
=> 1,
0x9000
=> 1,
0x9405
=> 231,
0xa40c
=> 220,
0x128
=> 1,
0x9003
=> 1,
0xa000
=> 1,
0xa460
=> 232,
0x12d
=> 1,
0x9004
=> 1,
0xa001
=> 1,
0xa461
=> 232,
0x131
=> 1,
0x9010
=> 231,
0xa002
=> 1,
0xa462
=> 232,
0x132
=> 1,
0x9011
=> 231,
0xa003
=> 1,
0xa420
=> 220,
0x13b
=> 1,
0x9012
=> 231,
0xa004
=> 1,
0xa430
=> 230,
0x13e
=> 1,
0x9101
=> 1,
0xa005
=> 210,
0xa431
=> 230,
0x13f
=> 1,
0x9102
=> 1,
0xa20b
=> 1,
0xa432
=> 230,
0x201
=> 1,
0x9201
=> 1,
0xa20c
=> 1,
0xa433
=> 230,
0x202
=> 1,
0x9202
=> 1,
0xa20e
=> 1,
0xa434
=> 230,
0x211
=> 1,
0x9203
=> 1,
0xa20f
=> 1,
0xa435
=> 230,
0x212
=> 1,
0x9204
=> 1,
0xa210
=> 1,
0xa500
=> 221,
0x213
=> 1,
0x9205
=> 1,
0xa214
=> 1,
0x214
=> 1,
0x9206
=> 1,
0xa215
=> 1,
0xa436
=> 300,
0xa437
=> 300,
0xa438
=> 300,
0xa439
=> 300,
0xa43a
=> 300,
0xa43b
=> 300,
0xa43c
=> 300,
);
my
%gpsVer
= (
0x01
=> 2000,
0x09
=> 2000,
0x11
=> 2000,
0x19
=> 2000,
0x02
=> 2000,
0x0a
=> 2000,
0x12
=> 2000,
0x1a
=> 2000,
0x03
=> 2000,
0x0b
=> 2000,
0x13
=> 2000,
0x1b
=> 2200,
0x04
=> 2000,
0x0c
=> 2000,
0x14
=> 2000,
0x1c
=> 2200,
0x05
=> 2000,
0x0d
=> 2000,
0x15
=> 2000,
0x1d
=> 2200,
0x06
=> 2000,
0x0e
=> 2000,
0x16
=> 2000,
0x1e
=> 2200,
0x07
=> 2000,
0x0f
=> 2000,
0x17
=> 2000,
0x1f
=> 2300,
0x08
=> 2000,
0x10
=> 2000,
0x18
=> 2000,
);
my
%verCheck
= (
ExifIFD
=> {
ExifVersion
=> \
%exifSpec
},
InteropIFD
=> {
ExifVersion
=> \
%exifSpec
},
GPS
=> {
GPSVersionID
=> \
%gpsVer
},
);
my
%otherSpec
= (
CR2
=> {
0xc5d8
=> 1,
0xc5d9
=> 1,
0xc5e0
=> 1,
0xc640
=> 1,
0xc6dc
=> 1,
0xc6dd
=> 1 },
NEF
=> {
0x9216
=> 1,
0x9217
=> 1 },
DNG
=> {
0x882a
=> 1,
0x9211
=> 1,
0x9216
=> 1 },
ARW
=> {
0x7000
=> 1,
0x7001
=> 1,
0x7010
=> 1,
0x7011
=> 1,
0x7020
=> 1,
0x7031
=> 1,
0x7032
=> 1,
0x7034
=> 1,
0x7035
=> 1,
0x7036
=> 1,
0x7037
=> 1,
0x7038
=> 1,
0x7310
=> 1,
0x7313
=> 1,
0x7316
=> 1,
0x74c7
=> 1,
0x74c8
=> 1,
0xa500
=> 1 },
RW2
=> {
All
=> 1 },
RWL
=> {
All
=> 1 },
RAF
=> {
All
=> 1 },
DCR
=> {
All
=> 1 },
KDC
=> {
All
=> 1 },
JXR
=> {
All
=> 1 },
SRW
=> {
0xa010
=> 1,
0xa011
=> 1,
0xa101
=> 1,
0xa102
=> 1 },
NRW
=> {
0x9216
=> 1,
0x9217
=> 1 },
X3F
=> {
0xa500
=> 1 },
CameraIFD
=> {
All
=> 1 },
);
my
%stdFormat
= (
ExifIFD
=> {
0xa002
=>
'int(16|32)u'
,
0xa003
=>
'int(16|32)u'
,
},
InteropIFD
=> {
0x01
=>
'string'
,
0x02
=>
'undef'
,
0x1000
=>
'string'
,
0x1001
=>
'int(16|32)u'
,
0x1002
=>
'int(16|32)u'
,
},
IFD
=> {
0xfe
=>
'int32u'
,
0x11f
=>
'rational64u'
,
0x14a
=>
'int32u'
,
0x205
=>
'int16u'
,
0xff
=>
'int16u'
,
0x120
=>
'int32u'
,
0x14c
=>
'int16u'
,
0x206
=>
'int16u'
,
0x100
=>
'int(16|32)u'
,
0x121
=>
'int32u'
,
0x14d
=>
'string'
,
0x207
=>
'int32u'
,
0x101
=>
'int(16|32)u'
,
0x122
=>
'int16u'
,
0x14e
=>
'int16u'
,
0x208
=>
'int32u'
,
0x107
=>
'int16u'
,
0x123
=>
'int16u'
,
0x150
=>
'int(8|16)u'
,
0x209
=>
'int32u'
,
0x108
=>
'int16u'
,
0x124
=>
'int32u'
,
0x151
=>
'string'
,
0x211
=>
'rational64u'
,
0x109
=>
'int16u'
,
0x125
=>
'int32u'
,
0x152
=>
'int16u'
,
0x212
=>
'int16u'
,
0x10a
=>
'int16u'
,
0x129
=>
'int16u'
,
0x153
=>
'int16u'
,
0x213
=>
'int16u'
,
0x10d
=>
'string'
,
0x13c
=>
'string'
,
0x154
=>
'.*'
,
0x214
=>
'rational64u'
,
0x111
=>
'int(16|32)u'
,
0x13d
=>
'int16u'
,
0x155
=>
'.*'
,
0x2bc
=>
'int8u'
,
0x116
=>
'int(16|32)u'
,
0x140
=>
'int16u'
,
0x156
=>
'int16u'
,
0x828d
=>
'int16u'
,
0x117
=>
'int(16|32)u'
,
0x141
=>
'int16u'
,
0x15b
=>
'undef'
,
0x828e
=>
'int8u'
,
0x118
=>
'int16u'
,
0x142
=>
'int(16|32)u'
,
0x200
=>
'int16u'
,
0x83bb
=>
'int32u'
,
0x119
=>
'int16u'
,
0x143
=>
'int(16|32)u'
,
0x201
=>
'int32u'
,
0x8649
=>
'int8u'
,
0x11d
=>
'string'
,
0x144
=>
'int32u'
,
0x202
=>
'int32u'
,
0x8773
=>
'undef'
,
0x11e
=>
'rational64u'
,
0x145
=>
'int(16|32)u'
,
0x203
=>
'int16u'
,
0xc4a5
=>
'undef'
,
0x9c9b
=>
'int8u'
,
0x9c9d
=>
'int8u'
,
0x9c9f
=>
'int8u'
,
0x9c9c
=>
'int8u'
,
0x9c9e
=>
'int8u'
,
0x830e
=>
'double'
,
0x8482
=>
'double'
,
0x87af
=>
'int16u'
,
0x87b1
=>
'string'
,
0x8480
=>
'double'
,
0x85d8
=>
'double'
,
0x87b0
=>
'double'
,
0xc615
=>
'(string|int8u)'
,
0xc6f4
=>
'(string|int8u)'
,
0xc61a
=>
'(int16u|int32u|rational64u)'
,
0xc6f6
=>
'(string|int8u)'
,
0xc61d
=>
'int(16|32)u'
,
0xc6f8
=>
'(string|int8u)'
,
0xc61f
=>
'(int16u|int32u|rational64u)'
,
0xc6fe
=>
'(string|int8u)'
,
0xc620
=>
'(int16u|int32u|rational64u)'
,
0xc716
=>
'(string|int8u)'
,
0xc628
=>
'(int16u|rational64u)'
,
0xc717
=>
'(string|int8u)'
,
0xc634
=>
'int8u'
,
0xc718
=>
'(string|int8u)'
,
0xc640
=>
''
,
0xc71e
=>
'int(16|32)u'
,
0xc660
=>
''
,
0xc71f
=>
'int(16|32)u'
,
0xc68b
=>
'(string|int8u)'
,
0xc791
=>
'int(16|32)u'
,
0xc68d
=>
'int(16|32)u'
,
0xc792
=>
'int(16|32)u'
,
0xc68e
=>
'int(16|32)u'
,
0xc793
=>
'(int16u|int32u|rational64u)'
,
0xc6d2
=>
''
,
0xcd43
=>
'int(16|32)u'
,
0xc6d3
=>
''
,
0xcd48
=>
'(string|int8u)'
,
0x10e
=>
'string|utf8'
,
0xa430
=>
'string|utf8'
,
0xa439
=>
'string|utf8'
,
0x10f
=>
'string|utf8'
,
0xa433
=>
'string|utf8'
,
0xa43a
=>
'string|utf8'
,
0x110
=>
'string|utf8'
,
0xa434
=>
'string|utf8'
,
0xa43b
=>
'string|utf8'
,
0x131
=>
'string|utf8'
,
0xa436
=>
'string|utf8'
,
0xa43c
=>
'string|utf8'
,
0x13b
=>
'string|utf8'
,
0xa437
=>
'string|utf8'
,
0xa43a
=>
'string|utf8'
,
0x8298
=>
'string|utf8'
,
0xa438
=>
'string|utf8'
,
},
);
my
%stdFormatAnyIFD
=
map
{ %{
$stdFormat
{
$_
}} }
keys
%stdFormat
;
my
%validValue
= (
JPEG
=> {
IFD0
=> {
0x100
=>
undef
,
0x101
=>
undef
,
0x102
=>
undef
,
0x103
=>
undef
,
0x106
=>
undef
,
0x111
=>
undef
,
0x115
=>
undef
,
0x116
=>
undef
,
0x117
=>
undef
,
0x11c
=>
undef
,
0x201
=>
undef
,
0x202
=>
undef
,
0x212
=>
undef
,
0x213
=>
'$val =~ /^[12]$/'
,
},
IFD1
=> {
0x100
=>
undef
,
0x101
=>
undef
,
0x102
=>
undef
,
0x103
=>
'$val == 6'
,
0x106
=>
undef
,
0x111
=>
undef
,
0x115
=>
undef
,
0x116
=>
undef
,
0x117
=>
undef
,
0x11a
=>
'defined $val'
,
0x11b
=>
'defined $val'
,
0x11c
=>
undef
,
0x128
=>
'$val =~ /^[123]$/'
,
0x201
=>
'defined $val'
,
0x202
=>
'defined $val'
,
0x212
=>
undef
,
},
ExifIFD
=> {
0x9000
=>
'defined $val and $val =~ /^\d{4}$/'
,
0x9101
=>
'defined $val'
,
0xa001
=>
'$val == 1 or $val == 0xffff'
,
0xa002
=>
'defined $val'
,
0xa003
=>
'defined $val'
,
},
GPS
=> {
0x00
=>
'defined $val and $val =~ /^\d \d \d \d$/'
,
0x1b
=>
'not defined $val or $val =~ /^(GPS|CELLID|WLAN|MANUAL)$/'
,
},
InteropIFD
=> { },
},
TIFF
=> {
IFD0
=> {
0x100
=>
'defined $val'
,
0x101
=>
'defined $val'
,
0x103
=>
q{
not defined $val or $val =~ /^(1|5|6|32773)$/ or
($val == 2 and (not defined $val{0x102}
or
$val
{0x102} == 1));
},
0x106
=>
'$val =~ /^[0123]$/'
,
0x111
=>
'defined $val'
,
0x115
=>
q{
my $pi = $val{0x106}
|| 0;
my
$xtra
= (
$val
{0x152} ?
scalar
(
split
' '
,
$val
{0x152}) : 0);
if
(
$pi
== 2 or
$pi
== 6) {
return
$val
== 3 +
$xtra
;
}
elsif
(
$pi
== 5) {
return
$val
== 4 +
$xtra
;
}
else
{
return
1;
}
},
0x116
=>
'defined $val'
,
0x117
=>
'defined $val'
,
0x11a
=>
'defined $val'
,
0x11b
=>
'defined $val'
,
0x128
=>
'not defined $val or $val =~ /^[123]$/'
,
0x140
=>
q{
return '' if defined $val{0x106}
and
$val
{0x106} == 3 xor
defined
$val
;
return
1
if
not
defined
$val
or
length
(
$val
) == 6 * 2 ** (
$val
{0x102} || 0);
return
'Invalid count for'
;
},
0x201
=>
undef
,
0x202
=>
undef
,
},
ExifIFD
=> {
0x9000
=>
'defined $val'
,
0x9101
=>
undef
,
0x9102
=>
undef
,
0xa000
=>
'defined $val'
,
0xa001
=>
'$val == 1 or $val == 0xffff'
,
0xa002
=>
undef
,
0xa003
=>
undef
,
},
InteropIFD
=> {
0x0001
=>
undef
,
},
GPS
=> {
0x00
=>
'defined $val and $val =~ /^\d \d \d \d$/'
,
0x1b
=>
'$val =~ /^(GPS|CELLID|WLAN|MANUAL)$/'
,
},
},
);
my
@validDateField
= (
[
'Month'
, 1, 12 ],
[
'Day'
, 1, 31 ],
[
'Hour'
, 0, 23 ],
[
'Minutes'
, 0, 59 ],
[
'Seconds'
, 0, 59 ],
[
'TZhr'
, 0, 14 ],
[
'TZmin'
, 0, 59 ],
);
my
%validateInfo
= (
Groups
=> {
0
=>
'ExifTool'
,
1
=>
'ExifTool'
,
2
=>
'ExifTool'
},
Notes
=>
q{
generated only if specifically requested. Requesting this tag automatically
enables the API L<Validate|../ExifTool.html#Validate> option, imposing
additional validation checks when extracting metadata. Returns the number
of errors, warnings and minor warnings encountered. Note that the Validate
feature focuses mainly on validation of EXIF/TIFF metadata
}
,
PrintConv
=> {
'0 0 0'
=>
'OK'
,
OTHER
=>
sub
{
my
@val
=
split
' '
,
shift
;
my
@rtn
;
push
@rtn
,
sprintf
(
'%d Error%s'
,
$val
[0],
$val
[0] == 1 ?
''
:
's'
)
if
$val
[0];
push
@rtn
,
sprintf
(
'%d Warning%s'
,
$val
[1],
$val
[1] == 1 ?
''
:
's'
)
if
$val
[1];
if
(
$val
[2]) {
my
$str
= (
$val
[1] ==
$val
[2] ? (
$val
[1] == 1 ?
''
:
'all '
) :
"$val[2] "
);
$rtn
[-1] .=
" (${str}minor)"
;
}
return
join
(
' and '
,
@rtn
);
},
},
);
AddTagToTable(\
%Image::ExifTool::Extra
,
Validate
=> \
%validateInfo
, 1);
sub
ValidateRaw($$$)
{
my
(
$self
,
$tag
,
$val
) =
@_
;
my
$tagInfo
=
$$self
{TAG_INFO}{
$tag
};
my
$wrn
;
if
(
$$tagInfo
{Validate}) {
local
$SIG
{
'__WARN__'
} = \
&Image::ExifTool::SetWarning
;
undef
$Image::ExifTool::evalWarning
;
my
$wrn
=
eval
$$tagInfo
{Validate};
my
$err
=
$Image::ExifTool::evalWarning
|| $@;
if
(
$wrn
or
$err
) {
my
$name
=
$$tagInfo
{Table}{GROUPS}{0} .
':'
. Image::ExifTool::GetTagName(
$tag
);
$self
->Warn(
"Validate $name: $err"
, 1)
if
$err
;
$self
->Warn(
"$wrn for $name"
, 1)
if
$wrn
;
}
}
if
(
ref
$$tagInfo
{PrintConv} eq
'HASH'
and (
$$tagInfo
{Table}{SHORT_NAME} eq
'GPS::Main'
or
(
$$tagInfo
{Table} eq \
%Image::ExifTool::Exif::Main
and
$exifSpec
{
$$tagInfo
{TagID}})))
{
my
$prt
=
$self
->GetValue(
$tag
,
'PrintConv'
);
$wrn
=
'Unknown value for'
if
$prt
and
$prt
=~ /^Unknown \(/;
}
$wrn
=
'Undefined value for'
if
$val
eq
'undef'
;
if
(
$wrn
) {
my
$name
=
$$self
{DIR_NAME} .
':'
. Image::ExifTool::GetTagName(
$tag
);
$self
->Warn(
"$wrn $name"
, 1);
}
}
sub
ValidateExifDate($)
{
my
$val
=
shift
;
if
(
$val
=~ /^\d{4}:(\d{2}):(\d{2}) (\d{2}):(\d{2}):(\d{2})$/) {
my
@a
= ($1,$2,$3,$4,$5);
my
(
$i
,
@bad
);
for
(
$i
=0;
$i
<
@a
; ++
$i
) {
next
if
$a
[
$i
] eq
' '
or (
$a
[
$i
] >=
$validDateField
[
$i
][1] and
$a
[
$i
] <=
$validDateField
[
$i
][2]);
push
@bad
,
$validDateField
[
$i
][0];
}
return
join
(
'+'
,
@bad
) .
' out of range'
if
@bad
;
}
elsif
(
$val
ne
' : : : : '
and
$val
ne
' '
) {
return
'Invalid date/time format'
;
}
return
undef
;
}
sub
ValidateXMPDate($)
{
my
$val
=
shift
;
if
(
$val
=~ /^\d{4}$/ or
$val
=~ /^\d{4}:(\d{2})$/ or
$val
=~ /^\d{4}:(\d{2}):(\d{2})$/ or
$val
=~ /^\d{4}:(\d{2}):(\d{2}) (\d{2}):(\d{2})()(Z|[-+](\d{2}):(\d{2}))?$/ or
$val
=~ /^\d{4}:(\d{2}):(\d{2}) (\d{2}):(\d{2}):(\d{2})(Z|[-+](\d{2}):(\d{2}))?$/ or
$val
=~ /^\d{4}:(\d{2}):(\d{2}) (\d{2}):(\d{2}):(\d{2})\.?\d*(Z|[-+](\d{2}):(\d{2}))?$/)
{
my
@a
= ($1,$2,$3,$4,$5,$7,$8);
my
(
$i
,
@bad
);
for
(
$i
=0;
$i
<
@a
; ++
$i
) {
last
unless
defined
$a
[
$i
];
next
if
$a
[
$i
] eq
''
or (
$a
[
$i
] >=
$validDateField
[
$i
][1] and
$a
[
$i
] <=
$validDateField
[
$i
][2]);
push
@bad
,
$validDateField
[
$i
][0];
}
return
join
(
'+'
,
@bad
) .
' out of range'
if
@bad
;
}
else
{
return
'Invalid date/time format'
;
}
return
undef
;
}
sub
ValidateExif($$$$$$$$)
{
my
(
$et
,
$tagTablePtr
,
$tag
,
$tagInfo
,
$lastTag
,
$ifd
,
$count
,
$formatStr
) =
@_
;
$et
->Warn(
"Entries in $ifd are out of order"
)
if
$tag
<=
$lastTag
;
if
(not
defined
$tagInfo
and
$$tagTablePtr
{
$tag
} and
ref
$$tagTablePtr
{
$tag
} eq
'HASH'
) {
$tagInfo
=
$$tagTablePtr
{
$tag
};
}
if
(
defined
$tagInfo
) {
my
$ti
=
$tagInfo
||
$$tagTablePtr
{
$tag
};
$ti
=
$$ti
[-1]
if
ref
$ti
eq
'ARRAY'
;
my
$stdFmt
=
$stdFormat
{
$ifd
} ||
$stdFormat
{IFD};
if
(
defined
$$stdFmt
{All} or (
$tagTablePtr
eq \
%Image::ExifTool::Exif::Main
and
(
$exifSpec
{
$tag
} or
$$stdFmt
{
$tag
} or
(
$tag
>= 0xc612 and
$tag
<= 0xcd48 and not
defined
$$stdFmt
{
$tag
}))) or
$$tagTablePtr
{SHORT_NAME} eq
'GPS::Main'
)
{
my
$wgp
=
$$ti
{WriteGroup} ||
$$tagTablePtr
{WRITE_GROUP};
if
(
$wgp
and
$wgp
ne
$ifd
and
$wgp
ne
'All'
and not
$$ti
{OffsetPair} and
(
$ifd
=~ /^(Sub|Profile)?IFD\d*$/ xor
$wgp
=~ /^(Sub)?IFD\d*$/) and
(
$$ti
{Writable} or
$$ti
{WriteGroup}) and
$ifd
!~ /^SRF\d+$/)
{
$et
->Warn(
sprintf
(
'Wrong IFD for 0x%.4x %s (should be %s not %s)'
,
$tag
,
$$ti
{Name},
$wgp
,
$ifd
));
}
my
$fmt
=
$$stdFmt
{
$tag
} ||
$$ti
{Writable};
if
(
$fmt
and
$formatStr
!~ /^
$fmt
$/ and (not
$tagInfo
or
not
$$tagInfo
{IsOffset} or
$Image::ExifTool::Exif::intFormat
{
$formatStr
}))
{
$et
->Warn(
sprintf
(
'Non-standard format (%s) for %s 0x%.4x %s'
,
$formatStr
,
$ifd
,
$tag
,
$$ti
{Name}))
}
}
elsif
(
$stdFormatAnyIFD
{
$tag
}) {
if
(
$$ti
{Writable} ||
$$ti
{WriteGroup}) {
my
$wgp
=
$$ti
{WriteGroup} ||
$$tagTablePtr
{WRITE_GROUP};
if
(
$wgp
and
$wgp
ne
$ifd
) {
$et
->Warn(
sprintf
(
'Wrong IFD for 0x%.4x %s (should be %s not %s)'
,
$tag
,
$$ti
{Name},
$wgp
,
$ifd
));
}
}
}
elsif
(not
$otherSpec
{
$$et
{FileType}} or
(not
$otherSpec
{
$$et
{FileType}}{
$tag
} and not
$otherSpec
{
$$et
{FileType}}{All}))
{
if
(
$tagTablePtr
eq \
%Image::ExifTool::Exif::Main
or
$$ti
{Unknown}) {
$et
->Warn(
sprintf
(
'Non-standard %s tag 0x%.4x %s'
,
$ifd
,
$tag
,
$$ti
{Name}), 1)
unless
$otherSpec
{
$ifd
};
}
}
my
$tiCount
=
$$ti
{Count};
if
(
$tiCount
) {
if
(
$$ti
{Format} and
$$ti
{Writable} and
$Image::ExifTool::Exif::formatNumber
{
$$ti
{Format}} and
$Image::ExifTool::Exif::formatNumber
{
$$ti
{Writable}})
{
my
$s1
=
$Image::ExifTool::Exif::formatSize
[
$Image::ExifTool::Exif::formatNumber
{
$$ti
{Format}}];
my
$s2
=
$Image::ExifTool::Exif::formatSize
[
$Image::ExifTool::Exif::formatNumber
{
$$ti
{Writable}}];
$tiCount
=
int
(
$tiCount
*
$s1
/
$s2
);
}
if
(
$tiCount
> 0 and
$count
!=
$tiCount
) {
$et
->Warn(
sprintf
(
'Non-standard count (%d) for %s 0x%.4x %s'
,
$count
,
$ifd
,
$tag
,
$$ti
{Name}));
}
}
}
elsif
(not
$otherSpec
{
$$et
{FileType}} or
(not
$otherSpec
{
$$et
{FileType}}{
$tag
} and not
$otherSpec
{
$$et
{FileType}}{All}))
{
$et
->Warn(
sprintf
(
'Unknown %s tag 0x%.4x'
,
$ifd
,
$tag
), 1)
unless
$otherSpec
{
$ifd
};
}
}
sub
ValidateOffsetInfo($$$;$)
{
local
$_
;
my
(
$et
,
$offsetInfo
,
$dirName
,
$minor
) =
@_
;
my
$fileSize
=
$$et
{VALUE}{FileSize} or
return
;
return
if
$$et
{DontValidateImageData};
return
if
$$et
{TIFF_TYPE} eq
'MRW'
and
$dirName
eq
'IFD0'
and
$$et
{Model} =~ /^DiMAGE A200/;
return
if
$$et
{TIFF_TYPE} =~ /^(3FR|RWL|RW2)$/;
Image::ExifTool::Exif::ValidateImageData(
$et
,
$offsetInfo
,
$dirName
);
while
(
%$offsetInfo
) {
my
(
$id1
) =
sort
keys
%$offsetInfo
;
my
$offsets
=
$$offsetInfo
{
$id1
};
delete
$$offsetInfo
{
$id1
};
next
unless
ref
$offsets
eq
'ARRAY'
;
my
$id2
=
$$offsets
[0]{OffsetPair};
unless
(
defined
$id2
and
$$offsetInfo
{
$id2
}) {
unless
(
$$offsets
[0]{NotRealPair} or (
defined
$id2
and
$id2
== -1)) {
my
$corr
=
$$offsets
[0]{IsOffset} ?
'size'
:
'offset'
;
$et
->Warn(
"$dirName:$$offsets[0]{Name} is missing the corresponding $corr tag"
)
unless
$minor
;
}
next
;
}
my
$sizes
=
$$offsetInfo
{
$id2
};
delete
$$offsetInfo
{
$id2
};
(
$sizes
,
$offsets
) = (
$offsets
,
$sizes
)
if
$$sizes
[0]{IsOffset};
my
@offsets
=
split
' '
,
$$offsets
[1];
my
@sizes
=
split
' '
,
$$sizes
[1];
if
(
@sizes
!=
@offsets
) {
$et
->Warn(
sprintf
(
'Wrong number of values in %s 0x%.4x %s'
,
$dirName
,
$$offsets
[0]{TagID},
$$offsets
[0]{Name}),
$minor
);
next
;
}
while
(
@offsets
) {
my
$start
=
pop
@offsets
;
my
$end
=
$start
+
pop
@sizes
;
$et
->Warn(
"$dirName:$$offsets[0]{Name} is zero"
,
$minor
)
if
$start
== 0;
$et
->Warn(
"$dirName:$$sizes[0]{Name} is zero"
,
$minor
)
if
$start
==
$end
;
next
unless
$end
>
$fileSize
;
if
(
$start
>=
$fileSize
) {
if
(
$start
== 0xffffffff) {
$et
->Warn(
"$dirName:$$offsets[0]{Name} is invalid (0xffffffff)"
,
$minor
);
}
else
{
$et
->Warn(
"$dirName:$$offsets[0]{Name} is past end of file"
,
$minor
);
}
}
else
{
$et
->Warn(
"$dirName:$$offsets[0]{Name}+$$sizes[0]{Name} runs past end of file"
,
$minor
);
}
last
;
}
}
}
sub
FinishValidate($$)
{
local
$_
;
my
(
$et
,
$mkTag
) =
@_
;
my
$fileType
=
$$et
{FILE_TYPE} ||
''
;
$fileType
=
$$et
{TIFF_TYPE}
if
$fileType
eq
'TIFF'
;
if
(
$validValue
{
$fileType
}) {
my
(
$grp
,
$tag
,
%val
);
local
$SIG
{
'__WARN__'
} = \
&Image::ExifTool::SetWarning
;
foreach
$grp
(
sort
keys
%{
$validValue
{
$fileType
}}) {
next
unless
$$et
{FOUND_DIR}{
$grp
};
my
(
$key
,
%val
,
%info
,
$minor
,
$verTag
,
$ver
,
$vstr
);
my
$verCheck
=
$verCheck
{
$grp
};
if
(
$verCheck
) {
(
$verTag
) =
keys
%$verCheck
;
(
$ver
=
$$et
{VALUE}{
$verTag
}) =~
tr
/0-9//dc;
undef
$ver
unless
$ver
=~ /^\d{4}$/;
}
foreach
$key
(
sort
keys
%{
$$et
{VALUE}}) {
next
unless
$et
->GetGroup(
$key
, 1) eq
$grp
;
next
if
$$et
{TAG_EXTRA}{
$key
}{G3};
my
$tag
=
$$et
{TAG_INFO}{
$key
}{TagID};
$val
{
$tag
} =
$$et
{VALUE}{
$key
};
$info
{
$tag
} =
$$et
{TAG_INFO}{
$key
};
next
unless
defined
$ver
;
my
$chk
=
$$verCheck
{
$verTag
};
next
if
not
defined
$$chk
{
$tag
} or
$$chk
{
$tag
} == 1 or
$ver
>=
$$chk
{
$tag
};
if
(
$verTag
eq
'GPSVersionID'
) {
(
$vstr
=
$$chk
{
$tag
}) =~ s/^(\d)(\d)(\d)/$1.$2.$3./;
}
else
{
$vstr
=
sprintf
(
'%.4d'
,
$$chk
{
$tag
});
}
$et
->Warn(
sprintf
(
'%s tag 0x%.4x %s requires %s %s or higher'
,
$grp
,
$tag
,
$$et
{TAG_INFO}{
$key
}{Name},
$verTag
,
$vstr
));
}
my
$validValue
=
$validValue
{
$fileType
}{
$grp
};
foreach
$tag
(
sort
{
$a
<=>
$b
}
keys
%$validValue
) {
my
$val
=
$val
{
$tag
};
my
(
$pre
,
$post
);
if
(
defined
$$validValue
{
$tag
}) {
my
$result
=
eval
$$validValue
{
$tag
};
if
(not
defined
$result
) {
$pre
=
'Internal error validating'
;
}
elsif
(
$result
eq
''
) {
$pre
=
defined
$val
?
'Invalid value for'
:
"Missing required $fileType"
;
}
else
{
next
if
$result
eq
'1'
;
$pre
=
$result
;
}
}
else
{
next
unless
defined
$val
;
$post
=
"is not allowed in $fileType"
;
$minor
= 1;
}
my
$name
;
if
(
$info
{
$tag
}) {
$name
=
$info
{
$tag
}{Name};
}
else
{
my
$table
=
'Image::ExifTool::'
.(
$grp
eq
'GPS'
?
'GPS'
:
'Exif'
).
'::Main'
;
my
$tagInfo
= GetTagTable(
$table
)->{
$tag
};
$tagInfo
=
$$tagInfo
[0]
if
ref
$tagInfo
eq
'ARRAY'
;
$name
=
$tagInfo
?
$$tagInfo
{Name} :
'<unknown>'
;
}
next
if
$$et
{WrongFormat} and
$$et
{WrongFormat}{
"$grp:$name"
};
$pre
? (
$pre
.=
' '
) : (
$pre
=
''
);
$post
? (
$post
=
' '
.
$post
) : (
$post
=
''
);
$et
->Warn(
sprintf
(
'%s%s tag 0x%.4x %s%s'
,
$pre
,
$grp
,
$tag
,
$name
,
$post
),
$minor
);
}
}
}
if
(
$$et
{FILENAME} ne
''
) {
my
$fileExt
= (
$$et
{FILENAME} =~ /^.*\.([^.]+)$/s) ?
uc
($1) :
''
;
my
$extFileType
= Image::ExifTool::GetFileType(
$fileExt
);
if
(
$extFileType
and
$extFileType
ne
$fileType
) {
my
$normExt
=
$$et
{VALUE}{FileTypeExtension};
if
(
$normExt
and
$normExt
ne
$fileExt
) {
my
$lkup
=
$Image::ExifTool::fileTypeLookup
{
$fileExt
};
if
(
ref
$lkup
or
$lkup
ne
$normExt
) {
$et
->Warn(
"File has wrong extension (should be $normExt, not $fileExt)"
);
}
}
}
}
$et
->Warn(
'Validation incomplete because FastScan option used'
)
if
$et
->Options(
'FastScan'
);
if
(
$mkTag
) {
my
(
@num
,
$key
);
push
@num
,
$$et
{VALUE}{Error} ? (
$$et
{DUPL_TAG}{Error} || 0) + 1 : 0,
$$et
{VALUE}{Warning} ? (
$$et
{DUPL_TAG}{Warning} || 0) + 1 : 0, 0;
for
(
$key
=
'Warning'
; ; ) {
++
$num
[2]
if
$$et
{VALUE}{
$key
} and
$$et
{VALUE}{
$key
} =~ /^\[minor\]/i;
$key
=
$et
->NextTagKey(
$key
) or
last
;
}
$et
->FoundTag(
Validate
=>
"@num"
);
}
}
1;