File Coverage

blib/lib/Image/ExifTool/IPTC.pm
Criterion Covered Total %
statement 110 162 67.9
branch 70 108 64.8
condition 42 77 54.5
subroutine 9 9 100.0
pod 0 5 0.0
total 231 361 63.9


line stmt bran cond sub pod time code
1             #------------------------------------------------------------------------------
2             # File: IPTC.pm
3             #
4             # Description: Read IPTC meta information
5             #
6             # Revisions: Jan. 08/2003 - P. Harvey Created
7             # Feb. 05/2004 - P. Harvey Added support for records other than 2
8             #
9             # References: 1) http://www.iptc.org/IIM/
10             #------------------------------------------------------------------------------
11              
12             package Image::ExifTool::IPTC;
13              
14 27     27   4016 use strict;
  27         51  
  27         873  
15 27     27   135 use vars qw($VERSION $AUTOLOAD %iptcCharset);
  27         43  
  27         1401  
16 27     27   196 use Image::ExifTool qw(:DataAccess :Utils);
  27         92  
  27         87813  
17              
18             $VERSION = '1.57';
19              
20             %iptcCharset = (
21             "\x1b%G" => 'UTF8',
22             # don't translate these (at least until we handle ISO 2022 shift codes)
23             # because the sets are only designated and not invoked
24             # "\x1b,A" => 'Latin', # G0 = ISO 8859-1 (similar to Latin1, but codes 0x80-0x9f are missing)
25             # "\x1b-A" => 'Latin', # G1 "
26             # "\x1b.A" => 'Latin', # G2
27             # "\x1b/A" => 'Latin', # G3
28             );
29              
30             sub ProcessIPTC($$$);
31             sub WriteIPTC($$$);
32             sub CheckIPTC($$$);
33             sub PrintCodedCharset($);
34             sub PrintInvCodedCharset($);
35              
36             # standard IPTC locations
37             # (MWG specifies locations only for JPEG, TIFF and PSD -- the rest are ExifTool-defined)
38             my %isStandardIPTC = (
39             'JPEG-APP13-Photoshop-IPTC' => 1,
40             'TIFF-IFD0-IPTC' => 1,
41             'PSD-IPTC' => 1,
42             'MIE-IPTC' => 1,
43             'EPS-Photoshop-IPTC' => 1,
44             'PS-Photoshop-IPTC' => 1,
45             'EXV-APP13-Photoshop-IPTC' => 1,
46             # set file types to 0 if they have a standard location
47             JPEG => 0,
48             TIFF => 0,
49             PSD => 0,
50             MIE => 0,
51             EPS => 0,
52             PS => 0,
53             EXV => 0,
54             );
55              
56             my %fileFormat = (
57             0 => 'No ObjectData',
58             1 => 'IPTC-NAA Digital Newsphoto Parameter Record',
59             2 => 'IPTC7901 Recommended Message Format',
60             3 => 'Tagged Image File Format (Adobe/Aldus Image data)',
61             4 => 'Illustrator (Adobe Graphics data)',
62             5 => 'AppleSingle (Apple Computer Inc)',
63             6 => 'NAA 89-3 (ANPA 1312)',
64             7 => 'MacBinary II',
65             8 => 'IPTC Unstructured Character Oriented File Format (UCOFF)',
66             9 => 'United Press International ANPA 1312 variant',
67             10 => 'United Press International Down-Load Message',
68             11 => 'JPEG File Interchange (JFIF)',
69             12 => 'Photo-CD Image-Pac (Eastman Kodak)',
70             13 => 'Bit Mapped Graphics File [.BMP] (Microsoft)',
71             14 => 'Digital Audio File [.WAV] (Microsoft & Creative Labs)',
72             15 => 'Audio plus Moving Video [.AVI] (Microsoft)',
73             16 => 'PC DOS/Windows Executable Files [.COM][.EXE]',
74             17 => 'Compressed Binary File [.ZIP] (PKWare Inc)',
75             18 => 'Audio Interchange File Format AIFF (Apple Computer Inc)',
76             19 => 'RIFF Wave (Microsoft Corporation)',
77             20 => 'Freehand (Macromedia/Aldus)',
78             21 => 'Hypertext Markup Language [.HTML] (The Internet Society)',
79             22 => 'MPEG 2 Audio Layer 2 (Musicom), ISO/IEC',
80             23 => 'MPEG 2 Audio Layer 3, ISO/IEC',
81             24 => 'Portable Document File [.PDF] Adobe',
82             25 => 'News Industry Text Format (NITF)',
83             26 => 'Tape Archive [.TAR]',
84             27 => 'Tidningarnas Telegrambyra NITF version (TTNITF DTD)',
85             28 => 'Ritzaus Bureau NITF version (RBNITF DTD)',
86             29 => 'Corel Draw [.CDR]',
87             );
88              
89             # main IPTC tag table
90             # Note: ALL entries in main IPTC table (except PROCESS_PROC) must be SubDirectory
91             # entries, each specifying a TagTable.
92             %Image::ExifTool::IPTC::Main = (
93             GROUPS => { 2 => 'Image' },
94             PROCESS_PROC => \&ProcessIPTC,
95             WRITE_PROC => \&WriteIPTC,
96             1 => {
97             Name => 'IPTCEnvelope',
98             SubDirectory => {
99             TagTable => 'Image::ExifTool::IPTC::EnvelopeRecord',
100             },
101             },
102             2 => {
103             Name => 'IPTCApplication',
104             SubDirectory => {
105             TagTable => 'Image::ExifTool::IPTC::ApplicationRecord',
106             },
107             },
108             3 => {
109             Name => 'IPTCNewsPhoto',
110             SubDirectory => {
111             TagTable => 'Image::ExifTool::IPTC::NewsPhoto',
112             },
113             },
114             7 => {
115             Name => 'IPTCPreObjectData',
116             SubDirectory => {
117             TagTable => 'Image::ExifTool::IPTC::PreObjectData',
118             },
119             },
120             8 => {
121             Name => 'IPTCObjectData',
122             SubDirectory => {
123             TagTable => 'Image::ExifTool::IPTC::ObjectData',
124             },
125             },
126             9 => {
127             Name => 'IPTCPostObjectData',
128             Groups => { 1 => 'IPTC#' }, #(just so this shows up in group list)
129             SubDirectory => {
130             TagTable => 'Image::ExifTool::IPTC::PostObjectData',
131             },
132             },
133             240 => {
134             Name => 'IPTCFotoStation',
135             SubDirectory => {
136             TagTable => 'Image::ExifTool::IPTC::FotoStation',
137             },
138             },
139             );
140              
141             # Record 1 -- EnvelopeRecord
142             %Image::ExifTool::IPTC::EnvelopeRecord = (
143             GROUPS => { 2 => 'Other' },
144             WRITE_PROC => \&WriteIPTC,
145             CHECK_PROC => \&CheckIPTC,
146             WRITABLE => 1,
147             0 => {
148             Name => 'EnvelopeRecordVersion',
149             Format => 'int16u',
150             Mandatory => 1,
151             },
152             5 => {
153             Name => 'Destination',
154             Flags => 'List',
155             Groups => { 2 => 'Location' },
156             Format => 'string[0,1024]',
157             },
158             20 => {
159             Name => 'FileFormat',
160             Groups => { 2 => 'Image' },
161             Format => 'int16u',
162             PrintConv => \%fileFormat,
163             },
164             22 => {
165             Name => 'FileVersion',
166             Groups => { 2 => 'Image' },
167             Format => 'int16u',
168             },
169             30 => {
170             Name => 'ServiceIdentifier',
171             Format => 'string[0,10]',
172             },
173             40 => {
174             Name => 'EnvelopeNumber',
175             Format => 'digits[8]',
176             },
177             50 => {
178             Name => 'ProductID',
179             Flags => 'List',
180             Format => 'string[0,32]',
181             },
182             60 => {
183             Name => 'EnvelopePriority',
184             Format => 'digits[1]',
185             PrintConv => {
186             0 => '0 (reserved)',
187             1 => '1 (most urgent)',
188             2 => 2,
189             3 => 3,
190             4 => 4,
191             5 => '5 (normal urgency)',
192             6 => 6,
193             7 => 7,
194             8 => '8 (least urgent)',
195             9 => '9 (user-defined priority)',
196             },
197             },
198             70 => {
199             Name => 'DateSent',
200             Groups => { 2 => 'Time' },
201             Format => 'digits[8]',
202             Shift => 'Time',
203             ValueConv => 'Image::ExifTool::Exif::ExifDate($val)',
204             ValueConvInv => 'Image::ExifTool::IPTC::IptcDate($val)',
205             PrintConvInv => 'Image::ExifTool::IPTC::InverseDateOrTime($self,$val)',
206             },
207             80 => {
208             Name => 'TimeSent',
209             Groups => { 2 => 'Time' },
210             Format => 'string[11]',
211             Shift => 'Time',
212             ValueConv => 'Image::ExifTool::Exif::ExifTime($val)',
213             ValueConvInv => 'Image::ExifTool::IPTC::IptcTime($val)',
214             PrintConvInv => 'Image::ExifTool::IPTC::InverseDateOrTime($self,$val)',
215             },
216             90 => {
217             Name => 'CodedCharacterSet',
218             Notes => q{
219             values are entered in the form "ESC X Y[, ...]". The escape sequence for
220             UTF-8 character coding is "ESC % G", but this is displayed as "UTF8" for
221             convenience. Either string may be used when writing. The value of this tag
222             affects the decoding of string values in the Application and NewsPhoto
223             records. This tag is marked as "unsafe" to prevent it from being copied by
224             default in a group operation because existing tags in the destination image
225             may use a different encoding. When creating a new IPTC record from scratch,
226             it is suggested that this be set to "UTF8" if special characters are a
227             possibility
228             },
229             Protected => 1,
230             Format => 'string[0,32]',
231             ValueConvInv => '$val =~ /^UTF-?8$/i ? "\x1b%G" : $val',
232             # convert ISO 2022 escape sequences to a more readable format
233             PrintConv => \&PrintCodedCharset,
234             PrintConvInv => \&PrintInvCodedCharset,
235             },
236             100 => {
237             Name => 'UniqueObjectName',
238             Format => 'string[14,80]',
239             },
240             120 => {
241             Name => 'ARMIdentifier',
242             Format => 'int16u',
243             },
244             122 => {
245             Name => 'ARMVersion',
246             Format => 'int16u',
247             },
248             );
249              
250             # Record 2 -- ApplicationRecord
251             %Image::ExifTool::IPTC::ApplicationRecord = (
252             GROUPS => { 2 => 'Other' },
253             WRITE_PROC => \&WriteIPTC,
254             CHECK_PROC => \&CheckIPTC,
255             WRITABLE => 1,
256             0 => {
257             Name => 'ApplicationRecordVersion',
258             Format => 'int16u',
259             Mandatory => 1,
260             },
261             3 => {
262             Name => 'ObjectTypeReference',
263             Format => 'string[3,67]',
264             },
265             4 => {
266             Name => 'ObjectAttributeReference',
267             Flags => 'List',
268             Format => 'string[4,68]',
269             },
270             5 => {
271             Name => 'ObjectName',
272             Format => 'string[0,64]',
273             },
274             7 => {
275             Name => 'EditStatus',
276             Format => 'string[0,64]',
277             },
278             8 => {
279             Name => 'EditorialUpdate',
280             Format => 'digits[2]',
281             PrintConv => {
282             '01' => 'Additional language',
283             },
284             },
285             10 => {
286             Name => 'Urgency',
287             Format => 'digits[1]',
288             PrintConv => {
289             0 => '0 (reserved)',
290             1 => '1 (most urgent)',
291             2 => 2,
292             3 => 3,
293             4 => 4,
294             5 => '5 (normal urgency)',
295             6 => 6,
296             7 => 7,
297             8 => '8 (least urgent)',
298             9 => '9 (user-defined priority)',
299             },
300             },
301             12 => {
302             Name => 'SubjectReference',
303             Flags => 'List',
304             Format => 'string[13,236]',
305             },
306             15 => {
307             Name => 'Category',
308             Format => 'string[0,3]',
309             },
310             20 => {
311             Name => 'SupplementalCategories',
312             Flags => 'List',
313             Format => 'string[0,32]',
314             },
315             22 => {
316             Name => 'FixtureIdentifier',
317             Format => 'string[0,32]',
318             },
319             25 => {
320             Name => 'Keywords',
321             Flags => 'List',
322             Format => 'string[0,64]',
323             },
324             26 => {
325             Name => 'ContentLocationCode',
326             Flags => 'List',
327             Groups => { 2 => 'Location' },
328             Format => 'string[3]',
329             },
330             27 => {
331             Name => 'ContentLocationName',
332             Flags => 'List',
333             Groups => { 2 => 'Location' },
334             Format => 'string[0,64]',
335             },
336             30 => {
337             Name => 'ReleaseDate',
338             Groups => { 2 => 'Time' },
339             Format => 'digits[8]',
340             Shift => 'Time',
341             ValueConv => 'Image::ExifTool::Exif::ExifDate($val)',
342             ValueConvInv => 'Image::ExifTool::IPTC::IptcDate($val)',
343             PrintConvInv => 'Image::ExifTool::IPTC::InverseDateOrTime($self,$val)',
344             },
345             35 => {
346             Name => 'ReleaseTime',
347             Groups => { 2 => 'Time' },
348             Format => 'string[11]',
349             Shift => 'Time',
350             ValueConv => 'Image::ExifTool::Exif::ExifTime($val)',
351             ValueConvInv => 'Image::ExifTool::IPTC::IptcTime($val)',
352             PrintConvInv => 'Image::ExifTool::IPTC::InverseDateOrTime($self,$val)',
353             },
354             37 => {
355             Name => 'ExpirationDate',
356             Groups => { 2 => 'Time' },
357             Format => 'digits[8]',
358             Shift => 'Time',
359             ValueConv => 'Image::ExifTool::Exif::ExifDate($val)',
360             ValueConvInv => 'Image::ExifTool::IPTC::IptcDate($val)',
361             PrintConvInv => 'Image::ExifTool::IPTC::InverseDateOrTime($self,$val)',
362             },
363             38 => {
364             Name => 'ExpirationTime',
365             Groups => { 2 => 'Time' },
366             Format => 'string[11]',
367             Shift => 'Time',
368             ValueConv => 'Image::ExifTool::Exif::ExifTime($val)',
369             ValueConvInv => 'Image::ExifTool::IPTC::IptcTime($val)',
370             PrintConvInv => 'Image::ExifTool::IPTC::InverseDateOrTime($self,$val)',
371             },
372             40 => {
373             Name => 'SpecialInstructions',
374             Format => 'string[0,256]',
375             },
376             42 => {
377             Name => 'ActionAdvised',
378             Format => 'digits[2]',
379             PrintConv => {
380             '' => '',
381             '01' => 'Object Kill',
382             '02' => 'Object Replace',
383             '03' => 'Object Append',
384             '04' => 'Object Reference',
385             },
386             },
387             45 => {
388             Name => 'ReferenceService',
389             Flags => 'List',
390             Format => 'string[0,10]',
391             },
392             47 => {
393             Name => 'ReferenceDate',
394             Groups => { 2 => 'Time' },
395             Flags => 'List',
396             Format => 'digits[8]',
397             Shift => 'Time',
398             ValueConv => 'Image::ExifTool::Exif::ExifDate($val)',
399             ValueConvInv => 'Image::ExifTool::IPTC::IptcDate($val)',
400             PrintConvInv => 'Image::ExifTool::IPTC::InverseDateOrTime($self,$val)',
401             },
402             50 => {
403             Name => 'ReferenceNumber',
404             Flags => 'List',
405             Format => 'digits[8]',
406             },
407             55 => {
408             Name => 'DateCreated',
409             Groups => { 2 => 'Time' },
410             Format => 'digits[8]',
411             Shift => 'Time',
412             ValueConv => 'Image::ExifTool::Exif::ExifDate($val)',
413             ValueConvInv => 'Image::ExifTool::IPTC::IptcDate($val)',
414             PrintConvInv => 'Image::ExifTool::IPTC::InverseDateOrTime($self,$val)',
415             },
416             60 => {
417             Name => 'TimeCreated',
418             Groups => { 2 => 'Time' },
419             Format => 'string[11]',
420             Shift => 'Time',
421             ValueConv => 'Image::ExifTool::Exif::ExifTime($val)',
422             ValueConvInv => 'Image::ExifTool::IPTC::IptcTime($val)',
423             PrintConvInv => 'Image::ExifTool::IPTC::InverseDateOrTime($self,$val)',
424             },
425             62 => {
426             Name => 'DigitalCreationDate',
427             Groups => { 2 => 'Time' },
428             Format => 'digits[8]',
429             Shift => 'Time',
430             ValueConv => 'Image::ExifTool::Exif::ExifDate($val)',
431             ValueConvInv => 'Image::ExifTool::IPTC::IptcDate($val)',
432             PrintConvInv => 'Image::ExifTool::IPTC::InverseDateOrTime($self,$val)',
433             },
434             63 => {
435             Name => 'DigitalCreationTime',
436             Groups => { 2 => 'Time' },
437             Format => 'string[11]',
438             Shift => 'Time',
439             ValueConv => 'Image::ExifTool::Exif::ExifTime($val)',
440             ValueConvInv => 'Image::ExifTool::IPTC::IptcTime($val)',
441             PrintConvInv => 'Image::ExifTool::IPTC::InverseDateOrTime($self,$val)',
442             },
443             65 => {
444             Name => 'OriginatingProgram',
445             Format => 'string[0,32]',
446             },
447             70 => {
448             Name => 'ProgramVersion',
449             Format => 'string[0,10]',
450             },
451             75 => {
452             Name => 'ObjectCycle',
453             Format => 'string[1]',
454             PrintConv => {
455             'a' => 'Morning',
456             'p' => 'Evening',
457             'b' => 'Both Morning and Evening',
458             },
459             },
460             80 => {
461             Name => 'By-line',
462             Flags => 'List',
463             Format => 'string[0,32]',
464             Groups => { 2 => 'Author' },
465             },
466             85 => {
467             Name => 'By-lineTitle',
468             Flags => 'List',
469             Format => 'string[0,32]',
470             Groups => { 2 => 'Author' },
471             },
472             90 => {
473             Name => 'City',
474             Format => 'string[0,32]',
475             Groups => { 2 => 'Location' },
476             },
477             92 => {
478             Name => 'Sub-location',
479             Format => 'string[0,32]',
480             Groups => { 2 => 'Location' },
481             },
482             95 => {
483             Name => 'Province-State',
484             Format => 'string[0,32]',
485             Groups => { 2 => 'Location' },
486             },
487             100 => {
488             Name => 'Country-PrimaryLocationCode',
489             Format => 'string[3]',
490             Groups => { 2 => 'Location' },
491             },
492             101 => {
493             Name => 'Country-PrimaryLocationName',
494             Format => 'string[0,64]',
495             Groups => { 2 => 'Location' },
496             },
497             103 => {
498             Name => 'OriginalTransmissionReference',
499             Format => 'string[0,32]',
500             Notes => 'now used as a job identifier',
501             },
502             105 => {
503             Name => 'Headline',
504             Format => 'string[0,256]',
505             },
506             110 => {
507             Name => 'Credit',
508             Groups => { 2 => 'Author' },
509             Format => 'string[0,32]',
510             },
511             115 => {
512             Name => 'Source',
513             Groups => { 2 => 'Author' },
514             Format => 'string[0,32]',
515             },
516             116 => {
517             Name => 'CopyrightNotice',
518             Groups => { 2 => 'Author' },
519             Format => 'string[0,128]',
520             },
521             118 => {
522             Name => 'Contact',
523             Flags => 'List',
524             Groups => { 2 => 'Author' },
525             Format => 'string[0,128]',
526             },
527             120 => {
528             Name => 'Caption-Abstract',
529             Format => 'string[0,2000]',
530             },
531             121 => {
532             Name => 'LocalCaption',
533             Format => 'string[0,256]', # (guess)
534             Notes => q{
535             I haven't found a reference for the format of tags 121, 184-188 and
536             225-232, so I have just make them writable as strings with
537             reasonable length. Beware that if this is wrong, other utilities
538             may not be able to read these tags as written by ExifTool
539             },
540             },
541             122 => {
542             Name => 'Writer-Editor',
543             Flags => 'List',
544             Groups => { 2 => 'Author' },
545             Format => 'string[0,32]',
546             },
547             125 => {
548             Name => 'RasterizedCaption',
549             Format => 'undef[7360]',
550             Binary => 1,
551             },
552             130 => {
553             Name => 'ImageType',
554             Groups => { 2 => 'Image' },
555             Format => 'string[2]',
556             },
557             131 => {
558             Name => 'ImageOrientation',
559             Groups => { 2 => 'Image' },
560             Format => 'string[1]',
561             PrintConv => {
562             P => 'Portrait',
563             L => 'Landscape',
564             S => 'Square',
565             },
566             },
567             135 => {
568             Name => 'LanguageIdentifier',
569             Format => 'string[2,3]',
570             },
571             150 => {
572             Name => 'AudioType',
573             Format => 'string[2]',
574             PrintConv => {
575             '1A' => 'Mono Actuality',
576             '2A' => 'Stereo Actuality',
577             '1C' => 'Mono Question and Answer Session',
578             '2C' => 'Stereo Question and Answer Session',
579             '1M' => 'Mono Music',
580             '2M' => 'Stereo Music',
581             '1Q' => 'Mono Response to a Question',
582             '2Q' => 'Stereo Response to a Question',
583             '1R' => 'Mono Raw Sound',
584             '2R' => 'Stereo Raw Sound',
585             '1S' => 'Mono Scener',
586             '2S' => 'Stereo Scener',
587             '0T' => 'Text Only',
588             '1V' => 'Mono Voicer',
589             '2V' => 'Stereo Voicer',
590             '1W' => 'Mono Wrap',
591             '2W' => 'Stereo Wrap',
592             },
593             },
594             151 => {
595             Name => 'AudioSamplingRate',
596             Format => 'digits[6]',
597             },
598             152 => {
599             Name => 'AudioSamplingResolution',
600             Format => 'digits[2]',
601             },
602             153 => {
603             Name => 'AudioDuration',
604             Format => 'digits[6]',
605             },
606             154 => {
607             Name => 'AudioOutcue',
608             Format => 'string[0,64]',
609             },
610             184 => {
611             Name => 'JobID',
612             Format => 'string[0,64]', # (guess)
613             },
614             185 => {
615             Name => 'MasterDocumentID',
616             Format => 'string[0,256]', # (guess)
617             },
618             186 => {
619             Name => 'ShortDocumentID',
620             Format => 'string[0,64]', # (guess)
621             },
622             187 => {
623             Name => 'UniqueDocumentID',
624             Format => 'string[0,128]', # (guess)
625             },
626             188 => {
627             Name => 'OwnerID',
628             Format => 'string[0,128]', # (guess)
629             },
630             200 => {
631             Name => 'ObjectPreviewFileFormat',
632             Groups => { 2 => 'Image' },
633             Format => 'int16u',
634             PrintConv => \%fileFormat,
635             },
636             201 => {
637             Name => 'ObjectPreviewFileVersion',
638             Groups => { 2 => 'Image' },
639             Format => 'int16u',
640             },
641             202 => {
642             Name => 'ObjectPreviewData',
643             Groups => { 2 => 'Preview' },
644             Format => 'undef[0,256000]',
645             Binary => 1,
646             },
647             221 => {
648             Name => 'Prefs',
649             Groups => { 2 => 'Image' },
650             Format => 'string[0,64]',
651             Notes => 'PhotoMechanic preferences',
652             PrintConv => q{
653             $val =~ s[\s*(\d+):\s*(\d+):\s*(\d+):\s*(\S*)]
654             [Tagged:$1, ColorClass:$2, Rating:$3, FrameNum:$4];
655             return $val;
656             },
657             PrintConvInv => q{
658             $val =~ s[Tagged:\s*(\d+).*ColorClass:\s*(\d+).*Rating:\s*(\d+).*FrameNum:\s*(\S*)]
659             [$1:$2:$3:$4]is;
660             return $val;
661             },
662             },
663             225 => {
664             Name => 'ClassifyState',
665             Format => 'string[0,64]', # (guess)
666             },
667             228 => {
668             Name => 'SimilarityIndex',
669             Format => 'string[0,32]', # (guess)
670             },
671             230 => {
672             Name => 'DocumentNotes',
673             Format => 'string[0,1024]', # (guess)
674             },
675             231 => {
676             Name => 'DocumentHistory',
677             Format => 'string[0,256]', # (guess)
678             ValueConv => '$val =~ s/\0+/\n/g; $val', # (have seen embedded nulls)
679             ValueConvInv => '$val',
680             },
681             232 => {
682             Name => 'ExifCameraInfo',
683             Format => 'string[0,4096]', # (guess)
684             },
685             255 => { #PH
686             Name => 'CatalogSets',
687             List => 1,
688             Format => 'string[0,256]', # (guess)
689             Notes => 'written by iView MediaPro',
690             },
691             );
692              
693             # Record 3 -- News photo
694             %Image::ExifTool::IPTC::NewsPhoto = (
695             GROUPS => { 2 => 'Image' },
696             WRITE_PROC => \&WriteIPTC,
697             CHECK_PROC => \&CheckIPTC,
698             WRITABLE => 1,
699             0 => {
700             Name => 'NewsPhotoVersion',
701             Format => 'int16u',
702             Mandatory => 1,
703             },
704             10 => {
705             Name => 'IPTCPictureNumber',
706             Format => 'string[16]',
707             Notes => '4 numbers: 1-Manufacturer ID, 2-Equipment ID, 3-Date, 4-Sequence',
708             PrintConv => 'Image::ExifTool::IPTC::ConvertPictureNumber($val)',
709             PrintConvInv => 'Image::ExifTool::IPTC::InvConvertPictureNumber($val)',
710             },
711             20 => {
712             Name => 'IPTCImageWidth',
713             Format => 'int16u',
714             },
715             30 => {
716             Name => 'IPTCImageHeight',
717             Format => 'int16u',
718             },
719             40 => {
720             Name => 'IPTCPixelWidth',
721             Format => 'int16u',
722             },
723             50 => {
724             Name => 'IPTCPixelHeight',
725             Format => 'int16u',
726             },
727             55 => {
728             Name => 'SupplementalType',
729             Format => 'int8u',
730             PrintConv => {
731             0 => 'Main Image',
732             1 => 'Reduced Resolution Image',
733             2 => 'Logo',
734             3 => 'Rasterized Caption',
735             },
736             },
737             60 => {
738             Name => 'ColorRepresentation',
739             Format => 'int16u',
740             PrintHex => 1,
741             PrintConv => {
742             0x000 => 'No Image, Single Frame',
743             0x100 => 'Monochrome, Single Frame',
744             0x300 => '3 Components, Single Frame',
745             0x301 => '3 Components, Frame Sequential in Multiple Objects',
746             0x302 => '3 Components, Frame Sequential in One Object',
747             0x303 => '3 Components, Line Sequential',
748             0x304 => '3 Components, Pixel Sequential',
749             0x305 => '3 Components, Special Interleaving',
750             0x400 => '4 Components, Single Frame',
751             0x401 => '4 Components, Frame Sequential in Multiple Objects',
752             0x402 => '4 Components, Frame Sequential in One Object',
753             0x403 => '4 Components, Line Sequential',
754             0x404 => '4 Components, Pixel Sequential',
755             0x405 => '4 Components, Special Interleaving',
756             },
757             },
758             64 => {
759             Name => 'InterchangeColorSpace',
760             Format => 'int8u',
761             PrintConv => {
762             1 => 'X,Y,Z CIE',
763             2 => 'RGB SMPTE',
764             3 => 'Y,U,V (K) (D65)',
765             4 => 'RGB Device Dependent',
766             5 => 'CMY (K) Device Dependent',
767             6 => 'Lab (K) CIE',
768             7 => 'YCbCr',
769             8 => 'sRGB',
770             },
771             },
772             65 => {
773             Name => 'ColorSequence',
774             Format => 'int8u',
775             },
776             66 => {
777             Name => 'ICC_Profile',
778             # ...could add SubDirectory support to read into this (if anybody cares)
779             Writable => 0,
780             Binary => 1,
781             },
782             70 => {
783             Name => 'ColorCalibrationMatrix',
784             Writable => 0,
785             Binary => 1,
786             },
787             80 => {
788             Name => 'LookupTable',
789             Writable => 0,
790             Binary => 1,
791             },
792             84 => {
793             Name => 'NumIndexEntries',
794             Format => 'int16u',
795             },
796             85 => {
797             Name => 'ColorPalette',
798             Writable => 0,
799             Binary => 1,
800             },
801             86 => {
802             Name => 'IPTCBitsPerSample',
803             Format => 'int8u',
804             },
805             90 => {
806             Name => 'SampleStructure',
807             Format => 'int8u',
808             PrintConv => {
809             0 => 'OrthogonalConstangSampling',
810             1 => 'Orthogonal4-2-2Sampling',
811             2 => 'CompressionDependent',
812             },
813             },
814             100 => {
815             Name => 'ScanningDirection',
816             Format => 'int8u',
817             PrintConv => {
818             0 => 'L-R, Top-Bottom',
819             1 => 'R-L, Top-Bottom',
820             2 => 'L-R, Bottom-Top',
821             3 => 'R-L, Bottom-Top',
822             4 => 'Top-Bottom, L-R',
823             5 => 'Bottom-Top, L-R',
824             6 => 'Top-Bottom, R-L',
825             7 => 'Bottom-Top, R-L',
826             },
827             },
828             102 => {
829             Name => 'IPTCImageRotation',
830             Format => 'int8u',
831             PrintConv => {
832             0 => 0,
833             1 => 90,
834             2 => 180,
835             3 => 270,
836             },
837             },
838             110 => {
839             Name => 'DataCompressionMethod',
840             Format => 'int32u',
841             },
842             120 => {
843             Name => 'QuantizationMethod',
844             Format => 'int8u',
845             PrintConv => {
846             0 => 'Linear Reflectance/Transmittance',
847             1 => 'Linear Density',
848             2 => 'IPTC Ref B',
849             3 => 'Linear Dot Percent',
850             4 => 'AP Domestic Analogue',
851             5 => 'Compression Method Specific',
852             6 => 'Color Space Specific',
853             7 => 'Gamma Compensated',
854             },
855             },
856             125 => {
857             Name => 'EndPoints',
858             Writable => 0,
859             Binary => 1,
860             },
861             130 => {
862             Name => 'ExcursionTolerance',
863             Format => 'int8u',
864             PrintConv => {
865             0 => 'Not Allowed',
866             1 => 'Allowed',
867             },
868             },
869             135 => {
870             Name => 'BitsPerComponent',
871             Format => 'int8u',
872             },
873             140 => {
874             Name => 'MaximumDensityRange',
875             Format => 'int16u',
876             },
877             145 => {
878             Name => 'GammaCompensatedValue',
879             Format => 'int16u',
880             },
881             );
882              
883             # Record 7 -- Pre-object Data
884             %Image::ExifTool::IPTC::PreObjectData = (
885             # (not actually writable, but used in BuildTagLookup to recognize IPTC tables)
886             WRITE_PROC => \&WriteIPTC,
887             10 => {
888             Name => 'SizeMode',
889             Format => 'int8u',
890             PrintConv => {
891             0 => 'Size Not Known',
892             1 => 'Size Known',
893             },
894             },
895             20 => {
896             Name => 'MaxSubfileSize',
897             Format => 'int32u',
898             },
899             90 => {
900             Name => 'ObjectSizeAnnounced',
901             Format => 'int32u',
902             },
903             95 => {
904             Name => 'MaximumObjectSize',
905             Format => 'int32u',
906             },
907             );
908              
909             # Record 8 -- ObjectData
910             %Image::ExifTool::IPTC::ObjectData = (
911             WRITE_PROC => \&WriteIPTC,
912             10 => {
913             Name => 'SubFile',
914             Flags => 'List',
915             Binary => 1,
916             },
917             );
918              
919             # Record 9 -- PostObjectData
920             %Image::ExifTool::IPTC::PostObjectData = (
921             WRITE_PROC => \&WriteIPTC,
922             10 => {
923             Name => 'ConfirmedObjectSize',
924             Format => 'int32u',
925             },
926             );
927              
928             # Record 240 -- FotoStation proprietary data (ref PH)
929             %Image::ExifTool::IPTC::FotoStation = (
930             GROUPS => { 2 => 'Other' },
931             WRITE_PROC => \&WriteIPTC,
932             CHECK_PROC => \&CheckIPTC,
933             WRITABLE => 1,
934             );
935              
936             # IPTC Composite tags
937             %Image::ExifTool::IPTC::Composite = (
938             GROUPS => { 2 => 'Image' },
939             DateTimeCreated => {
940             Description => 'Date/Time Created',
941             Groups => { 2 => 'Time' },
942             Require => {
943             0 => 'IPTC:DateCreated',
944             1 => 'IPTC:TimeCreated',
945             },
946             ValueConv => '"$val[0] $val[1]"',
947             PrintConv => '$self->ConvertDateTime($val)',
948             },
949             DigitalCreationDateTime => {
950             Description => 'Digital Creation Date/Time',
951             Groups => { 2 => 'Time' },
952             Require => {
953             0 => 'IPTC:DigitalCreationDate',
954             1 => 'IPTC:DigitalCreationTime',
955             },
956             ValueConv => '"$val[0] $val[1]"',
957             PrintConv => '$self->ConvertDateTime($val)',
958             },
959             );
960              
961             # add our composite tags
962             Image::ExifTool::AddCompositeTags('Image::ExifTool::IPTC');
963              
964              
965             #------------------------------------------------------------------------------
966             # AutoLoad our writer routines when necessary
967             #
968             sub AUTOLOAD
969             {
970 21     21   118 return Image::ExifTool::DoAutoLoad($AUTOLOAD, @_);
971             }
972              
973             #------------------------------------------------------------------------------
974             # Print conversion for CodedCharacterSet
975             # Inputs: 0) value
976             sub PrintCodedCharset($)
977             {
978 2     2 0 6 my $val = shift;
979 2 50       14 return $iptcCharset{$val} if $iptcCharset{$val};
980 0         0 $val =~ s/(.)/ $1/g;
981 0         0 $val =~ s/ \x1b/, ESC/g;
982 0         0 $val =~ s/^,? //;
983 0         0 return $val;
984             }
985              
986             #------------------------------------------------------------------------------
987             # Handle CodedCharacterSet
988             # Inputs: 0) ExifTool ref, 1) CodedCharacterSet value
989             # Returns: IPTC character set if translation required (or 'bad' if unknown)
990             sub HandleCodedCharset($$)
991             {
992 4     4 0 14 my ($et, $val) = @_;
993 4         13 my $xlat = $iptcCharset{$val};
994 4 50       12 unless ($xlat) {
995 0 0       0 if ($val =~ /^\x1b\x25/) {
996             # some unknown character set invoked
997 0         0 $xlat = 'bad'; # flag unsupported coding
998             } else {
999 0         0 $xlat = $et->Options('CharsetIPTC');
1000             }
1001             }
1002             # no need to translate if Charset is the same
1003 4 50       15 undef $xlat if $xlat eq $et->Options('Charset');
1004 4         13 return $xlat;
1005             }
1006              
1007             #------------------------------------------------------------------------------
1008             # Encode or decode coded string
1009             # Inputs: 0) ExifTool ref, 1) value ptr, 2) IPTC charset (or 'bad') ref
1010             # 3) flag set to decode (read) value from IPTC
1011             # Updates value on return
1012             sub TranslateCodedString($$$$)
1013             {
1014 3     3 0 10 my ($et, $valPtr, $xlatPtr, $read) = @_;
1015 3 50       17 if ($$xlatPtr eq 'bad') {
    100          
    50          
1016 0         0 $et->Warn('Some IPTC characters not converted (unsupported CodedCharacterSet)');
1017 0         0 undef $$xlatPtr;
1018             } elsif (not $read) {
1019 1         5 $$valPtr = $et->Decode($$valPtr, undef, undef, $$xlatPtr);
1020             } elsif ($$valPtr !~ /[\x14\x15\x1b]/) {
1021 2         10 $$valPtr = $et->Decode($$valPtr, $$xlatPtr);
1022             } else {
1023             # don't yet support reading ISO 2022 shifted character sets
1024 0         0 $et->WarnOnce('Some IPTC characters not converted (ISO 2022 shifting not supported)');
1025             }
1026             }
1027              
1028             #------------------------------------------------------------------------------
1029             # Is this IPTC in a standard location?
1030             # Inputs: 0) Current metadata path string
1031             # Returns: true if path is standard, 0 if file type doesn't have standard IPTC,
1032             # or undef if IPTC is non-standard
1033             sub IsStandardIPTC($)
1034             {
1035 151     151 0 288 my $path = shift;
1036 151 100       562 return 1 if $isStandardIPTC{$path};
1037 70 100 66     662 return 0 unless $path =~ /^(\w+)/ and defined $isStandardIPTC{$1};
1038 44         184 return undef; # non-standard
1039             }
1040              
1041             #------------------------------------------------------------------------------
1042             # get IPTC info
1043             # Inputs: 0) ExifTool object reference, 1) dirInfo reference
1044             # 2) reference to tag table
1045             # Returns: 1 on success, 0 otherwise
1046             sub ProcessIPTC($$$)
1047             {
1048 175     175 0 418 my ($et, $dirInfo, $tagTablePtr) = @_;
1049 175         357 my $dataPt = $$dirInfo{DataPt};
1050 175   100     636 my $pos = $$dirInfo{DirStart} || 0;
1051 175   50     506 my $dirLen = $$dirInfo{DirLen} || 0;
1052 175         356 my $dirEnd = $pos + $dirLen;
1053 175         523 my $verbose = $et->Options('Verbose');
1054 175         492 my $validate = $et->Options('Validate');
1055 175         452 my $success = 0;
1056 175         331 my ($lastRec, $recordPtr, $recordName);
1057              
1058 175 50 33     568 $verbose and $dirInfo and $et->VerboseDir('IPTC', 0, $$dirInfo{DirLen});
1059              
1060 175 100       605 if ($tagTablePtr eq \%Image::ExifTool::IPTC::Main) {
1061 150         435 my $path = $et->MetadataPath();
1062 150         608 my $isStd = IsStandardIPTC($path);
1063 150 100 66     760 if (defined $isStd and not $$et{DIR_COUNT}{STD_IPTC}) {
1064             # set flag to ensure we only have one family 1 "IPTC" group
1065 106         335 $$et{DIR_COUNT}{STD_IPTC} = 1;
1066             # calculate MD5 if Digest::MD5 is available (truly standard IPTC only)
1067 106 100       280 if ($isStd) {
1068 80         135 my $md5;
1069 80 50       188 if (eval { require Digest::MD5 }) {
  80         697  
1070 80 100 66     353 if ($pos or $dirLen != length($$dataPt)) {
1071 68         648 $md5 = Digest::MD5::md5(substr $$dataPt, $pos, $dirLen);
1072             } else {
1073 12         88 $md5 = Digest::MD5::md5($$dataPt);
1074             }
1075             } else {
1076             # a zero digest indicates IPTC exists but we don't have Digest::MD5
1077 0         0 $md5 = "\0" x 16;
1078             }
1079 80         386 $et->FoundTag('CurrentIPTCDigest', $md5);
1080             }
1081             } else {
1082 44 100 66     210 if (($Image::ExifTool::MWG::strict or $et->Options('Validate')) and
      66        
1083             $$et{FILE_TYPE} =~ /^(JPEG|TIFF|PSD)$/)
1084             {
1085 4 50       12 if ($Image::ExifTool::MWG::strict) {
1086             # ignore non-standard IPTC while in strict MWG compatibility mode
1087 4         50 $et->Warn("Ignored non-standard IPTC at $path");
1088 4         13 return 1;
1089             } else {
1090 0         0 $et->Warn("Non-standard IPTC at $path", 1);
1091             }
1092             }
1093             # extract non-standard IPTC
1094 40   100     165 my $count = ($$et{DIR_COUNT}{IPTC} || 0) + 1; # count non-standard IPTC
1095 40         91 $$et{DIR_COUNT}{IPTC} = $count;
1096 40         86 $$et{LOW_PRIORITY_DIR}{IPTC} = 1; # lower priority of non-standard IPTC
1097 40         141 $$et{SET_GROUP1} = '+' . ($count + 1); # add number to family 1 group name
1098             }
1099             }
1100             # begin by assuming default IPTC encoding
1101 171         583 my $xlat = $et->Options('CharsetIPTC');
1102 171 50       492 undef $xlat if $xlat eq $et->Options('Charset');
1103              
1104             # quick check for improperly byte-swapped IPTC
1105 171 50 33     1136 if ($dirLen >= 4 and substr($$dataPt, $pos, 1) ne "\x1c" and
      33        
1106             substr($$dataPt, $pos + 3, 1) eq "\x1c")
1107             {
1108 0         0 $et->Warn('IPTC data was improperly byte-swapped');
1109 0         0 my $newData = pack('N*', unpack('V*', substr($$dataPt, $pos, $dirLen) . "\0\0\0"));
1110 0         0 $dataPt = \$newData;
1111 0         0 $pos = 0;
1112 0         0 $dirEnd = $pos + $dirLen;
1113             # NOTE: MUST NOT access $dirInfo DataPt, DirStart or DataLen after this!
1114             }
1115             # extract IPTC as a block if specified
1116 171 100 66     1090 if ($$et{REQ_TAG_LOOKUP}{iptc} or ($$et{TAGS_FROM_FILE} and
      100        
1117             not $$et{EXCL_TAG_LOOKUP}{iptc}))
1118             {
1119 35 100 66     159 if ($pos or $dirLen != length($$dataPt)) {
1120 17         83 $et->FoundTag('IPTC', substr($$dataPt, $pos, $dirLen));
1121             } else {
1122 18         51 $et->FoundTag('IPTC', $$dataPt);
1123             }
1124             }
1125 171         591 while ($pos + 5 <= $dirEnd) {
1126 2465         3950 my $buff = substr($$dataPt, $pos, 5);
1127 2465         6055 my ($id, $rec, $tag, $len) = unpack("CCCn", $buff);
1128 2465 100       4457 unless ($id == 0x1c) {
1129 3 50       14 unless ($id) {
1130             # scan the rest of the data an give warning unless all zeros
1131             # (iMatch pads the IPTC block with nulls for some reason)
1132 3         13 my $remaining = substr($$dataPt, $pos, $dirEnd - $pos);
1133 3 50       52 last unless $remaining =~ /[^\0]/;
1134             }
1135 0         0 $et->Warn(sprintf('Bad IPTC data tag (marker 0x%x)',$id));
1136 0         0 last;
1137             }
1138 2462         3122 $pos += 5; # step to after field header
1139             # handle extended IPTC entry if necessary
1140 2462 50       4008 if ($len & 0x8000) {
1141 0         0 my $n = $len & 0x7fff; # get num bytes in length field
1142 0 0 0     0 if ($pos + $n > $dirEnd or $n > 8) {
1143 0         0 $et->VPrint(0, "Invalid extended IPTC entry (dataset $rec:$tag, len $len)\n");
1144 0         0 $success = 0;
1145 0         0 last;
1146             }
1147             # determine length (a big-endian, variable sized int)
1148 0         0 for ($len = 0; $n; ++$pos, --$n) {
1149 0         0 $len = $len * 256 + ord(substr($$dataPt, $pos, 1));
1150             }
1151             }
1152 2462 50       3979 if ($pos + $len > $dirEnd) {
1153 0         0 $et->VPrint(0, "Invalid IPTC entry (dataset $rec:$tag, len $len)\n");
1154 0         0 $success = 0;
1155 0         0 last;
1156             }
1157 2462 100 100     6898 if (not defined $lastRec or $lastRec != $rec) {
1158 174 0 33     529 if ($validate and defined $lastRec and $rec < $lastRec) {
      33        
1159 0         0 $et->Warn("IPTC doesn't conform to spec: Records out of sequence",1)
1160             }
1161 174         402 my $tableInfo = $tagTablePtr->{$rec};
1162 174 50       453 unless ($tableInfo) {
1163 0         0 $et->WarnOnce("Unrecognized IPTC record $rec (ignored)");
1164 0         0 $pos += $len;
1165 0         0 next; # ignore this entry
1166             }
1167 174         416 my $tableName = $tableInfo->{SubDirectory}->{TagTable};
1168 174 50       403 unless ($tableName) {
1169 0         0 $et->Warn("No table for IPTC record $rec!");
1170 0         0 last; # this shouldn't happen
1171             }
1172 174         349 $recordName = $$tableInfo{Name};
1173 174         478 $recordPtr = Image::ExifTool::GetTagTable($tableName);
1174 174         1033 $et->VPrint(0,$$et{INDENT},"-- $recordName record --\n");
1175 174         331 $lastRec = $rec;
1176             }
1177 2462         4339 my $val = substr($$dataPt, $pos, $len);
1178              
1179             # add tagInfo for all unknown tags:
1180 2462 100       5614 unless ($$recordPtr{$tag}) {
1181             # - no Format so format is auto-detected
1182             # - no Name so name is generated automatically with decimal tag number
1183 22         80 AddTagToTable($recordPtr, $tag, { Unknown => 1 });
1184             }
1185              
1186 2462         5166 my $tagInfo = $et->GetTagInfo($recordPtr, $tag);
1187 2462         3125 my $format;
1188             # (could use $$recordPtr{FORMAT} if no Format below, but don't do this to
1189             # be backward compatible with improperly written PhotoMechanic tags)
1190 2462 100       5176 $format = $$tagInfo{Format} if $tagInfo;
1191 2462 100       4549 if (not $format) {
    50          
1192             # guess at "int" format if not specified
1193 230 100 66     1150 $format = 'int' if $len <= 4 and $len != 3 and $val =~ /[\0-\x08]/;
      100        
1194             } elsif ($validate) {
1195 0         0 my ($fmt,$min,$max);
1196 0 0       0 if ($format =~ /(.*)\[(\d+)(,(\d+))?\]/) {
1197 0         0 $fmt = $1;
1198 0         0 $min = $2;
1199 0   0     0 $max = $4 || $2;
1200             } else {
1201 0         0 $fmt = $format;
1202 0         0 $min = $max = 1;
1203             }
1204 0   0     0 my $siz = Image::ExifTool::FormatSize($fmt) || 1;
1205 0         0 $min *= $siz; $max *= $siz;
  0         0  
1206 0 0 0     0 if ($len < $min or $len > $max) {
1207 0 0       0 my $should = ($min == $max) ? $min : ($len < $min ? "$min min" : "$max max");
    0          
1208 0 0       0 my $what = ($len < $siz * $min) ? 'short' : 'long';
1209 0         0 $et->Warn("IPTC $$tagInfo{Name} too $what ($len bytes; should be $should)", 1);
1210             }
1211             }
1212 2462 100       3829 if ($format) {
1213 2407 100       7260 if ($format =~ /^int/) {
    100          
    100          
    50          
1214 332 100       722 if ($len <= 8) { # limit integer conversion to 8 bytes long
1215 328         464 $val = 0;
1216 328         471 my $i;
1217 328         751 for ($i=0; $i<$len; ++$i) {
1218 1006         2035 $val = $val * 256 + ord(substr($$dataPt, $pos+$i, 1));
1219             }
1220             }
1221             } elsif ($format =~ /^string/) {
1222             # some braindead softwares add null terminators
1223 1885 50 33     4139 if ($val =~ s/\0+$// and $validate) {
1224 0         0 $et->Warn("IPTC $$tagInfo{Name} improperly terminated", 1);
1225             }
1226 1885 100 66     8088 if ($rec == 1) {
    100 100        
1227             # handle CodedCharacterSet tag
1228 2 50       12 $xlat = HandleCodedCharset($et, $val) if $tag == 90;
1229             # translate characters if necessary and special characters exist
1230             } elsif ($xlat and $rec < 7 and $val =~ /[\x80-\xff]/) {
1231             # translate to specified character set
1232 2         6 TranslateCodedString($et, \$val, \$xlat, 1);
1233             }
1234             } elsif ($format =~ /^digits/) {
1235 188 50 33     574 if ($val =~ s/\0+$// and $validate) {
1236 0         0 $et->Warn("IPTC $$tagInfo{Name} improperly terminated", 1);
1237             }
1238             } elsif ($format !~ /^undef/) {
1239 0         0 warn("Invalid IPTC format: $format"); # (this would be a programming error)
1240             }
1241             }
1242             $verbose and $et->VerboseInfo($tag, $tagInfo,
1243             Table => $tagTablePtr,
1244             Value => $val,
1245             DataPt => $dataPt,
1246             DataPos => $$dirInfo{DataPos},
1247 2462 50       3962 Size => $len,
1248             Start => $pos,
1249             Extra => ", $recordName record",
1250             Format => $format,
1251             );
1252 2462 100       6992 $et->FoundTag($tagInfo, $val) if $tagInfo;
1253 2462         2936 $success = 1;
1254              
1255 2462         5080 $pos += $len; # increment to next field
1256             }
1257 171         420 delete $$et{SET_GROUP1};
1258 171         337 delete $$et{LOW_PRIORITY_DIR}{IPTC};
1259 171         522 return $success;
1260             }
1261              
1262             1; # end
1263              
1264              
1265             __END__