- Timestamp:
- 2021-02-26T19:39:51+13:00 (3 years ago)
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
main/trunk/greenstone2/perllib/cpan/Image/ExifTool/XMP.pm
r24107 r34921 24 24 # 9) http://www.w3.org/TR/SVG11/ 25 25 # 10) http://www.adobe.com/devnet/xmp/pdfs/XMPSpecificationPart2.pdf (Oct 2008) 26 # 11) http://www.extensis.com/en/support/kb_article.jsp?articleNumber=6102211 27 # 12) http://www.cipa.jp/std/documents/e/DC-010-2012_E.pdf 28 # 13) http://www.cipa.jp/std/documents/e/DC-010-2017_E.pdf (changed to 29 # http://www.cipa.jp/std/documents/e/DC-X010-2017.pdf) 26 30 # 27 31 # Notes: - Property qualifiers are handled as if they were separate … … 40 44 41 45 use strict; 42 use vars qw($VERSION $AUTOLOAD @ISA @EXPORT_OK $xlatNamespace %nsURI %dateTimeInfo43 % xmpTableDefaults %specialStruct %sDimensions %sArea %sColorant);46 use vars qw($VERSION $AUTOLOAD @ISA @EXPORT_OK %stdXlatNS %nsURI %latConv %longConv 47 %dateTimeInfo %xmpTableDefaults %specialStruct %sDimensions %sArea %sColorant); 44 48 use Image::ExifTool qw(:Utils); 45 49 use Image::ExifTool::Exif; 50 use Image::ExifTool::GPS; 46 51 require Exporter; 47 52 48 $VERSION = ' 2.37';53 $VERSION = '3.38'; 49 54 @ISA = qw(Exporter); 50 55 @EXPORT_OK = qw(EscapeXML UnescapeXML); … … 52 57 sub ProcessXMP($$;$); 53 58 sub WriteXMP($$;$); 54 sub CheckXMP($$$ );55 sub ParseXMPElement($$$;$$$ );59 sub CheckXMP($$$;$); 60 sub ParseXMPElement($$$;$$$$); 56 61 sub DecodeBase64($); 62 sub EncodeBase64($;$); 57 63 sub SaveBlankInfo($$$;$); 58 64 sub ProcessBlankInfo($$$;$); 59 65 sub ValidateXMP($;$); 60 sub UnescapeChar($$); 61 sub AddFlattenedTags($$); 66 sub ValidateProperty($$;$); 67 sub UnescapeChar($$;$); 68 sub AddFlattenedTags($;$$); 62 69 sub FormatXMPDate($); 63 70 sub ConvertRational($); 64 65 my %curNS; # namespaces currently in effect while parsing the file 66 67 # lookup for translating to ExifTool namespaces 68 # Note: Use $xlatNamespace (only valid during processing) to do the translation 69 my %stdXlatNS = ( 71 sub ConvertRationalList($); 72 sub WriteGSpherical($$$); 73 74 # lookup for translating to ExifTool namespaces (and family 1 group names) 75 %stdXlatNS = ( 70 76 # shorten ugly namespace prefixes 71 77 'Iptc4xmpCore' => 'iptcCore', … … 74 80 'MicrosoftPhoto' => 'microsoft', 75 81 'prismusagerights' => 'pur', 76 ); 77 78 # translate ExifTool namespaces to standard XMP namespace prefixes 82 'GettyImagesGIFT' => 'getty', 83 ); 84 85 # translate ExifTool XMP family 1 group names back to standard XMP namespace prefixes 79 86 my %xmpNS = ( 80 # shorten ugly namespace prefixes81 87 'iptcCore' => 'Iptc4xmpCore', 82 88 'iptcExt' => 'Iptc4xmpExt', 83 'photomech anic'=> 'photomech',89 'photomech'=> 'photomechanic', 84 90 'microsoft' => 'MicrosoftPhoto', 91 'getty' => 'GettyImagesGIFT', 85 92 # (prism changed their spec to now use 'pur') 86 93 # 'pur' => 'prismusagerights', 87 94 ); 88 95 89 # Lookup to translate our namespace prefixes into URI's. This list need 90 # not be complete, but it must contain an entry for each namespace prefix 91 # (NAMESPACE) for writable tags in the XMP tables or in structures 96 # Lookup to translate standard XMP namespace prefixes into URI's. This list 97 # need not be complete, but it must contain an entry for each namespace prefix 98 # (NAMESPACE) for writable tags in the XMP tables or in structures that doesn't 99 # define a URI. Also, the namespace must be defined here for non-standard 100 # namespace prefixes to be recognized. 92 101 %nsURI = ( 93 102 aux => 'http://ns.adobe.com/exif/1.0/aux/', 94 103 album => 'http://ns.adobe.com/album/1.0/', 95 104 cc => 'http://creativecommons.org/ns#', # changed 2007/12/21 - PH 105 crd => 'http://ns.adobe.com/camera-raw-defaults/1.0/', 96 106 crs => 'http://ns.adobe.com/camera-raw-settings/1.0/', 97 107 crss => 'http://ns.adobe.com/camera-raw-saved-settings/1.0/', 98 108 dc => 'http://purl.org/dc/elements/1.1/', 99 109 exif => 'http://ns.adobe.com/exif/1.0/', 110 exifEX => 'http://cipa.jp/exif/1.0/', 100 111 iX => 'http://ns.adobe.com/iX/1.0/', 101 112 pdf => 'http://ns.adobe.com/pdf/1.3/', … … 105 116 rdfs => 'http://www.w3.org/2000/01/rdf-schema#', 106 117 stDim => 'http://ns.adobe.com/xap/1.0/sType/Dimensions#', 107 stArea => 'http://ns.adobe.com/xap/1.0/sType/Area#',108 118 stEvt => 'http://ns.adobe.com/xap/1.0/sType/ResourceEvent#', 109 119 stFnt => 'http://ns.adobe.com/xap/1.0/sType/Font#', … … 127 137 dex => 'http://ns.optimasc.com/dex/1.0/', 128 138 mediapro => 'http://ns.iview-multimedia.com/mediapro/1.0/', 139 expressionmedia => 'http://ns.microsoft.com/expressionmedia/1.0/', 129 140 Iptc4xmpCore => 'http://iptc.org/std/Iptc4xmpCore/1.0/xmlns/', 130 141 Iptc4xmpExt => 'http://iptc.org/std/Iptc4xmpExt/2008-02-29/', … … 136 147 lr => 'http://ns.adobe.com/lightroom/1.0/', 137 148 DICOM => 'http://ns.adobe.com/DICOM/', 149 'drone-dji'=> 'http://www.dji.com/drone-dji/1.0/', 138 150 svg => 'http://www.w3.org/2000/svg', 139 151 et => 'http://ns.exiftool.ca/1.0/', 140 # namespaces defined in XMP2.pl: 152 # 153 # namespaces defined in XMP2.pl: 154 # 141 155 plus => 'http://ns.useplus.org/ldf/xmp/1.0/', 142 prism => 'http://prismstandard.org/namespaces/basic/2.1/', 156 # (prism recommendations from http://www.prismstandard.org/specifications/3.0/Image_Guide_3.0.htm) 157 prism => 'http://prismstandard.org/namespaces/basic/2.0/', 143 158 prl => 'http://prismstandard.org/namespaces/prl/2.1/', 144 159 pur => 'http://prismstandard.org/namespaces/prismusagerights/2.1/', 160 pmi => 'http://prismstandard.org/namespaces/pmi/2.2/', 161 prm => 'http://prismstandard.org/namespaces/prm/3.0/', 145 162 acdsee => 'http://ns.acdsee.com/iptc/1.0/', 146 163 digiKam => 'http://www.digikam.org/ns/1.0/', 147 swf => 'http://ns.adobe.com/swf/1.0 ',164 swf => 'http://ns.adobe.com/swf/1.0/', 148 165 cell => 'http://developer.sonyericsson.com/cell/1.0/', 166 aas => 'http://ns.apple.com/adjustment-settings/1.0/', 149 167 'mwg-rs' => 'http://www.metadataworkinggroup.com/schemas/regions/', 150 168 'mwg-kw' => 'http://www.metadataworkinggroup.com/schemas/keywords/', 151 169 'mwg-coll' => 'http://www.metadataworkinggroup.com/schemas/collections/', 170 stArea => 'http://ns.adobe.com/xmp/sType/Area#', 171 extensis => 'http://ns.extensis.com/extensis/1.0/', 172 ics => 'http://ns.idimager.com/ics/1.0/', 173 fpv => 'http://ns.fastpictureviewer.com/fpv/1.0/', 174 creatorAtom=>'http://ns.adobe.com/creatorAtom/1.0/', 175 'apple-fi' => 'http://ns.apple.com/faceinfo/1.0/', 176 GAudio => 'http://ns.google.com/photos/1.0/audio/', 177 GImage => 'http://ns.google.com/photos/1.0/image/', 178 GPano => 'http://ns.google.com/photos/1.0/panorama/', 179 GSpherical=> 'http://ns.google.com/videos/1.0/spherical/', 180 GDepth => 'http://ns.google.com/photos/1.0/depthmap/', 181 GFocus => 'http://ns.google.com/photos/1.0/focus/', 182 GCamera => 'http://ns.google.com/photos/1.0/camera/', 183 GCreations=> 'http://ns.google.com/photos/1.0/creations/', 184 dwc => 'http://rs.tdwg.org/dwc/index.htm', 185 GettyImagesGIFT => 'http://xmp.gettyimages.com/gift/1.0/', 186 LImage => 'http://ns.leiainc.com/photos/1.0/image/', 187 Profile => 'http://ns.google.com/photos/dd/1.0/profile/', 152 188 ); 153 189 154 190 # build reverse namespace lookup 155 my %uri2ns ;191 my %uri2ns = ( 'http://ns.exiftool.org/1.0/' => 'et' ); # (allow exiftool.org as well as exiftool.ca) 156 192 { 157 193 my $ns; … … 162 198 163 199 # conversions for GPS coordinates 164 sub ToDegrees 165 { 166 require Image::ExifTool::GPS; 167 Image::ExifTool::GPS::ToDegrees($_[0], 1); 168 } 169 my %latConv = ( 170 ValueConv => \&ToDegrees, 171 RawConv => 'require Image::ExifTool::GPS; $val', # to load Composite tags and routines 172 ValueConvInv => q{ 173 require Image::ExifTool::GPS; 174 Image::ExifTool::GPS::ToDMS($self, $val, 2, "N"); 175 }, 200 %latConv = ( 201 ValueConv => 'Image::ExifTool::GPS::ToDegrees($val, 1)', 202 ValueConvInv => 'Image::ExifTool::GPS::ToDMS($self, $val, 2, "N")', 176 203 PrintConv => 'Image::ExifTool::GPS::ToDMS($self, $val, 1, "N")', 177 PrintConvInv => \&ToDegrees, 178 ); 179 my %longConv = ( 180 ValueConv => \&ToDegrees, 181 RawConv => 'require Image::ExifTool::GPS; $val', 182 ValueConvInv => q{ 183 require Image::ExifTool::GPS; 184 Image::ExifTool::GPS::ToDMS($self, $val, 2, "E"); 185 }, 204 PrintConvInv => 'Image::ExifTool::GPS::ToDegrees($val, 1)', 205 ); 206 %longConv = ( 207 ValueConv => 'Image::ExifTool::GPS::ToDegrees($val, 1)', 208 ValueConvInv => 'Image::ExifTool::GPS::ToDMS($self, $val, 2, "E")', 186 209 PrintConv => 'Image::ExifTool::GPS::ToDMS($self, $val, 1, "E")', 187 PrintConvInv => \&ToDegrees,210 PrintConvInv => 'Image::ExifTool::GPS::ToDegrees($val, 1)', 188 211 ); 189 212 %dateTimeInfo = ( … … 191 214 Writable => 'date', 192 215 Shift => 'Time', 216 Validate => 'ValidateXMPDate($val)', 193 217 PrintConv => '$self->ConvertDateTime($val)', 194 218 PrintConvInv => '$self->InverseDateTime($val,undef,1)', 219 ); 220 221 # this conversion allows alternate language support for designated boolean tags 222 my %boolConv = ( 223 PrintConv => { 224 OTHER => sub { # (inverse conversion is the same) 225 my $val = shift; 226 return 'False' if lc $val eq 'false'; 227 return 'True' if lc $val eq 'true'; 228 return $val; 229 }, 230 True => 'True', 231 False => 'False', 232 }, 195 233 ); 196 234 … … 198 236 # (Note: namespaces with non-standard prefixes aren't currently ignored) 199 237 my %ignoreNamespace = ( 'x'=>1, rdf=>1, xmlns=>1, xml=>1, svg=>1, et=>1, office=>1 ); 238 239 # XMP properties to ignore (set dynamically via dirInfo IgnoreProp) 240 my %ignoreProp; 200 241 201 242 # these are the attributes that we handle for properties that contain … … 211 252 'rdf:nodeID' => 1, 212 253 'et:toolkit' => 1, 213 'rdf:xmlns' => 1, # this is presumably the default namespace, which we currently ignore 254 'rdf:xmlns' => 1, # this is presumably the default namespace, which we currently ignore 255 'lastUpdate' => [ 'Image::ExifTool::XMP::XML', 'lastUpdate', 'LastUpdate' ], # found in XML from Sony ILCE-7S MP4 214 256 ); 215 257 … … 222 264 TYPE => 1, # [optional] rdf:type resource for struct (if used, the StructType flag 223 265 # will be set automatically for all derived flattened tags when writing) 266 GROUPS => 1, # [optional] specifies family group 2 name for the structure 224 267 ); 225 268 # XMP structures (each structure is similar to a tag table so we can … … 252 295 # added May 2010 253 296 originalDocumentID => { }, # (undocumented property written by Adobe InDesign) 297 # added Aug 2016 (INDD again) 298 lastURL => { }, 299 linkForm => { }, 300 linkCategory => { }, 301 placedXResolution => { }, 302 placedYResolution => { }, 303 placedResolutionUnit => { }, 254 304 ); 255 305 my %sResourceEvent = ( … … 287 337 'format' => { }, 288 338 image => { 339 Avoid => 1, 340 Groups => { 2 => 'Preview' }, 289 341 ValueConv => 'Image::ExifTool::XMP::DecodeBase64($val)', 290 342 ValueConvInv => 'Image::ExifTool::XMP::EncodeBase64($val)', … … 299 351 'format' => { }, 300 352 image => { 353 Groups => { 2 => 'Preview' }, 301 354 ValueConv => 'Image::ExifTool::XMP::DecodeBase64($val)', 302 355 ValueConvInv => 'Image::ExifTool::XMP::EncodeBase64($val)', … … 339 392 green => { Writable => 'integer' }, 340 393 blue => { Writable => 'integer' }, 394 gray => { Writable => 'integer' }, 341 395 L => { Writable => 'real' }, 342 396 A => { Writable => 'integer' }, 343 397 B => { Writable => 'integer' }, 398 # 'tint' observed in INDD sample - PH 399 tint => { Writable => 'integer', Notes => 'not part of 2010 XMP specification' }, 400 ); 401 my %sSwatchGroup = ( 402 STRUCT_NAME => 'SwatchGroup', 403 NAMESPACE => 'xmpG', 404 groupName => { }, 405 groupType => { Writable => 'integer' }, 406 Colorants => { 407 FlatName => 'SwatchColorant', 408 Struct => \%sColorant, 409 List => 'Seq', 410 }, 344 411 ); 345 412 my %sFont = ( … … 356 423 ); 357 424 my %sOECF = ( 425 STRUCT_NAME => 'OECF', 358 426 NAMESPACE => 'exif', 359 STRUCT_NAME => 'OECF',360 427 Columns => { Writable => 'integer' }, 361 428 Rows => { Writable => 'integer' }, … … 368 435 STRUCT_NAME => 'CorrectionMask', 369 436 NAMESPACE => 'crs', 370 What => { }, 371 MaskValue => { Writable => 'real' }, 372 Radius => { Writable => 'real' }, 373 Flow => { Writable => 'real' }, 374 CenterWeight => { Writable => 'real' }, 437 # disable List behaviour of flattened Gradient/PaintBasedCorrections 438 # because these are nested in lists and the flattened tags can't 439 # do justice to this complex structure 440 What => { List => 0 }, 441 MaskValue => { Writable => 'real', List => 0, FlatName => 'Value' }, 442 Radius => { Writable => 'real', List => 0 }, 443 Flow => { Writable => 'real', List => 0 }, 444 CenterWeight => { Writable => 'real', List => 0 }, 375 445 Dabs => { List => 'Seq' }, 376 ZeroX => { Writable => 'real' }, 377 ZeroY => { Writable => 'real' }, 378 FullX => { Writable => 'real' }, 379 FullY => { Writable => 'real' }, 446 ZeroX => { Writable => 'real', List => 0 }, 447 ZeroY => { Writable => 'real', List => 0 }, 448 FullX => { Writable => 'real', List => 0 }, 449 FullY => { Writable => 'real', List => 0 }, 450 # new elements used in CircularGradientBasedCorrections CorrectionMasks 451 # and RetouchAreas Masks 452 Top => { Writable => 'real', List => 0 }, 453 Left => { Writable => 'real', List => 0 }, 454 Bottom => { Writable => 'real', List => 0 }, 455 Right => { Writable => 'real', List => 0 }, 456 Angle => { Writable => 'real', List => 0 }, 457 Midpoint => { Writable => 'real', List => 0 }, 458 Roundness => { Writable => 'real', List => 0 }, 459 Feather => { Writable => 'real', List => 0 }, 460 Flipped => { Writable => 'boolean', List => 0 }, 461 Version => { Writable => 'integer', List => 0 }, 462 SizeX => { Writable => 'real', List => 0 }, 463 SizeY => { Writable => 'real', List => 0 }, 464 X => { Writable => 'real', List => 0 }, 465 Y => { Writable => 'real', List => 0 }, 466 Alpha => { Writable => 'real', List => 0 }, 467 CenterValue => { Writable => 'real', List => 0 }, 468 PerimeterValue=>{ Writable => 'real', List => 0 }, 469 ); 470 my %sCorrectionRangeMask = ( 471 STRUCT_NAME => 'CorrectionRangeMask', 472 NAMESPACE => 'crs', 473 Version => { }, 474 Type => { }, 475 ColorAmount => { Writable => 'real' }, 476 LumMin => { Writable => 'real' }, 477 LumMax => { Writable => 'real' }, 478 LumFeather => { Writable => 'real' }, 479 DepthMin => { Writable => 'real' }, 480 DepthMax => { Writable => 'real' }, 481 DepthFeather=> { Writable => 'real' }, 380 482 ); 381 483 my %sCorrection = ( 382 484 STRUCT_NAME => 'Correction', 383 485 NAMESPACE => 'crs', 384 What => { }, 385 CorrectionAmount => { Writable => 'real' }, 386 CorrectionActive => { Writable => 'boolean' }, 387 LocalExposure => { Writable => 'real' }, 388 LocalSaturation => { Writable => 'real' }, 389 LocalContrast => { Writable => 'real' }, 390 LocalClarity => { Writable => 'real' }, 391 LocalSharpness => { Writable => 'real' }, 392 LocalBrightness => { Writable => 'real' }, 393 LocalToningHue => { Writable => 'real' }, 394 LocalToningSaturation => { Writable => 'real' }, 395 CorrectionMasks => { Struct => \%sCorrectionMask, List => 'Seq' }, 396 ); 397 398 # IPTC Extension 1.0 structures 399 my %sLocationDetails = ( 400 NAMESPACE => 'Iptc4xmpExt', 401 STRUCT_NAME => 'LocationDetails', 402 City => { }, 403 CountryCode => { }, 404 CountryName => { }, 405 ProvinceState=> { }, 406 Sublocation => { }, 407 WorldRegion => { }, 486 What => { List => 0 }, 487 CorrectionAmount => { FlatName => 'Amount', Writable => 'real', List => 0 }, 488 CorrectionActive => { FlatName => 'Active', Writable => 'boolean', List => 0 }, 489 LocalExposure => { FlatName => 'Exposure', Writable => 'real', List => 0 }, 490 LocalSaturation => { FlatName => 'Saturation', Writable => 'real', List => 0 }, 491 LocalContrast => { FlatName => 'Contrast', Writable => 'real', List => 0 }, 492 LocalClarity => { FlatName => 'Clarity', Writable => 'real', List => 0 }, 493 LocalSharpness => { FlatName => 'Sharpness', Writable => 'real', List => 0 }, 494 LocalBrightness => { FlatName => 'Brightness', Writable => 'real', List => 0 }, 495 LocalToningHue => { FlatName => 'Hue', Writable => 'real', List => 0 }, 496 LocalToningSaturation => { FlatName => 'Saturation', Writable => 'real', List => 0 }, 497 LocalExposure2012 => { FlatName => 'Exposure2012', Writable => 'real', List => 0 }, 498 LocalContrast2012 => { FlatName => 'Contrast2012', Writable => 'real', List => 0 }, 499 LocalHighlights2012 => { FlatName => 'Highlights2012', Writable => 'real', List => 0 }, 500 LocalShadows2012 => { FlatName => 'Shadows2012', Writable => 'real', List => 0 }, 501 LocalClarity2012 => { FlatName => 'Clarity2012', Writable => 'real', List => 0 }, 502 LocalLuminanceNoise => { FlatName => 'LuminanceNoise', Writable => 'real', List => 0 }, 503 LocalMoire => { FlatName => 'Moire', Writable => 'real', List => 0 }, 504 LocalDefringe => { FlatName => 'Defringe', Writable => 'real', List => 0 }, 505 LocalTemperature => { FlatName => 'Temperature',Writable => 'real', List => 0 }, 506 LocalTint => { FlatName => 'Tint', Writable => 'real', List => 0 }, 507 LocalHue => { FlatName => 'Hue', Writable => 'real', List => 0 }, 508 LocalWhites2012 => { FlatName => 'Whites2012', Writable => 'real', List => 0 }, 509 LocalBlacks2012 => { FlatName => 'Blacks2012', Writable => 'real', List => 0 }, 510 LocalDehaze => { FlatName => 'Dehaze', Writable => 'real', List => 0 }, 511 LocalTexture => { FlatName => 'Texture', Writable => 'real', List => 0 }, 512 CorrectionRangeMask => { 513 FlatName => 'RangeMask', 514 Struct => \%sCorrectionRangeMask, 515 }, 516 CorrectionMasks => { 517 FlatName => 'Mask', 518 Struct => \%sCorrectionMask, 519 List => 'Seq', 520 }, 521 ); 522 my %sRetouchArea = ( 523 STRUCT_NAME => 'RetouchArea', 524 NAMESPACE => 'crs', 525 SpotType => { List => 0 }, 526 SourceState => { List => 0 }, 527 Method => { List => 0 }, 528 SourceX => { Writable => 'real', List => 0 }, 529 OffsetY => { Writable => 'real', List => 0 }, 530 Opacity => { Writable => 'real', List => 0 }, 531 Feather => { Writable => 'real', List => 0 }, 532 Seed => { Writable => 'integer', List => 0 }, 533 Masks => { 534 FlatName => 'Mask', 535 Struct => \%sCorrectionMask, 536 List => 'Seq', 537 }, 408 538 ); 409 539 … … 457 587 SubDirectory => { TagTable => 'Image::ExifTool::XMP::photoshop' }, 458 588 }, 589 crd => { 590 Name => 'crd', 591 SubDirectory => { TagTable => 'Image::ExifTool::XMP::crd' }, 592 }, 459 593 crs => { 460 594 Name => 'crs', 461 595 SubDirectory => { TagTable => 'Image::ExifTool::XMP::crs' }, 462 596 }, 463 # crss - it would be difficultto add the ability to write this597 # crss - it would be tedious to add the ability to write this 464 598 aux => { 465 599 Name => 'aux', … … 474 608 SubDirectory => { TagTable => 'Image::ExifTool::XMP::exif' }, 475 609 }, 610 exifEX => { 611 Name => 'exifEX', 612 SubDirectory => { TagTable => 'Image::ExifTool::XMP::exifEX' }, 613 }, 476 614 iptcCore => { 477 615 Name => 'iptcCore', … … 491 629 plus => { 492 630 Name => 'plus', 493 SubDirectory => { TagTable => 'Image::ExifTool:: XMP::plus' },631 SubDirectory => { TagTable => 'Image::ExifTool::PLUS::XMP' }, 494 632 }, 495 633 cc => { … … 509 647 SubDirectory => { TagTable => 'Image::ExifTool::XMP::MediaPro' }, 510 648 }, 649 expressionmedia => { 650 Name => 'expressionmedia', 651 SubDirectory => { TagTable => 'Image::ExifTool::XMP::ExpressionMedia' }, 652 }, 511 653 microsoft => { 512 654 Name => 'microsoft', … … 545 687 SubDirectory => { TagTable => 'Image::ExifTool::XMP::pur' }, 546 688 }, 689 pmi => { 690 Name => 'pmi', 691 SubDirectory => { TagTable => 'Image::ExifTool::XMP::pmi' }, 692 }, 693 prm => { 694 Name => 'prm', 695 SubDirectory => { TagTable => 'Image::ExifTool::XMP::prm' }, 696 }, 547 697 rdf => { 548 698 Name => 'rdf', … … 569 719 SubDirectory => { TagTable => 'Image::ExifTool::XMP::cell' }, 570 720 }, 721 aas => { 722 Name => 'aas', 723 SubDirectory => { TagTable => 'Image::ExifTool::XMP::aas' }, 724 }, 571 725 'mwg-rs' => { 572 726 Name => 'mwg-rs', 573 SubDirectory => { TagTable => 'Image::ExifTool:: XMP::mwg_rs' },727 SubDirectory => { TagTable => 'Image::ExifTool::MWG::Regions' }, 574 728 }, 575 729 'mwg-kw' => { 576 730 Name => 'mwg-kw', 577 SubDirectory => { TagTable => 'Image::ExifTool:: XMP::mwg_kw' },731 SubDirectory => { TagTable => 'Image::ExifTool::MWG::Keywords' }, 578 732 }, 579 733 'mwg-coll' => { 580 734 Name => 'mwg-coll', 581 SubDirectory => { TagTable => 'Image::ExifTool::XMP::mwg_coll' }, 735 SubDirectory => { TagTable => 'Image::ExifTool::MWG::Collections' }, 736 }, 737 extensis => { 738 Name => 'extensis', 739 SubDirectory => { TagTable => 'Image::ExifTool::XMP::extensis' }, 740 }, 741 ics => { 742 Name => 'ics', 743 SubDirectory => { TagTable => 'Image::ExifTool::XMP::ics' }, 744 }, 745 fpv => { 746 Name => 'fpv', 747 SubDirectory => { TagTable => 'Image::ExifTool::XMP::fpv' }, 748 }, 749 creatorAtom => { 750 Name => 'creatorAtom', 751 SubDirectory => { TagTable => 'Image::ExifTool::XMP::creatorAtom' }, 752 }, 753 'apple-fi' => { 754 Name => 'apple-fi', 755 SubDirectory => { TagTable => 'Image::ExifTool::XMP::apple_fi' }, 756 }, 757 GAudio => { 758 Name => 'GAudio', 759 SubDirectory => { TagTable => 'Image::ExifTool::XMP::GAudio' }, 760 }, 761 GImage => { 762 Name => 'GImage', 763 SubDirectory => { TagTable => 'Image::ExifTool::XMP::GImage' }, 764 }, 765 GPano => { 766 Name => 'GPano', 767 SubDirectory => { TagTable => 'Image::ExifTool::XMP::GPano' }, 768 }, 769 GSpherical => { 770 Name => 'GSpherical', 771 SubDirectory => { TagTable => 'Image::ExifTool::XMP::GSpherical' }, 772 }, 773 GDepth => { 774 Name => 'GDepth', 775 SubDirectory => { TagTable => 'Image::ExifTool::XMP::GDepth' }, 776 }, 777 GFocus => { 778 Name => 'GFocus', 779 SubDirectory => { TagTable => 'Image::ExifTool::XMP::GFocus' }, 780 }, 781 GCamera => { 782 Name => 'GCamera', 783 SubDirectory => { TagTable => 'Image::ExifTool::XMP::GCamera' }, 784 }, 785 GCreations => { 786 Name => 'GCreations', 787 SubDirectory => { TagTable => 'Image::ExifTool::XMP::GCreations' }, 788 }, 789 dwc => { 790 Name => 'dwc', 791 SubDirectory => { TagTable => 'Image::ExifTool::DarwinCore::Main' }, 792 }, 793 getty => { 794 Name => 'getty', 795 SubDirectory => { TagTable => 'Image::ExifTool::XMP::GettyImages' }, 796 }, 797 'drone-dji' => { 798 Name => 'drone-dji', 799 SubDirectory => { TagTable => 'Image::ExifTool::DJI::XMP' }, 800 }, 801 LImage => { 802 Name => 'LImage', 803 SubDirectory => { TagTable => 'Image::ExifTool::XMP::LImage' }, 804 }, 805 Device => { 806 Name => 'Device', 807 SubDirectory => { TagTable => 'Image::ExifTool::XMP::Device' }, 808 }, 809 ); 810 811 # hack to allow XML containing Dublin Core metadata to be handled like XMP (eg. EPUB - see ZIP.pm) 812 %Image::ExifTool::XMP::XML = ( 813 GROUPS => { 0 => 'XML', 1 => 'XML', 2 => 'Unknown' }, 814 PROCESS_PROC => \&ProcessXMP, 815 dc => { 816 Name => 'dc', 817 SubDirectory => { TagTable => 'Image::ExifTool::XMP::dc' }, 818 }, 819 lastUpdate => { 820 Groups => { 2 => 'Time' }, 821 ValueConv => 'Image::ExifTool::XMP::ConvertXMPDate($val)', 822 PrintConv => '$self->ConvertDateTime($val)', 582 823 }, 583 824 ); 584 825 585 826 # 586 # Tag tables for all XMP schemas:827 # Tag tables for all XMP namespaces: 587 828 # 588 829 # Writable - only need to define this for writable tags if not plain text … … 621 862 The "x" namespace is used for the "xmpmeta" wrapper, and may contain an 622 863 "xmptk" attribute that is extracted as the XMPToolkit tag. When writing, 623 the XMPToolkit tag is automatically generatedby ExifTool unless864 the XMPToolkit tag is generated automatically by ExifTool unless 624 865 specifically set to another value. 625 866 }, … … 627 868 ); 628 869 629 # Dublin Core schemaproperties (dc)870 # Dublin Core namespace properties (dc) 630 871 %Image::ExifTool::XMP::dc = ( 631 872 %xmpTableDefaults, … … 633 874 NAMESPACE => 'dc', 634 875 TABLE_DESC => 'XMP Dublin Core', 635 NOTES => 'Dublin Core schematags.',876 NOTES => 'Dublin Core namespace tags.', 636 877 contributor => { Groups => { 2 => 'Author' }, List => 'Bag' }, 637 878 coverage => { }, … … 651 892 ); 652 893 653 # XMP Basic schemaproperties (xmp, xap)894 # XMP namespace properties (xmp, xap) 654 895 %Image::ExifTool::XMP::xmp = ( 655 896 %xmpTableDefaults, … … 657 898 NAMESPACE => 'xmp', 658 899 NOTES => q{ 659 XMP Basic schematags. If the older "xap", "xapBJ", "xapMM" or "xapRights"900 XMP namespace tags. If the older "xap", "xapBJ", "xapMM" or "xapRights" 660 901 namespace prefixes are found, they are translated to the newer "xmp", 661 902 "xmpBJ", "xmpMM" and "xmpRights" prefixes for use in family 1 group names. 662 903 }, 663 Advisory => { List => 'Bag' }, # (deprecated)904 Advisory => { List => 'Bag', Notes => 'deprecated' }, 664 905 BaseURL => { }, 665 906 # (date/time tags not as reliable as EXIF) … … 672 913 Nickname => { }, 673 914 Rating => { Writable => 'real', Notes => 'a value from 0 to 5, or -1 for "rejected"' }, 674 Thumbnails => { Struct => \%sThumbnail, List => 'Alt' }, 675 ThumbnailsHeight => { Name => 'ThumbnailHeight', Flat => 1 }, 676 ThumbnailsWidth => { Name => 'ThumbnailWidth', Flat => 1 }, 677 ThumbnailsFormat => { Name => 'ThumbnailFormat', Flat => 1 }, 678 ThumbnailsImage => { Name => 'ThumbnailImage', Flat => 1, Avoid => 1 }, 915 RatingPercent=>{ Writable => 'real', Avoid => 1, Notes => 'non-standard' }, 916 Thumbnails => { 917 FlatName => 'Thumbnail', 918 Struct => \%sThumbnail, 919 List => 'Alt', 920 }, 679 921 # the following written by Adobe InDesign, not part of XMP spec: 680 PageInfo => { Struct => \%sPageInfo, List => 'Seq' }, 681 PageInfoPageNumber=>{Name => 'PageImagePageNumber', Flat => 1 }, 682 PageInfoHeight => { Name => 'PageImageHeight', Flat => 1 }, 683 PageInfoWidth => { Name => 'PageImageWidth', Flat => 1 }, 684 PageInfoFormat => { Name => 'PageImageFormat', Flat => 1 }, 685 PageInfoImage => { Name => 'PageImage', Flat => 1 }, 686 ); 687 688 # XMP Rights Management schema properties (xmpRights, xapRights) 922 PageInfo => { 923 FlatName => 'PageImage', 924 Struct => \%sPageInfo, 925 List => 'Seq', 926 }, 927 PageInfoImage => { Name => 'PageImage', Flat => 1 }, 928 Title => { Avoid => 1, Notes => 'non-standard', Writable => 'lang-alt' }, #11 929 Author => { Avoid => 1, Notes => 'non-standard', Groups => { 2 => 'Author' } }, #11 930 Keywords => { Avoid => 1, Notes => 'non-standard' }, #11 931 Description => { Avoid => 1, Notes => 'non-standard', Writable => 'lang-alt' }, #11 932 Format => { Avoid => 1, Notes => 'non-standard' }, #11 933 ); 934 935 # XMP Rights Management namespace properties (xmpRights, xapRights) 689 936 %Image::ExifTool::XMP::xmpRights = ( 690 937 %xmpTableDefaults, 691 938 GROUPS => { 1 => 'XMP-xmpRights', 2 => 'Author' }, 692 939 NAMESPACE => 'xmpRights', 693 NOTES => 'XMP Rights Management schematags.',940 NOTES => 'XMP Rights Management namespace tags.', 694 941 Certificate => { }, 695 942 Marked => { Writable => 'boolean' }, … … 699 946 ); 700 947 701 # XMP Note schemaproperties (xmpNote)948 # XMP Note namespace properties (xmpNote) 702 949 %Image::ExifTool::XMP::xmpNote = ( 703 950 %xmpTableDefaults, 704 951 GROUPS => { 1 => 'XMP-xmpNote' }, 705 952 NAMESPACE => 'xmpNote', 706 NOTES => 'XMP Note schema tags.', 707 HasExtendedXMP => { Writable => 'boolean', Protected => 2 }, 953 NOTES => 'XMP Note namespace tags.', 954 HasExtendedXMP => { 955 Notes => q{ 956 this tag is protected so it is not writable directly. Instead, it is set 957 automatically to the GUID of the extended XMP when writing extended XMP to a 958 JPEG image 959 }, 960 Protected => 2, 961 }, 708 962 ); 709 963 710 964 # XMP xmpMM ManifestItem struct (ref PH, written by Adobe PDF library 8.0) 711 965 my %sManifestItem = ( 712 NAMESPACE => 'stMfs',713 966 STRUCT_NAME => 'ManifestItem', 967 NAMESPACE => 'stMfs', 714 968 linkForm => { }, 715 969 placedXResolution => { Namespace => 'xmpMM', Writable => 'real' }, … … 721 975 # the xmpMM Pantry 722 976 my %sPantryItem = ( 723 NAMESPACE => undef, # stores any top-level XMP tags724 977 STRUCT_NAME => 'PantryItem', 978 NAMESPACE => undef, # stores any top-level XMP tags 725 979 NOTES => q{ 726 980 This structure must have an InstanceID field, but may also contain any other 727 981 XMP properties. 728 982 }, 729 InstanceID => { Namespace => 'xmpMM' },730 ); 731 732 # XMP Media Management schemaproperties (xmpMM, xapMM)983 InstanceID => { Namespace => 'xmpMM', List => 0 }, 984 ); 985 986 # XMP Media Management namespace properties (xmpMM, xapMM) 733 987 %Image::ExifTool::XMP::xmpMM = ( 734 988 %xmpTableDefaults, … … 736 990 NAMESPACE => 'xmpMM', 737 991 TABLE_DESC => 'XMP Media Management', 738 NOTES => 'XMP Media Management schematags.',992 NOTES => 'XMP Media Management namespace tags.', 739 993 DerivedFrom => { Struct => \%sResourceRef }, 740 994 DocumentID => { }, … … 759 1013 RenditionOf => { Struct => \%sResourceRef }, # (deprecated) 760 1014 SaveID => { Writable => 'integer' }, # (deprecated) 761 ); 762 763 # XMP Basic Job Ticket schema properties (xmpBJ, xapBJ) 1015 subject => { List => 'Seq', Avoid => 1, Notes => 'undocumented' }, 1016 ); 1017 1018 # XMP Basic Job Ticket namespace properties (xmpBJ, xapBJ) 764 1019 %Image::ExifTool::XMP::xmpBJ = ( 765 1020 %xmpTableDefaults, … … 767 1022 NAMESPACE => 'xmpBJ', 768 1023 TABLE_DESC => 'XMP Basic Job Ticket', 769 NOTES => 'XMP Basic Job Ticket schematags.',1024 NOTES => 'XMP Basic Job Ticket namespace tags.', 770 1025 # Note: JobRef is a List of structures. To accomplish this, we set the XMP 771 1026 # List=>'Bag', but since SubDirectory is defined, this tag isn't writable … … 775 1030 ); 776 1031 777 # XMP Paged-Text schemaproperties (xmpTPg)1032 # XMP Paged-Text namespace properties (xmpTPg) 778 1033 %Image::ExifTool::XMP::xmpTPg = ( 779 1034 %xmpTableDefaults, … … 781 1036 NAMESPACE => 'xmpTPg', 782 1037 TABLE_DESC => 'XMP Paged-Text', 783 NOTES => 'XMP Paged-Text schematags.',1038 NOTES => 'XMP Paged-Text namespace tags.', 784 1039 MaxPageSize => { Struct => \%sDimensions }, 785 1040 NPages => { Writable => 'integer' }, 786 Fonts => { Struct => \%sFont, List => 'Bag' }, 787 FontsFontName => { Flat => 1, Name => 'FontName' }, 788 FontsFontFamily => { Flat => 1, Name => 'FontFamily' }, 789 FontsFontFace => { Flat => 1, Name => 'FontFace' }, 790 FontsFontType => { Flat => 1, Name => 'FontType' }, 791 FontsVersionString => { Flat => 1, Name => 'FontVersion' }, 792 FontsComposite => { Flat => 1, Name => 'FontComposite' }, 793 FontsFontFileName => { Flat => 1, Name => 'FontFileName' }, 794 FontsChildFontFiles => { Flat => 1, Name => 'ChildFontFiles' }, 795 Colorants => { Struct => \%sColorant, List => 'Seq' }, 796 ColorantsSwatchName => { Flat => 1, Name => 'ColorantSwatchName' }, 797 ColorantsMode => { Flat => 1, Name => 'ColorantMode' }, 798 ColorantsType => { Flat => 1, Name => 'ColorantType' }, 799 ColorantsCyan => { Flat => 1, Name => 'ColorantCyan' }, 800 ColorantsMagenta => { Flat => 1, Name => 'ColorantMagenta' }, 801 ColorantsYellow => { Flat => 1, Name => 'ColorantYellow' }, 802 ColorantsBlack => { Flat => 1, Name => 'ColorantBlack' }, 803 ColorantsRed => { Flat => 1, Name => 'ColorantRed' }, 804 ColorantsGreen => { Flat => 1, Name => 'ColorantGreen' }, 805 ColorantsBlue => { Flat => 1, Name => 'ColorantBlue' }, 806 ColorantsL => { Flat => 1, Name => 'ColorantL' }, 807 ColorantsA => { Flat => 1, Name => 'ColorantA' }, 808 ColorantsB => { Flat => 1, Name => 'ColorantB' }, 1041 Fonts => { 1042 FlatName => '', 1043 Struct => \%sFont, 1044 List => 'Bag', 1045 }, 1046 FontsVersionString => { Name => 'FontVersion', Flat => 1 }, 1047 FontsComposite => { Name => 'FontComposite', Flat => 1 }, 1048 Colorants => { 1049 FlatName => 'Colorant', 1050 Struct => \%sColorant, 1051 List => 'Seq', 1052 }, 809 1053 PlateNames => { List => 'Seq' }, 810 ); 811 812 # PDF schema properties (pdf) 1054 # the following found in an AI file: 1055 HasVisibleTransparency => { Writable => 'boolean' }, 1056 HasVisibleOverprint => { Writable => 'boolean' }, 1057 SwatchGroups => { 1058 Struct => \%sSwatchGroup, 1059 List => 'Seq', 1060 }, 1061 SwatchGroupsColorants => { Name => 'SwatchGroupsColorants', Flat => 1 }, 1062 SwatchGroupsGroupName => { Name => 'SwatchGroupName', Flat => 1 }, 1063 SwatchGroupsGroupType => { Name => 'SwatchGroupType', Flat => 1 }, 1064 ); 1065 1066 # PDF namespace properties (pdf) 813 1067 %Image::ExifTool::XMP::pdf = ( 814 1068 %xmpTableDefaults, … … 817 1071 TABLE_DESC => 'XMP PDF', 818 1072 NOTES => q{ 819 Adobe PDF schematags. The official XMP specification defines only1073 Adobe PDF namespace tags. The official XMP specification defines only 820 1074 Keywords, PDFVersion, Producer and Trapped. The other tags are included 821 1075 because they have been observed in PDF files, but some are avoided when … … 836 1090 PrintConv => { True => 'True', False => 'False', Unknown => 'Unknown' }, 837 1091 }, 838 Keywords => { },1092 Keywords => { Priority => -1 }, # (-1 to get below Priority 0 PDF:Keywords) 839 1093 PDFVersion => { }, 840 1094 Producer => { Groups => { 2 => 'Author' } }, 841 1095 ); 842 1096 843 # PDF extension schemaproperties (pdfx)1097 # PDF extension namespace properties (pdfx) 844 1098 %Image::ExifTool::XMP::pdfx = ( 845 1099 %xmpTableDefaults, … … 853 1107 ); 854 1108 855 # Photoshop schemaproperties (photoshop)1109 # Photoshop namespace properties (photoshop) 856 1110 %Image::ExifTool::XMP::photoshop = ( 857 1111 %xmpTableDefaults, … … 859 1113 NAMESPACE => 'photoshop', 860 1114 TABLE_DESC => 'XMP Photoshop', 861 NOTES => 'Adobe Photoshop schematags.',1115 NOTES => 'Adobe Photoshop namespace tags.', 862 1116 AuthorsPosition => { Groups => { 2 => 'Author' } }, 863 1117 CaptionWriter => { Groups => { 2 => 'Author' } }, … … 882 1136 DateCreated => { Groups => { 2 => 'Time' }, %dateTimeInfo }, 883 1137 DocumentAncestors => { 884 List => 'bag', 885 Struct => { 886 STRUCT_NAME => 'Ancestor', 887 NAMESPACE => 'photoshop', 888 AncestorID => { }, 889 }, 890 }, 891 DocumentAncestorsAncestorID => { Name => 'DocumentAncestorID', Flat => 1 }, 1138 List => 'Bag', 1139 # Contrary to their own XMP specification, Adobe writes this as a simple Bag 1140 # of strings instead of structures, so comment out the structure definition... 1141 # FlatName => 'Document', 1142 # Struct => { 1143 # STRUCT_NAME => 'Ancestor', 1144 # NAMESPACE => 'photoshop', 1145 # AncestorID => { }, 1146 # }, 1147 }, 892 1148 Headline => { }, 893 1149 History => { }, #PH (CS3) … … 904 1160 SupplementalCategories => { List => 'Bag' }, 905 1161 TextLayers => { 906 List => 'seq', 1162 FlatName => 'Text', 1163 List => 'Seq', 907 1164 Struct => { 908 1165 STRUCT_NAME => 'Layer', 909 1166 NAMESPACE => 'photoshop', 910 LayerName => { },1167 LayerName => { }, 911 1168 LayerText => { }, 912 1169 }, 913 1170 }, 914 TextLayersLayerName => { Flat => 1, Name => 'TextLayerName' }, 915 TextLayersLayerText => { Flat => 1, Name => 'TextLayerText' }, 916 TransmissionReference => { }, 1171 TransmissionReference => { Notes => 'Now used as a job identifier' }, 917 1172 Urgency => { 918 1173 Writable => 'integer', … … 931 1186 }, 932 1187 }, 933 ); 934 935 # Photoshop Camera Raw Schema properties (crs) - (ref 8,PH) 1188 EmbeddedXMPDigest => { }, #PH (LR5) 1189 ); 1190 1191 # Photoshop Camera Raw namespace properties (crs) - (ref 8,PH) 936 1192 %Image::ExifTool::XMP::crs = ( 937 1193 %xmpTableDefaults, 938 1194 GROUPS => { 1 => 'XMP-crs', 2 => 'Image' }, 939 1195 NAMESPACE => 'crs', 940 TABLE_DESC => 'Photoshop Camera Raw Schema', 941 NOTES => 'Photoshop Camera Raw Schema tags.', 1196 TABLE_DESC => 'Photoshop Camera Raw namespace', 1197 NOTES => q{ 1198 Photoshop Camera Raw namespace tags. It is a shame that Adobe pollutes the 1199 metadata space with these incredibly bulky image editing parameters. 1200 }, 942 1201 AlreadyApplied => { Writable => 'boolean' }, #PH (written by LightRoom beta 4.1) 943 1202 AutoBrightness => { Writable => 'boolean' }, … … 984 1243 Sharpness => { Writable => 'integer', Avoid => 1 }, 985 1244 Smoothness => { Writable => 'integer' }, 986 Temperature => { Writable => 'integer', Avoid => 1,Name => 'ColorTemperature' },1245 Temperature => { Writable => 'integer', Name => 'ColorTemperature' }, 987 1246 Tint => { Writable => 'integer' }, 988 1247 ToneCurve => { List => 'Seq' }, … … 1055 1314 SharpenEdgeMasking => { Writable => 'integer' }, 1056 1315 SharpenRadius => { Writable => 'real' }, 1057 SplitToningBalance => { Writable => 'integer' },1058 SplitToningHighlightHue => { Writable => 'integer' },1059 SplitToningHighlightSaturation => { Writable => 'integer' },1060 SplitToningShadowHue => { Writable => 'integer' },1061 SplitToningShadowSaturation => { Writable => 'integer' },1316 SplitToningBalance => { Writable => 'integer', Notes => 'also used for newer ColorGrade settings' }, 1317 SplitToningHighlightHue => { Writable => 'integer', Notes => 'also used for newer ColorGrade settings' }, 1318 SplitToningHighlightSaturation => { Writable => 'integer', Notes => 'also used for newer ColorGrade settings' }, 1319 SplitToningShadowHue => { Writable => 'integer', Notes => 'also used for newer ColorGrade settings' }, 1320 SplitToningShadowSaturation => { Writable => 'integer', Notes => 'also used for newer ColorGrade settings' }, 1062 1321 Vibrance => { Writable => 'integer' }, 1063 1322 # new tags written by LR 1.4 (not sure in what version they first appeared) … … 1090 1349 # because these are nested in lists and the flattened tags can't 1091 1350 # do justice to this complex structure 1092 GradientBasedCorrections => { Struct => \%sCorrection, List => 'Seq' }, 1093 GradientBasedCorrectionsWhat => { 1094 Name => 'GradientBasedCorrWhat', 1095 Flat => 1, List => 0, 1096 }, 1097 GradientBasedCorrectionsCorrectionAmount => { 1098 Name => 'GradientBasedCorrAmount', 1099 Flat => 1, List => 0, 1100 }, 1101 GradientBasedCorrectionsCorrectionActive => { 1102 Name => 'GradientBasedCorrActive', 1103 Flat => 1, List => 0, 1104 }, 1105 GradientBasedCorrectionsLocalExposure => { 1106 Name => 'GradientBasedCorrExposure', 1107 Flat => 1, List => 0, 1108 }, 1109 GradientBasedCorrectionsLocalSaturation => { 1110 Name => 'GradientBasedCorrSaturation', 1111 Flat => 1, List => 0, 1112 }, 1113 GradientBasedCorrectionsLocalContrast => { 1114 Name => 'GradientBasedCorrContrast', 1115 Flat => 1, List => 0, 1116 }, 1117 GradientBasedCorrectionsLocalClarity => { 1118 Name => 'GradientBasedCorrClarity', 1119 Flat => 1, List => 0, 1120 }, 1121 GradientBasedCorrectionsLocalSharpness => { 1122 Name => 'GradientBasedCorrSharpness', 1123 Flat => 1, List => 0, 1124 }, 1125 GradientBasedCorrectionsLocalBrightness => { 1126 Name => 'GradientBasedCorrBrightness', 1127 Flat => 1, List => 0, 1128 }, 1129 GradientBasedCorrectionsLocalToningHue => { 1130 Name => 'GradientBasedCorrHue', 1131 Flat => 1, List => 0, 1132 }, 1133 GradientBasedCorrectionsLocalToningSaturation => { 1134 Name => 'GradientBasedCorrSaturation', 1135 Flat => 1, List => 0, 1351 GradientBasedCorrections => { 1352 FlatName => 'GradientBasedCorr', 1353 Struct => \%sCorrection, 1354 List => 'Seq', 1136 1355 }, 1137 1356 GradientBasedCorrectionsCorrectionMasks => { 1138 1357 Name => 'GradientBasedCorrMasks', 1358 FlatName => 'GradientBasedCorrMask', 1139 1359 Flat => 1 1140 },1141 GradientBasedCorrectionsCorrectionMasksWhat => {1142 Name => 'GradientBasedCorrMaskWhat',1143 Flat => 1, List => 0,1144 },1145 GradientBasedCorrectionsCorrectionMasksMaskValue => {1146 Name => 'GradientBasedCorrMaskValue',1147 Flat => 1, List => 0,1148 },1149 GradientBasedCorrectionsCorrectionMasksRadius => {1150 Name => 'GradientBasedCorrMaskRadius',1151 Flat => 1, List => 0,1152 },1153 GradientBasedCorrectionsCorrectionMasksFlow => {1154 Name => 'GradientBasedCorrMaskFlow',1155 Flat => 1, List => 0,1156 },1157 GradientBasedCorrectionsCorrectionMasksCenterWeight => {1158 Name => 'GradientBasedCorrMaskCenterWeight',1159 Flat => 1, List => 0,1160 1360 }, 1161 1361 GradientBasedCorrectionsCorrectionMasksDabs => { … … 1163 1363 Flat => 1, List => 0, 1164 1364 }, 1165 GradientBasedCorrectionsCorrectionMasksZeroX => { 1166 Name => 'GradientBasedCorrMaskZeroX', 1167 Flat => 1, List => 0, 1168 }, 1169 GradientBasedCorrectionsCorrectionMasksZeroY => { 1170 Name => 'GradientBasedCorrMaskZeroY', 1171 Flat => 1, List => 0, 1172 }, 1173 GradientBasedCorrectionsCorrectionMasksFullX => { 1174 Name => 'GradientBasedCorrMaskFullX', 1175 Flat => 1, List => 0, 1176 }, 1177 GradientBasedCorrectionsCorrectionMasksFullY => { 1178 Name => 'GradientBasedCorrMaskFullY', 1179 Flat => 1, List => 0, 1180 }, 1181 PaintBasedCorrections => { Struct => \%sCorrection, List => 'Seq' }, 1182 PaintBasedCorrectionsWhat => { 1183 Name => 'PaintCorrectionWhat', 1184 Flat => 1, List => 0, 1185 }, 1186 PaintBasedCorrectionsCorrectionAmount => { 1187 Name => 'PaintCorrectionAmount', 1188 Flat => 1, List => 0, 1189 }, 1190 PaintBasedCorrectionsCorrectionActive => { 1191 Name => 'PaintCorrectionActive', 1192 Flat => 1, List => 0, 1193 }, 1194 PaintBasedCorrectionsLocalExposure => { 1195 Name => 'PaintCorrectionExposure', 1196 Flat => 1, List => 0, 1197 }, 1198 PaintBasedCorrectionsLocalSaturation => { 1199 Name => 'PaintCorrectionSaturation', 1200 Flat => 1, List => 0, 1201 }, 1202 PaintBasedCorrectionsLocalContrast => { 1203 Name => 'PaintCorrectionContrast', 1204 Flat => 1, List => 0, 1205 }, 1206 PaintBasedCorrectionsLocalClarity => { 1207 Name => 'PaintCorrectionClarity', 1208 Flat => 1, List => 0, 1209 }, 1210 PaintBasedCorrectionsLocalSharpness => { 1211 Name => 'PaintCorrectionSharpness', 1212 Flat => 1, List => 0, 1213 }, 1214 PaintBasedCorrectionsLocalBrightness => { 1215 Name => 'PaintCorrectionBrightness', 1216 Flat => 1, List => 0, 1217 }, 1218 PaintBasedCorrectionsLocalToningHue => { 1219 Name => 'PaintCorrectionHue', 1220 Flat => 1, List => 0, 1221 }, 1222 PaintBasedCorrectionsLocalToningSaturation => { 1223 Name => 'PaintCorrectionSaturation', 1224 Flat => 1, List => 0, 1365 PaintBasedCorrections => { 1366 FlatName => 'PaintCorrection', 1367 Struct => \%sCorrection, 1368 List => 'Seq', 1225 1369 }, 1226 1370 PaintBasedCorrectionsCorrectionMasks => { 1227 1371 Name => 'PaintBasedCorrectionMasks', 1372 FlatName => 'PaintCorrectionMask', 1228 1373 Flat => 1, 1229 },1230 PaintBasedCorrectionsCorrectionMasksWhat => {1231 Name => 'PaintCorrectionMaskWhat',1232 Flat => 1, List => 0,1233 },1234 PaintBasedCorrectionsCorrectionMasksMaskValue => {1235 Name => 'PaintCorrectionMaskValue',1236 Flat => 1, List => 0,1237 },1238 PaintBasedCorrectionsCorrectionMasksRadius => {1239 Name => 'PaintCorrectionMaskRadius',1240 Flat => 1, List => 0,1241 },1242 PaintBasedCorrectionsCorrectionMasksFlow => {1243 Name => 'PaintCorrectionMaskFlow',1244 Flat => 1, List => 0,1245 },1246 PaintBasedCorrectionsCorrectionMasksCenterWeight => {1247 Name => 'PaintCorrectionMaskCenterWeight',1248 Flat => 1, List => 0,1249 1374 }, 1250 1375 PaintBasedCorrectionsCorrectionMasksDabs => { … … 1252 1377 Flat => 1, List => 0, 1253 1378 }, 1254 PaintBasedCorrectionsCorrectionMasksZeroX => { 1255 Name => 'PaintCorrectionMaskZeroX', 1379 # new tags written by LR 3 (thanks Wolfgang Guelcker) 1380 ProcessVersion => { }, 1381 LensProfileEnable => { Writable => 'integer' }, 1382 LensProfileSetup => { }, 1383 LensProfileName => { }, 1384 LensProfileFilename => { }, 1385 LensProfileDigest => { }, 1386 LensProfileDistortionScale => { Writable => 'integer' }, 1387 LensProfileChromaticAberrationScale => { Writable => 'integer' }, 1388 LensProfileVignettingScale => { Writable => 'integer' }, 1389 LensManualDistortionAmount => { Writable => 'integer' }, 1390 PerspectiveVertical => { Writable => 'integer' }, 1391 PerspectiveHorizontal => { Writable => 'integer' }, 1392 PerspectiveRotate => { Writable => 'real' }, 1393 PerspectiveScale => { Writable => 'integer' }, 1394 CropConstrainToWarp => { Writable => 'integer' }, 1395 LuminanceNoiseReductionDetail => { Writable => 'integer' }, 1396 LuminanceNoiseReductionContrast => { Writable => 'integer' }, 1397 ColorNoiseReductionDetail => { Writable => 'integer' }, 1398 GrainAmount => { Writable => 'integer' }, 1399 GrainSize => { Writable => 'integer' }, 1400 GrainFrequency => { Writable => 'integer' }, 1401 # new tags written by LR4 1402 AutoLateralCA => { Writable => 'integer' }, 1403 Exposure2012 => { Writable => 'real' }, 1404 Contrast2012 => { Writable => 'integer' }, 1405 Highlights2012 => { Writable => 'integer' }, 1406 Highlight2012 => { Writable => 'integer' }, # (written by Nikon software) 1407 Shadows2012 => { Writable => 'integer' }, 1408 Whites2012 => { Writable => 'integer' }, 1409 Blacks2012 => { Writable => 'integer' }, 1410 Clarity2012 => { Writable => 'integer' }, 1411 PostCropVignetteHighlightContrast => { Writable => 'integer' }, 1412 ToneCurveName2012 => { }, 1413 ToneCurveRed => { List => 'Seq' }, 1414 ToneCurveGreen => { List => 'Seq' }, 1415 ToneCurveBlue => { List => 'Seq' }, 1416 ToneCurvePV2012 => { List => 'Seq' }, 1417 ToneCurvePV2012Red => { List => 'Seq' }, 1418 ToneCurvePV2012Green => { List => 'Seq' }, 1419 ToneCurvePV2012Blue => { List => 'Seq' }, 1420 DefringePurpleAmount => { Writable => 'integer' }, 1421 DefringePurpleHueLo => { Writable => 'integer' }, 1422 DefringePurpleHueHi => { Writable => 'integer' }, 1423 DefringeGreenAmount => { Writable => 'integer' }, 1424 DefringeGreenHueLo => { Writable => 'integer' }, 1425 DefringeGreenHueHi => { Writable => 'integer' }, 1426 # new tags written by LR5 1427 AutoWhiteVersion => { Writable => 'integer' }, 1428 CircularGradientBasedCorrections => { 1429 FlatName => 'CircGradBasedCorr', 1430 Struct => \%sCorrection, 1431 List => 'Seq', 1432 }, 1433 CircularGradientBasedCorrectionsCorrectionMasks => { 1434 Name => 'CircGradBasedCorrMasks', 1435 FlatName => 'CircGradBasedCorrMask', 1436 Flat => 1 1437 }, 1438 CircularGradientBasedCorrectionsCorrectionMasksDabs => { 1439 Name => 'CircGradBasedCorrMaskDabs', 1256 1440 Flat => 1, List => 0, 1257 1441 }, 1258 PaintBasedCorrectionsCorrectionMasksZeroY => { 1259 Name => 'PaintCorrectionMaskZeroY', 1442 ColorNoiseReductionSmoothness => { Writable => 'integer' }, 1443 PerspectiveAspect => { Writable => 'integer' }, 1444 PerspectiveUpright => { Writable => 'integer' }, 1445 RetouchAreas => { 1446 FlatName => 'RetouchArea', 1447 Struct => \%sRetouchArea, 1448 List => 'Seq', 1449 }, 1450 RetouchAreasMasks => { 1451 Name => 'RetouchAreaMasks', 1452 FlatName => 'RetouchAreaMask', 1453 Flat => 1 1454 }, 1455 RetouchAreasMasksDabs => { 1456 Name => 'RetouchAreaMaskDabs', 1260 1457 Flat => 1, List => 0, 1261 1458 }, 1262 PaintBasedCorrectionsCorrectionMasksFullX => { 1263 Name => 'PaintCorrectionMaskFullX', 1264 Flat => 1, List => 0, 1265 }, 1266 PaintBasedCorrectionsCorrectionMasksFullY => { 1267 Name => 'PaintCorrectionMaskFullY', 1268 Flat => 1, List => 0, 1269 }, 1270 # new tags written by LR 3 (thanks Wolfgang Guelcker) 1271 ProcessVersion => { }, 1272 LensProfileEnable => { Writable => 'integer' }, 1273 LensProfileSetup => { }, 1274 LensProfileName => { }, 1275 LensProfileFilename => { }, 1276 LensProfileDigest => { }, 1277 LensProfileDistortionScale => { Writable => 'integer' }, 1278 LensProfileChromaticAberrationScale => { Writable => 'integer' }, 1279 LensProfileVignettingScale => { Writable => 'integer' }, 1280 LensManualDistortionAmount => { Writable => 'integer' }, 1281 PerspectiveVertical => { Writable => 'integer' }, 1282 PerspectiveHorizontal => { Writable => 'integer' }, 1283 PerspectiveRotate => { Writable => 'real' }, 1284 PerspectiveScale => { Writable => 'integer' }, 1285 CropConstrainToWarp => { Writable => 'integer' }, 1286 LuminanceNoiseReductionDetail => { Writable => 'integer' }, 1287 LuminanceNoiseReductionContrast => { Writable => 'integer' }, 1288 ColorNoiseReductionDetail => { Writable => 'integer' }, 1289 GrainAmount => { Writable => 'integer' }, 1290 GrainSize => { Writable => 'integer' }, 1291 GrainFrequency => { Writable => 'integer' }, 1292 ); 1293 1294 # Tiff schema properties (tiff) 1459 UprightVersion => { Writable => 'integer' }, 1460 UprightCenterMode => { Writable => 'integer' }, 1461 UprightCenterNormX => { Writable => 'real' }, 1462 UprightCenterNormY => { Writable => 'real' }, 1463 UprightFocalMode => { Writable => 'integer' }, 1464 UprightFocalLength35mm => { Writable => 'real' }, 1465 UprightPreview => { Writable => 'boolean' }, 1466 UprightTransformCount => { Writable => 'integer' }, 1467 UprightDependentDigest => { }, 1468 UprightTransform_0 => { }, 1469 UprightTransform_1 => { }, 1470 UprightTransform_2 => { }, 1471 UprightTransform_3 => { }, 1472 UprightTransform_4 => { }, 1473 UprightTransform_5 => { }, 1474 # more stuff seen in lens profile file (unknown source) 1475 What => { }, # (with value "LensProfileDefaultSettings") 1476 LensProfileMatchKeyExifMake => { }, 1477 LensProfileMatchKeyExifModel => { }, 1478 LensProfileMatchKeyCameraModelName => { }, 1479 LensProfileMatchKeyLensInfo => { }, 1480 LensProfileMatchKeyLensID => { }, 1481 LensProfileMatchKeyLensName => { }, 1482 LensProfileMatchKeyIsRaw => { Writable => 'boolean' }, 1483 LensProfileMatchKeySensorFormatFactor=>{ Writable => 'real' }, 1484 # more stuff (ref forum6993) 1485 DefaultAutoTone => { Writable => 'boolean' }, 1486 DefaultAutoGray => { Writable => 'boolean' }, 1487 DefaultsSpecificToSerial => { Writable => 'boolean' }, 1488 DefaultsSpecificToISO => { Writable => 'boolean' }, 1489 DNGIgnoreSidecars => { Writable => 'boolean' }, 1490 NegativeCachePath => { }, 1491 NegativeCacheMaximumSize => { Writable => 'real' }, 1492 NegativeCacheLargePreviewSize => { Writable => 'integer' }, 1493 JPEGHandling => { }, 1494 TIFFHandling => { }, 1495 Dehaze => { Writable => 'real' }, 1496 ToneMapStrength => { Writable => 'real' }, 1497 # yet more 1498 PerspectiveX => { Writable => 'real' }, 1499 PerspectiveY => { Writable => 'real' }, 1500 UprightFourSegmentsCount => { Writable => 'integer' }, 1501 AutoTone => { Writable => 'boolean' }, 1502 Texture => { Writable => 'integer' }, 1503 # more stuff (ref forum10721) 1504 OverrideLookVignette => { Writable => 'boolean' }, 1505 Look => { 1506 Struct => { 1507 STRUCT_NAME => 'Look', 1508 NAMESPACE => 'crs', 1509 Name => { }, 1510 Amount => { }, 1511 Cluster => { }, 1512 UUID => { }, 1513 SupportsMonochrome => { }, 1514 SupportsAmount => { }, 1515 SupportsOutputReferred => { }, 1516 Copyright => { }, 1517 Group => { Writable => 'lang-alt' }, 1518 Parameters => { 1519 Struct => { 1520 STRUCT_NAME => 'LookParms', 1521 NAMESPACE => 'crs', 1522 Version => { }, 1523 ProcessVersion => { }, 1524 Clarity2012 => { }, 1525 ConvertToGrayscale => { }, 1526 CameraProfile => { }, 1527 LookTable => { }, 1528 ToneCurvePV2012 => { List => 'Seq' }, 1529 }, 1530 }, 1531 } 1532 }, 1533 # more again (ref forum11258) 1534 GrainSeed => { }, 1535 ClipboardOrientation => { Writable => 'integer' }, 1536 ClipboardAspectRatio => { Writable => 'integer' }, 1537 PresetType => { }, 1538 Cluster => { }, 1539 UUID => { Avoid => 1 }, 1540 SupportsAmount => { Writable => 'boolean' }, 1541 SupportsColor => { Writable => 'boolean' }, 1542 SupportsMonochrome => { Writable => 'boolean' }, 1543 SupportsHighDynamicRange=> { Writable => 'boolean' }, 1544 SupportsNormalDynamicRange=> { Writable => 'boolean' }, 1545 SupportsSceneReferred => { Writable => 'boolean' }, 1546 SupportsOutputReferred => { Writable => 'boolean' }, 1547 CameraModelRestriction => { }, 1548 Copyright => { Avoid => 1 }, 1549 ContactInfo => { }, 1550 GrainSeed => { Writable => 'integer' }, 1551 Name => { Writable => 'lang-alt', Avoid => 1 }, 1552 ShortName => { Writable => 'lang-alt' }, 1553 SortName => { Writable => 'lang-alt' }, 1554 Group => { Writable => 'lang-alt', Avoid => 1 }, 1555 Description => { Writable => 'lang-alt', Avoid => 1 }, 1556 # new for DNG converter 13.0 1557 LookName => { NotFlat => 1 }, # (grr... conflicts with "Name" element of "Look" struct!) 1558 # new for Lightroom CC 2021 (ref forum11745) 1559 ColorGradeMidtoneHue => { Writable => 'integer' }, 1560 ColorGradeMidtoneSat => { Writable => 'integer' }, 1561 ColorGradeShadowLum => { Writable => 'integer' }, 1562 ColorGradeMidtoneLum => { Writable => 'integer' }, 1563 ColorGradeHighlightLum => { Writable => 'integer' }, 1564 ColorGradeBlending => { Writable => 'integer' }, 1565 ColorGradeGlobalHue => { Writable => 'integer' }, 1566 ColorGradeGlobalSat => { Writable => 'integer' }, 1567 ColorGradeGlobalLum => { Writable => 'integer' }, 1568 # new for Adobe Camera Raw 13 (ref forum11745) 1569 LensProfileIsEmbedded => { Writable => 'boolean'}, 1570 AutoToneDigest => { }, 1571 AutoToneDigestNoSat => { }, 1572 ToggleStyleDigest => { }, 1573 ToggleStyleAmount => { Writable => 'integer' }, 1574 ); 1575 1576 # Tiff namespace properties (tiff) 1295 1577 %Image::ExifTool::XMP::tiff = ( 1296 1578 %xmpTableDefaults, … … 1299 1581 PRIORITY => 0, # not as reliable as actual TIFF tags 1300 1582 TABLE_DESC => 'XMP TIFF', 1301 NOTES => 'EXIF schema for TIFF tags.', 1583 NOTES => q{ 1584 EXIF namespace for TIFF tags. See 1585 L<https://web.archive.org/web/20180921145139if_/http://www.cipa.jp:80/std/documents/e/DC-010-2017_E.pdf> 1586 for the specification. 1587 }, 1302 1588 ImageWidth => { Writable => 'integer' }, 1303 1589 ImageLength => { Writable => 'integer', Name => 'ImageHeight' }, … … 1324 1610 }, 1325 1611 }, 1326 YCbCrSubSampling => { PrintConv => \%Image::ExifTool::JPEG::yCbCrSubSampling }, 1612 YCbCrSubSampling => { 1613 Writable => 'integer', 1614 List => 'Seq', 1615 # join the raw values before conversion to allow PrintConv to operate on 1616 # the combined string as it does for the corresponding EXIF tag 1617 RawJoin => 1, 1618 Notes => q{ 1619 while technically this is a list-type tag, for compatibility with its EXIF 1620 counterpart it is written and read as a simple string 1621 }, 1622 PrintConv => \%Image::ExifTool::JPEG::yCbCrSubSampling, 1623 }, 1327 1624 YCbCrPositioning => { 1328 1625 Writable => 'integer', … … 1343 1640 }, 1344 1641 }, 1345 TransferFunction => { Writable => 'integer', List => 'Seq' },1642 TransferFunction => { Writable => 'integer', List => 'Seq', AutoSplit => 1 }, 1346 1643 WhitePoint => { Writable => 'rational', List => 'Seq', AutoSplit => 1 }, 1347 1644 PrimaryChromaticities => { Writable => 'rational', List => 'Seq', AutoSplit => 1 }, … … 1354 1651 }, 1355 1652 ImageDescription => { Writable => 'lang-alt' }, 1356 Make => { Groups => { 2 => 'Camera' } }, 1357 Model => { Groups => { 2 => 'Camera' }, Description => 'Camera Model Name' }, 1653 Make => { 1654 Groups => { 2 => 'Camera' }, 1655 RawConv => '$$self{Make} ? $val : $$self{Make} = $val', 1656 }, 1657 Model => { 1658 Groups => { 2 => 'Camera' }, 1659 Description => 'Camera Model Name', 1660 RawConv => '$$self{Model} ? $val : $$self{Model} = $val', 1661 }, 1358 1662 Software => { }, 1359 1663 Artist => { Groups => { 2 => 'Author' } }, … … 1362 1666 ); 1363 1667 1364 # Exif schemaproperties (exif)1668 # Exif namespace properties (exif) 1365 1669 %Image::ExifTool::XMP::exif = ( 1366 1670 %xmpTableDefaults, … … 1368 1672 NAMESPACE => 'exif', 1369 1673 PRIORITY => 0, # not as reliable as actual EXIF tags 1370 NOTES => 'EXIF schema for EXIF tags.', 1674 NOTES => q{ 1675 EXIF namespace for EXIF tags. See 1676 L<https://web.archive.org/web/20180921145139if_/http://www.cipa.jp:80/std/documents/e/DC-010-2017_E.pdf> 1677 for the specification. 1678 }, 1371 1679 ExifVersion => { }, 1372 1680 FlashpixVersion => { }, … … 1383 1691 }, 1384 1692 ComponentsConfiguration => { 1693 Writable => 'integer', 1385 1694 List => 'Seq', 1386 Writable => 'integer',1387 1695 AutoSplit => 1, 1388 1696 PrintConvColumns => 2, … … 1416 1724 Writable => 'rational', 1417 1725 PrintConv => 'Image::ExifTool::Exif::PrintExposureTime($val)', 1418 PrintConvInv => ' Image::ExifTool::Exif::ConvertFraction($val)',1726 PrintConvInv => '$val', 1419 1727 }, 1420 1728 FNumber => { 1421 1729 Writable => 'rational', 1422 PrintConv => ' sprintf("%.1f",$val)',1730 PrintConv => 'Image::ExifTool::Exif::PrintFNumber($val)', 1423 1731 PrintConvInv => '$val', 1424 1732 }, … … 1447 1755 OECF => { 1448 1756 Name => 'Opto-ElectricConvFactor', 1757 FlatName => 'OECF', 1449 1758 Groups => { 2 => 'Camera' }, 1450 1759 Struct => \%sOECF, 1451 1760 }, 1452 OECFColumns => { Flat => 1 },1453 OECFRows => { Flat => 1 },1454 OECFNames => { Flat => 1 },1455 OECFValues => { Flat => 1 },1456 1761 ShutterSpeedValue => { 1457 1762 Writable => 'rational', … … 1512 1817 STRUCT_NAME => 'Flash', 1513 1818 NAMESPACE => 'exif', 1514 Fired => { Writable => 'boolean' },1819 Fired => { Writable => 'boolean', %boolConv }, 1515 1820 Return => { 1516 1821 Writable => 'integer', … … 1530 1835 }, 1531 1836 }, 1532 Function => { Writable => 'boolean' },1533 RedEyeMode => { Writable => 'boolean' },1837 Function => { Writable => 'boolean', %boolConv }, 1838 RedEyeMode => { Writable => 'boolean', %boolConv }, 1534 1839 }, 1535 1840 }, … … 1713 2018 Groups => { 2 => 'Location' }, 1714 2019 Writable => 'rational', 1715 RawConv => 'require Image::ExifTool::GPS; $val', # to load Composite tags and routines1716 2020 # extricate unsigned decimal number from string 1717 2021 ValueConvInv => '$val=~/((?=\d|\.\d)\d*(?:\.\d*)?)/ ? $1 : undef', … … 1766 2070 GPSTrack => { Groups => { 2 => 'Location' }, Writable => 'rational' }, 1767 2071 GPSImgDirectionRef => { 2072 Groups => { 2 => 'Location' }, 1768 2073 PrintConv => { 1769 2074 M => 'Magnetic North', … … 1805 2110 }, 1806 2111 }, 2112 GPSHPositioningError => { #12 2113 Description => 'GPS Horizontal Positioning Error', 2114 Groups => { 2 => 'Location' }, 2115 Writable => 'rational', 2116 PrintConv => '"$val m"', 2117 PrintConvInv => '$val=~s/\s*m$//; $val', 2118 }, 1807 2119 NativeDigest => { }, #PH 1808 ); 1809 1810 # Auxiliary schema properties (aux) - not fully documented (ref PH) 1811 %Image::ExifTool::XMP::aux = ( 2120 # new Exif 2121 ); 2122 2123 # Exif extended properties (exifEX, ref 12) 2124 %Image::ExifTool::XMP::exifEX = ( 1812 2125 %xmpTableDefaults, 1813 GROUPS => { 1 => 'XMP-aux', 2 => 'Camera' }, 1814 NAMESPACE => 'aux', 1815 NOTES => 'Photoshop Auxiliary schema tags.', 1816 Firmware => { }, #7 1817 FlashCompensation => { Writable => 'rational' }, #7 1818 ImageNumber => { }, #7 1819 LensInfo => { #7 1820 Notes => '4 rational values giving focal and aperture ranges', 1821 # convert to floating point values (or 'inf' or 'undef') 1822 ValueConv => sub { 1823 my $val = shift; 1824 my @vals = split ' ', $val; 1825 return $val unless @vals == 4; 1826 foreach (@vals) { 1827 ConvertRational($_) or return $val; 1828 } 1829 return join ' ', @vals; 1830 }, 2126 GROUPS => { 1 => 'XMP-exifEX', 2 => 'Image' }, 2127 NAMESPACE => 'exifEX', 2128 PRIORITY => 0, # not as reliable as actual EXIF tags 2129 NOTES => q{ 2130 EXIF tags added by the EXIF 2.31 for XMP specification (see 2131 L<http://www.cipa.jp/std/documents/e/DC-X010-2017.pdf>). 2132 }, 2133 Gamma => { Writable => 'rational' }, 2134 PhotographicSensitivity => { Writable => 'integer' }, 2135 SensitivityType => { 2136 Writable => 'integer', 2137 PrintConv => { 2138 0 => 'Unknown', 2139 1 => 'Standard Output Sensitivity', 2140 2 => 'Recommended Exposure Index', 2141 3 => 'ISO Speed', 2142 4 => 'Standard Output Sensitivity and Recommended Exposure Index', 2143 5 => 'Standard Output Sensitivity and ISO Speed', 2144 6 => 'Recommended Exposure Index and ISO Speed', 2145 7 => 'Standard Output Sensitivity, Recommended Exposure Index and ISO Speed', 2146 }, 2147 }, 2148 StandardOutputSensitivity => { Writable => 'integer' }, 2149 RecommendedExposureIndex => { Writable => 'integer' }, 2150 ISOSpeed => { Writable => 'integer' }, 2151 ISOSpeedLatitudeyyy => { 2152 Description => 'ISO Speed Latitude yyy', 2153 Writable => 'integer', 2154 }, 2155 ISOSpeedLatitudezzz => { 2156 Description => 'ISO Speed Latitude zzz', 2157 Writable => 'integer', 2158 }, 2159 CameraOwnerName => { Name => 'OwnerName' }, 2160 BodySerialNumber => { Name => 'SerialNumber', Groups => { 2 => 'Camera' } }, 2161 LensSpecification => { 2162 Name => 'LensInfo', 2163 Writable => 'rational', 2164 Groups => { 2 => 'Camera' }, 2165 List => 'Seq', 2166 RawJoin => 1, # join list into a string before ValueConv 2167 ValueConv => \&ConvertRationalList, 1831 2168 ValueConvInv => sub { 1832 2169 my $val = shift; … … 1840 2177 $_ = join '/', @a; 1841 2178 } 1842 return join ' ', @vals; 2179 return \@vals; # return list reference (List-type tag) 2180 }, 2181 PrintConv => \&Image::ExifTool::Exif::PrintLensInfo, 2182 PrintConvInv => \&Image::ExifTool::Exif::ConvertLensInfo, 2183 Notes => q{ 2184 unfortunately the EXIF 2.3 for XMP specification defined this new tag 2185 instead of using the existing XMP-aux:LensInfo 2186 }, 2187 }, 2188 LensMake => { Groups => { 2 => 'Camera' } }, 2189 LensModel => { Groups => { 2 => 'Camera' } }, 2190 LensSerialNumber => { Groups => { 2 => 'Camera' } }, 2191 InteroperabilityIndex => { 2192 Name => 'InteropIndex', 2193 Description => 'Interoperability Index', 2194 PrintConv => { 2195 R98 => 'R98 - DCF basic file (sRGB)', 2196 R03 => 'R03 - DCF option file (Adobe RGB)', 2197 THM => 'THM - DCF thumbnail file', 2198 }, 2199 }, 2200 # new in Exif 2.31 2201 Temperature => { Writable => 'rational', Name => 'AmbientTemperature' }, 2202 Humidity => { Writable => 'rational' }, 2203 Pressure => { Writable => 'rational' }, 2204 WaterDepth => { Writable => 'rational' }, 2205 Acceleration => { Writable => 'rational' }, 2206 CameraElevationAngle=> { Writable => 'rational' }, 2207 # new in Exif 2.32 (according to the spec, these should use a different namespace 2208 # URI, but the same namespace prefix... Exactly how is that supposed to work?!! 2209 # -- I'll just stick with the same URI) 2210 CompositeImage => { Writable => 'integer', 2211 PrintConv => { 2212 0 => 'Unknown', 2213 1 => 'Not a Composite Image', 2214 2 => 'General Composite Image', 2215 3 => 'Composite Image Captured While Shooting', 2216 }, 2217 }, 2218 CompositeImageCount => { List => 'Seq', Writable => 'integer' }, 2219 CompositeImageExposureTimes => { 2220 FlatName => 'CompImage', 2221 Struct => { 2222 STRUCT_NAME => 'CompImageExp', 2223 NAMESPACE => 'exifEX', 2224 TotalExposurePeriod => { Writable => 'rational' }, 2225 SumOfExposureTimesOfAll => { Writable => 'rational', FlatName => 'SumExposureAll' }, 2226 SumOfExposureTimesOfUsed=> { Writable => 'rational', FlatName => 'SumExposureUsed' }, 2227 MaxExposureTimesOfAll => { Writable => 'rational', FlatName => 'MaxExposureAll' }, 2228 MaxExposureTimesOfUsed => { Writable => 'rational', FlatName => 'MaxExposureUsed' }, 2229 MinExposureTimesOfAll => { Writable => 'rational', FlatName => 'MinExposureAll' }, 2230 MinExposureTimesOfUsed => { Writable => 'rational', FlatName => 'MinExposureUsed' }, 2231 NumberOfSequences => { Writable => 'integer', FlatName => 'NumSequences' }, 2232 NumberOfImagesInSequences=>{ Writable => 'integer', FlatName => 'ImagesPerSequence' }, 2233 Values => { List => 'Seq', Writable => 'rational' }, 2234 }, 2235 }, 2236 ); 2237 2238 # Auxiliary namespace properties (aux) - not fully documented (ref PH) 2239 %Image::ExifTool::XMP::aux = ( 2240 %xmpTableDefaults, 2241 GROUPS => { 1 => 'XMP-aux', 2 => 'Camera' }, 2242 NAMESPACE => 'aux', 2243 NOTES => q{ 2244 Adobe-defined auxiliary EXIF tags. This namespace existed in the XMP 2245 specification until it was dropped in 2012, presumably due to the 2246 introduction of the EXIF 2.3 for XMP specification and the exifEX namespace 2247 at this time. For this reason, tags below with equivalents in the 2248 L<exifEX namespace|/XMP exifEX Tags> are avoided when writing. 2249 }, 2250 Firmware => { }, #7 2251 FlashCompensation => { Writable => 'rational' }, #7 2252 ImageNumber => { }, #7 2253 LensInfo => { #7 2254 Notes => '4 rational values giving focal and aperture ranges', 2255 Avoid => 1, 2256 # convert to floating point values (or 'inf' or 'undef') 2257 ValueConv => \&ConvertRationalList, 2258 ValueConvInv => sub { 2259 my $val = shift; 2260 my @vals = split ' ', $val; 2261 return $val unless @vals == 4; 2262 foreach (@vals) { 2263 $_ eq 'inf' and $_ = '1/0', next; 2264 $_ eq 'undef' and $_ = '0/0', next; 2265 Image::ExifTool::IsFloat($_) or return $val; 2266 my @a = Image::ExifTool::Rationalize($_); 2267 $_ = join '/', @a; 2268 } 2269 return join ' ', @vals; # return string (string tag) 1843 2270 }, 1844 2271 # convert to the form "12-20mm f/3.8-4.5" or "50mm f/1.4" … … 1847 2274 }, 1848 2275 Lens => { }, 1849 OwnerName => { }, #7 1850 SerialNumber => { }, 2276 OwnerName => { Avoid => 1 }, #7 2277 SerialNumber => { Avoid => 1 }, 2278 LensSerialNumber=> { Avoid => 1 }, 1851 2279 LensID => { 1852 2280 Priority => 0, … … 1858 2286 }, 1859 2287 ApproximateFocusDistance => { Writable => 'rational' }, #PH (LR3) 1860 ); 1861 1862 # IPTC Core schema properties (Iptc4xmpCore) (ref 4) 2288 # the following new in LR6 (ref forum6497) 2289 IsMergedPanorama => { Writable => 'boolean' }, 2290 IsMergedHDR => { Writable => 'boolean' }, 2291 DistortionCorrectionAlreadyApplied => { Writable => 'boolean' }, 2292 VignetteCorrectionAlreadyApplied => { Writable => 'boolean' }, 2293 LateralChromaticAberrationCorrectionAlreadyApplied => { Writable => 'boolean' }, 2294 LensDistortInfo => { }, # (LR 7.5.1, 4 signed rational values) 2295 ); 2296 2297 # IPTC Core namespace properties (Iptc4xmpCore) (ref 4) 1863 2298 %Image::ExifTool::XMP::iptcCore = ( 1864 2299 %xmpTableDefaults, … … 1867 2302 TABLE_DESC => 'XMP IPTC Core', 1868 2303 NOTES => q{ 1869 IPTC Core schematags. The actual IPTC Core namespace prefix is2304 IPTC Core namespace tags. The actual IPTC Core namespace prefix is 1870 2305 "Iptc4xmpCore", which is the prefix recorded in the file, but ExifTool 1871 shortens this for the "XMP-iptcCore"family 1 group name. (see2306 shortens this for the family 1 group name. (see 1872 2307 L<http://www.iptc.org/IPTC4XMP/>) 1873 2308 }, … … 1899 2334 Scene => { Groups => { 2 => 'Other' }, List => 'Bag' }, 1900 2335 SubjectCode => { Groups => { 2 => 'Other' }, List => 'Bag' }, 1901 ); 1902 1903 # IPTC Extension schema properties (Iptc4xmpExt) (ref 4) 1904 %Image::ExifTool::XMP::iptcExt = ( 1905 %xmpTableDefaults, 1906 GROUPS => { 1 => 'XMP-iptcExt', 2 => 'Author' }, 1907 NAMESPACE => 'Iptc4xmpExt', 1908 TABLE_DESC => 'XMP IPTC Extension', 1909 NOTES => q{ 1910 IPTC Extension schema tags. The actual namespace prefix is "Iptc4xmpExt", 1911 but ExifTool shortens this for the "XMP-iptcExt" family 1 group name. 1912 (see L<http://www.iptc.org/IPTC4XMP/>) 1913 }, 1914 AddlModelInfo => { Name => 'AdditionalModelInformation' }, 1915 ArtworkOrObject => { 1916 Struct => { 1917 STRUCT_NAME => 'ArtworkOrObjectDetails', 1918 NAMESPACE => 'Iptc4xmpExt', 1919 AOCopyrightNotice => { }, 1920 AOCreator => { List => 'Seq' }, 1921 AODateCreated=> { Groups => { 2 => 'Time' }, %dateTimeInfo }, 1922 AOSource => { }, 1923 AOSourceInvNo=> { }, 1924 AOTitle => { Writable => 'lang-alt' }, 1925 }, 1926 List => 'Bag', 1927 }, 1928 ArtworkOrObjectAOCopyrightNotice=> { Flat => 1, Name => 'ArtworkCopyrightNotice' }, 1929 ArtworkOrObjectAOCreator => { Flat => 1, Name => 'ArtworkCreator' }, 1930 ArtworkOrObjectAODateCreated => { Flat => 1, Name => 'ArtworkDateCreated' }, 1931 ArtworkOrObjectAOSource => { Flat => 1, Name => 'ArtworkSource' }, 1932 ArtworkOrObjectAOSourceInvNo => { Flat => 1, Name => 'ArtworkSourceInventoryNo' }, 1933 ArtworkOrObjectAOTitle => { Flat => 1, Name => 'ArtworkTitle' }, 1934 OrganisationInImageCode => { List => 'Bag' }, 1935 CVterm => { 1936 Name => 'ControlledVocabularyTerm', 1937 List => 'Bag', 1938 }, 1939 LocationShown => { 1940 Struct => \%sLocationDetails, 1941 Groups => { 2 => 'Location' }, 1942 List => 'Bag', 1943 }, 1944 ModelAge => { List => 'Bag', Writable => 'integer' }, 1945 OrganisationInImageName => { List => 'Bag' }, 1946 PersonInImage => { List => 'Bag' }, 1947 DigImageGUID => { Name => 'DigitalImageGUID' }, 1948 DigitalSourcefileType => { 1949 Name => 'DigitalSourceFileType', 1950 Notes => 'now deprecated -- replaced by DigitalSourceType', 1951 }, 1952 DigitalSourceType => { Name => 'DigitalSourceType' }, 1953 Event => { Writable => 'lang-alt' }, 1954 RegistryId => { 1955 Struct => { 1956 STRUCT_NAME => 'RegistryEntryDetails', 1957 NAMESPACE => 'Iptc4xmpExt', 1958 RegItemId => { }, 1959 RegOrgId => { }, 1960 }, 1961 List => 'Bag', 1962 }, 1963 RegistryIdRegItemId => { Flat => 1, Name => 'RegistryItemID' }, 1964 RegistryIdRegOrgId => { Flat => 1, Name => 'RegistryOrganisationID' }, 1965 IptcLastEdited => { Groups => { 2 => 'Time' }, %dateTimeInfo }, 1966 LocationCreated => { 1967 Struct => \%sLocationDetails, 1968 Groups => { 2 => 'Location' }, 1969 List => 'Bag', 1970 }, 1971 MaxAvailHeight => { Writable => 'integer' }, 1972 MaxAvailWidth => { Writable => 'integer' }, 1973 ); 1974 1975 # Adobe Lightroom schema properties (lr) (ref PH) 2336 # Copyright - have seen this in a sample (Jan 2021), but I think it is non-standard 2337 ); 2338 2339 # Adobe Lightroom namespace properties (lr) (ref PH) 1976 2340 %Image::ExifTool::XMP::Lightroom = ( 1977 2341 %xmpTableDefaults, … … 1979 2343 NAMESPACE => 'lr', 1980 2344 TABLE_DESC => 'XMP Adobe Lightroom', 1981 NOTES => 'Adobe Lightroom "lr" schematags.',2345 NOTES => 'Adobe Lightroom "lr" namespace tags.', 1982 2346 privateRTKInfo => { }, 1983 2347 hierarchicalSubject => { List => 'Bag' }, 1984 2348 ); 1985 2349 1986 # Adobe Album schemaproperties (album) (ref PH)2350 # Adobe Album namespace properties (album) (ref PH) 1987 2351 %Image::ExifTool::XMP::Album = ( 1988 2352 %xmpTableDefaults, … … 1990 2354 NAMESPACE => 'album', 1991 2355 TABLE_DESC => 'XMP Adobe Album', 1992 NOTES => 'Adobe Album schematags.',2356 NOTES => 'Adobe Album namespace tags.', 1993 2357 Notes => { }, 1994 2358 ); … … 2002 2366 # Composite XMP tags 2003 2367 %Image::ExifTool::XMP::Composite = ( 2004 # get latitude/lo gitude reference from XMP lat/long tags2368 # get latitude/longitude reference from XMP lat/long tags 2005 2369 # (used to set EXIF GPS position from XMP tags) 2006 2370 GPSLatitudeRef => { 2007 Require => 'XMP:GPSLatitude', 2371 Require => 'XMP-exif:GPSLatitude', 2372 Groups => { 2 => 'Location' }, 2373 # Note: Do not Inihibit based on EXIF:GPSLatitudeRef (see forum10192) 2008 2374 ValueConv => q{ 2009 2375 IsFloat($val[0]) and return $val[0] < 0 ? "S" : "N"; 2010 $val[0] =~ / .*([NS])/;2376 $val[0] =~ /^.*([NS])/; 2011 2377 return $1; 2012 2378 }, 2013 PrintConv => { 2014 N => 'North', 2015 S => 'South', 2016 }, 2379 PrintConv => { N => 'North', S => 'South' }, 2017 2380 }, 2018 2381 GPSLongitudeRef => { 2019 Require => 'XMP:GPSLongitude', 2382 Require => 'XMP-exif:GPSLongitude', 2383 Groups => { 2 => 'Location' }, 2020 2384 ValueConv => q{ 2021 2385 IsFloat($val[0]) and return $val[0] < 0 ? "W" : "E"; 2022 $val[0] =~ / .*([EW])/;2386 $val[0] =~ /^.*([EW])/; 2023 2387 return $1; 2024 2388 }, 2025 PrintConv => { 2026 E => 'East', 2027 W => 'West', 2389 PrintConv => { E => 'East', W => 'West' }, 2390 }, 2391 GPSDestLatitudeRef => { 2392 Require => 'XMP-exif:GPSDestLatitude', 2393 Groups => { 2 => 'Location' }, 2394 ValueConv => q{ 2395 IsFloat($val[0]) and return $val[0] < 0 ? "S" : "N"; 2396 $val[0] =~ /^.*([NS])/; 2397 return $1; 2398 }, 2399 PrintConv => { N => 'North', S => 'South' }, 2400 }, 2401 GPSDestLongitudeRef => { 2402 Require => 'XMP-exif:GPSDestLongitude', 2403 Groups => { 2 => 'Location' }, 2404 ValueConv => q{ 2405 IsFloat($val[0]) and return $val[0] < 0 ? "W" : "E"; 2406 $val[0] =~ /^.*([EW])/; 2407 return $1; 2408 }, 2409 PrintConv => { E => 'East', W => 'West' }, 2410 }, 2411 LensID => { 2412 Notes => 'attempt to convert numerical XMP-aux:LensID stored by Adobe applications', 2413 Require => { 2414 0 => 'XMP-aux:LensID', 2415 1 => 'Make', 2416 }, 2417 Desire => { 2418 2 => 'LensInfo', 2419 3 => 'FocalLength', 2420 4 => 'LensModel', 2421 5 => 'MaxApertureValue', 2422 }, 2423 Inhibit => { 2424 6 => 'Composite:LensID', # don't override existing Composite:LensID 2425 }, 2426 Groups => { 2 => 'Camera' }, 2427 ValueConv => '$val', 2428 PrintConv => 'Image::ExifTool::XMP::PrintLensID($self, @val)', 2429 }, 2430 Flash => { 2431 Notes => 'facilitates copying camera flash information between XMP and EXIF', 2432 Desire => { 2433 0 => 'XMP:FlashFired', 2434 1 => 'XMP:FlashReturn', 2435 2 => 'XMP:FlashMode', 2436 3 => 'XMP:FlashFunction', 2437 4 => 'XMP:FlashRedEyeMode', 2438 5 => 'XMP:Flash', # handle structured flash information too 2439 }, 2440 Groups => { 2 => 'Camera' }, 2441 Writable => 1, 2442 PrintHex => 1, 2443 SeparateTable => 'EXIF Flash', 2444 ValueConv => q{ 2445 if (ref $val[5] eq 'HASH') { 2446 # copy structure fields into value array 2447 my $i = 0; 2448 $val[$i++] = $val[5]{$_} foreach qw(Fired Return Mode Function RedEyeMode); 2449 } 2450 return((($val[0] and lc($val[0]) eq 'true') ? 0x01 : 0) | 2451 (($val[1] || 0) << 1) | 2452 (($val[2] || 0) << 3) | 2453 (($val[3] and lc($val[3]) eq 'true') ? 0x20 : 0) | 2454 (($val[4] and lc($val[4]) eq 'true') ? 0x40 : 0)); 2455 }, 2456 PrintConv => \%Image::ExifTool::Exif::flash, 2457 WriteAlso => { 2458 'XMP:FlashFired' => '$val & 0x01 ? "True" : "False"', 2459 'XMP:FlashReturn' => '($val & 0x06) >> 1', 2460 'XMP:FlashMode' => '($val & 0x18) >> 3', 2461 'XMP:FlashFunction' => '$val & 0x20 ? "True" : "False"', 2462 'XMP:FlashRedEyeMode' => '$val & 0x40 ? "True" : "False"', 2028 2463 }, 2029 2464 }, … … 2057 2492 # Inputs: 0) string to be unescaped 2058 2493 # 1) optional hash reference to convert entity names to numbers 2494 # 2) optional character encoding 2059 2495 # Returns: unescaped string 2060 2496 my %charNum = ('quot'=>34, 'amp'=>38, 'apos'=>39, 'lt'=>60, 'gt'=>62); 2061 sub UnescapeXML($;$ )2497 sub UnescapeXML($;$$) 2062 2498 { 2063 my ($str, $conv ) = @_;2499 my ($str, $conv, $enc) = @_; 2064 2500 $conv = \%charNum unless $conv; 2065 $str =~ s/&(#?\w+);/UnescapeChar($1,$conv )/sge;2501 $str =~ s/&(#?\w+);/UnescapeChar($1,$conv,$enc)/sge; 2066 2502 return $str; 2067 2503 } … … 2099 2535 #------------------------------------------------------------------------------ 2100 2536 # Convert XML character reference to UTF-8 2101 # Inputs: 0) XML character reference stripped of the '&' and ';' ( ie. 'quot', '#34', '#x22')2537 # Inputs: 0) XML character reference stripped of the '&' and ';' (eg. 'quot', '#34', '#x22') 2102 2538 # 1) hash reference for looking up character numbers by name 2539 # 2) optional character encoding (default 'UTF8') 2103 2540 # Returns: UTF-8 equivalent (or original character on conversion error) 2104 sub UnescapeChar($$ )2541 sub UnescapeChar($$;$) 2105 2542 { 2106 my ($ch, $conv ) = @_;2543 my ($ch, $conv, $enc) = @_; 2107 2544 my $val = $$conv{$ch}; 2108 2545 unless (defined $val) { … … 2116 2553 } 2117 2554 return chr($val) if $val < 0x80; # simple ASCII 2118 return pack('C0U', $val) if $] >= 5.006001; 2119 return Image::ExifTool::PackUTF8($val); 2555 $val = $] >= 5.006001 ? pack('C0U', $val) : Image::ExifTool::PackUTF8($val); 2556 $val = Image::ExifTool::Decode(undef, $val, 'UTF8', undef, $enc) if $enc and $enc ne 'UTF8'; 2557 return $val; 2120 2558 } 2121 2559 2122 2560 #------------------------------------------------------------------------------ 2123 2561 # Does a string contain valid UTF-8 characters? 2124 # Inputs: 0) string reference 2562 # Inputs: 0) string reference, 1) true to allow last character to be truncated 2125 2563 # Returns: 0=regular ASCII, -1=invalid UTF-8, 1=valid UTF-8 with maximum 16-bit 2126 2564 # wide characters, 2=valid UTF-8 requiring 32-bit wide characters 2127 2565 # Notes: Changes current string position 2128 sub IsUTF8($) 2566 # (see http://www.fileformat.info/info/unicode/utf8.htm for help understanding this) 2567 sub IsUTF8($;$) 2129 2568 { 2130 my $strPt = shift;2569 my ($strPt, $trunc) = @_; 2131 2570 pos($$strPt) = 0; # start at beginning of string 2132 2571 return 0 unless $$strPt =~ /([\x80-\xff])/g; … … 2150 2589 $rtnVal = 2; 2151 2590 } 2152 return -1 unless $$strPt =~ /\G[\x80-\xbf]{$n}/g; 2591 my $pos = pos $$strPt; 2592 unless ($$strPt =~ /\G([\x80-\xbf]{$n})/g) { 2593 return $rtnVal if $trunc and $pos + $n > length $$strPt; 2594 return -1; 2595 } 2596 # the following is ref https://www.cl.cam.ac.uk/%7Emgk25/ucs/utf8_check.c 2597 if ($n == 2) { 2598 return -1 if ($ch == 0xe0 and (ord($1) & 0xe0) == 0x80) or 2599 ($ch == 0xed and (ord($1) & 0xe0) == 0xa0) or 2600 ($ch == 0xef and ord($1) == 0xbf and 2601 (ord(substr $1, 1) & 0xfe) == 0xbe); 2602 } else { 2603 return -1 if ($ch == 0xf0 and (ord($1) & 0xf0) == 0x80) or 2604 ($ch == 0xf4 and ord($1) > 0x8f) or $ch > 0xf4; 2605 } 2153 2606 last unless $$strPt =~ /([\x80-\xff])/g; 2154 2607 } … … 2157 2610 2158 2611 #------------------------------------------------------------------------------ 2159 # Fix malformed UTF8 (by replacing bad bytes with '?') 2160 # Inputs: 0) string reference 2612 # Fix malformed UTF8 (by replacing bad bytes with specified character) 2613 # Inputs: 0) string reference, 1) string to replace each bad byte, 2614 # may be '' to delete bad bytes, or undef to use '?' 2161 2615 # Returns: true if string was fixed, and updates string 2162 sub FixUTF8($ )2616 sub FixUTF8($;$) 2163 2617 { 2164 my $strPt = shift;2618 my ($strPt, $bad) = @_; 2165 2619 my $fixed; 2166 2620 pos($$strPt) = 0; # start at beginning of string … … 2172 2626 if ($ch >= 0xc2 and $ch < 0xf8) { 2173 2627 my $n = $ch < 0xe0 ? 1 : ($ch < 0xf0 ? 2 : 3); 2174 next if $$strPt =~ /\G[\x80-\xbf]{$n}/g; 2175 } 2176 # replace bad character with '?' 2177 substr($$strPt, $pos-1, 1) = '?'; 2178 pos($$strPt) = $fixed = $pos; 2628 if ($$strPt =~ /\G([\x80-\xbf]{$n})/g) { 2629 next if $n == 1; 2630 if ($n == 2) { 2631 next unless ($ch == 0xe0 and (ord($1) & 0xe0) == 0x80) or 2632 ($ch == 0xed and (ord($1) & 0xe0) == 0xa0) or 2633 ($ch == 0xef and ord($1) == 0xbf and 2634 (ord(substr $1, 1) & 0xfe) == 0xbe); 2635 } else { 2636 next unless ($ch == 0xf0 and (ord($1) & 0xf0) == 0x80) or 2637 ($ch == 0xf4 and ord($1) > 0x8f) or $ch > 0xf4; 2638 } 2639 } 2640 } 2641 # replace bad character 2642 $bad = '?' unless defined $bad; 2643 substr($$strPt, $pos-1, 1) = $bad; 2644 pos($$strPt) = $pos-1 + length $bad; 2645 $fixed = 1; 2179 2646 } 2180 2647 return $fixed; … … 2218 2685 2219 2686 #------------------------------------------------------------------------------ 2220 # Generate a namefor this XMP tag2687 # Generate a tag ID for this XMP tag 2221 2688 # Inputs: 0) tag property name list ref, 1) array ref for receiving structure property list 2222 2689 # 2) array for receiving namespace list … … 2230 2697 # (Note: namespace can be '' for property qualifiers) 2231 2698 my ($ns, $nm) = ($prop =~ /(.*?):(.*)/) ? ($1, $2) : ('', $prop); 2232 if ($ignoreNamespace{$ns} ) {2699 if ($ignoreNamespace{$ns} or $ignoreProp{$prop}) { 2233 2700 # special case: don't ignore rdf numbered items 2701 # (not technically allowed in XMP, but used in RDF/XML) 2234 2702 unless ($prop =~ /^rdf:(_\d+)$/) { 2235 2703 # save list index if necessary for structures … … 2244 2712 # all uppercase is ugly, so convert it 2245 2713 if ($nm !~ /[a-z]/) { 2246 my $xlat NS = $$xlatNamespace{$ns} || $ns;2247 my $info = $Image::ExifTool::XMP::Main{$xlat NS};2714 my $xlat = $stdXlatNS{$ns} || $ns; 2715 my $info = $Image::ExifTool::XMP::Main{$xlat}; 2248 2716 my $table; 2249 if (ref $info eq 'HASH' and $ info->{SubDirectory}) {2250 $table = GetTagTable($ info->{SubDirectory}{TagTable});2717 if (ref $info eq 'HASH' and $$info{SubDirectory}) { 2718 $table = GetTagTable($$info{SubDirectory}{TagTable}); 2251 2719 } 2252 unless ($table and $ table->{$nm}) {2720 unless ($table and $$table{$nm}) { 2253 2721 $nm = lc($nm); 2254 2722 $nm =~ s/_([a-z])/\u$1/g; … … 2278 2746 #------------------------------------------------------------------------------ 2279 2747 # Register namespace for specified user-defined table 2280 # Inputs: 0) tag orstructure table ref2748 # Inputs: 0) tag/structure table ref 2281 2749 # Returns: namespace prefix 2282 2750 sub RegisterNamespace($) … … 2290 2758 $ns = $$nsRef[0]; 2291 2759 $nsURI{$ns} = $$nsRef[1]; 2760 $uri2ns{$$nsRef[1]} = $ns; 2292 2761 } else { # must be a hash 2293 2762 my @ns = sort keys %$nsRef; # allow multiple namespace definitions … … 2295 2764 $ns = pop @ns; 2296 2765 if ($nsURI{$ns} and $nsURI{$ns} ne $$nsRef{$ns}) { 2297 warn "User-defined namespace prefix '$ ns' conflicts with existing namespace\n";2766 warn "User-defined namespace prefix '${ns}' conflicts with existing namespace\n"; 2298 2767 } 2299 2768 $nsURI{$ns} = $$nsRef{$ns}; 2769 $uri2ns{$$nsRef{$ns}} = $ns; 2300 2770 } 2301 2771 } … … 2305 2775 #------------------------------------------------------------------------------ 2306 2776 # Generate flattened tags and add to table 2307 # Inputs: 0) tag table ref, 1) tag ID for Struct tag in table 2777 # Inputs: 0) tag table ref, 1) tag ID for Struct tag (if not defined, whole table is done), 2778 # 2) flag to not expand sub-structures 2308 2779 # Returns: number of tags added (not counting those just initialized) 2309 2780 # Notes: Must have verified that $$tagTablePtr{$tagID}{Struct} exists before calling this routine 2310 2781 # - makes sure that the tagInfo Struct is a HASH reference 2311 sub AddFlattenedTags($ $)2782 sub AddFlattenedTags($;$$) 2312 2783 { 2313 2784 local $_; 2314 my ($tagTablePtr, $tagID) = @_; 2315 my $tagInfo = $$tagTablePtr{$tagID}; 2316 2317 $$tagInfo{Flattened} and return 0; # only generate flattened tags once 2318 $$tagInfo{Flattened} = 1; 2319 2320 my $strTable = $$tagInfo{Struct}; 2321 unless (ref $strTable) { # (allow a structure name for backward compatibility only) 2322 my $strName = $strTable; 2323 $strTable = $Image::ExifTool::UserDefined::xmpStruct{$strTable} or return 0; 2324 $$strTable{STRUCT_NAME} or $$strTable{STRUCT_NAME} = $strName; 2325 $$tagInfo{Struct} = $strTable; # replace old-style name with HASH ref 2326 delete $$tagInfo{SubDirectory}; # deprecated use of SubDirectory in Struct tags 2785 my ($tagTablePtr, $tagID, $noSubStruct) = @_; 2786 my $count = 0; 2787 my @tagIDs; 2788 2789 if (defined $tagID) { 2790 push @tagIDs, $tagID; 2791 } else { 2792 foreach $tagID (TagTableKeys($tagTablePtr)) { 2793 my $tagInfo = $$tagTablePtr{$tagID}; 2794 next unless ref $tagInfo eq 'HASH' and $$tagInfo{Struct}; 2795 push @tagIDs, $tagID; 2796 } 2327 2797 } 2328 # do not add flattened tags to variable-namespace structures 2329 return 0 if exists $$strTable{NAMESPACE} and not defined $$strTable{NAMESPACE}; 2330 2331 # get family 2 group name for this structure tag 2332 my ($tagG2, $field); 2333 $tagG2 = $$tagInfo{Groups}{2} if $$tagInfo{Groups}; 2334 $tagG2 or $tagG2 = $$tagTablePtr{GROUPS}{2}; 2335 2336 my $count = 0; 2337 foreach $field (keys %$strTable) { 2338 next if $specialStruct{$field}; 2339 my $fieldInfo = $$strTable{$field}; 2340 next if $$fieldInfo{LangCode}; # don't flatten lang-alt tags 2341 # build a tag ID for the corresponding flattened tag 2342 my $fieldName = ucfirst($field); 2343 my $flatID = $tagID . $fieldName; 2344 my $flatInfo = $$tagTablePtr{$flatID}; 2345 if ($flatInfo) { 2346 ref $flatInfo eq 'HASH' or warn("$flatInfo is not a HASH!\n"), next; # (to be safe) 2347 # pre-defined flattened tags should have Flat flag set 2348 if (not defined $$flatInfo{Flat} and $Image::ExifTool::debug) { 2349 warn "Missing Flat flag for $$flatInfo{Name}\n"; 2350 } 2351 $$flatInfo{Flat} = 0; 2352 # copy all missing entries from field information 2353 foreach (keys %$fieldInfo) { 2354 # must not copy PropertyPath (but can't delete it afterwards 2355 # because the flat tag may already have this set) 2356 next if $_ eq 'PropertyPath'; 2357 $$flatInfo{$_} = $$fieldInfo{$_} unless defined $$flatInfo{$_}; 2358 } 2359 # NOTE: Do NOT delete Groups because we need them if GotGroups was done 2360 # --> just override group 2 later according to field group 2361 # re-generate List flag unless it is set to 0 2362 delete $$flatInfo{List} if $$flatInfo{List}; 2363 } else { 2364 # generate new flattened tag information based on structure field 2365 $flatInfo = { %$fieldInfo, Name => $$tagInfo{Name} . $fieldName, Flat => 0 }; 2366 # add new flattened tag to table 2367 Image::ExifTool::AddTagToTable($tagTablePtr, $flatID, $flatInfo); 2368 ++$count; 2369 } 2370 # propagate List flag (unless set to 0 in pre-defined flattened tag) 2371 unless (defined $$flatInfo{List}) { 2372 $$flatInfo{List} = $$fieldInfo{List} || 1 if $$fieldInfo{List} or $$tagInfo{List}; 2373 } 2374 # set group 2 name from the first existing family 2 group in the: 2375 # 1) structure field Groups, 2) structure table GROUPS, 3) structure tag Groups 2376 if ($$fieldInfo{Groups} and $$fieldInfo{Groups}{2}) { 2377 $$flatInfo{Groups}{2} = $$fieldInfo{Groups}{2}; 2378 } elsif ($$strTable{GROUPS} and $$strTable{GROUPS}{2}) { 2379 $$flatInfo{Groups}{2} = $$strTable{GROUPS}{2}; 2380 } else { 2381 $$flatInfo{Groups}{2} = $tagG2; 2382 } 2383 # save reference to top-level structure 2384 $$flatInfo{RootTagInfo} = $$tagInfo{RootTagInfo} || $tagInfo; 2385 # recursively generate flattened tags for sub-structures 2386 next unless $$flatInfo{Struct}; 2387 length($flatID) > 150 and warn("Possible deep recursion for tag $flatID\n"), last; 2388 # reset flattened tag just in case we flattened hierarchy in the wrong order 2389 # because we must start from the outtermost structure to get the List flags right 2390 # (this should only happen when building tag tables) 2391 delete $$flatInfo{Flattened}; 2392 $count += AddFlattenedTags($tagTablePtr, $flatID); 2798 2799 # loop through specified tags 2800 foreach $tagID (@tagIDs) { 2801 2802 my $tagInfo = $$tagTablePtr{$tagID}; 2803 2804 $$tagInfo{Flattened} and next; # only generate flattened tags once 2805 $$tagInfo{Flattened} = 1; 2806 2807 my $strTable = $$tagInfo{Struct}; 2808 unless (ref $strTable) { # (allow a structure name for backward compatibility only) 2809 my $strName = $strTable; 2810 $strTable = $Image::ExifTool::UserDefined::xmpStruct{$strTable} or next; 2811 $$strTable{STRUCT_NAME} or $$strTable{STRUCT_NAME} = "XMP $strName"; 2812 $$tagInfo{Struct} = $strTable; # replace old-style name with HASH ref 2813 delete $$tagInfo{SubDirectory}; # deprecated use of SubDirectory in Struct tags 2814 } 2815 2816 # get prefix for flattened tag names 2817 my $flat = (defined $$tagInfo{FlatName} ? $$tagInfo{FlatName} : $$tagInfo{Name}); 2818 2819 # get family 2 group name for this structure tag 2820 my ($tagG2, $field); 2821 $tagG2 = $$tagInfo{Groups}{2} if $$tagInfo{Groups}; 2822 $tagG2 or $tagG2 = $$tagTablePtr{GROUPS}{2}; 2823 2824 foreach $field (keys %$strTable) { 2825 next if $specialStruct{$field}; 2826 my $fieldInfo = $$strTable{$field}; 2827 next if $$fieldInfo{LangCode}; # don't flatten lang-alt tags 2828 next if $$fieldInfo{Struct} and $noSubStruct; # don't expand sub-structures if specified 2829 # build a tag ID for the corresponding flattened tag 2830 my $fieldName = ucfirst($field); 2831 my $flatField = $$fieldInfo{FlatName} || $fieldName; 2832 my $flatID = $tagID . $fieldName; 2833 my $flatInfo = $$tagTablePtr{$flatID}; 2834 if ($flatInfo) { 2835 ref $flatInfo eq 'HASH' or warn("$flatInfo is not a HASH!\n"), next; # (to be safe) 2836 # pre-defined flattened tags should have Flat flag set 2837 if (not defined $$flatInfo{Flat}) { 2838 next if $$flatInfo{NotFlat}; 2839 warn "Missing Flat flag for $$flatInfo{Name}\n" if $Image::ExifTool::debug; 2840 } 2841 $$flatInfo{Flat} = 0; 2842 # copy all missing entries from field information 2843 foreach (keys %$fieldInfo) { 2844 # must not copy PropertyPath (but can't delete it afterwards 2845 # because the flat tag may already have this set) 2846 next if $_ eq 'PropertyPath' or defined $$flatInfo{$_}; 2847 # copy the property (making a copy of the Groups hash) 2848 $$flatInfo{$_} = $_ eq 'Groups' ? { %{$$fieldInfo{$_}} } : $$fieldInfo{$_}; 2849 } 2850 # (NOTE: Can NOT delete Groups because we need them if GotGroups was done) 2851 # re-generate List flag unless it is set to 0 2852 delete $$flatInfo{List} if $$flatInfo{List}; 2853 } else { 2854 # generate new flattened tag information based on structure field 2855 my $flatName = $flat . $flatField; 2856 $flatInfo = { %$fieldInfo, Name => $flatName, Flat => 0 }; 2857 $$flatInfo{FlatName} = $flatName if $$fieldInfo{FlatName}; 2858 # make a copy of the Groups hash if necessary 2859 $$flatInfo{Groups} = { %{$$fieldInfo{Groups}} } if $$fieldInfo{Groups}; 2860 # add new flattened tag to table 2861 AddTagToTable($tagTablePtr, $flatID, $flatInfo); 2862 ++$count; 2863 } 2864 # propagate List flag (unless set to 0 in pre-defined flattened tag) 2865 unless (defined $$flatInfo{List}) { 2866 $$flatInfo{List} = $$fieldInfo{List} || 1 if $$fieldInfo{List} or $$tagInfo{List}; 2867 } 2868 # set group 2 name from the first existing family 2 group in the: 2869 # 1) structure field Groups, 2) structure table GROUPS, 3) structure tag Groups 2870 if ($$fieldInfo{Groups} and $$fieldInfo{Groups}{2}) { 2871 $$flatInfo{Groups}{2} = $$fieldInfo{Groups}{2}; 2872 } elsif ($$strTable{GROUPS} and $$strTable{GROUPS}{2}) { 2873 $$flatInfo{Groups}{2} = $$strTable{GROUPS}{2}; 2874 } else { 2875 $$flatInfo{Groups}{2} = $tagG2; 2876 } 2877 # save reference to top-level structure 2878 $$flatInfo{RootTagInfo} = $$tagInfo{RootTagInfo} || $tagInfo; 2879 # recursively generate flattened tags for sub-structures 2880 next unless $$flatInfo{Struct}; 2881 length($flatID) > 250 and warn("Possible deep recursion for tag $flatID\n"), last; 2882 # reset flattened tag just in case we flattened hierarchy in the wrong order 2883 # because we must start from the outtermost structure to get the List flags right 2884 # (this should only happen when building tag tables) 2885 delete $$flatInfo{Flattened}; 2886 $count += AddFlattenedTags($tagTablePtr, $flatID, $$flatInfo{NoSubStruct}); 2887 } 2393 2888 } 2394 2889 return $count; … … 2397 2892 #------------------------------------------------------------------------------ 2398 2893 # Get localized version of tagInfo hash 2399 # Inputs: 0) tagInfo hash ref, 1) language code ( ie. "x-default")2894 # Inputs: 0) tagInfo hash ref, 1) language code (eg. "x-default") 2400 2895 # Returns: new tagInfo hash ref, or undef if invalid 2401 2896 sub GetLangInfo($$) … … 2406 2901 $langCode =~ tr/_/-/; # RFC 3066 specifies '-' as a separator 2407 2902 my $langInfo = Image::ExifTool::GetLangInfo($tagInfo, $langCode); 2408 # save reference to source tagInfo hash in case we need to set the PropertyPath later2409 $$langInfo{SrcTagInfo} = $tagInfo;2410 2903 return $langInfo; 2411 2904 } … … 2430 2923 sub ScanForXMP($$) 2431 2924 { 2432 my ($e xifTool, $raf) = @_;2925 my ($et, $raf) = @_; 2433 2926 my ($buff, $xmp); 2434 2927 my $lastBuff = ''; 2435 2928 2436 $e xifTool->VPrint(0,"Scanning for XMP\n");2929 $et->VPrint(0,"Scanning for XMP\n"); 2437 2930 for (;;) { 2438 2931 defined $buff or $raf->Read($buff, 65536) or return 0; … … 2462 2955 } 2463 2956 } 2464 unless ($ exifTool->{VALUE}{FileType}) {2465 $ exifTool->{FILE_TYPE} = $exifTool->{FILE_EXT};2466 $e xifTool->SetFileType('<unknown file containing XMP>');2957 unless ($$et{VALUE}{FileType}) { 2958 $$et{FILE_TYPE} = $$et{FILE_EXT}; 2959 $et->SetFileType('<unknown file containing XMP>', undef, ''); 2467 2960 } 2468 2961 my %dirInfo = ( 2469 DataPt => \$xmp,2470 DirLen => length $xmp,2962 DataPt => \$xmp, 2963 DirLen => length $xmp, 2471 2964 DataLen => length $xmp, 2472 2965 ); 2473 ProcessXMP($e xifTool, \%dirInfo);2966 ProcessXMP($et, \%dirInfo); 2474 2967 return 1; 2968 } 2969 2970 #------------------------------------------------------------------------------ 2971 # Print conversion for XMP-aux:LensID 2972 # Inputs: 0) ExifTool ref, 1) LensID, 2) Make, 3) LensInfo, 4) FocalLength, 2973 # 5) LensModel, 6) MaxApertureValue 2974 # (yes, this is ugly -- blame Adobe) 2975 sub PrintLensID(@) 2976 { 2977 local $_; 2978 my ($et, $id, $make, $info, $focalLength, $lensModel, $maxAv) = @_; 2979 my ($mk, $printConv); 2980 my %alt = ( Pentax => 'Ricoh' ); # Pentax changed its name to Ricoh 2981 # missing: Olympus (no XMP:LensID written by Adobe) 2982 foreach $mk (qw(Canon Nikon Pentax Sony Sigma Samsung Leica)) { 2983 next unless $make =~ /$mk/i or ($alt{$mk} and $make =~ /$alt{$mk}/i); 2984 # get name of module containing the lens lookup (default "Make.pm") 2985 my $mod = { Sigma => 'SigmaRaw', Leica => 'Panasonic' }->{$mk} || $mk; 2986 require "Image/ExifTool/$mod.pm"; 2987 # get the name of the lens name lookup (default "makeLensTypes") 2988 # (canonLensTypes, pentaxLensTypes, nikonLensIDs, etc) 2989 my $convName = "Image::ExifTool::${mod}::" . 2990 ({ Nikon => 'nikonLensIDs' }->{$mk} || lc($mk) . 'LensTypes'); 2991 no strict 'refs'; 2992 %$convName or last; 2993 my $printConv = \%$convName; 2994 use strict 'refs'; 2995 # sf = short focal 2996 # lf = long focal 2997 # sa = max aperture at short focal 2998 # la = max aperture at long focal 2999 my ($sf, $lf, $sa, $la); 3000 if ($info) { 3001 my @a = split ' ', $info; 3002 $_ eq 'undef' and $_ = undef foreach @a; 3003 ($sf, $lf, $sa, $la) = @a; 3004 # for Sony and ambiguous LensID, $info data may be incorrect: 3005 # use only if it agrees with $focalLength and $maxAv (ref JR) 3006 if ($mk eq 'Sony' and 3007 (($focalLength and (($sf and $focalLength < $sf - 0.5) or 3008 ($lf and $focalLength > $lf + 0.5))) or 3009 ($maxAv and (($sa and $maxAv < $sa - 0.15) or 3010 ($la and $maxAv > $la + 0.15))))) 3011 { 3012 undef $sf; 3013 undef $lf; 3014 undef $sa; 3015 undef $la; 3016 } elsif ($maxAv) { 3017 # (using the short-focal-length max aperture in place of MaxAperture 3018 # is a bad approximation, so don't do this if MaxApertureValue exists) 3019 undef $sa; 3020 } 3021 } 3022 if ($mk eq 'Pentax' and $id =~ /^\d+$/) { 3023 # for Pentax, CS4 stores an int16u, but we use 2 x int8u 3024 $id = join(' ', unpack('C*', pack('n', $id))); 3025 } 3026 # Nikon is a special case because Adobe doesn't store the full LensID 3027 # (Apple Photos does, but we have to convert back to hex) 3028 if ($mk eq 'Nikon') { 3029 $id = sprintf('%X', $id); 3030 $id = "0$id" if length($id) & 0x01; # pad with leading 0 if necessary 3031 $id =~ s/(..)/$1 /g and $id =~ s/ $//; # put spaces between bytes 3032 my (%newConv, %used); 3033 my $i = 0; 3034 foreach (grep /^$id/, keys %$printConv) { 3035 my $lens = $$printConv{$_}; 3036 next if $used{$lens}; # avoid duplicates 3037 $used{$lens} = 1; 3038 $newConv{$i ? "$id.$i" : $id} = $lens; 3039 ++$i; 3040 } 3041 $printConv = \%newConv; 3042 } 3043 my $str = $$printConv{$id} || "Unknown ($id)"; 3044 return Image::ExifTool::Exif::PrintLensID($et, $str, $printConv, 3045 undef, $id, $focalLength, $sa, $maxAv, $sf, $lf, $lensModel); 3046 } 3047 return "Unknown ($id)"; 2475 3048 } 2476 3049 … … 2499 3072 my $val = $_[0]; 2500 3073 $val =~ m{^(-?\d+)/(-?\d+)$} or return undef; 2501 if ($2 ) {3074 if ($2 != 0) { 2502 3075 $_[0] = $1 / $2; # calculate quotient 2503 3076 } elsif ($1) { … … 2507 3080 } 2508 3081 return 1; 3082 } 3083 3084 #------------------------------------------------------------------------------ 3085 # Convert a string of floating point values to rationals 3086 # Inputs: 0) string of floating point numbers separated by spaces 3087 # Returns: string of rational numbers separated by spaces 3088 sub ConvertRationalList($) 3089 { 3090 my $val = shift; 3091 my @vals = split ' ', $val; 3092 return $val unless @vals == 4; 3093 foreach (@vals) { 3094 ConvertRational($_) or return $val; 3095 } 3096 return join ' ', @vals; 2509 3097 } 2510 3098 … … 2518 3106 { 2519 3107 local $_; 2520 my ($e xifTool, $tagTablePtr, $props, $val, $attrs) = @_;2521 my ($lang, @structProps );2522 my ($tag, $ns) = GetXMPTagID($props, $ exifTool->{OPTIONS}{Struct} ? \@structProps : undef);3108 my ($et, $tagTablePtr, $props, $val, $attrs) = @_; 3109 my ($lang, @structProps, $rawVal, $rational); 3110 my ($tag, $ns) = GetXMPTagID($props, $$et{OPTIONS}{Struct} ? \@structProps : undef); 2523 3111 return 0 unless $tag; # ignore things that aren't valid tags 2524 3112 2525 3113 # translate namespace if necessary 2526 $ns = $ $xlatNamespace{$ns} if $$xlatNamespace{$ns};2527 my $info = $ tagTablePtr->{$ns};3114 $ns = $stdXlatNS{$ns} if $stdXlatNS{$ns}; 3115 my $info = $$tagTablePtr{$ns}; 2528 3116 my ($table, $added, $xns, $tagID); 2529 3117 if ($info) { 2530 $table = $ info->{SubDirectory}{TagTable} or warn "Missing TagTable for $tag!\n";3118 $table = $$info{SubDirectory}{TagTable} or warn "Missing TagTable for $tag!\n"; 2531 3119 } elsif ($$props[0] eq 'svg:svg') { 2532 3120 if (not $ns) { … … 2541 3129 } 2542 3130 2543 # look up this tag in the appropriate table 2544 $table or $table = 'Image::ExifTool::XMP::other'; 2545 $tagTablePtr = GetTagTable($table); 2546 if ($$tagTablePtr{NAMESPACE}) { 3131 my $xmlGroups; 3132 my $grp0 = $$tagTablePtr{GROUPS}{0}; 3133 if (not $ns and $grp0 ne 'XMP') { 2547 3134 $tagID = $tag; 3135 } elsif ($grp0 eq 'XML' and not $table) { 3136 # this is an XML table (no namespace lookup) 3137 $tagID = "$ns:$tag"; 2548 3138 } else { 2549 # add XMP namespace prefix to avoid collisions in variable-namespace tables 2550 $xns = $xmpNS{$ns} || $ns; 2551 $tagID = "$xns:$tag"; 2552 # add namespace to top-level structure property 2553 $structProps[0][0] = "$xns:" . $structProps[0][0] if @structProps; 3139 $xmlGroups = 1 if $grp0 eq 'XML'; 3140 # look up this tag in the appropriate table 3141 $table or $table = 'Image::ExifTool::XMP::other'; 3142 $tagTablePtr = GetTagTable($table); 3143 if ($$tagTablePtr{NAMESPACE}) { 3144 $tagID = $tag; 3145 } else { 3146 $xns = $xmpNS{$ns}; 3147 unless (defined $xns) { 3148 $xns = $ns; 3149 # validate namespace prefix 3150 unless ($ns =~ /^[A-Z_a-z\x80-\xff][-.0-9A-Z_a-z\x80-\xff]*$/ or $ns eq '') { 3151 $et->Warn("Invalid XMP namespace prefix '${ns}'"); 3152 # clean up prefix for use as an ExifTool group name 3153 $ns =~ tr/-.0-9A-Z_a-z\x80-\xff//dc; 3154 $ns =~ /^[A-Z_a-z\x80-\xff]/ or $ns = "ns_$ns"; 3155 $stdXlatNS{$xns} = $ns; 3156 $xmpNS{$ns} = $xns; 3157 } 3158 } 3159 # add XMP namespace prefix to avoid collisions in variable-namespace tables 3160 $tagID = "$xns:$tag"; 3161 # add namespace to top-level structure property 3162 $structProps[0][0] = "$xns:" . $structProps[0][0] if @structProps; 3163 } 2554 3164 } 2555 my $tagInfo = $e xifTool->GetTagInfo($tagTablePtr, $tagID);3165 my $tagInfo = $et->GetTagInfo($tagTablePtr, $tagID); 2556 3166 2557 3167 $lang = $$attrs{'xml:lang'} if $attrs; … … 2573 3183 next unless ref $ti eq 'HASH' and $$ti{Struct}; 2574 3184 $addedFlat = AddFlattenedTags($tagTablePtr, $t); 2575 if ($tagInfo) {2576 # all done if we just wanted to initialize the flattened tag2577 if ($$tagInfo{Flat}) {2578 warn "Orphan tagInfo with Flat flag set: $$tagInfo{Name}\n";2579 delete $$tagInfo{Flat};2580 }2581 last NoLoop;2582 }2583 3185 # all done if we generated the tag we are looking for 2584 3186 $tagInfo = $$tagTablePtr{$tagID} and last NoLoop if $addedFlat; … … 2597 3199 next unless ref $ti eq 'HASH'; 2598 3200 my $strTable = $$ti{Struct} or next; 2599 $name = $$ti{Name} . ucfirst($t2); 3201 my $flat = (defined $$ti{FlatName} ? $$ti{FlatName} : $$ti{Name}); 3202 $name = $flat . ucfirst($t2); 2600 3203 # don't continue if structure is known but field is not 2601 3204 last if $$strTable{NAMESPACE} or not exists $$strTable{NAMESPACE}; … … 2605 3208 my $n = $nsList[$i+1]; # namespace of structure field 2606 3209 # translate to standard ExifTool namespace 2607 $n = $ $xlatNamespace{$n} if $$xlatNamespace{$n};3210 $n = $stdXlatNS{$n} if $stdXlatNS{$n}; 2608 3211 my $xn = $xmpNS{$n} || $n; # standard XMP namespace 2609 3212 # no need to continue with variable-namespace logic if … … 2620 3223 last unless ref $tg eq 'HASH' and $$tg{SubDirectory}; 2621 3224 my $tbl = GetTagTable($$tg{SubDirectory}{TagTable}) or last; 2622 my $sti = $e xifTool->GetTagInfo($tbl, $t2);3225 my $sti = $et->GetTagInfo($tbl, $t2); 2623 3226 if (not $sti or $$sti{Flat}) { 2624 3227 # again, we must initialize flattened tags if necessary 2625 3228 # (but don't bother to recursively apply full logic to 2626 # allow nest variable-namespace strucures until someone3229 # allow nested variable-namespace strucures until someone 2627 3230 # actually wants to do such a silly thing) 2628 3231 my $t3 = ''; … … 2637 3240 last unless $sti; 2638 3241 } 2639 $tagInfo = { 2640 %$sti, 2641 Name => $$ti{Name} . $$sti{Name}, 2642 WasAdded => 1, 2643 }; 3242 # generate new tagInfo hash based on existing top-level tag 3243 $tagInfo = { %$sti, Name => $flat . $$sti{Name} }; 2644 3244 # be careful not to copy elements we shouldn't... 2645 3245 delete $$tagInfo{Description}; # Description will be different … … 2651 3251 } 2652 3252 } 2653 $tagInfo or $tagInfo = { Name => $name, WasAdded => 1 }; 3253 # generate a default tagInfo hash if necessary 3254 $tagInfo or $tagInfo = { Name => $name, IsDefault => 1, Priority => 0 }; 2654 3255 2655 3256 # add tag Namespace entry for tags in variable-namespace tables 2656 3257 $$tagInfo{Namespace} = $xns if $xns; 2657 if ($ curNS{$ns} and $curNS{$ns} =~ m{^http://ns.exiftool.ca/(.*?)/(.*?)/}) {3258 if ($$et{curURI}{$ns} and $$et{curURI}{$ns} =~ m{^http://ns.exiftool.(?:ca|org)/(.*?)/(.*?)/}) { 2658 3259 my %grps = ( 0 => $1, 1 => $2 ); 2659 3260 # apply a little magic to recover original group names … … 2683 3284 # $$tagInfo{List} = 1; 2684 3285 } 2685 Image::ExifTool::AddTagToTable($tagTablePtr, $tagID, $tagInfo); 2686 $added = 1; 3286 # save property list for verbose "adding" message unless this tag already exists 3287 $added = \@tagList unless $$tagTablePtr{$tagID}; 3288 AddTagToTable($tagTablePtr, $tagID, $tagInfo); 2687 3289 last; 2688 3290 } … … 2717 3319 } 2718 3320 # decode from UTF8 2719 $val = $e xifTool->Decode($val, 'UTF8');3321 $val = $et->Decode($val, 'UTF8'); 2720 3322 # convert rational and date values to a more sensible format 2721 3323 my $fmt = $$tagInfo{Writable}; 2722 my $new = $$tagInfo{WasAdded}; 2723 if (($fmt or $new)) { 2724 unless (($new or $fmt eq 'rational') and ConvertRational($val)) { 3324 my $new = $$tagInfo{IsDefault} && $$et{OPTIONS}{XMPAutoConv}; 3325 if ($fmt or $new) { 3326 $rawVal = $val; # save raw value for verbose output 3327 if (($new or $fmt eq 'rational') and ConvertRational($val)) { 3328 $rational = $rawVal; 3329 } else { 2725 3330 $val = ConvertXMPDate($val, $new) if $new or $fmt eq 'date'; 2726 3331 } 3332 if ($$et{XmpValidate} and $fmt and $fmt eq 'boolean') { 3333 $et->WarnOnce("Boolean value for XMP-$ns:$$tagInfo{Name} should be capitalized",1); 3334 } 3335 # protect against large binary data in unknown tags 3336 $$tagInfo{Binary} = 1 if $new and length($val) > 65536; 2727 3337 } 2728 3338 # store the value for this tag 2729 my $key = $exifTool->FoundTag($tagInfo, $val); 3339 my $key = $et->FoundTag($tagInfo, $val) or return 0; 3340 # save original components of rational numbers (used when copying) 3341 $$et{RATIONAL}{$key} = $rational if defined $rational; 3342 # allow read-only subdirectories (eg. embedded base64 XMP/IPTC in NKSC files) 3343 if ($$tagInfo{SubDirectory} and not $$et{IsWriting}) { 3344 my $subdir = $$tagInfo{SubDirectory}; 3345 my $dataPt = ref $$et{VALUE}{$key} ? $$et{VALUE}{$key} : \$$et{VALUE}{$key}; 3346 # process subdirectory information 3347 my %dirInfo = ( 3348 DirName => $$subdir{DirName} || $$tagInfo{Name}, 3349 DataPt => $dataPt, 3350 DirLen => length $$dataPt, 3351 IsExtended => 1, # (hack to avoid Duplicate warning for embedded XMP) 3352 ); 3353 my $oldOrder = GetByteOrder(); 3354 SetByteOrder($$subdir{ByteOrder}) if $$subdir{ByteOrder}; 3355 my $oldNS = $$et{definedNS}; 3356 delete $$et{definedNS}; 3357 my $subTablePtr = GetTagTable($$subdir{TagTable}) || $tagTablePtr; 3358 $et->ProcessDirectory(\%dirInfo, $subTablePtr, $$subdir{ProcessProc}); 3359 SetByteOrder($oldOrder); 3360 $$et{definedNS} = $oldNS; 3361 } 2730 3362 # save structure/list information if necessary 2731 if (@structProps and (@structProps > 1 or defined $structProps[0][1])) { 2732 $exifTool->{TAG_EXTRA}{$key}{Struct} = \@structProps; 2733 $exifTool->{IsStruct} = 1; 3363 if (@structProps and (@structProps > 1 or defined $structProps[0][1]) and 3364 not $$et{NO_STRUCT}) 3365 { 3366 $$et{TAG_EXTRA}{$key}{Struct} = \@structProps; 3367 $$et{IsStruct} = 1; 2734 3368 } 2735 if ($ns and not $$tagInfo{StaticGroup1}) { 3369 if ($xmlGroups) { 3370 $et->SetGroup($key, 'XML', 0); 3371 $et->SetGroup($key, "XML-$ns", 1); 3372 } elsif ($ns and not $$tagInfo{StaticGroup1}) { 2736 3373 # set group1 dynamically according to the namespace 2737 $e xifTool->SetGroup($key, "$tagTablePtr->{GROUPS}{0}-$ns");3374 $et->SetGroup($key, "$$tagTablePtr{GROUPS}{0}-$ns"); 2738 3375 } 2739 if ($ exifTool->{OPTIONS}{Verbose}) {3376 if ($$et{OPTIONS}{Verbose}) { 2740 3377 if ($added) { 2741 my $g1 = $exifTool->GetGroup($key, 1); 2742 $exifTool->VPrint(0, $exifTool->{INDENT}, "[adding $g1:$tag]\n"); 3378 my $props; 3379 if (@$added > 1) { 3380 $$tagInfo{Flat} = 0; # this is a flattened tag 3381 my @props = map { $$_[0] } @$added; 3382 $props = ' (' . join('/',@props) . ')'; 3383 } else { 3384 $props = ''; 3385 } 3386 my $g1 = $et->GetGroup($key, 1); 3387 $et->VPrint(0, $$et{INDENT}, "[adding $g1:$tag]$props\n"); 2743 3388 } 2744 3389 my $tagID = join('/',@$props); 2745 $e xifTool->VerboseInfo($tagID, $tagInfo, Value=>$val);3390 $et->VerboseInfo($tagID, $tagInfo, Value => $rawVal || $val); 2746 3391 } 2747 3392 return 1; … … 2750 3395 #------------------------------------------------------------------------------ 2751 3396 # Recursively parse nested XMP data element 2752 # Inputs: 0) ExifTool object reference 2753 # 1) Pointer to tag table 2754 # 2) reference to XMP data 2755 # 3) start of xmp element 2756 # 4) reference to array of enclosing XMP property names (undef if none) 2757 # 5) reference to blank node information hash 3397 # Inputs: 0) ExifTool ref, 1) tag table ref, 2) XMP data ref 3398 # 3) offset to start of XMP element, 4) offset to end of XMP element 3399 # 5) reference to array of enclosing XMP property names (undef if none) 3400 # 6) reference to blank node information hash 2758 3401 # Returns: Number of contained XMP elements 2759 sub ParseXMPElement($$$;$$$ )3402 sub ParseXMPElement($$$;$$$$) 2760 3403 { 2761 my ($exifTool, $tagTablePtr, $dataPt, $start, $propListPt, $blankInfo) = @_; 3404 local $_; 3405 my ($et, $tagTablePtr, $dataPt, $start, $end, $propList, $blankInfo) = @_; 2762 3406 my ($count, $nItems) = (0, 0); 2763 my $isWriting = $exifTool->{XMP_CAPTURE}; 2764 my $isSVG = $$exifTool{XMP_IS_SVG}; 3407 my $isWriting = $$et{XMP_CAPTURE}; 3408 my $isSVG = $$et{XMP_IS_SVG}; 3409 my $saveNS; # save xlatNS lookup if changed for the scope of this element 3410 my (%definedNS, %usedNS); # namespaces defined and used in this scope 2765 3411 2766 3412 # get our parse procs 2767 3413 my ($attrProc, $foundProc); 2768 if ($$e xifTool{XMPParseOpts}) {2769 $attrProc = $$e xifTool{XMPParseOpts}{AttrProc};2770 $foundProc = $$e xifTool{XMPParseOpts}{FoundProc} || \&FoundXMP;3414 if ($$et{XMPParseOpts}) { 3415 $attrProc = $$et{XMPParseOpts}{AttrProc}; 3416 $foundProc = $$et{XMPParseOpts}{FoundProc} || \&FoundXMP; 2771 3417 } else { 2772 3418 $foundProc = \&FoundXMP; 2773 3419 } 2774 3420 $start or $start = 0; 2775 $propListPt or $propListPt = [ ]; 3421 $end or $end = length $$dataPt; 3422 $propList or $propList = [ ]; 2776 3423 2777 3424 my $processBlankInfo; … … 2780 3427 # keep track of current nodeID at this nesting level 2781 3428 my $oldNodeID = $$blankInfo{NodeID}; 2782 2783 3429 pos($$dataPt) = $start; 3430 3431 # lookup for translating namespace prefixes 3432 my $xlatNS = $$et{xlatNS}; 3433 2784 3434 Element: for (;;) { 3435 # all done if there isn't enough data for another element 3436 # (the smallest possible element is 4 bytes, eg. "<a/>") 3437 last if pos($$dataPt) > $end - 4; 2785 3438 # reset nodeID before processing each element 2786 3439 my $nodeID = $$blankInfo{NodeID} = $oldNodeID; 2787 3440 # get next element 2788 last unless $$dataPt =~ m/<([-\w:.\x80-\xff]+)(.*?)>/sg; 2789 my ($prop, $attrs) = ($1, $2); 2790 my $val = ''; 3441 last if $$dataPt !~ m{<([?/]?)([-\w:.\x80-\xff]+|!--)([^>]*)>}sg or pos($$dataPt) > $end; 3442 # (the only reason we match '<[?/]' is to keep from scanning past the 3443 # "<?xpacket end..." terminator or other closing token, so 3444 next if $1; 3445 my ($prop, $attrs) = ($2, $3); 3446 # skip comments 3447 if ($prop eq '!--') { 3448 next if $attrs =~ /--$/ or $$dataPt =~ /-->/sg; 3449 last; 3450 } 3451 my $valStart = pos($$dataPt); 3452 my $valEnd; 2791 3453 # only look for closing token if this is not an empty element 2792 # (empty elements end with '/', ie. <a:b/>)3454 # (empty elements end with '/', eg. <a:b/>) 2793 3455 if ($attrs !~ s/\/$//) { 2794 3456 my $nesting = 1; 2795 3457 for (;;) { 2796 3458 # this match fails with perl 5.6.2 (perl bug!), but it works without 2797 # the '(.*?)', so do it the hard way instead...3459 # the '(.*?)', so we must do it differently... 2798 3460 # $$dataPt =~ m/(.*?)<\/$prop>/sg or last Element; 2799 3461 # my $val2 = $1; 2800 my $pos = pos($$dataPt); 2801 unless ($$dataPt =~ m/<\/$prop>/sg) { 2802 $exifTool->Warn("XMP format error (no closing tag for $prop)"); 3462 # find next matching closing token, or the next opening token 3463 # of a nested same-named element 3464 if ($$dataPt !~ m{<(/?)$prop([-\w:.\x80-\xff]*)(.*?(/?))>}sg or 3465 pos($$dataPt) > $end) 3466 { 3467 $et->Warn("XMP format error (no closing tag for $prop)"); 2803 3468 last Element; 2804 3469 } 2805 my $len = pos($$dataPt) - $pos - length($prop) - 3; 2806 my $val2 = substr($$dataPt, $pos, $len); 2807 # increment nesting level for each contained similar opening token 2808 ++$nesting while $val2 =~ m/<$prop\b.*?(\/?)>/sg and $1 ne '/'; 2809 $val .= $val2; 2810 --$nesting or last; 2811 $val .= "</$prop>"; 2812 } 2813 } 2814 my $parseResource; 3470 next if $2; # ignore opening properties with different names 3471 if ($1) { 3472 next if --$nesting; 3473 $valEnd = pos($$dataPt) - length($prop) - length($3) - 3; 3474 last; # this element is complete 3475 } 3476 # this is a nested opening token (or empty element) 3477 ++$nesting unless $4; 3478 } 3479 } else { 3480 $valEnd = $valStart; 3481 } 3482 $start = pos($$dataPt); # start from here the next time around 3483 3484 # extract property attributes 3485 my ($parseResource, %attrs, @attrs); 3486 while ($attrs =~ m/(\S+?)\s*=\s*(['"])(.*?)\2/sg) { 3487 my ($attr, $val) = ($1, $3); 3488 # handle namespace prefixes (defined by xmlns:PREFIX, or used with PREFIX:tag) 3489 if ($attr =~ /(.*?):/) { 3490 if ($1 eq 'xmlns') { 3491 my $ns = substr($attr, 6); 3492 my $stdNS = $uri2ns{$val}; 3493 # keep track of namespace prefixes defined in this scope (for Validate) 3494 $$et{definedNS}{$ns} = $definedNS{$ns} = 1 unless $$et{definedNS}{$ns}; 3495 unless ($stdNS) { 3496 my $try = $val; 3497 # patch for Nikon NX2 URI bug for Microsoft PhotoInfo namespace 3498 $try =~ s{/$}{} or $try .= '/'; 3499 $stdNS = $uri2ns{$try}; 3500 if ($stdNS) { 3501 $val = $try; 3502 $et->WarnOnce("Fixed incorrect URI for xmlns:$ns", 1); 3503 } else { 3504 # look for same namespace with different version number 3505 $try = quotemeta $val; # (note: escapes slashes too) 3506 $try =~ s{\\/\d+\\\.\d+(\\/|$)}{\\/\\d+\\\.\\d+$1}; 3507 my ($good) = grep /^$try$/, keys %uri2ns; 3508 if ($good) { 3509 $stdNS = $uri2ns{$good}; 3510 $et->VPrint(0, $$et{INDENT}, "[different $stdNS version: $val]\n"); 3511 } 3512 } 3513 } 3514 # tame wild namespace prefixes (patches Microsoft stupidity) 3515 my $newNS; 3516 if ($stdNS) { 3517 # use standard namespace prefix if pre-defined 3518 if ($stdNS ne $ns) { 3519 $newNS = $stdNS; 3520 } elsif ($$xlatNS{$ns}) { 3521 # this prefix is re-defined to the standard prefix in this scope 3522 $newNS = ''; 3523 } 3524 } elsif ($$et{curNS}{$val}) { 3525 # use a consistent prefix over the entire XMP for a given namespace URI 3526 $newNS = $$et{curNS}{$val} if $$et{curNS}{$val} ne $ns; 3527 } else { 3528 my $curURI = $$et{curURI}; 3529 my $curNS = $$et{curNS}; 3530 my $usedNS = $ns; 3531 # use unique prefixes for all namespaces across the entire XMP 3532 if ($$curURI{$ns} or $nsURI{$ns}) { 3533 # generate a temporary namespace prefix to resolve any conflict 3534 my $i = 0; 3535 ++$i while $$curURI{"tmp$i"}; 3536 $newNS = $usedNS = "tmp$i"; 3537 } 3538 # keep track of the namespace prefixes and URI's used in this XMP 3539 $$curNS{$val} = $usedNS; 3540 $$curURI{$usedNS} = $val; 3541 } 3542 if (defined $newNS) { 3543 # save translation used in containing scope if necessary 3544 # create new namespace translation for the scope of this element 3545 $saveNS or $saveNS = $xlatNS, $xlatNS = $$et{xlatNS} = { %$xlatNS }; 3546 if (length $newNS) { 3547 # use the new namespace prefix 3548 $$xlatNS{$ns} = $newNS; 3549 $attr = 'xmlns:' . $newNS; 3550 # must go through previous attributes and change prefixes if necessary 3551 foreach (@attrs) { 3552 next unless /(.*?):/ and $1 eq $ns and $1 ne $newNS; 3553 my $newAttr = $newNS . substr($_, length($ns)); 3554 $attrs{$newAttr} = $attrs{$_}; 3555 delete $attrs{$_}; 3556 $_ = $newAttr; 3557 } 3558 } else { 3559 delete $$xlatNS{$ns}; 3560 } 3561 } 3562 } else { 3563 $attr = $$xlatNS{$1} . substr($attr, length($1)) if $$xlatNS{$1}; 3564 $usedNS{$1} = 1; 3565 } 3566 } 3567 push @attrs, $attr; # preserve order 3568 $attrs{$attr} = $val; 3569 } 3570 if ($prop =~ /(.*?):/) { 3571 $usedNS{$1} = 1; 3572 # tame wild namespace prefixes (patch for Microsoft stupidity) 3573 $prop = $$xlatNS{$1} . substr($prop, length($1)) if $$xlatNS{$1}; 3574 } 3575 2815 3576 if ($prop eq 'rdf:li') { 3577 # impose a reasonable maximum on the number of items in a list 3578 if ($nItems == 1000) { 3579 my ($tg,$ns) = GetXMPTagID($propList); 3580 if ($isWriting) { 3581 $et->Warn("Excessive number of items for $ns:$tg. Processing may be slow", 1); 3582 } elsif (not $$et{OPTIONS}{IgnoreMinorErrors}) { 3583 $et->Warn("Extracted only 1000 $ns:$tg items. Ignore minor errors to extract all", 2); 3584 last; 3585 } 3586 } 2816 3587 # add index to list items so we can keep them in order 2817 3588 # (this also enables us to keep structure elements grouped properly … … 2823 3594 # 10,11,12-19,210,211-299,3100,3101-3999,41000...9999999999. 2824 3595 $prop .= ' ' . length($nItems) . $nItems; 3596 # reset LIST_TAGS at the start of the outtermost list 3597 # (avoids accumulating incorrectly-written elements in a correctly-written list) 3598 if (not $nItems and not grep /^rdf:li /, @$propList) { 3599 $$et{LIST_TAGS} = { }; 3600 } 2825 3601 ++$nItems; 2826 3602 } elsif ($prop eq 'rdf:Description') { 2827 # trim comments and whitespace from rdf:Description properties only2828 $val =~ s/<!--.*?-->//g;2829 $val =~ s/^\s*(.*)\s*$/$1/;2830 3603 # remove unnecessary rdf:Description elements since parseType='Resource' 2831 3604 # is more efficient (also necessary to make property path consistent) 2832 $parseResource = 1 if grep /^rdf:Description$/, @$propList Pt;3605 $parseResource = 1 if grep /^rdf:Description$/, @$propList; 2833 3606 } elsif ($prop eq 'xmp:xmpmeta') { 2834 3607 # patch MicrosoftPhoto unconformity 2835 3608 $prop = 'x:xmpmeta'; 2836 } 2837 2838 # extract property attributes 2839 my (%attrs, @attrs); 2840 while ($attrs =~ m/(\S+?)\s*=\s*(['"])(.*?)\2/sg) { 2841 push @attrs, $1; # preserve order 2842 $attrs{$1} = $3; 3609 $et->Warn('Wrong namespace for xmpmeta') if $$et{XmpValidate}; 2843 3610 } 2844 3611 2845 3612 # hook for special parsing of attributes 2846 $attrProc and &$attrProc(\@attrs, \%attrs, \$prop, \$val); 2847 3613 my $val; 3614 if ($attrProc) { 3615 $val = substr($$dataPt, $valStart, $valEnd - $valStart); 3616 if (&$attrProc(\@attrs, \%attrs, \$prop, \$val)) { 3617 # the value was changed, so reset $valStart/$valEnd to use $val instead 3618 $valStart = $valEnd; 3619 } 3620 } 3621 2848 3622 # add nodeID to property path (with leading ' #') if it exists 2849 3623 if (defined $attrs{'rdf:nodeID'}) { … … 2855 3629 2856 3630 # push this property name onto our hierarchy list 2857 push @$propList Pt, $prop unless $parseResource;3631 push @$propList, $prop unless $parseResource; 2858 3632 2859 3633 if ($isSVG) { 2860 3634 # ignore everything but top level SVG tags and metadata unless Unknown set 2861 unless ($ exifTool->{OPTIONS}{Unknown} > 1 or $exifTool->{OPTIONS}{Verbose}) {2862 if (@$propList Pt > 1 and $$propListPt[1] !~ /\b(metadata|desc|title)$/) {2863 pop @$propList Pt;3635 unless ($$et{OPTIONS}{Unknown} > 1 or $$et{OPTIONS}{Verbose}) { 3636 if (@$propList > 1 and $$propList[1] !~ /\b(metadata|desc|title)$/) { 3637 pop @$propList; 2864 3638 next; 2865 3639 } … … 2867 3641 if ($prop eq 'svg' or $prop eq 'metadata') { 2868 3642 # add svg namespace prefix if missing to ignore these entries in the tag name 2869 $$propListPt[-1] = "svg:$prop"; 3643 $$propList[-1] = "svg:$prop"; 3644 } 3645 } elsif ($$et{XmpIgnoreProps}) { # ignore specified properties for tag name 3646 foreach (@{$$et{XmpIgnoreProps}}) { 3647 last unless @$propList; 3648 pop @$propList if $_ eq $$propList[0]; 2870 3649 } 2871 3650 } … … 2875 3654 my ($shortName, $shorthand, $ignored); 2876 3655 foreach $shortName (@attrs) { 3656 next unless defined $attrs{$shortName}; 2877 3657 my $propName = $shortName; 2878 3658 my ($ns, $name); … … 2891 3671 $name = $propName; 2892 3672 } 2893 # keep track of the namespace prefixes used 2894 if ($ns eq 'xmlns') { 2895 unless ($attrs{$shortName}) { 2896 $exifTool->WarnOnce("Duplicate namespace '$shortName'"); 2897 next; 2898 } 2899 $curNS{$name} = $attrs{$shortName}; 2900 my $stdNS = $uri2ns{$attrs{$shortName}}; 2901 # translate namespace if non-standard (except 'x' and 'iX') 2902 if ($stdNS and $name ne $stdNS and $stdNS ne 'x' and $stdNS ne 'iX') { 2903 # make a copy of the standard translations so we can modify it 2904 $xlatNamespace = { %stdXlatNS } if $xlatNamespace eq \%stdXlatNS; 2905 # translate this namespace prefix to the standard version 2906 $$xlatNamespace{$name} = $stdXlatNS{$stdNS} || $stdNS; 3673 if ($propName eq 'rdf:about') { 3674 if (not $$et{XmpAbout}) { 3675 $$et{XmpAbout} = $attrs{$shortName}; 3676 } elsif ($$et{XmpAbout} ne $attrs{$shortName}) { 3677 if ($isWriting) { 3678 my $str = "Different 'rdf:about' attributes not handled"; 3679 unless ($$et{WARNED_ONCE}{$str}) { 3680 $et->Error($str, 1); 3681 $$et{WARNED_ONCE}{$str} = 1; 3682 } 3683 } elsif ($$et{XmpValidate}) { 3684 $et->WarnOnce("Different 'rdf:about' attributes"); 3685 } 2907 3686 } 2908 3687 } … … 2912 3691 my $stdNS = $uri2ns{$attrs{$shortName}}; 2913 3692 unless ($stdNS and ($stdNS eq 'x' or $stdNS eq 'iX')) { 2914 my $nsUsed = $ exifTool->{XMP_NS};3693 my $nsUsed = $$et{XMP_NS}; 2915 3694 $$nsUsed{$name} = $attrs{$shortName} unless defined $$nsUsed{$name}; 2916 3695 } … … 2918 3697 next; 2919 3698 } elsif ($recognizedAttrs{$propName}) { 2920 # save UUID to use same ID when writing2921 if ($propName eq 'rdf:about') {2922 if (not $exifTool->{XMP_ABOUT}) {2923 $exifTool->{XMP_ABOUT} = $attrs{$shortName};2924 } elsif ($exifTool->{XMP_ABOUT} ne $attrs{$shortName}) {2925 $exifTool->Error("Different 'rdf:about' attributes not handled", 1);2926 }2927 }2928 3699 next; 2929 3700 } 2930 3701 } 2931 3702 my $shortVal = $attrs{$shortName}; 2932 if ($ignoreNamespace{$ns} ) {3703 if ($ignoreNamespace{$ns} or $ignoreProp{$prop}) { 2933 3704 $ignored = $propName; 2934 3705 # handle special attributes (extract as tags only once if not empty) 2935 3706 if (ref $recognizedAttrs{$propName} and $shortVal) { 2936 3707 my ($tbl, $id, $name) = @{$recognizedAttrs{$propName}}; 2937 my $ val = UnescapeXML($shortVal);2938 unless (defined $$e xifTool{VALUE}{$name} and $$exifTool{VALUE}{$name} eq $val) {2939 $e xifTool->HandleTag(GetTagTable($tbl), $id, $val);3708 my $tval = UnescapeXML($shortVal); 3709 unless (defined $$et{VALUE}{$name} and $$et{VALUE}{$name} eq $tval) { 3710 $et->HandleTag(GetTagTable($tbl), $id, $tval); 2940 3711 } 2941 3712 } … … 2943 3714 } 2944 3715 delete $attrs{$shortName}; # don't re-use this attribute 2945 push @$propList Pt, $propName;3716 push @$propList, $propName; 2946 3717 # save this shorthand XMP property 2947 3718 if (defined $nodeID) { 2948 SaveBlankInfo($blankInfo, $propList Pt, $shortVal);3719 SaveBlankInfo($blankInfo, $propList, $shortVal); 2949 3720 } elsif ($isWriting) { 2950 CaptureXMP($e xifTool, $propListPt, $shortVal);3721 CaptureXMP($et, $propList, $shortVal); 2951 3722 } else { 2952 &$foundProc($exifTool, $tagTablePtr, $propListPt, $shortVal); 2953 } 2954 pop @$propListPt; 3723 ValidateProperty($et, $propList) if $$et{XmpValidate}; 3724 &$foundProc($et, $tagTablePtr, $propList, $shortVal); 3725 } 3726 pop @$propList; 2955 3727 $shorthand = 1; 2956 3728 } 2957 3729 if ($isWriting) { 2958 if (ParseXMPElement($exifTool, $tagTablePtr, \$val, 0, $propListPt, $blankInfo)) { 2959 # undefine value since we found more properties within this one 2960 undef $val; 3730 if (ParseXMPElement($et, $tagTablePtr, $dataPt, $valStart, $valEnd, 3731 $propList, $blankInfo)) 3732 { 3733 # (no value since we found more properties within this one) 2961 3734 # set an error on any ignored attributes here, because they will be lost 2962 $exifTool->{XMP_ERROR} = "Can't handle XMP attribute '$ignored'" if $ignored; 2963 } 2964 if (defined $val and (length $val or not $shorthand)) { 3735 $$et{XMP_ERROR} = "Can't handle XMP attribute '${ignored}'" if $ignored; 3736 } elsif (not $shorthand or $valEnd != $valStart) { 3737 $val = substr($$dataPt, $valStart, $valEnd - $valStart); 3738 # remove comments and whitespace from rdf:Description only 3739 if ($prop eq 'rdf:Description') { 3740 $val =~ s/<!--.*?-->//g; $val =~ s/^\s+//; $val =~ s/\s+$//; 3741 } 2965 3742 if (defined $nodeID) { 2966 SaveBlankInfo($blankInfo, $propList Pt, $val, \%attrs);3743 SaveBlankInfo($blankInfo, $propList, $val, \%attrs); 2967 3744 } else { 2968 CaptureXMP($e xifTool, $propListPt, $val, \%attrs);3745 CaptureXMP($et, $propList, $val, \%attrs); 2969 3746 } 2970 3747 } 2971 3748 } else { 2972 # if element value is empty, take value from 'resource' attribute 2973 # (preferentially) or 'about' attribute (if no 'resource') 2974 my $wasEmpty; 2975 if ($val eq '' and ($attrs =~ /\bresource=(['"])(.*?)\1/ or 2976 $attrs =~ /\babout=(['"])(.*?)\1/)) 3749 # look for additional elements contained within this one 3750 if ($valStart == $valEnd or 3751 !ParseXMPElement($et, $tagTablePtr, $dataPt, $valStart, $valEnd, 3752 $propList, $blankInfo)) 2977 3753 { 2978 $val = $2; 2979 $wasEmpty = 1; 2980 } 2981 # look for additional elements contained within this one 2982 if (!ParseXMPElement($exifTool, $tagTablePtr, \$val, 0, $propListPt, $blankInfo)) { 3754 my $wasEmpty; 3755 unless (defined $val) { 3756 $val = substr($$dataPt, $valStart, $valEnd - $valStart); 3757 # remove comments and whitespace from rdf:Description only 3758 if ($prop eq 'rdf:Description' and $val) { 3759 $val =~ s/<!--.*?-->//g; $val =~ s/^\s+//; $val =~ s/\s+$//; 3760 } 3761 # if element value is empty, take value from RDF 'value' or 'resource' attribute 3762 # (preferentially) or 'about' attribute (if no 'value' or 'resource') 3763 if ($val eq '' and ($attrs =~ /\brdf:(?:value|resource)=(['"])(.*?)\1/ or 3764 $attrs =~ /\brdf:about=(['"])(.*?)\1/)) 3765 { 3766 $val = $2; 3767 $wasEmpty = 1; 3768 } 3769 } 2983 3770 # there are no contained elements, so this must be a simple property value 2984 3771 # (unless we already extracted shorthand values from this element) 2985 3772 if (length $val or not $shorthand) { 2986 my $lastProp = $$propList Pt[-1];3773 my $lastProp = $$propList[-1]; 2987 3774 if (defined $nodeID) { 2988 SaveBlankInfo($blankInfo, $propList Pt, $val);3775 SaveBlankInfo($blankInfo, $propList, $val); 2989 3776 } elsif ($lastProp eq 'rdf:type' and $wasEmpty) { 2990 3777 # do not extract empty structure types (for now) 2991 3778 } elsif ($lastProp =~ /^et:(desc|prt|val)$/ and ($count or $1 eq 'desc')) { 2992 # ignore et:desc, and et:val if prece eded by et:prt3779 # ignore et:desc, and et:val if preceded by et:prt 2993 3780 --$count; 2994 3781 } else { 2995 &$foundProc($exifTool, $tagTablePtr, $propListPt, $val, \%attrs); 3782 ValidateProperty($et, $propList, \%attrs) if $$et{XmpValidate}; 3783 &$foundProc($et, $tagTablePtr, $propList, $val, \%attrs); 2996 3784 } 2997 3785 } 2998 3786 } 2999 3787 } 3000 pop @$propList Ptunless $parseResource;3788 pop @$propList unless $parseResource; 3001 3789 ++$count; 3790 3791 # validate namespace prefixes used at this level if necessary 3792 if ($$et{XmpValidate}) { 3793 foreach (sort keys %usedNS) { 3794 next if $$et{definedNS}{$_} or $_ eq 'xml'; 3795 if (defined $$et{definedNS}{$_}) { 3796 $et->Warn("XMP namespace $_ is used out of scope"); 3797 } else { 3798 $et->Warn("Undefined XMP namespace: $_"); 3799 } 3800 $$et{definedNS}{$_} = -1; # (don't warn again for this namespace) 3801 } 3802 # reset namespaces that went out of scope 3803 $$et{definedNS}{$_} = 0 foreach keys %definedNS; 3804 undef %usedNS; 3805 undef %definedNS; 3806 } 3807 3808 last if $start >= $end; 3809 pos($$dataPt) = $start; 3810 $$dataPt =~ /\G\s+/gc; # skip white space after closing token 3002 3811 } 3003 3812 # … … 3005 3814 # 3006 3815 if ($processBlankInfo and %{$$blankInfo{Prop}}) { 3007 ProcessBlankInfo($e xifTool, $tagTablePtr, $blankInfo, $isWriting);3816 ProcessBlankInfo($et, $tagTablePtr, $blankInfo, $isWriting); 3008 3817 %$blankInfo = (); # free some memory 3009 3818 } 3819 # restore namespace lookup from the containing scope 3820 $$et{xlatNS} = $saveNS if $saveNS; 3821 3010 3822 return $count; # return the number of elements found at this level 3011 3823 } … … 3027 3839 sub ProcessXMP($$;$) 3028 3840 { 3029 my ($e xifTool, $dirInfo, $tagTablePtr) = @_;3841 my ($et, $dirInfo, $tagTablePtr) = @_; 3030 3842 my $dataPt = $$dirInfo{DataPt}; 3031 my ($dirStart, $dirLen, $dataLen );3843 my ($dirStart, $dirLen, $dataLen, $double); 3032 3844 my ($buff, $fmt, $hasXMP, $isXML, $isRDF, $isSVG); 3033 3845 my $rtnVal = 0; 3034 3846 my $bom = 0; 3035 undef %curNS; 3847 3848 # namespaces and prefixes currently in effect while parsing the file, 3849 # and lookup to translate brain-dead-Microsoft-Photo-software prefixes 3850 $$et{curURI} = { }; 3851 $$et{curNS} = { }; 3852 $$et{xlatNS} = { }; 3853 $$et{definedNS} = { }; 3854 delete $$et{XmpAbout}; 3855 delete $$et{XmpValidate}; # don't validate by default 3856 delete $$et{XmpValidateLangAlt}; 3036 3857 3037 3858 # ignore non-standard XMP while in strict MWG compatibility mode 3038 if ($Image::ExifTool::MWG::strict and not $$exifTool{XMP_CAPTURE} and 3039 $$exifTool{FILE_TYPE} =~ /^(JPEG|TIFF|PSD)$/) 3859 if (($Image::ExifTool::MWG::strict or $$et{OPTIONS}{Validate}) and 3860 not ($$et{XMP_CAPTURE} or $$et{DOC_NUM}) and 3861 (($$dirInfo{DirName} || '') eq 'XMP' or $$et{FILE_TYPE} eq 'XMP')) 3040 3862 { 3041 my $path = $exifTool->MetadataPath(); 3042 unless ($path =~ /^(JPEG-APP1-XMP|TIFF-IFD0-XMP|PSD-XMP)$/) { 3043 $exifTool->Warn("Ignored non-standard XMP at $path"); 3863 $$et{XmpValidate} = { } if $$et{OPTIONS}{Validate}; 3864 my $path = $et->MetadataPath(); 3865 my $nonStd; 3866 if ($$et{FILE_TYPE} =~ /^(JPEG|TIFF|PSD)$/ and $path !~ /^(JPEG-APP1-XMP|TIFF-IFD0-XMP|PSD-XMP)$/) { 3867 $nonStd = 1; 3868 } 3869 if ($nonStd and $Image::ExifTool::MWG::strict) { 3870 $et->Warn("Ignored non-standard XMP at $path"); 3044 3871 return 1; 3872 } 3873 if ($nonStd) { 3874 $et->Warn("Non-standard XMP at $path", 1); 3875 } elsif (not $$dirInfo{IsExtended}) { 3876 $et->Warn("Duplicate XMP at $path") if $$et{DIR_COUNT}{XMP}; 3877 $$et{DIR_COUNT}{XMP} = ($$et{DIR_COUNT}{XMP} || 0) + 1; # count standard XMP 3045 3878 } 3046 3879 } … … 3049 3882 $dirLen = $$dirInfo{DirLen} || (length($$dataPt) - $dirStart); 3050 3883 $dataLen = $$dirInfo{DataLen} || length($$dataPt); 3884 # check leading BOM (may indicate double-encoded UTF) 3885 pos($$dataPt) = $dirStart; 3886 $double = $1 if $$dataPt =~ /\G((\0\0)?\xfe\xff|\xff\xfe(\0\0)?|\xef\xbb\xbf)\0*<\0*\?\0*x\0*p\0*a\0*c\0*k\0*e\0*t/g; 3051 3887 } else { 3052 my $type;3888 my ($type, $mime, $buf2, $buf3); 3053 3889 # read information from XMP file 3054 3890 my $raf = $$dirInfo{RAF} or return 0; 3055 3891 $raf->Read($buff, 256) or return 0; 3056 my ($buf2, $buf3, $double);3057 3892 ($buf2 = $buff) =~ tr/\0//d; # cheap conversion to UTF-8 3058 # remove leading comments if they exist ( ie. ImageIngester)3893 # remove leading comments if they exist (eg. ImageIngester) 3059 3894 while ($buf2 =~ /^\s*<!--/) { 3060 3895 # remove the comment if it is complete … … 3081 3916 } elsif ($buf2 =~ /^(\xff\xfe)(<\?xml|<rdf:RDF|<x(mp)?:x[ma]pmeta)/g) { 3082 3917 $fmt = 'v'; # UTF-16 or 32 II with BOM 3083 } elsif ($buf2 =~ /^(\xef\xbb\xbf)?(<\?xml|<rdf:RDF|<x(mp)?:x[ma]pmeta )/g) {3918 } elsif ($buf2 =~ /^(\xef\xbb\xbf)?(<\?xml|<rdf:RDF|<x(mp)?:x[ma]pmeta|<svg\b)/g) { 3084 3919 $fmt = 0; # UTF-8 with BOM or unknown encoding without BOM 3085 3920 } elsif ($buf2 =~ /^(\xfe\xff|\xff\xfe|\xef\xbb\xbf)(<\?xpacket begin=)/g) { … … 3090 3925 $bom = 1 if $1; 3091 3926 if ($2 eq '<?xml') { 3092 if ($buf2 =~ /<x(mp)?:x[ma]pmeta/) { 3927 if (defined $fmt and not $fmt and $buf2 =~ /^[^\n\r]*[\n\r]+<\?aid /s) { 3928 undef $$et{XmpValidate}; # don't validate INX 3929 if ($$et{XMP_CAPTURE}) { 3930 $et->Error("ExifTool does not yet support writing of INX files"); 3931 return 0; 3932 } 3933 $type = 'INX'; 3934 } elsif ($buf2 =~ /<x(mp)?:x[ma]pmeta/) { 3093 3935 $hasXMP = 1; 3094 3936 } else { 3095 # identify SVG images by DOCTYPE if available 3937 undef $$et{XmpValidate}; # don't validate XML 3938 # identify SVG images and PLIST files by DOCTYPE if available 3096 3939 if ($buf2 =~ /<!DOCTYPE\s+(\w+)/) { 3097 3940 if ($1 eq 'svg') { … … 3099 3942 } elsif ($1 eq 'plist') { 3100 3943 $type = 'PLIST'; 3944 } elsif ($1 eq 'REDXIF') { 3945 $type = 'RMD'; 3946 $mime = 'application/xml'; 3101 3947 } else { 3102 3948 return 0; … … 3106 3952 } elsif ($buf2 =~ /<rdf:RDF/) { 3107 3953 $isRDF = 1; 3954 } elsif ($buf2 =~ /<plist[\s>]/) { 3955 $type = 'PLIST'; 3108 3956 } 3109 if ($isSVG and $ exifTool->{XMP_CAPTURE}) {3110 $e xifTool->Error("ExifTool does not yet support writing of SVG images");3957 if ($isSVG and $$et{XMP_CAPTURE}) { 3958 $et->Error("ExifTool does not yet support writing of SVG images"); 3111 3959 return 0; 3112 3960 } … … 3118 3966 if ($buff =~ /^\0\0/) { 3119 3967 $fmt = 'N'; # UTF-32 MM with or without BOM 3120 } elsif ($buff =~ /^..\0\0/ ) {3968 } elsif ($buff =~ /^..\0\0/s) { 3121 3969 $fmt = 'V'; # UTF-32 II with or without BOM 3122 3970 } elsif (not $fmt) { 3123 3971 if ($buff =~ /^\0/) { 3124 3972 $fmt = 'n'; # UTF-16 MM without BOM 3125 } elsif ($buff =~ /^.\0/ ) {3973 } elsif ($buff =~ /^.\0/s) { 3126 3974 $fmt = 'v'; # UTF-16 II without BOM 3127 3975 } 3128 3976 } 3129 3977 } 3130 $raf->Seek(0, 2) or return 0; 3131 my $size = $raf->Tell() or return 0; 3132 $raf->Seek(0, 0) or return 0; 3133 $raf->Read($buff, $size) == $size or return 0; 3134 # decode the first layer of double-encoded UTF text 3135 if ($double) { 3136 $buff = substr($buff, length $double); # remove leading BOM 3137 Image::ExifTool::SetWarning(undef); # clear old warning 3138 local $SIG{'__WARN__'} = \&Image::ExifTool::SetWarning; 3139 my $tmp; 3140 # assume that character data has been re-encoded in UTF, so re-pack 3141 # as characters and look for warnings indicating a false assumption 3142 if ($double eq "\xef\xbb\xbf") { 3143 require Image::ExifTool::Charset; 3144 my $uni = Image::ExifTool::Charset::Decompose(undef,$buff,'UTF8'); 3145 $tmp = pack('C*', @$uni); 3146 } else { 3147 my $fmt = ($double eq "\xfe\xff") ? 'n' : 'v'; 3148 $tmp = pack('C*', unpack("$fmt*",$buff)); 3149 } 3150 if (Image::ExifTool::GetWarning()) { 3151 $exifTool->Warn('Superfluous BOM at start of XMP'); 3152 } else { 3153 $exifTool->Warn('XMP is double UTF-encoded'); 3154 $buff = $tmp; # use the decoded XMP 3155 } 3156 $size = length $buff; 3157 } 3158 $dataPt = \$buff; 3159 $dirStart = 0; 3160 $dirLen = $dataLen = $size; 3161 unless ($type) { 3978 my $size; 3979 if ($type) { 3980 if ($type eq 'PLIST') { 3981 my $ext = $$et{FILE_EXT}; 3982 $type = $ext if $ext and $ext eq 'MODD'; 3983 $tagTablePtr = GetTagTable('Image::ExifTool::PLIST::Main'); 3984 $$dirInfo{XMPParseOpts}{FoundProc} = \&Image::ExifTool::PLIST::FoundTag; 3985 } 3986 } else { 3162 3987 if ($isSVG) { 3163 3988 $type = 'SVG'; 3164 3989 } elsif ($isXML and not $hasXMP and not $isRDF) { 3165 3990 $type = 'XML'; 3166 } 3167 } 3168 $exifTool->SetFileType($type); 3169 } 3170 3171 # take substring if necessary 3172 if ($dataLen != $dirStart + $dirLen) { 3173 $buff = substr($$dataPt, $dirStart, $dirLen); 3991 my $ext = $$et{FILE_EXT}; 3992 $type = $ext if $ext and $ext eq 'COS'; # recognize COS by extension 3993 } 3994 } 3995 $et->SetFileType($type, $mime); 3996 3997 my $fast = $et->Options('FastScan'); 3998 return 1 if $fast and $fast == 3; 3999 4000 if ($type and $type eq 'INX') { 4001 # brute force search for first XMP packet in INX file 4002 # start: '<![CDATA[<?xpacket begin' (24 bytes) 4003 # end: '<?xpacket end="r"?>]]>' (22 bytes) 4004 $raf->Seek(0, 0) or return 0; 4005 $raf->Read($buff, 65536) or return 1; 4006 for (;;) { 4007 last if $buff =~ /<!\[CDATA\[<\?xpacket begin/g; 4008 $raf->Read($buf2, 65536) or return 1; 4009 $buff = substr($buff, -24) . $buf2; 4010 } 4011 $buff = substr($buff, pos($buff) - 15); # (discard '<![CDATA[' and before) 4012 for (;;) { 4013 last if $buff =~ /<\?xpacket end="[rw]"\?>\]\]>/g; 4014 my $n = length $buff; 4015 $raf->Read($buf2, 65536) or $et->Warn('Missing xpacket end'), return 1; 4016 $buff .= $buf2; 4017 pos($buff) = $n - 22; # don't miss end pattern if it was split 4018 } 4019 $size = pos($buff) - 3; # (discard ']]>' and after) 4020 $buff = substr($buff, 0, $size); 4021 } else { 4022 # read the entire file 4023 $raf->Seek(0, 2) or return 0; 4024 $size = $raf->Tell() or return 0; 4025 $raf->Seek(0, 0) or return 0; 4026 $raf->Read($buff, $size) == $size or return 0; 4027 } 3174 4028 $dataPt = \$buff; 3175 4029 $dirStart = 0; 4030 $dirLen = $dataLen = $size; 3176 4031 } 3177 # extract XMP as a block if specified 3178 if (($exifTool->{REQ_TAG_LOOKUP}{xmp} or $exifTool->{OPTIONS}{Binary}) and not $isSVG) { 3179 $exifTool->FoundTag('XMP', substr($$dataPt, $dirStart, $dirLen)); 4032 4033 # decode the first layer of double-encoded UTF text (if necessary) 4034 if ($double) { 4035 my ($buf2, $fmt); 4036 $buff = substr($$dataPt, $dirStart + length $double); # remove leading BOM 4037 Image::ExifTool::SetWarning(undef); # clear old warning 4038 local $SIG{'__WARN__'} = \&Image::ExifTool::SetWarning; 4039 # assume that character data has been re-encoded in UTF, so re-pack 4040 # as characters and look for warnings indicating a false assumption 4041 if ($double eq "\xef\xbb\xbf") { 4042 require Image::ExifTool::Charset; 4043 my $uni = Image::ExifTool::Charset::Decompose(undef,$buff,'UTF8'); 4044 $buf2 = pack('C*', @$uni); 4045 } else { 4046 if (length($double) == 2) { 4047 $fmt = ($double eq "\xfe\xff") ? 'n' : 'v'; 4048 } else { 4049 $fmt = ($double eq "\0\0\xfe\xff") ? 'N' : 'V'; 4050 } 4051 $buf2 = pack('C*', unpack("$fmt*",$buff)); 4052 } 4053 if (Image::ExifTool::GetWarning()) { 4054 $et->Warn('Superfluous BOM at start of XMP'); 4055 $dataPt = \$buff; # use XMP with the BOM removed 4056 } else { 4057 $et->Warn('XMP is double UTF-encoded'); 4058 $dataPt = \$buf2; # use the decoded XMP 4059 } 4060 $dirStart = 0; 4061 $dirLen = $dataLen = length $$dataPt; 3180 4062 } 3181 if ($exifTool->Options('Verbose') and not $exifTool->{XMP_CAPTURE}) { 3182 $exifTool->VerboseDir($isSVG ? 'SVG' : 'XMP', 0, $dirLen); 4063 4064 # extract XMP/XML as a block if specified 4065 my $blockName = $$dirInfo{BlockInfo} ? $$dirInfo{BlockInfo}{Name} : 'XMP'; 4066 if (($$et{REQ_TAG_LOOKUP}{lc $blockName} or ($$et{TAGS_FROM_FILE} and 4067 not $$et{EXCL_TAG_LOOKUP}{lc $blockName})) and 4068 (($$et{FileType} eq 'XMP' and $blockName eq 'XMP') or 4069 ($$dirInfo{DirName} and $$dirInfo{DirName} eq $blockName))) 4070 { 4071 $et->FoundTag($$dirInfo{BlockInfo} || 'XMP', substr($$dataPt, $dirStart, $dirLen)); 4072 } 4073 4074 $tagTablePtr or $tagTablePtr = GetTagTable('Image::ExifTool::XMP::Main'); 4075 if ($et->Options('Verbose') and not $$et{XMP_CAPTURE}) { 4076 my $dirType = $isSVG ? 'SVG' : $$tagTablePtr{GROUPS}{1}; 4077 $et->VerboseDir($dirType, 0, $dirLen); 3183 4078 } 3184 4079 # … … 3186 4081 # 3187 4082 my $begin = '<?xpacket begin='; 4083 my $dirEnd = $dirStart + $dirLen; 3188 4084 pos($$dataPt) = $dirStart; 3189 delete $$e xifTool{XMP_IS_XML};3190 delete $$e xifTool{XMP_IS_SVG};4085 delete $$et{XMP_IS_XML}; 4086 delete $$et{XMP_IS_SVG}; 3191 4087 if ($isXML or $isRDF) { 3192 $$e xifTool{XMP_IS_XML} = $isXML;3193 $$e xifTool{XMP_IS_SVG} = $isSVG;3194 $$e xifTool{XMP_NO_XPACKET} = 1 + $bom;4088 $$et{XMP_IS_XML} = $isXML; 4089 $$et{XMP_IS_SVG} = $isSVG; 4090 $$et{XMP_NO_XPACKET} = 1 + $bom; 3195 4091 } elsif ($$dataPt =~ /\G\Q$begin\E/gc) { 3196 delete $$exifTool{XMP_NO_XPACKET}; 3197 } elsif ($$dataPt =~ /<x(mp)?:x[ma]pmeta/gc) { 3198 $$exifTool{XMP_NO_XPACKET} = 1 + $bom; 4092 delete $$et{XMP_NO_XPACKET}; 4093 } elsif ($$dataPt =~ /<x(mp)?:x[ma]pmeta/gc and 4094 pos($$dataPt) > $dirStart and pos($$dataPt) < $dirEnd) 4095 { 4096 $$et{XMP_NO_XPACKET} = 1 + $bom; 3199 4097 } else { 3200 delete $$e xifTool{XMP_NO_XPACKET};4098 delete $$et{XMP_NO_XPACKET}; 3201 4099 # check for UTF-16 encoding (insert one \0 between characters) 3202 4100 $begin = join "\0", split //, $begin; 3203 4101 # must reset pos because it was killed by previous unsuccessful //g match 3204 4102 pos($$dataPt) = $dirStart; 3205 if ($$dataPt =~ /\G(\0)?\Q$begin\E\0./ g) {4103 if ($$dataPt =~ /\G(\0)?\Q$begin\E\0./sg) { 3206 4104 # validate byte ordering by checking for U+FEFF character 3207 4105 if ($1) { … … 3215 4113 $begin =~ s/\0/\0\0\0/g; 3216 4114 pos($$dataPt) = $dirStart; 3217 if ($$dataPt !~ /\G(\0\0\0)?\Q$begin\E\0\0\0./ g) {4115 if ($$dataPt !~ /\G(\0\0\0)?\Q$begin\E\0\0\0./sg) { 3218 4116 $fmt = 0; # set format to zero as indication we didn't find encoded XMP 3219 4117 } elsif ($1) { … … 3224 4122 } 3225 4123 } 3226 defined $fmt or $e xifTool->Warn('XMP character encoding error');4124 defined $fmt or $et->Warn('XMP character encoding error'); 3227 4125 } 3228 4126 if ($fmt) { 3229 4127 # trim if necessary to avoid converting non-UTF data 3230 if ($dirStart or $dir Len != length($$dataPt) - $dirStart) {4128 if ($dirStart or $dirEnd != length($$dataPt)) { 3231 4129 $buff = substr($$dataPt, $dirStart, $dirLen); 3232 4130 $dataPt = \$buff; … … 3241 4139 $dirStart = 0; 3242 4140 $dirLen = length $$dataPt; 4141 $dirEnd = $dirStart + $dirLen; 3243 4142 } 3244 # initialize namespace translation3245 $xlatNamespace = \%stdXlatNS;3246 3247 4143 # avoid scanning for XMP later in case ScanForXMP is set 3248 $$e xifTool{FoundXMP} = 1;4144 $$et{FoundXMP} = 1 if $tagTablePtr eq \%Image::ExifTool::XMP::Main; 3249 4145 3250 4146 # set XMP parsing options 3251 $$exifTool{XMPParseOpts} = $$dirInfo{XMPParseOpts}; 4147 $$et{XMPParseOpts} = $$dirInfo{XMPParseOpts}; 4148 4149 # ignore any specified properties (XML hack) 4150 if ($$dirInfo{IgnoreProp}) { 4151 %ignoreProp = %{$$dirInfo{IgnoreProp}}; 4152 } else { 4153 undef %ignoreProp; 4154 } 3252 4155 3253 4156 # need to preserve list indices to be able to handle multi-dimensional lists 3254 $$exifTool{NO_LIST} = 1 if $exifTool->Options('Struct'); 4157 my $keepFlat; 4158 if ($$et{OPTIONS}{Struct}) { 4159 if ($$et{OPTIONS}{Struct} eq '2') { 4160 $keepFlat = 1; # preserve flattened tags 4161 # setting NO_LIST to 0 combines list items in a TAG_EXTRA "NoList" element 4162 # to allow them to be re-listed later if necessary. A "NoListDel" element 4163 # is also created for tags that wouldn't have existed. 4164 $$et{NO_LIST} = 0; 4165 } else { 4166 $$et{NO_LIST} = 1; 4167 } 4168 } 4169 4170 # don't generate structures if this isn't real XMP 4171 $$et{NO_STRUCT} = 1 if $$dirInfo{BlockInfo} or $$dirInfo{NoStruct}; 3255 4172 3256 4173 # parse the XMP 3257 $tagTablePtr or $tagTablePtr = GetTagTable('Image::ExifTool::XMP::Main'); 3258 $rtnVal = 1 if ParseXMPElement($exifTool, $tagTablePtr, $dataPt, $dirStart); 4174 if (ParseXMPElement($et, $tagTablePtr, $dataPt, $dirStart, $dirEnd)) { 4175 $rtnVal = 1; 4176 } elsif ($$dirInfo{DirName} and $$dirInfo{DirName} eq 'XMP') { 4177 # if DirName was 'XMP' we expect well-formed XMP, so set Warning since it wasn't 4178 # (but allow empty XMP as written by some PhaseOne cameras) 4179 my $xmp = substr($$dataPt, $dirStart, $dirLen); 4180 if ($xmp =~ /^ *\0*$/) { 4181 $et->Warn('Invalid XMP'); 4182 } else { 4183 $et->Warn('Empty XMP',1); 4184 $rtnVal = 1; 4185 } 4186 } 4187 delete $$et{NO_STRUCT}; 3259 4188 3260 4189 # return DataPt if successful in case we want it for writing … … 3262 4191 3263 4192 # restore structures if necessary 3264 if ($$e xifTool{IsStruct}) {4193 if ($$et{IsStruct}) { 3265 4194 require 'Image/ExifTool/XMPStruct.pl'; 3266 RestoreStruct($e xifTool);3267 delete $$e xifTool{IsStruct};4195 RestoreStruct($et, $keepFlat); 4196 delete $$et{IsStruct}; 3268 4197 } 3269 4198 # reset NO_LIST flag (must do this _after_ RestoreStruct() above) 3270 delete $$exifTool{NO_LIST}; 3271 3272 undef %curNS; 4199 delete $$et{NO_LIST}; 4200 delete $$et{XMPParseOpts}; 4201 delete $$et{curURI}; 4202 delete $$et{curNS}; 4203 delete $$et{xlatNS}; 4204 delete $$et{definedNS}; 4205 3273 4206 return $rtnVal; 3274 4207 } … … 3296 4229 =head1 AUTHOR 3297 4230 3298 Copyright 2003-20 11, Phil Harvey (phil at owl.phy.queensu.ca)4231 Copyright 2003-2021, Phil Harvey (philharvey66 at gmail.com) 3299 4232 3300 4233 This library is free software; you can redistribute it and/or modify it
Note:
See TracChangeset
for help on using the changeset viewer.