File Coverage

blib/lib/Image/ExifTool/PICT.pm
Criterion Covered Total %
statement 46 176 26.1
branch 13 136 9.5
condition 3 36 8.3
subroutine 4 6 66.6
pod 0 3 0.0
total 66 357 18.4


line stmt bran cond sub pod time code
1             #------------------------------------------------------------------------------
2             # File: PICT.pm
3             #
4             # Description: Read PICT meta information
5             #
6             # Revisions: 10/10/2005 - P. Harvey Created
7             #
8             # Notes: Extraction of PICT opcodes is still experimental
9             #
10             # - size difference in PixPat color table?? (imagemagick reads only 1 long per entry)
11             # - other differences in the way imagemagick reads 16-bit images
12             #
13             # References: 1) http://developer.apple.com/documentation/mac/QuickDraw/QuickDraw-2.html
14             # 2) http://developer.apple.com/documentation/QuickTime/INMAC/QT/iqImageCompMgr.a.htm
15             #------------------------------------------------------------------------------
16              
17             package Image::ExifTool::PICT;
18              
19 1     1   6002 use strict;
  1         3  
  1         38  
20 1     1   6 use vars qw($VERSION);
  1         2  
  1         43  
21 1     1   8 use Image::ExifTool qw(:DataAccess :Utils);
  1         2  
  1         3350  
22              
23             $VERSION = '1.05';
24              
25             sub ReadPictValue($$$;$);
26              
27             my ($vers, $extended); # PICT version number, and extended flag
28             my ($verbose, $out, $indent); # used in verbose mode
29              
30             # ranges of reserved opcodes.
31             # opcodes at the start of each range must be defined in the tag table
32             my @reserved = (
33             0x0017 => 0x0019, 0x0024 => 0x0027, 0x0035 => 0x0037, 0x003d => 0x003f,
34             0x0045 => 0x0047, 0x004d => 0x004f, 0x0055 => 0x0057, 0x005d => 0x005f,
35             0x0065 => 0x0067, 0x006d => 0x006f, 0x0075 => 0x0077, 0x007d => 0x007f,
36             0x0085 => 0x0087, 0x008d => 0x008f, 0x0092 => 0x0097, 0x00a2 => 0x00af,
37             0x00b0 => 0x00cf, 0x00d0 => 0x00fe, 0x0100 => 0x01ff, 0x0300 => 0x0bfe,
38             0x0c01 => 0x7eff, 0x7f00 => 0x7fff, 0x8000 => 0x80ff, 0x8100 => 0x81ff,
39             0x8201 => 0xffff,
40             );
41              
42             # Apple data structures in PICT images
43             my %structs = (
44             Arc => [
45             rect => 'Rect',
46             startAng => 'int16s',
47             arcAng => 'int16s',
48             ],
49             BitMap => [
50             # (no baseAddr)
51             rowBytes => 'int16u',
52             bounds => 'Rect',
53             ],
54             # BitsRect data for PICT version 1
55             BitsRect1 => [
56             bitMap => 'BitMap',
57             srcRect => 'Rect',
58             dstRect => 'Rect',
59             mode => 'int16u',
60             dataSize => 'int16u',
61             bitData => 'binary[$val{dataSize}]',
62             ],
63             # BitsRect data for PICT version 2
64             BitsRect2 => [
65             pixMap => 'PixMap',
66             colorTable => 'ColorTable',
67             srcRect => 'Rect',
68             dstRect => 'Rect',
69             mode => 'int16u',
70             pixData => \ 'GetPixData($val{pixMap}, $raf)',
71             ],
72             # BitsRgn data for PICT version 1
73             BitsRgn1 => [
74             bitMap => 'BitMap',
75             srcRect => 'Rect',
76             dstRect => 'Rect',
77             mode => 'int16u',
78             maskRgn => 'Rgn',
79             dataSize => 'int16u',
80             bitData => 'binary[$val{dataSize}]',
81             ],
82             # BitsRgn data for PICT version 2
83             BitsRgn2 => [
84             pixMap => 'PixMap',
85             colorTable => 'ColorTable',
86             srcRect => 'Rect',
87             dstRect => 'Rect',
88             mode => 'int16u',
89             maskRgn => 'Rgn',
90             pixData => \ 'GetPixData($val{pixMap}, $raf)',
91             ],
92             ColorSpec => [
93             value => 'int16u',
94             rgb => 'RGBColor',
95             ],
96             ColorTable => [
97             ctSeed => 'int32u',
98             ctFlags => 'int16u',
99             ctSize => 'int16u',
100             ctTable => 'ColorSpec[$val{ctSize}+1]',
101             ],
102             # http://developer.apple.com/documentation/QuickTime/INMAC/QT/iqImageCompMgr.a.htm
103             CompressedQuickTime => [
104             size => 'int32u', # size NOT including size word
105             version => 'int16u',
106             matrix => 'int32u[9]',
107             matteSize => 'int32u',
108             matteRect => 'Rect',
109             mode => 'int16u',
110             srcRect => 'Rect',
111             accuracy => 'int32u',
112             maskSize => 'int32u',
113             matteDescr => 'Int32uData[$val{matteSize} ? 1 : 0]',
114             matteData => 'int8u[$val{matteSize}]',
115             maskRgn => 'int8u[$val{maskSize}]',
116             imageDescr => 'ImageDescription',
117             # size should be $val{imageDescr}->{dataSize}, but this is unreliable
118             imageData => q{binary[$val{size} - 68 - $val{maskSize} - $val{imageDescr}->{size} -
119             ($val{matteSize} ? $val{mattSize} + $val{matteDescr}->{size} : 0)]
120             },
121             ],
122             DirectBitsRect => [
123             baseAddr => 'int32u',
124             pixMap => 'PixMap',
125             srcRect => 'Rect',
126             dstRect => 'Rect',
127             mode => 'int16u',
128             pixData => \ 'GetPixData($val{pixMap}, $raf)',
129             ],
130             DirectBitsRgn => [
131             baseAddr => 'int32u',
132             pixMap => 'PixMap',
133             srcRect => 'Rect',
134             dstRect => 'Rect',
135             mode => 'int16u',
136             maskRgn => 'Rgn',
137             pixData => \ 'GetPixData($val{pixMap}, $raf)',
138             ],
139             # http://developer.apple.com/technotes/qd/qd_01.html
140             FontName => [
141             size => 'int16u', # size NOT including size word
142             oldFontID => 'int16u',
143             nameLen => 'int8u',
144             fontName => 'string[$val{nameLen}]',
145             padding => 'binary[$val{size} - $val{nameLen} - 3]',
146             ],
147             # http://developer.apple.com/documentation/QuickTime/APIREF/imagedescription.htm
148             ImageDescription => [
149             size => 'int32u', # size INCLUDING size word
150             cType => 'string[4]',
151             res1 => 'int32u',
152             res2 => 'int16u',
153             dataRefIndex => 'int16u',
154             version => 'int16u',
155             revision => 'int16u',
156             vendor => 'string[4]',
157             temporalQuality => 'int32u',
158             quality => 'int32u',
159             width => 'int16u',
160             height => 'int16u',
161             hRes => 'fixed32u',
162             vRes => 'fixed32u',
163             dataSize => 'int32u',
164             frameCount => 'int16u',
165             nameLen => 'int8u',
166             compressor => 'string[31]',
167             depth => 'int16u',
168             clutID => 'int16u',
169             clutData => 'binary[$val{size}-86]',
170             ],
171             Int8uText => [
172             val => 'int8u',
173             count => 'int8u',
174             text => 'string[$val{count}]',
175             ],
176             Int8u2Text => [
177             val => 'int8u[2]',
178             count => 'int8u',
179             text => 'string[$val{count}]',
180             ],
181             Int16Data => [
182             size => 'int16u', # size NOT including size word
183             data => 'int8u[$val{size}]',
184             ],
185             Int32uData => [
186             size => 'int32u', # size NOT including size word
187             data => 'int8u[$val{size}]',
188             ],
189             LongComment => [
190             kind => 'int16u',
191             size => 'int16u', # size of data only
192             data => 'binary[$val{size}]',
193             ],
194             PixMap => [
195             # Note: does not contain baseAddr
196             # (except for DirectBits opcodes in which it is loaded separately)
197             rowBytes => 'int16u',
198             bounds => 'Rect',
199             pmVersion => 'int16u',
200             packType => 'int16u',
201             packSize => 'int32u',
202             hRes => 'fixed32s',
203             vRes => 'fixed32s',
204             pixelType => 'int16u',
205             pixelSize => 'int16u',
206             cmpCount => 'int16u',
207             cmpSize => 'int16u',
208             planeBytes => 'int32u',
209             pmTable => 'int32u',
210             pmReserved => 'int32u',
211             ],
212             PixPat => [
213             patType => 'int16u', # 1 = non-dithered, 2 = dithered
214             pat1Data => 'int8u[8]',
215             # dithered PixPat has RGB entry
216             RGB => 'RGBColor[$val{patType} == 2 ? 1 : 0]',
217             # non-dithered PixPat has other stuff instead
218             nonDithered=> 'PixPatNonDithered[$val{patType} == 2 ? 0 : 1]',
219             ],
220             PixPatNonDithered => [
221             pixMap => 'PixMap',
222             colorTable => 'ColorTable',
223             pixData => \ 'GetPixData($val{pixMap}, $raf)',
224             ],
225             Point => [
226             v => 'int16s',
227             h => 'int16s',
228             ],
229             PointText => [
230             txLoc => 'Point',
231             count => 'int8u',
232             text => 'string[$val{count}]',
233             ],
234             Polygon => [
235             polySize => 'int16u',
236             polyBBox => 'Rect',
237             polyPoints => 'int16u[($val{polySize}-10)/2]',
238             ],
239             Rect => [
240             topLeft => 'Point',
241             botRight => 'Point',
242             ],
243             RGBColor => [
244             red => 'int16u',
245             green => 'int16u',
246             blue => 'int16u',
247             ],
248             Rgn => [
249             rgnSize => 'int16u',
250             rgnBBox => 'Rect',
251             data => 'int8u[$val{rgnSize}-10]',
252             ],
253             ShortLine => [
254             pnLoc => 'Point',
255             dh => 'int8s',
256             dv => 'int8s',
257             ],
258             # http://developer.apple.com/documentation/QuickTime/INMAC/QT/iqImageCompMgr.a.htm
259             UncompressedQuickTime => [
260             size => 'int32u', # size NOT including size word
261             version => 'int16u',
262             matrix => 'int32u[9]',
263             matteSize => 'int32u',
264             matteRect => 'Rect',
265             matteDescr => 'Int32uData[$val{matteSize} ? 1 : 0]',
266             matteData => 'binary[$val{matteSize}]',
267             subOpcodeData => q{
268             binary[ $val{size} - 50 -
269             ($val{matteSize} ? $val{mattSize} + $val{matteDescr}->{size} : 0)]
270             },
271             ],
272             );
273              
274             # PICT image opcodes
275             %Image::ExifTool::PICT::Main = (
276             VARS => { NO_LOOKUP => 1 }, # omit tags from lookup
277             NOTES => q{
278             The PICT format contains no true meta information, except for the possible
279             exception of the LongComment opcode. By default, only ImageWidth,
280             ImageHeight and X/YResolution are extracted from a PICT image. Tags in the
281             following table represent image opcodes. Extraction of these tags is
282             experimental, and is only enabled with the Verbose or Unknown options.
283             },
284             0x0000 => {
285             Name => 'Nop',
286             Description => 'No Operation',
287             Format => 'null',
288             },
289             0x0001 => {
290             Name => 'ClipRgn',
291             Description => 'Clipping Region',
292             Format => 'Rgn',
293             },
294             0x0002 => {
295             Name => 'BkPat',
296             Description => 'Background Pattern',
297             Format => 'int8u[8]',
298             },
299             0x0003 => {
300             Name => 'TxFont',
301             Description => 'Font Number',
302             Format => 'int16u',
303             },
304             0x0004 => {
305             Name => 'TxFace',
306             Description => 'Text Font Style',
307             Format => 'int8u',
308             },
309             0x0005 => {
310             Name => 'TxMode',
311             Description => 'Text Source Mode',
312             Format => 'int16u',
313             },
314             0x0006 => {
315             Name => 'SpExtra',
316             Description => 'Extra Space',
317             Format => 'fixed32s',
318             },
319             0x0007 => {
320             Name => 'PnSize',
321             Description => 'Pen Size',
322             Format => 'Point',
323             },
324             0x0008 => {
325             Name => 'PnMode',
326             Description => 'Pen Mode',
327             Format => 'int16u',
328             },
329             0x0009 => {
330             Name => 'PnPat',
331             Description => 'Pen Pattern',
332             Format => 'int8u[8]',
333             },
334             0x000a => {
335             Name => 'FillPat',
336             Description => 'Fill Pattern',
337             Format => 'int8u[8]',
338             },
339             0x000b => {
340             Name => 'OvSize',
341             Description => 'Oval Size',
342             Format => 'Point',
343             },
344             0x000c => {
345             Name => 'Origin',
346             Format => 'Point',
347             },
348             0x000d => {
349             Name => 'TxSize',
350             Description => 'Text Size',
351             Format => 'int16u',
352             },
353             0x000e => {
354             Name => 'FgColor',
355             Description => 'Foreground Color',
356             Format => 'int32u',
357             },
358             0x000f => {
359             Name => 'BkColor',
360             Description => 'Background Color',
361             Format => 'int32u',
362             },
363             0x0010 => {
364             Name => 'TxRatio',
365             Description => 'Text Ratio',
366             Format => 'Rect',
367             },
368             0x0011 => {
369             Name => 'VersionOp',
370             Description => 'Version',
371             Format => 'int8u',
372             },
373             0x0012 => {
374             Name => 'BkPixPat',
375             Description => 'Background Pixel Pattern',
376             Format => 'PixPat',
377             },
378             0x0013 => {
379             Name => 'PnPixPat',
380             Description => 'Pen Pixel Pattern',
381             Format => 'PixPat',
382             },
383             0x0014 => {
384             Name => 'FillPixPat',
385             Description => 'Fill Pixel Pattern',
386             Format => 'PixPat',
387             },
388             0x0015 => {
389             Name => 'PnLocHFrac',
390             Description => 'Fractional Pen Position',
391             Format => 'int16u',
392             },
393             0x0016 => {
394             Name => 'ChExtra',
395             Description => 'Added Width for NonSpace Characters',
396             Format => 'int16u',
397             },
398             0x0017 => {
399             Name => 'Reserved',
400             Format => 'Unknown',
401             },
402             0x001a => {
403             Name => 'RGBFgCol',
404             Description => 'Foreground Color',
405             Format => 'RGBColor',
406             },
407             0x001b => {
408             Name => 'RGBBkCol',
409             Description => 'Background Color',
410             Format => 'RGBColor',
411             },
412             0x001c => {
413             Name => 'HiliteMode',
414             Description => 'Highlight Mode Flag',
415             Format => 'null',
416             },
417             0x001d => {
418             Name => 'HiliteColor',
419             Description => 'Highlight Color',
420             Format => 'RGBColor',
421             },
422             0x001e => {
423             Name => 'DefHilite',
424             Description => 'Use Default Highlight Color',
425             Format => 'null',
426             },
427             0x001f => {
428             Name => 'OpColor',
429             Format => 'RGBColor',
430             },
431             0x0020 => {
432             Name => 'Line',
433             Format => 'Rect',
434             },
435             0x0021 => {
436             Name => 'LineFrom',
437             Format => 'Point',
438             },
439             0x0022 => {
440             Name => 'ShortLine',
441             Format => 'ShortLine',
442             },
443             0x0023 => {
444             Name => 'ShortLineFrom',
445             Format => 'int8u[2]',
446             },
447             0x0024 => {
448             Name => 'Reserved',
449             Format => 'Int16Data',
450             },
451             0x0028 => {
452             Name => 'LongText',
453             Format => 'PointText',
454             },
455             0x0029 => {
456             Name => 'DHText',
457             Format => 'Int8uText',
458             },
459             0x002a => {
460             Name => 'DVText',
461             Format => 'Int8uText',
462             },
463             0x002b => {
464             Name => 'DHDVText',
465             Format => 'Int8u2Text',
466             },
467             0x002c => {
468             Name => 'FontName',
469             Format => 'FontName',
470             },
471             0x002d => {
472             Name => 'LineJustify',
473             Format => 'int8u[10]',
474             },
475             0x002e => {
476             Name => 'GlyphState',
477             Format => 'int8u[8]',
478             },
479             0x002f => {
480             Name => 'Reserved',
481             Format => 'Int16Data',
482             },
483             0x0030 => {
484             Name => 'FrameRect',
485             Format => 'Rect',
486             },
487             0x0031 => {
488             Name => 'PaintRect',
489             Format => 'Rect',
490             },
491             0x0032 => {
492             Name => 'EraseRect',
493             Format => 'Rect',
494             },
495             0x0033 => {
496             Name => 'InvertRect',
497             Format => 'Rect',
498             },
499             0x0034 => {
500             Name => 'FillRect',
501             Format => 'Rect',
502             },
503             0x0035 => {
504             Name => 'Reserved',
505             Format => 'Rect',
506             },
507             0x0038 => {
508             Name => 'FrameSameRect',
509             Format => 'null',
510             },
511             0x0039 => {
512             Name => 'PaintSameRect',
513             Format => 'null',
514             },
515             0x003a => {
516             Name => 'EraseSameRect',
517             Format => 'null',
518             },
519             0x003b => {
520             Name => 'InvertSameRect',
521             Format => 'null',
522             },
523             0x003c => {
524             Name => 'FillSameRect',
525             Format => 'null',
526             },
527             0x003d => {
528             Name => 'Reserved',
529             Format => 'null',
530             },
531             0x0040 => {
532             Name => 'FrameRRect',
533             Format => 'Rect',
534             },
535             0x0041 => {
536             Name => 'PaintRRect',
537             Format => 'Rect',
538             },
539             0x0042 => {
540             Name => 'EraseRRect',
541             Format => 'Rect',
542             },
543             0x0043 => {
544             Name => 'InvertRRect',
545             Format => 'Rect',
546             },
547             0x0044 => {
548             Name => 'FillRRect',
549             Format => 'Rect',
550             },
551             0x0045 => {
552             Name => 'Reserved',
553             Format => 'Rect',
554             },
555             0x0048 => {
556             Name => 'FrameSameRRect',
557             Format => 'null',
558             },
559             0x0049 => {
560             Name => 'PaintSameRRect',
561             Format => 'null',
562             },
563             0x004a => {
564             Name => 'EraseSameRRect',
565             Format => 'null',
566             },
567             0x004b => {
568             Name => 'InvertSameRRect',
569             Format => 'null',
570             },
571             0x004c => {
572             Name => 'FillSameRRect',
573             Format => 'null',
574             },
575             0x004d => {
576             Name => 'Reserved',
577             Format => 'null',
578             },
579             0x0050 => {
580             Name => 'FrameOval',
581             Format => 'Rect',
582             },
583             0x0051 => {
584             Name => 'PaintOval',
585             Format => 'Rect',
586             },
587             0x0052 => {
588             Name => 'EraseOval',
589             Format => 'Rect',
590             },
591             0x0053 => {
592             Name => 'InvertOval',
593             Format => 'Rect',
594             },
595             0x0054 => {
596             Name => 'FillOval',
597             Format => 'Rect',
598             },
599             0x0055 => {
600             Name => 'Reserved',
601             Format => 'Rect',
602             },
603             0x0058 => {
604             Name => 'FrameSameOval',
605             Format => 'null',
606             },
607             0x0059 => {
608             Name => 'PaintSameOval',
609             Format => 'null',
610             },
611             0x005a => {
612             Name => 'EraseSameOval',
613             Format => 'null',
614             },
615             0x005b => {
616             Name => 'InvertSameOval',
617             Format => 'null',
618             },
619             0x005c => {
620             Name => 'FillSameOval',
621             Format => 'null',
622             },
623             0x005d => {
624             Name => 'Reserved',
625             Format => 'null',
626             },
627             0x0060 => {
628             Name => 'FrameArc',
629             Format => 'Arc',
630             },
631             0x0061 => {
632             Name => 'PaintArc',
633             Format => 'Arc',
634             },
635             0x0062 => {
636             Name => 'EraseArc',
637             Format => 'Arc',
638             },
639             0x0063 => {
640             Name => 'InvertArc',
641             Format => 'Arc',
642             },
643             0x0064 => {
644             Name => 'FillArc',
645             Format => 'Arc',
646             },
647             0x0065 => {
648             Name => 'Reserved',
649             Format => 'Arc',
650             },
651             0x0068 => {
652             Name => 'FrameSameArc',
653             Format => 'Point',
654             },
655             0x0069 => {
656             Name => 'PaintSameArc',
657             Format => 'Point',
658             },
659             0x006a => {
660             Name => 'EraseSameArc',
661             Format => 'Point',
662             },
663             0x006b => {
664             Name => 'InvertSameArc',
665             Format => 'Point',
666             },
667             0x006c => {
668             Name => 'FillSameArc',
669             Format => 'Point',
670             },
671             0x006d => {
672             Name => 'Reserved',
673             Format => 'int32u',
674             },
675             0x0070 => {
676             Name => 'FramePoly',
677             Format => 'Polygon',
678             },
679             0x0071 => {
680             Name => 'PaintPoly',
681             Format => 'Polygon',
682             },
683             0x0072 => {
684             Name => 'ErasePoly',
685             Format => 'Polygon',
686             },
687             0x0073 => {
688             Name => 'InvertPoly',
689             Format => 'Polygon',
690             },
691             0x0074 => {
692             Name => 'FillPoly',
693             Format => 'Polygon',
694             },
695             0x0075 => {
696             Name => 'Reserved',
697             Format => 'Polygon',
698             },
699             0x0078 => {
700             Name => 'FrameSamePoly',
701             Format => 'null',
702             },
703             0x0079 => {
704             Name => 'PaintSamePoly',
705             Format => 'null',
706             },
707             0x007a => {
708             Name => 'EraseSamePoly',
709             Format => 'null',
710             },
711             0x007b => {
712             Name => 'InvertSamePoly',
713             Format => 'null',
714             },
715             0x007c => {
716             Name => 'FillSamePoly',
717             Format => 'null',
718             },
719             0x007d => {
720             Name => 'Reserved',
721             Format => 'null',
722             },
723             0x0080 => {
724             Name => 'FrameRgn',
725             Format => 'Rgn',
726             },
727             0x0081 => {
728             Name => 'PaintRgn',
729             Format => 'Rgn',
730             },
731             0x0082 => {
732             Name => 'EraseRgn',
733             Format => 'Rgn',
734             },
735             0x0083 => {
736             Name => 'InvertRgn',
737             Format => 'Rgn',
738             },
739             0x0084 => {
740             Name => 'FillRgn',
741             Format => 'Rgn',
742             },
743             0x0085 => {
744             Name => 'Reserved',
745             Format => 'Rgn',
746             },
747             0x0088 => {
748             Name => 'FrameSameRgn',
749             Format => 'null',
750             },
751             0x0089 => {
752             Name => 'PaintSameRgn',
753             Format => 'null',
754             },
755             0x008a => {
756             Name => 'EraseSameRgn',
757             Format => 'null',
758             },
759             0x008b => {
760             Name => 'InvertSameRgn',
761             Format => 'null',
762             },
763             0x008c => {
764             Name => 'FillSameRgn',
765             Format => 'null',
766             },
767             0x008d => {
768             Name => 'Reserved',
769             Format => 'null',
770             },
771             0x0090 => {
772             Name => 'BitsRect',
773             Description => 'CopyBits with Clipped Rectangle',
774             Format => 'BitsRect#', # (version-dependent format)
775             },
776             0x0091 => {
777             Name => 'BitsRgn',
778             Description => 'CopyBits with Clipped Region',
779             Format => 'BitsRgn#', # (version-dependent format)
780             },
781             0x0092 => {
782             Name => 'Reserved',
783             Format => 'Int16Data',
784             },
785             0x0098 => {
786             Name => 'PackBitsRect',
787             Description => 'Packed CopyBits with Clipped Rectangle',
788             Format => 'BitsRect#', # (version-dependent format)
789             },
790             0x0099 => {
791             Name => 'PackBitsRgn',
792             Description => 'Packed CopyBits with Clipped Region',
793             Format => 'BitsRgn#', # (version-dependent format)
794             },
795             0x009a => {
796             Name => 'DirectBitsRect',
797             Format => 'DirectBitsRect',
798             },
799             0x009b => {
800             Name => 'DirectBitsRgn',
801             Format => 'DirectBitsRgn',
802             },
803             0x009c => {
804             Name => 'Reserved',
805             Format => 'Int16Data',
806             },
807             0x009d => {
808             Name => 'Reserved',
809             Format => 'Int16Data',
810             },
811             0x009e => {
812             Name => 'Reserved',
813             Format => 'Int16Data',
814             },
815             0x009f => {
816             Name => 'Reserved',
817             Format => 'Int16Data',
818             },
819             0x00a0 => {
820             Name => 'ShortComment',
821             Format => 'int16u',
822             },
823             0x00a1 => [
824             # this list for documentation only [currently not extracted]
825             {
826             # (not actually a full Photohop IRB record it appears, but it does start
827             # with '8BIM', and does contain resolution information at offset 0x0a)
828             Name => 'LongComment', # kind = 498
829             Format => 'LongComment',
830             SubDirectory => { TagTable => 'Image::ExifTool::Photoshop::Main' },
831             },
832             {
833             Name => 'LongComment', # kind = 224
834             Format => 'LongComment',
835             SubDirectory => {
836             TagTable => 'Image::ExifTool::ICC_Profile::Main',
837             Start => '$valuePtr + 4',
838             },
839             },
840             ],
841             0x00a2 => {
842             Name => 'Reserved',
843             Format => 'Int16Data',
844             },
845             0x00b0 => {
846             Name => 'Reserved',
847             Format => 'null',
848             },
849             0x00d0 => {
850             Name => 'Reserved',
851             Format => 'Int32uData',
852             },
853             0x00ff => {
854             Name => 'OpEndPic',
855             Description => 'End of picture',
856             Format => 'null', # 2 for version 2!?
857             },
858             0x0100 => {
859             Name => 'Reserved',
860             Format => 'int16u',
861             },
862             0x0200 => {
863             Name => 'Reserved',
864             Format => 'int32u',
865             },
866             0x02ff => {
867             Name => 'Version',
868             Description => 'Version number of picture',
869             Format => 'int16u',
870             },
871             0x0300 => {
872             Name => 'Reserved',
873             Format => 'int16u',
874             },
875             0x0bff => {
876             Name => 'Reserved',
877             Format => 'int8u[22]',
878             },
879             0x0c00 => {
880             Name => 'HeaderOp',
881             Format => 'int16u[12]',
882             },
883             0x0c01 => {
884             Name => 'Reserved',
885             Format => 'int8u[24]',
886             },
887             0x7f00 => {
888             Name => 'Reserved',
889             Format => 'int8u[254]',
890             },
891             0x8000 => {
892             Name => 'Reserved',
893             Format => 'null',
894             },
895             0x8100 => {
896             Name => 'Reserved',
897             Format => 'Int32uData',
898             },
899             0x8200 => {
900             Name => 'CompressedQuickTime',
901             Format => 'CompressedQuickTime',
902             },
903             0x8201 => {
904             Name => 'UncompressedQuickTime',
905             Format => 'Int32uData',
906             },
907             0xffff => {
908             Name => 'Reserved',
909             Format => 'Int32uData',
910             },
911             );
912              
913             # picture comment 'kind' codes
914             # http://developer.apple.com/technotes/qd/qd_10.html
915             my %commentKind = (
916             150 => 'TextBegin',
917             151 => 'TextEnd',
918             152 => 'StringBegin',
919             153 => 'StringEnd',
920             154 => 'TextCenter',
921             155 => 'LineLayoutOff',
922             156 => 'LineLayoutOn',
923             157 => 'ClientLineLayout',
924             160 => 'PolyBegin',
925             161 => 'PolyEnd',
926             163 => 'PolyIgnore',
927             164 => 'PolySmooth',
928             165 => 'PolyClose',
929             180 => 'DashedLine',
930             181 => 'DashedStop',
931             182 => 'SetLineWidth',
932             190 => 'PostScriptBegin',
933             191 => 'PostScriptEnd',
934             192 => 'PostScriptHandle',
935             193 => 'PostScriptFile',
936             194 => 'TextIsPostScript',
937             195 => 'ResourcePS',
938             196 => 'PSBeginNoSave',
939             197 => 'SetGrayLevel',
940             200 => 'RotateBegin',
941             201 => 'RotateEnd',
942             202 => 'RotateCenter',
943             210 => 'FormsPrinting',
944             211 => 'EndFormsPrinting',
945             224 => '',
946             498 => '',
947             1000 => 'BitMapThinningOff',
948             1001 => 'BitMapThinningOn',
949             );
950              
951             #------------------------------------------------------------------------------
952             # Get PixData data
953             # Inputs: 0) reference to PixMap, 1) RAF reference
954             # Returns: reference to PixData or undef on error
955             sub GetPixData($$)
956             {
957 0     0 0 0 my ($pixMap, $raf) = @_;
958 0         0 my $packType = $pixMap->{packType};
959 0         0 my $rowBytes = $pixMap->{rowBytes} & 0x3fff; # remove flags bits
960             my $height = $pixMap->{bounds}->{botRight}->{v} -
961 0         0 $pixMap->{bounds}->{topLeft}->{v};
962 0         0 my ($data, $size, $buff, $i);
963              
964 0 0 0     0 if ($packType == 1 or $rowBytes < 8) { # unpacked data
    0          
965 0         0 $size = $rowBytes * $height;
966 0 0       0 return undef unless $raf->Read($data, $size) == $size;
967             } elsif ($packType == 2) { # pad byte dropped
968 0         0 $size = int($rowBytes * $height * 3 / 4 + 0.5);
969 0 0       0 return undef unless $raf->Read($data, $size) == $size;
970             } else {
971 0         0 $data = '';
972 0         0 for ($i=0; $i<$height; ++$i) {
973 0 0       0 if ($rowBytes > 250) {
974 0 0       0 $raf->Read($buff,2) == 2 or return undef;
975 0         0 $size = unpack('n',$buff);
976             } else {
977 0 0       0 $raf->Read($buff,1) == 1 or return undef;
978 0         0 $size = unpack('C',$buff);
979             }
980 0         0 $data .= $buff;
981 0 0       0 $raf->Read($buff,$size) == $size or return undef;
982 0         0 $data .= $buff;
983             }
984             }
985 0         0 return \$data;
986             }
987              
988             #------------------------------------------------------------------------------
989             # Read value from PICT file
990             # Inputs: 0) RAF reference, 1) tag, 2) format, 3) optional count
991             # Returns: value, reference to structure hash, or undef on error
992             sub ReadPictValue($$$;$)
993             {
994 0     0 0 0 my ($raf, $tag, $format, $count) = @_;
995 0 0       0 return undef unless $format;
996 0 0       0 unless (defined $count) {
997 0 0       0 if ($format =~ /(.+)\[(.+)\]/s) {
998 0         0 $format = $1;
999 0         0 $count = $2;
1000             } else {
1001 0         0 $count = 1; # count undefined: assume 1
1002             }
1003             }
1004 0 0       0 my $cntStr = ($count == 1) ? '' : "[$count]";
1005             # no size if count is 0
1006 0 0       0 my $size = $count ? Image::ExifTool::FormatSize($format) : 0;
1007 0 0 0     0 if (defined $size or $format eq 'null') {
1008 0         0 my $val;
1009 0 0       0 if ($size) {
1010 0         0 my $buff;
1011 0         0 $size *= $count;
1012 0 0       0 $raf->Read($buff, $size) == $size or return undef;
1013 0         0 $val = ReadValue(\$buff, 0, $format, $count, $size);
1014             } else {
1015 0         0 $val = '';
1016             }
1017 0 0       0 if ($verbose) {
1018 0         0 print $out "${indent}$tag ($format$cntStr)";
1019 0 0       0 if ($size) {
1020 0 0       0 if (not defined $val) {
    0          
1021 0         0 print $out " = \n";
1022             } elsif ($format eq 'binary') {
1023 0         0 print $out " = \n";
1024 0 0       0 if ($verbose > 2) {
1025 0         0 my %parms = ( Out => $out );
1026 0 0       0 $parms{MaxLen} = 96 if $verbose < 4;
1027 0         0 HexDump(\$val, undef, %parms);
1028             }
1029             } else {
1030 0         0 print $out " = $val\n";
1031             }
1032             } else {
1033 0         0 print $out "\n";
1034             }
1035             }
1036 0 0 0     0 return \$val if $format eq 'binary' and defined $val;
1037 0         0 return $val;
1038             }
1039 0 0       0 $verbose and print $out "${indent}$tag ($format$cntStr):\n";
1040 0 0       0 my $struct = $structs{$format} or return undef;
1041 0         0 my ($c, @vals);
1042 0         0 for ($c=0; $c<$count; ++$c) {
1043 0         0 my (%val, $i);
1044 0         0 for ($i=0; ; $i+=2) {
1045 0 0       0 my $tag = $$struct[$i] or last;
1046 0         0 my $fmt = $$struct[$i+1];
1047 0         0 my ($cnt, $val);
1048 0         0 $indent .= ' ';
1049 0 0       0 if (ref $fmt) {
    0          
1050 0         0 $val = eval $$fmt;
1051 0 0       0 $@ and warn $@;
1052 0 0 0     0 if ($verbose and defined $val) {
1053 0         0 printf $out "${indent}$tag (binary[%d]) = \n",length($$val);
1054 0 0       0 if ($verbose > 2) {
1055 0         0 my %parms = ( Out => $out );
1056 0 0       0 $parms{MaxLen} = 96 if $verbose < 4;
1057 0         0 HexDump($val, undef, %parms);
1058             }
1059             }
1060             } elsif ($fmt =~ /(.+)\[(.+)\]/s) {
1061 0         0 $fmt = $1;
1062 0         0 $cnt = eval $2;
1063 0 0       0 $@ and warn $@;
1064 0         0 $val = ReadPictValue($raf, $tag, $fmt, $cnt);
1065             } else {
1066 0         0 $val = ReadPictValue($raf, $tag, $fmt);
1067             }
1068 0         0 $indent = substr($indent, 2);
1069 0 0       0 return undef unless defined $val;
1070 0         0 $val{$tag} = $val;
1071             }
1072 0 0       0 return \%val if $count == 1;
1073 0         0 push @vals, \%val;
1074             }
1075 0         0 return \@vals;
1076             }
1077              
1078             #------------------------------------------------------------------------------
1079             # Extract meta information from a PICT image
1080             # Inputs: 0) ExifTool object reference, 1) dirInfo reference
1081             # Returns: 1 on success, 0 if this wasn't a valid PICT image
1082             sub ProcessPICT($$)
1083             {
1084 1     1 0 3 my ($et, $dirInfo) = @_;
1085 1         3 my $raf = $$dirInfo{RAF};
1086 1         5 $verbose = $et->Options('Verbose');
1087 1         4 $out = $et->Options('TextOut');
1088 1         3 $indent = '';
1089 1         2 my ($buff, $tried, @hdr, $op, $hRes, $vRes);
1090              
1091             # recognize both PICT files and PICT resources (PICT files have a
1092             # 512-byte header that we ignore, but PICT resources do not)
1093 1         2 for (;;) {
1094 1 50       4 $raf->Read($buff, 12) == 12 or return 0;
1095 1         8 @hdr = unpack('x2n5', $buff);
1096 1         3 $op = pop @hdr;
1097             # check for PICT version 1 format
1098 1 50       12 if ($op == 0x1101) {
1099 0         0 $vers = 1;
1100 0         0 undef $extended;
1101 0         0 last;
1102             }
1103             # check for PICT version 2 format
1104 1 50       5 if ($op == 0x0011) {
1105 1 50       5 $raf->Read($buff, 28) == 28 or return 0;
1106 1 50       5 if ($buff =~ /^\x02\xff\x0c\x00\xff\xff/) {
1107 0         0 $vers = 2;
1108 0         0 undef $extended;
1109 0         0 last;
1110             }
1111 1 50       6 if ($buff =~ /^\x02\xff\x0c\x00\xff\xfe/) {
1112 1         3 $vers = 2;
1113 1         2 $extended = 1;
1114 1         5 ($hRes, $vRes) = unpack('x8N2', $buff);
1115 1         3 last;
1116             }
1117             }
1118 0 0       0 return 0 if $tried;
1119 0         0 $tried = 1;
1120 0 0       0 $raf->Seek(512, 0) or return 0;
1121             }
1122             # make the bounding rect signed
1123 1         3 foreach (@hdr) {
1124 4 50       10 $_ >= 0x8000 and $_ -= 0x10000;
1125             }
1126 1         3 my $w = $hdr[3] - $hdr[1];
1127 1         2 my $h = $hdr[2] - $hdr[0];
1128 1 50 33     16 return 0 unless $w > 0 and $h > 0;
1129              
1130 1         7 SetByteOrder('MM');
1131              
1132 1 50       3 if ($extended) {
1133             # extended version 2 pictures contain resolution information
1134             # and image bounds are in 72-dpi equivalent units
1135 1         6 $hRes = GetFixed32s(\$buff, 8);
1136 1         5 $vRes = GetFixed32s(\$buff, 12);
1137 1 50 33     12 return 0 unless $hRes and $vRes;
1138 1         3 $w = int($w * $hRes / 72 + 0.5);
1139 1         4 $h = int($h * $vRes / 72 + 0.5);
1140             }
1141 1         6 $et->SetFileType();
1142 1         5 $et->FoundTag('ImageWidth', $w);
1143 1         4 $et->FoundTag('ImageHeight', $h);
1144 1 50       6 $et->FoundTag('XResolution', $hRes) if $hRes;
1145 1 50       7 $et->FoundTag('YResolution', $vRes) if $vRes;
1146              
1147             # don't extract image opcodes unless verbose
1148 1 50 33     22 return 1 unless $verbose or $et->Options('Unknown');
1149              
1150 0 0         $verbose and printf $out "PICT version $vers%s\n", $extended ? ' extended' : '';
    0          
1151              
1152 0           my $tagTablePtr = GetTagTable('Image::ExifTool::PICT::Main');
1153              
1154 0           my $success;
1155 0           for (;;) {
1156 0 0         if ($vers == 1) {
1157 0 0         $raf->Read($buff, 1) == 1 or last;
1158 0           $op = ord($buff);
1159             } else {
1160             # must start version 2 opcode on an even byte
1161 0 0         $raf->Read($buff, 1) if $raf->Tell() & 0x01;
1162 0 0         $raf->Read($buff, 2) == 2 or last;
1163 0           $op = unpack('n', $buff);
1164             }
1165 0           my $tagInfo = $et->GetTagInfo($tagTablePtr, $op);
1166 0 0         unless ($tagInfo) {
1167 0           my $i;
1168             # search for reserved tag info
1169 0           for ($i=0; $i
1170 0 0         next unless $op >= $reserved[$i];
1171 0 0         last if $op > $reserved[$i+1];
1172 0           $tagInfo = $et->GetTagInfo($tagTablePtr, $reserved[$i]);
1173 0           last;
1174             }
1175 0 0         last unless $tagInfo;
1176             }
1177 0 0         if ($op == 0xff) {
1178 0 0         $verbose and print $out "End of picture\n";
1179 0           $success = 1;
1180 0           last;
1181             }
1182 0           my $format = $$tagInfo{Format};
1183 0 0         unless ($format) {
1184 0           $et->Warn("Missing format for $$tagInfo{Name}");
1185 0           last;
1186             }
1187             # replace version number for version-dependent formats
1188 0           $format =~ s/#$/$vers/;
1189 0           my $wid = $vers * 2;
1190 0 0         $verbose and printf $out "Tag 0x%.${wid}x, ", $op;
1191 0           my $val = ReadPictValue($raf, $$tagInfo{Name}, $format);
1192 0 0         unless (defined $val) {
1193 0           $et->Warn("Error reading $$tagInfo{Name} information");
1194 0           last;
1195             }
1196 0 0         if (ref $val eq 'HASH') {
1197             # extract JPEG image from CompressedQuickTime imageData
1198 0 0 0       if ($$tagInfo{Name} eq 'CompressedQuickTime' and
      0        
      0        
      0        
      0        
1199             ref $val->{imageDescr} eq 'HASH' and
1200             $val->{imageDescr}->{compressor} and
1201             $val->{imageDescr}->{compressor} eq 'Photo - JPEG' and
1202             ref $val->{imageData} eq 'SCALAR' and
1203             $et->ValidateImage($val->{imageData}, 'PreviewImage'))
1204             {
1205 0           $et->FoundTag('PreviewImage', $val->{imageData});
1206             }
1207             } else {
1208             # $et->FoundTag($tagInfo, $val);
1209             }
1210             }
1211 0 0         $success or $et->Warn('End of picture not found');
1212 0           return 1;
1213             }
1214              
1215             1; # end
1216              
1217             __END__