MWG.pm 30 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753
  1. #------------------------------------------------------------------------------
  2. # File: MWG.pm
  3. #
  4. # Description: Metadata Working Group support
  5. #
  6. # Revisions: 2009/10/21 - P. Harvey Created
  7. #
  8. # References: 1) http://www.metadataworkinggroup.org/
  9. #------------------------------------------------------------------------------
  10. package Image::ExifTool::MWG;
  11. use strict;
  12. use vars qw($VERSION);
  13. use Image::ExifTool qw(:DataAccess :Utils);
  14. use Image::ExifTool::Exif;
  15. use Image::ExifTool::XMP;
  16. $VERSION = '1.16';
  17. sub RecoverTruncatedIPTC($$$);
  18. sub ListToString($);
  19. sub StringToList($$);
  20. sub OverwriteStringList($$$$);
  21. my $mwgLoaded; # flag set if we alreaded Load()ed the MWG tags
  22. # MWG Composite tags
  23. %Image::ExifTool::MWG::Composite = (
  24. GROUPS => { 0 => 'Composite', 1 => 'MWG', 2 => 'Image' },
  25. VARS => { NO_ID => 1 },
  26. NOTES => q{
  27. The table below lists special Composite tags which are used to access other
  28. tags based on the MWG 2.0 recommendations. These tags are only accessible
  29. when explicitly loaded, but this is done automatically by the exiftool
  30. application if MWG is specified as a group for any tag on the command line,
  31. or manually with the C<-use MWG> option. Via the API, the MWG Composite
  32. tags are loaded by calling "C<Image::ExifTool::MWG::Load()>".
  33. When reading, the value of each MWG tag is B<Derived From> the specified
  34. tags based on the MWG guidelines. When writing, the appropriate associated
  35. tags are written. The value of the IPTCDigest tag is updated automatically
  36. when the IPTC is changed if either the IPTCDigest tag didn't exist
  37. beforehand or its value agreed with the original IPTC digest (indicating
  38. that the XMP is synchronized with the IPTC). IPTC information is written
  39. only if the original file contained IPTC.
  40. Loading the MWG module activates "strict MWG conformance mode", which has
  41. the effect of causing EXIF, IPTC and XMP in non-standard locations to be
  42. ignored when reading, as per the MWG recommendations. Instead, a "Warning"
  43. tag is generated when non-standard metadata is encountered. This feature
  44. may be disabled by setting C<$Image::ExifTool::MWG::strict = 0> in the
  45. L<ExifTool config file|../config.html> (or from your Perl script when using the API). Note
  46. that the behaviour when writing is not changed: ExifTool always creates new
  47. records only in the standard location, but writes new tags to any
  48. EXIF/IPTC/XMP records that exist.
  49. Contrary to the EXIF specification, the MWG recommends that EXIF "ASCII"
  50. string values be stored as UTF-8. To honour this, the exiftool application
  51. sets the default internal EXIF string encoding to "UTF8" when the MWG module
  52. is loaded, but via the API this must be done manually by setting the
  53. CharsetEXIF option.
  54. A complication of the MWG specification is that although the MWG:Creator
  55. property may consist of multiple values, the associated EXIF tag
  56. (EXIF:Artist) is only a simple string. To resolve this discrepancy the MWG
  57. recommends a technique which allows a list of values to be stored in a
  58. string by using a semicolon-space separator (with quotes around values if
  59. necessary). When the MWG module is loaded, ExifTool automatically
  60. implements this policy and changes EXIF:Artist to a list-type tag.
  61. },
  62. Keywords => {
  63. Flags => ['Writable','List'],
  64. Desire => {
  65. 0 => 'IPTC:Keywords', # (64-character limit)
  66. 1 => 'XMP-dc:Subject',
  67. 2 => 'CurrentIPTCDigest',
  68. 3 => 'IPTCDigest',
  69. },
  70. RawConv => q{
  71. return $val[1] if not defined $val[2] or (defined $val[1] and
  72. (not defined $val[3] or $val[2] eq $val[3]));
  73. return Image::ExifTool::MWG::RecoverTruncatedIPTC($val[0], $val[1], 64);
  74. },
  75. DelCheck => 'Image::ExifTool::MWG::ReconcileIPTCDigest($self)',
  76. WriteCheck => 'Image::ExifTool::MWG::ReconcileIPTCDigest($self)',
  77. WriteAlso => {
  78. # only write Keywords if IPTC exists (eg. set EditGroup option)
  79. 'IPTC:Keywords' => '$opts{EditGroup} = 1; $val',
  80. 'XMP-dc:Subject' => '$val',
  81. },
  82. },
  83. Description => {
  84. Writable => 1,
  85. Desire => {
  86. 0 => 'EXIF:ImageDescription',
  87. 1 => 'IPTC:Caption-Abstract', # (2000-character limit)
  88. 2 => 'XMP-dc:Description',
  89. 3 => 'CurrentIPTCDigest',
  90. 4 => 'IPTCDigest',
  91. },
  92. RawConv => q{
  93. return $val[0] if defined $val[0] and $val[0] !~ /^ *$/;
  94. return $val[2] if not defined $val[3] or (defined $val[2] and
  95. (not defined $val[4] or $val[3] eq $val[4]));
  96. return Image::ExifTool::MWG::RecoverTruncatedIPTC($val[1], $val[2], 2000);
  97. },
  98. DelCheck => 'Image::ExifTool::MWG::ReconcileIPTCDigest($self)',
  99. WriteCheck => 'Image::ExifTool::MWG::ReconcileIPTCDigest($self)',
  100. WriteAlso => {
  101. 'EXIF:ImageDescription' => '$val',
  102. 'IPTC:Caption-Abstract' => '$opts{EditGroup} = 1; $val',
  103. 'XMP-dc:Description' => '$val',
  104. },
  105. },
  106. DateTimeOriginal => {
  107. Description => 'Date/Time Original',
  108. Groups => { 2 => 'Time' },
  109. Notes => '"creation date of the intellectual content being shown" - MWG',
  110. Writable => 1,
  111. Desire => {
  112. 0 => 'EXIF:DateTimeOriginal',
  113. 1 => 'EXIF:SubSecTimeOriginal',
  114. 2 => 'IPTC:DateCreated',
  115. 3 => 'IPTC:TimeCreated',
  116. 4 => 'XMP-photoshop:DateCreated',
  117. 5 => 'CurrentIPTCDigest',
  118. 6 => 'IPTCDigest',
  119. },
  120. # must check for validity in RawConv to avoid hiding a same-named tag,
  121. # but IPTC dates use a ValueConv so we need to derive the value there
  122. RawConv => '(defined $val[0] or defined $val[2] or defined $val[4]) ? $val : undef',
  123. ValueConv => q{
  124. if (defined $val[0] and $val[0] !~ /^[: ]*$/) {
  125. return ($val[1] and $val[1] !~ /^ *$/) ? "$val[0].$val[1]" : $val[0];
  126. }
  127. return $val[4] if not defined $val[5] or (defined $val[4] and
  128. (not defined $val[6] or $val[5] eq $val[6]));
  129. return $val[3] ? "$val[2] $val[3]" : $val[2] if $val[2];
  130. return undef;
  131. },
  132. PrintConv => '$self->ConvertDateTime($val)',
  133. PrintConvInv => '$self->InverseDateTime($val,undef,1)',
  134. DelCheck => 'Image::ExifTool::MWG::ReconcileIPTCDigest($self)',
  135. WriteCheck => 'Image::ExifTool::MWG::ReconcileIPTCDigest($self)',
  136. WriteAlso => {
  137. # set EXIF date/time values according to PrintConv option instead
  138. # of defaulting to Type=ValueConv to allow reformatting to be applied
  139. 'EXIF:DateTimeOriginal' => 'delete $opts{Type}; $val',
  140. 'EXIF:SubSecTimeOriginal' => 'delete $opts{Type}; $val',
  141. 'IPTC:DateCreated' => '$opts{EditGroup} = 1; $val',
  142. 'IPTC:TimeCreated' => '$opts{EditGroup} = 1; $val',
  143. 'XMP-photoshop:DateCreated' => '$val',
  144. },
  145. },
  146. CreateDate => {
  147. Groups => { 2 => 'Time' },
  148. Notes => '"creation date of the digital representation" - MWG',
  149. Writable => 1,
  150. Desire => {
  151. 0 => 'EXIF:CreateDate',
  152. 1 => 'EXIF:SubSecTimeDigitized',
  153. 2 => 'IPTC:DigitalCreationDate',
  154. 3 => 'IPTC:DigitalCreationTime',
  155. 4 => 'XMP-xmp:CreateDate',
  156. 5 => 'CurrentIPTCDigest',
  157. 6 => 'IPTCDigest',
  158. },
  159. RawConv => '(defined $val[0] or defined $val[2] or defined $val[4]) ? $val : undef',
  160. ValueConv => q{
  161. if (defined $val[0] and $val[0] !~ /^[: ]*$/) {
  162. return ($val[1] and $val[1] !~ /^ *$/) ? "$val[0].$val[1]" : $val[0];
  163. }
  164. return $val[4] if not defined $val[5] or (defined $val[4] and
  165. (not defined $val[6] or $val[5] eq $val[6]));
  166. return $val[3] ? "$val[2] $val[3]" : $val[2] if $val[2];
  167. return undef;
  168. },
  169. PrintConv => '$self->ConvertDateTime($val)',
  170. PrintConvInv => '$self->InverseDateTime($val,undef,1)',
  171. DelCheck => 'Image::ExifTool::MWG::ReconcileIPTCDigest($self)',
  172. WriteCheck => 'Image::ExifTool::MWG::ReconcileIPTCDigest($self)',
  173. WriteAlso => {
  174. 'EXIF:CreateDate' => 'delete $opts{Type}; $val',
  175. 'EXIF:SubSecTimeDigitized' => 'delete $opts{Type}; $val',
  176. 'IPTC:DigitalCreationDate' => '$opts{EditGroup} = 1; $val',
  177. 'IPTC:DigitalCreationTime' => '$opts{EditGroup} = 1; $val',
  178. 'XMP-xmp:CreateDate' => '$val',
  179. },
  180. },
  181. ModifyDate => {
  182. Groups => { 2 => 'Time' },
  183. Notes => '"modification date of the digital image file" - MWG',
  184. Writable => 1,
  185. Desire => {
  186. 0 => 'EXIF:ModifyDate',
  187. 1 => 'EXIF:SubSecTime',
  188. 2 => 'XMP-xmp:ModifyDate',
  189. 3 => 'CurrentIPTCDigest',
  190. 4 => 'IPTCDigest',
  191. },
  192. RawConv => q{
  193. if (defined $val[0] and $val[0] !~ /^[: ]*$/) {
  194. return ($val[1] and $val[1] !~ /^ *$/) ? "$val[0].$val[1]" : $val[0];
  195. }
  196. return $val[2] if not defined $val[3] or not defined $val[4] or $val[3] eq $val[4];
  197. return undef;
  198. },
  199. PrintConv => '$self->ConvertDateTime($val)',
  200. PrintConvInv => '$self->InverseDateTime($val,undef,1)',
  201. # return empty string from check routines so this tag will never be set
  202. # (only WriteAlso tags are written), the only difference is a -v2 message
  203. DelCheck => '""',
  204. WriteCheck => '""',
  205. WriteAlso => {
  206. 'EXIF:ModifyDate' => 'delete $opts{Type}; $val',
  207. 'EXIF:SubSecTime' => 'delete $opts{Type}; $val',
  208. 'XMP-xmp:ModifyDate' => '$val',
  209. },
  210. },
  211. Orientation => {
  212. Writable => 1,
  213. Require => 'EXIF:Orientation',
  214. ValueConv => '$val',
  215. PrintConv => \%Image::ExifTool::Exif::orientation,
  216. DelCheck => '""',
  217. WriteCheck => '""',
  218. WriteAlso => {
  219. 'EXIF:Orientation' => '$val',
  220. },
  221. },
  222. Rating => {
  223. Writable => 1,
  224. Require => 'XMP-xmp:Rating',
  225. ValueConv => '$val',
  226. DelCheck => '""',
  227. WriteCheck => '""',
  228. WriteAlso => {
  229. 'XMP-xmp:Rating' => '$val',
  230. },
  231. },
  232. Copyright => {
  233. Groups => { 2 => 'Author' },
  234. Writable => 1,
  235. Desire => {
  236. 0 => 'EXIF:Copyright',
  237. 1 => 'IPTC:CopyrightNotice', # (128-character limit)
  238. 2 => 'XMP-dc:Rights',
  239. 3 => 'CurrentIPTCDigest',
  240. 4 => 'IPTCDigest',
  241. },
  242. RawConv => q{
  243. return $val[0] if defined $val[0] and $val[0] !~ /^ *$/;
  244. return $val[2] if not defined $val[3] or (defined $val[2] and
  245. (not defined $val[4] or $val[3] eq $val[4]));
  246. return Image::ExifTool::MWG::RecoverTruncatedIPTC($val[1], $val[2], 128);
  247. },
  248. DelCheck => 'Image::ExifTool::MWG::ReconcileIPTCDigest($self)',
  249. WriteCheck => 'Image::ExifTool::MWG::ReconcileIPTCDigest($self)',
  250. WriteAlso => {
  251. 'EXIF:Copyright' => '$val',
  252. 'IPTC:CopyrightNotice' => '$opts{EditGroup} = 1; $val',
  253. 'XMP-dc:Rights' => '$val',
  254. },
  255. },
  256. Creator => {
  257. Groups => { 2 => 'Author' },
  258. Flags => ['Writable','List'],
  259. Desire => {
  260. 0 => 'EXIF:Artist',
  261. 1 => 'IPTC:By-line', # (32-character limit)
  262. 2 => 'XMP-dc:Creator',
  263. 3 => 'CurrentIPTCDigest',
  264. 4 => 'IPTCDigest',
  265. },
  266. RawConv => q{
  267. return $val[0] if defined $val[0] and $val[0] !~ /^ *$/;
  268. return $val[2] if not defined $val[3] or (defined $val[2] and
  269. (not defined $val[4] or $val[3] eq $val[4]));
  270. return Image::ExifTool::MWG::RecoverTruncatedIPTC($val[1], $val[2], 32);
  271. },
  272. DelCheck => 'Image::ExifTool::MWG::ReconcileIPTCDigest($self)',
  273. WriteCheck => 'Image::ExifTool::MWG::ReconcileIPTCDigest($self)',
  274. WriteAlso => {
  275. 'EXIF:Artist' => '$val',
  276. 'IPTC:By-line' => '$opts{EditGroup} = 1; $val',
  277. 'XMP-dc:Creator' => '$val',
  278. },
  279. },
  280. Country => {
  281. Groups => { 2 => 'Location' },
  282. Writable => 1,
  283. Desire => {
  284. 0 => 'IPTC:Country-PrimaryLocationName', # (64-character limit)
  285. 1 => 'XMP-photoshop:Country',
  286. 2 => 'XMP-iptcExt:LocationShownCountryName',
  287. 3 => 'CurrentIPTCDigest',
  288. 4 => 'IPTCDigest',
  289. },
  290. RawConv => q{
  291. my $xmpVal = $val[2] || $val[1];
  292. return $xmpVal if not defined $val[3] or (defined $xmpVal and
  293. (not defined $val[4] or $val[3] eq $val[4]));
  294. return Image::ExifTool::MWG::RecoverTruncatedIPTC($val[0], $xmpVal, 64);
  295. },
  296. DelCheck => 'Image::ExifTool::MWG::ReconcileIPTCDigest($self)',
  297. WriteCheck => 'Image::ExifTool::MWG::ReconcileIPTCDigest($self)',
  298. WriteAlso => {
  299. 'IPTC:Country-PrimaryLocationName' => '$opts{EditGroup} = 1; $val',
  300. 'XMP-photoshop:Country' => '$val', # (legacy)
  301. 'XMP-iptcExt:LocationShownCountryName' => '$val',
  302. },
  303. },
  304. State => {
  305. Groups => { 2 => 'Location' },
  306. Writable => 1,
  307. Desire => {
  308. 0 => 'IPTC:Province-State', # (32-character limit)
  309. 1 => 'XMP-photoshop:State',
  310. 2 => 'XMP-iptcExt:LocationShownProvinceState',
  311. 3 => 'CurrentIPTCDigest',
  312. 4 => 'IPTCDigest',
  313. },
  314. RawConv => q{
  315. my $xmpVal = $val[2] || $val[1];
  316. return $xmpVal if not defined $val[3] or (defined $xmpVal and
  317. (not defined $val[4] or $val[3] eq $val[4]));
  318. return Image::ExifTool::MWG::RecoverTruncatedIPTC($val[0], $xmpVal, 32);
  319. },
  320. DelCheck => 'Image::ExifTool::MWG::ReconcileIPTCDigest($self)',
  321. WriteCheck => 'Image::ExifTool::MWG::ReconcileIPTCDigest($self)',
  322. WriteAlso => {
  323. 'IPTC:Province-State' => '$opts{EditGroup} = 1; $val',
  324. 'XMP-photoshop:State' => '$val', # (legacy)
  325. 'XMP-iptcExt:LocationShownProvinceState' => '$val',
  326. },
  327. },
  328. City => {
  329. Groups => { 2 => 'Location' },
  330. Writable => 1,
  331. Desire => {
  332. 0 => 'IPTC:City', # (32-character limit)
  333. 1 => 'XMP-photoshop:City',
  334. 2 => 'XMP-iptcExt:LocationShownCity',
  335. 3 => 'CurrentIPTCDigest',
  336. 4 => 'IPTCDigest',
  337. },
  338. RawConv => q{
  339. my $xmpVal = $val[2] || $val[1];
  340. return $xmpVal if not defined $val[3] or (defined $xmpVal and
  341. (not defined $val[4] or $val[3] eq $val[4]));
  342. return Image::ExifTool::MWG::RecoverTruncatedIPTC($val[0], $xmpVal, 32);
  343. },
  344. DelCheck => 'Image::ExifTool::MWG::ReconcileIPTCDigest($self)',
  345. WriteCheck => 'Image::ExifTool::MWG::ReconcileIPTCDigest($self)',
  346. WriteAlso => {
  347. 'IPTC:City' => '$opts{EditGroup} = 1; $val',
  348. 'XMP-photoshop:City' => '$val', # (legacy)
  349. 'XMP-iptcExt:LocationShownCity' => '$val',
  350. },
  351. },
  352. Location => {
  353. Groups => { 2 => 'Location' },
  354. Writable => 1,
  355. Desire => {
  356. 0 => 'IPTC:Sub-location', # (32-character limit)
  357. 1 => 'XMP-iptcCore:Location',
  358. 2 => 'XMP-iptcExt:LocationShownSublocation',
  359. 3 => 'CurrentIPTCDigest',
  360. 4 => 'IPTCDigest',
  361. },
  362. RawConv => q{
  363. my $xmpVal = $val[2] || $val[1];
  364. return $xmpVal if not defined $val[3] or (defined $xmpVal and
  365. (not defined $val[4] or $val[3] eq $val[4]));
  366. return Image::ExifTool::MWG::RecoverTruncatedIPTC($val[0], $xmpVal, 32);
  367. },
  368. DelCheck => 'Image::ExifTool::MWG::ReconcileIPTCDigest($self)',
  369. WriteCheck => 'Image::ExifTool::MWG::ReconcileIPTCDigest($self)',
  370. WriteAlso => {
  371. 'IPTC:Sub-location' => '$opts{EditGroup} = 1; $val',
  372. 'XMP-iptcCore:Location' => '$val', # (legacy)
  373. 'XMP-iptcExt:LocationShownSublocation' => '$val',
  374. },
  375. },
  376. );
  377. # MWG XMP structures
  378. my %sExtensions = (
  379. STRUCT_NAME => 'MWG Extensions',
  380. NAMESPACE => undef, # variable namespace
  381. NOTES => q{
  382. This structure may contain any top-level XMP tags, but none have been
  383. pre-defined in ExifTool. Since no flattened tags have been pre-defined,
  384. RegionExtensions is writable only as a structure (eg.
  385. C<{xmp-dc:creator=me,rating=5}>). Fields for this structure are identified
  386. using the standard ExifTool tag name (with optional leading group name,
  387. and/or trailing language code, and/or trailing C<#> symbol to disable print
  388. conversion).
  389. },
  390. );
  391. my %sRegionStruct = (
  392. STRUCT_NAME => 'MWG RegionStruct',
  393. NAMESPACE => 'mwg-rs',
  394. Area => { Struct => \%Image::ExifTool::XMP::sArea },
  395. Type => {
  396. PrintConv => {
  397. Face => 'Face',
  398. Pet => 'Pet',
  399. Focus => 'Focus',
  400. BarCode => 'BarCode',
  401. },
  402. },
  403. Name => { },
  404. Description => { },
  405. FocusUsage => {
  406. PrintConv => {
  407. EvaluatedUsed => 'Evaluated, Used',
  408. EvaluatedNotUsed => 'Evaluated, Not Used',
  409. NotEvaluatedNotUsed => 'Not Evaluated, Not Used',
  410. },
  411. },
  412. BarCodeValue=> { },
  413. Extensions => { Struct => \%sExtensions },
  414. Rotation => { # (observed in LR6 XMP)
  415. Writable => 'real',
  416. Notes => 'RegionsRegionListRotation, not part of MWG 2.0 spec',
  417. },
  418. seeAlso => { Namespace => 'rdfs', Resource => 1 },
  419. );
  420. my %sKeywordStruct;
  421. %sKeywordStruct = (
  422. STRUCT_NAME => 'MWG KeywordStruct',
  423. NAMESPACE => 'mwg-kw',
  424. Keyword => { },
  425. Applied => { Writable => 'boolean' },
  426. Children => { Struct => \%sKeywordStruct, List => 'Bag' },
  427. );
  428. # MWG 2.0 XMP region namespace tags
  429. %Image::ExifTool::MWG::Regions = (
  430. %Image::ExifTool::XMP::xmpTableDefaults,
  431. GROUPS => { 0 => 'XMP', 1 => 'XMP-mwg-rs', 2 => 'Image' },
  432. NAMESPACE => 'mwg-rs',
  433. NOTES => q{
  434. Image region metadata defined by the MWG 2.0 specification. These tags
  435. belong to the ExifTool XMP-mwg-rs group, and as such they may be accessed
  436. without the need to load the MWG Composite tags above. See
  437. L<http://www.metadataworkinggroup.org/> for the official specification.
  438. },
  439. Regions => {
  440. Name => 'RegionInfo',
  441. FlatName => 'Region',
  442. Struct => {
  443. STRUCT_NAME => 'MWG RegionInfo',
  444. NAMESPACE => 'mwg-rs',
  445. RegionList => {
  446. FlatName => 'Region',
  447. Struct => \%sRegionStruct,
  448. List => 'Bag',
  449. },
  450. AppliedToDimensions => { Struct => \%Image::ExifTool::XMP::sDimensions },
  451. },
  452. },
  453. RegionsRegionList => { Flat => 1, Name => 'RegionList' },
  454. );
  455. # MWG 2.0 XMP hierarchical keyword namespace tags
  456. %Image::ExifTool::MWG::Keywords = (
  457. %Image::ExifTool::XMP::xmpTableDefaults,
  458. GROUPS => { 0 => 'XMP', 1 => 'XMP-mwg-kw', 2 => 'Image' },
  459. NAMESPACE => 'mwg-kw',
  460. NOTES => q{
  461. Hierarchical keywords metadata defined by the MWG 2.0 specification. These
  462. tags belong to the ExifTool XMP-mwg-kw group. ExifTool unrolls keyword
  463. structures to an arbitrary depth of 6 to allow individual levels to be
  464. accessed with different tag names, and to avoid infinite recursion. See
  465. L<http://www.metadataworkinggroup.org/> for the official specification.
  466. },
  467. # arbitrarily define only the first 6 levels of the keyword hierarchy
  468. Keywords => {
  469. Name => 'KeywordInfo',
  470. Struct => {
  471. STRUCT_NAME => 'MWG KeywordInfo',
  472. NAMESPACE => 'mwg-kw',
  473. Hierarchy => { Struct => \%sKeywordStruct, List => 'Bag' },
  474. },
  475. },
  476. KeywordsHierarchy => { Name => 'HierarchicalKeywords', Flat => 1 },
  477. KeywordsHierarchyKeyword => { Name => 'HierarchicalKeywords1', Flat => 1 },
  478. KeywordsHierarchyApplied => { Name => 'HierarchicalKeywords1Applied', Flat => 1 },
  479. KeywordsHierarchyChildren => { Name => 'HierarchicalKeywords1Children', Flat => 1 },
  480. KeywordsHierarchyChildrenKeyword => { Name => 'HierarchicalKeywords2', Flat => 1 },
  481. KeywordsHierarchyChildrenApplied => { Name => 'HierarchicalKeywords2Applied', Flat => 1 },
  482. KeywordsHierarchyChildrenChildren => { Name => 'HierarchicalKeywords2Children', Flat => 1 },
  483. KeywordsHierarchyChildrenChildrenKeyword => { Name => 'HierarchicalKeywords3', Flat => 1 },
  484. KeywordsHierarchyChildrenChildrenApplied => { Name => 'HierarchicalKeywords3Applied', Flat => 1 },
  485. KeywordsHierarchyChildrenChildrenChildren => { Name => 'HierarchicalKeywords3Children', Flat => 1 },
  486. KeywordsHierarchyChildrenChildrenChildrenKeyword => { Name => 'HierarchicalKeywords4', Flat => 1 },
  487. KeywordsHierarchyChildrenChildrenChildrenApplied => { Name => 'HierarchicalKeywords4Applied', Flat => 1 },
  488. KeywordsHierarchyChildrenChildrenChildrenChildren => { Name => 'HierarchicalKeywords4Children', Flat => 1 },
  489. KeywordsHierarchyChildrenChildrenChildrenChildrenKeyword => { Name => 'HierarchicalKeywords5', Flat => 1 },
  490. KeywordsHierarchyChildrenChildrenChildrenChildrenApplied => { Name => 'HierarchicalKeywords5Applied', Flat => 1 },
  491. KeywordsHierarchyChildrenChildrenChildrenChildrenChildren => { Name => 'HierarchicalKeywords5Children', Flat => 1, NoSubStruct => 1 }, # break infinite recursion
  492. KeywordsHierarchyChildrenChildrenChildrenChildrenChildrenKeyword => { Name => 'HierarchicalKeywords6', Flat => 1 },
  493. KeywordsHierarchyChildrenChildrenChildrenChildrenChildrenApplied => { Name => 'HierarchicalKeywords6Applied', Flat => 1 },
  494. );
  495. # MWG 2.0 XMP collections namespace tags
  496. %Image::ExifTool::MWG::Collections = (
  497. %Image::ExifTool::XMP::xmpTableDefaults,
  498. GROUPS => { 0 => 'XMP', 1 => 'XMP-mwg-coll', 2 => 'Image' },
  499. NAMESPACE => 'mwg-coll',
  500. NOTES => q{
  501. Collections metadata defined by the MWG 2.0 specification. These tags
  502. belong to the ExifTool XMP-mwg-coll group. See
  503. L<http://www.metadataworkinggroup.org/> for the official specification.
  504. },
  505. Collections => {
  506. FlatName => '',
  507. List => 'Bag',
  508. Struct => {
  509. STRUCT_NAME => 'MWG CollectionInfo',
  510. NAMESPACE => 'mwg-coll',
  511. CollectionName => { },
  512. CollectionURI => { },
  513. },
  514. },
  515. );
  516. #------------------------------------------------------------------------------
  517. # Load the MWG Composite tags
  518. sub Load()
  519. {
  520. return if $mwgLoaded;
  521. # add our composite tags
  522. Image::ExifTool::AddCompositeTags('Image::ExifTool::MWG');
  523. # must also add to lookup so we can write them
  524. # (since MWG tags aren't in the tag lookup by default)
  525. Image::ExifTool::AddTagsToLookup(\%Image::ExifTool::MWG::Composite,
  526. 'Image::ExifTool::Composite');
  527. # modify EXIF:Artist to behave as a list-type tag
  528. my $artist = $Image::ExifTool::Exif::Main{0x13b};
  529. $$artist{List} = 1;
  530. $$artist{IsOverwriting} = \&OverwriteStringList;
  531. $$artist{RawConv} = \&StringToList;
  532. # enable MWG strict mode if not set already
  533. # (causes non-standard EXIF, IPTC and XMP to be ignored)
  534. $Image::ExifTool::MWG::strict = 1 unless defined $Image::ExifTool::MWG::strict;
  535. $mwgLoaded = 1;
  536. }
  537. #------------------------------------------------------------------------------
  538. # Change a list of values to a string using MWG rules
  539. # Inputs: 0)reference to list of values
  540. # Returns: string of values (and may reformat list entries)
  541. sub ListToString($)
  542. {
  543. my $vals = shift;
  544. foreach (@$vals) {
  545. # double all quotes in value and quote the value if it begins
  546. # with a quote or contains a semicolon-space separator
  547. if (/^"/ or /; /) {
  548. s/"/""/g; # double all quotes
  549. $_ = qq{"$_"}; # quote the value
  550. }
  551. }
  552. return join('; ', @$vals);
  553. }
  554. #------------------------------------------------------------------------------
  555. # Change a string value to a list of values using MWG rules
  556. # Inputs: 0) string of values, 1) ExifTool ref
  557. # Returns: value or list reference if more than one value
  558. # Notes: Sets Warning tag on error
  559. sub StringToList($$)
  560. {
  561. my ($str, $et) = @_;
  562. my (@vals, $inQuotes);
  563. my @t = split '; ', $str, -1;
  564. foreach (@t) {
  565. my $wasQuotes = $inQuotes;
  566. $inQuotes = 1 if not $inQuotes and s/^"//;
  567. if ($inQuotes) {
  568. # remove the last quote and reset the inQuotes flag if
  569. # the value ended in an odd number of quotes
  570. $inQuotes = 0 if s/((^|[^"])("")*)"$/$1/;
  571. s/""/"/g; # un-double the contained quotes
  572. }
  573. if ($wasQuotes) {
  574. # previous separator was quoted, so concatinate with previous value
  575. $vals[-1] .= '; ' . $_;
  576. } else {
  577. push @vals, $_;
  578. }
  579. }
  580. $et->Warn('Incorrectly quoted MWG string-list value') if $inQuotes;
  581. return @vals > 1 ? \@vals : $vals[0];
  582. }
  583. #------------------------------------------------------------------------------
  584. # Handle logic for overwriting EXIF string-type list tag
  585. # Inputs: 0) new value hash ref, 1) new value hash ref,
  586. # 2) old string value (or undef if it didn't exist), 3) new value ref
  587. # Returns: 1 and sets the new value for the tag
  588. sub OverwriteStringList($$$$)
  589. {
  590. local $_;
  591. my ($et, $nvHash, $val, $newValuePt) = @_;
  592. my (@new, $delIndex);
  593. if ($$nvHash{DelValue} and defined $val) {
  594. # preserve specified old values
  595. my $old = StringToList($val, $et);
  596. my @old = ref $old eq 'ARRAY' ? @$old : $old;
  597. if (@{$$nvHash{DelValue}}) {
  598. my %del;
  599. $del{$_} = 1 foreach @{$$nvHash{DelValue}};
  600. foreach (@old) {
  601. $del{$_} or push(@new, $_), next;
  602. $delIndex or $delIndex = scalar @new;
  603. }
  604. } else {
  605. push @new, @old;
  606. }
  607. }
  608. # add new values (at location of deleted values, if any)
  609. if ($$nvHash{Value}) {
  610. if (defined $delIndex) {
  611. splice @new, $delIndex, 0, @{$$nvHash{Value}};
  612. } else {
  613. push @new, @{$$nvHash{Value}};
  614. }
  615. }
  616. if (@new) {
  617. # convert back to string format
  618. $$newValuePt = ListToString(\@new);
  619. } else {
  620. $$newValuePt = undef; # delete the tag
  621. }
  622. return 1;
  623. }
  624. #------------------------------------------------------------------------------
  625. # Reconcile IPTC digest after writing an MWG tag
  626. # Inputs: 0) ExifTool object ref
  627. # Returns: empty string
  628. sub ReconcileIPTCDigest($)
  629. {
  630. my $et = shift;
  631. # set new value for IPTCDigest if not done already
  632. unless ($Image::ExifTool::Photoshop::iptcDigestInfo and
  633. $$et{NEW_VALUE}{$Image::ExifTool::Photoshop::iptcDigestInfo})
  634. {
  635. # write new IPTCDigest only if it doesn't exist or
  636. # is the same as the digest of the original IPTC
  637. my @a; # (capture warning messages)
  638. @a = $et->SetNewValue('Photoshop:IPTCDigest', 'old', Protected => 1, DelValue => 1);
  639. @a = $et->SetNewValue('Photoshop:IPTCDigest', 'new', Protected => 1);
  640. }
  641. return '';
  642. }
  643. #------------------------------------------------------------------------------
  644. # Recover strings which were truncated by IPTC dataset length limit
  645. # Inputs: 0) IPTC value, 1) XMP value, 2) length limit
  646. # Notes: handles the case where IPTC and/or XMP values are lists
  647. sub RecoverTruncatedIPTC($$$)
  648. {
  649. my ($iptc, $xmp, $limit) = @_;
  650. return $iptc unless defined $xmp;
  651. if (ref $iptc) {
  652. $xmp = [ $xmp ] unless ref $xmp;
  653. my ($i, @vals);
  654. for ($i=0; $i<@$iptc; ++$i) {
  655. push @vals, RecoverTruncatedIPTC($$iptc[$i], $$xmp[$i], $limit);
  656. }
  657. return \@vals;
  658. } elsif (defined $iptc and length $iptc == $limit) {
  659. $xmp = $$xmp[0] if ref $xmp; # take first element of list
  660. return $xmp if length $xmp > $limit and $iptc eq substr($xmp, 0, $limit);
  661. }
  662. return $iptc;
  663. }
  664. 1; # end
  665. __END__
  666. =head1 NAME
  667. Image::ExifTool::MWG - Metadata Working Group support
  668. =head1 SYNOPSIS
  669. # enable MWG Composite tags
  670. use Image::ExifTool::MWG;
  671. Image::ExifTool::MWG::Load();
  672. # enable MWG strict mode
  673. $Image::ExifTool::MWG::strict = 1;
  674. # disable MWG strict mode
  675. $Image::ExifTool::MWG::strict = 0;
  676. =head1 DESCRIPTION
  677. The MWG module contains Composite tag definitions which are designed to
  678. simplify implementation of the Metadata Working Group guidelines. These
  679. special MWG Composite tags are enabled by calling the Load() method:
  680. use Image::ExifTool::MWG;
  681. Image::ExifTool::MWG::Load();
  682. By default, loading the MWG Composite tags enables "strict MWG conformance"
  683. unless previously enabled or disabled by the user. In this mode, ExifTool
  684. will generate a Warning instead of extracting EXIF, IPTC and XMP from
  685. non-standard locations. The strict mode may be disabled or enabled at any
  686. time by setting the MWG "strict" flag to 0 or 1. eg)
  687. $Image::ExifTool::MWG::strict = 0;
  688. This module also contains the MWG XMP tags which are loaded automatically by
  689. ExifTool as required, and are independent of the MWG Composite tags which
  690. must be loaded explicitly as described above.
  691. =head1 AUTHOR
  692. Copyright 2003-2016, Phil Harvey (phil at owl.phy.queensu.ca)
  693. This library is free software; you can redistribute it and/or modify it
  694. under the same terms as Perl itself.
  695. =head1 REFERENCES
  696. =over 4
  697. =item L<http://www.metadataworkinggroup.org/>
  698. =back
  699. =head1 SEE ALSO
  700. L<Image::ExifTool::TagNames/MWG Tags>,
  701. L<Image::ExifTool(3pm)|Image::ExifTool>
  702. =cut