test_pie/external/exiftool/lib/Image/ExifTool/PICT.pm

1261 lines
33 KiB
Perl
Raw Normal View History

2023-09-14 11:12:02 +02:00
#------------------------------------------------------------------------------
# File: PICT.pm
#
# Description: Read PICT meta information
#
# Revisions: 10/10/2005 - P. Harvey Created
#
# Notes: Extraction of PICT opcodes is still experimental
#
# - size difference in PixPat color table?? (imagemagick reads only 1 long per entry)
# - other differences in the way imagemagick reads 16-bit images
#
# References: 1) http://developer.apple.com/documentation/mac/QuickDraw/QuickDraw-2.html
# 2) http://developer.apple.com/documentation/QuickTime/INMAC/QT/iqImageCompMgr.a.htm
#------------------------------------------------------------------------------
package Image::ExifTool::PICT;
use strict;
use vars qw($VERSION);
use Image::ExifTool qw(:DataAccess :Utils);
$VERSION = '1.05';
sub ReadPictValue($$$;$);
my ($vers, $extended); # PICT version number, and extended flag
my ($verbose, $out, $indent); # used in verbose mode
# ranges of reserved opcodes.
# opcodes at the start of each range must be defined in the tag table
my @reserved = (
0x0017 => 0x0019, 0x0024 => 0x0027, 0x0035 => 0x0037, 0x003d => 0x003f,
0x0045 => 0x0047, 0x004d => 0x004f, 0x0055 => 0x0057, 0x005d => 0x005f,
0x0065 => 0x0067, 0x006d => 0x006f, 0x0075 => 0x0077, 0x007d => 0x007f,
0x0085 => 0x0087, 0x008d => 0x008f, 0x0092 => 0x0097, 0x00a2 => 0x00af,
0x00b0 => 0x00cf, 0x00d0 => 0x00fe, 0x0100 => 0x01ff, 0x0300 => 0x0bfe,
0x0c01 => 0x7eff, 0x7f00 => 0x7fff, 0x8000 => 0x80ff, 0x8100 => 0x81ff,
0x8201 => 0xffff,
);
# Apple data structures in PICT images
my %structs = (
Arc => [
rect => 'Rect',
startAng => 'int16s',
arcAng => 'int16s',
],
BitMap => [
# (no baseAddr)
rowBytes => 'int16u',
bounds => 'Rect',
],
# BitsRect data for PICT version 1
BitsRect1 => [
bitMap => 'BitMap',
srcRect => 'Rect',
dstRect => 'Rect',
mode => 'int16u',
dataSize => 'int16u',
bitData => 'binary[$val{dataSize}]',
],
# BitsRect data for PICT version 2
BitsRect2 => [
pixMap => 'PixMap',
colorTable => 'ColorTable',
srcRect => 'Rect',
dstRect => 'Rect',
mode => 'int16u',
pixData => \ 'GetPixData($val{pixMap}, $raf)',
],
# BitsRgn data for PICT version 1
BitsRgn1 => [
bitMap => 'BitMap',
srcRect => 'Rect',
dstRect => 'Rect',
mode => 'int16u',
maskRgn => 'Rgn',
dataSize => 'int16u',
bitData => 'binary[$val{dataSize}]',
],
# BitsRgn data for PICT version 2
BitsRgn2 => [
pixMap => 'PixMap',
colorTable => 'ColorTable',
srcRect => 'Rect',
dstRect => 'Rect',
mode => 'int16u',
maskRgn => 'Rgn',
pixData => \ 'GetPixData($val{pixMap}, $raf)',
],
ColorSpec => [
value => 'int16u',
rgb => 'RGBColor',
],
ColorTable => [
ctSeed => 'int32u',
ctFlags => 'int16u',
ctSize => 'int16u',
ctTable => 'ColorSpec[$val{ctSize}+1]',
],
# http://developer.apple.com/documentation/QuickTime/INMAC/QT/iqImageCompMgr.a.htm
CompressedQuickTime => [
size => 'int32u', # size NOT including size word
version => 'int16u',
matrix => 'int32u[9]',
matteSize => 'int32u',
matteRect => 'Rect',
mode => 'int16u',
srcRect => 'Rect',
accuracy => 'int32u',
maskSize => 'int32u',
matteDescr => 'Int32uData[$val{matteSize} ? 1 : 0]',
matteData => 'int8u[$val{matteSize}]',
maskRgn => 'int8u[$val{maskSize}]',
imageDescr => 'ImageDescription',
# size should be $val{imageDescr}->{dataSize}, but this is unreliable
imageData => q{binary[$val{size} - 68 - $val{maskSize} - $val{imageDescr}->{size} -
($val{matteSize} ? $val{mattSize} + $val{matteDescr}->{size} : 0)]
},
],
DirectBitsRect => [
baseAddr => 'int32u',
pixMap => 'PixMap',
srcRect => 'Rect',
dstRect => 'Rect',
mode => 'int16u',
pixData => \ 'GetPixData($val{pixMap}, $raf)',
],
DirectBitsRgn => [
baseAddr => 'int32u',
pixMap => 'PixMap',
srcRect => 'Rect',
dstRect => 'Rect',
mode => 'int16u',
maskRgn => 'Rgn',
pixData => \ 'GetPixData($val{pixMap}, $raf)',
],
# http://developer.apple.com/technotes/qd/qd_01.html
FontName => [
size => 'int16u', # size NOT including size word
oldFontID => 'int16u',
nameLen => 'int8u',
fontName => 'string[$val{nameLen}]',
padding => 'binary[$val{size} - $val{nameLen} - 3]',
],
# http://developer.apple.com/documentation/QuickTime/APIREF/imagedescription.htm
ImageDescription => [
size => 'int32u', # size INCLUDING size word
cType => 'string[4]',
res1 => 'int32u',
res2 => 'int16u',
dataRefIndex => 'int16u',
version => 'int16u',
revision => 'int16u',
vendor => 'string[4]',
temporalQuality => 'int32u',
quality => 'int32u',
width => 'int16u',
height => 'int16u',
hRes => 'fixed32u',
vRes => 'fixed32u',
dataSize => 'int32u',
frameCount => 'int16u',
nameLen => 'int8u',
compressor => 'string[31]',
depth => 'int16u',
clutID => 'int16u',
clutData => 'binary[$val{size}-86]',
],
Int8uText => [
val => 'int8u',
count => 'int8u',
text => 'string[$val{count}]',
],
Int8u2Text => [
val => 'int8u[2]',
count => 'int8u',
text => 'string[$val{count}]',
],
Int16Data => [
size => 'int16u', # size NOT including size word
data => 'int8u[$val{size}]',
],
Int32uData => [
size => 'int32u', # size NOT including size word
data => 'int8u[$val{size}]',
],
LongComment => [
kind => 'int16u',
size => 'int16u', # size of data only
data => 'binary[$val{size}]',
],
PixMap => [
# Note: does not contain baseAddr
# (except for DirectBits opcodes in which it is loaded separately)
rowBytes => 'int16u',
bounds => 'Rect',
pmVersion => 'int16u',
packType => 'int16u',
packSize => 'int32u',
hRes => 'fixed32s',
vRes => 'fixed32s',
pixelType => 'int16u',
pixelSize => 'int16u',
cmpCount => 'int16u',
cmpSize => 'int16u',
planeBytes => 'int32u',
pmTable => 'int32u',
pmReserved => 'int32u',
],
PixPat => [
patType => 'int16u', # 1 = non-dithered, 2 = dithered
pat1Data => 'int8u[8]',
# dithered PixPat has RGB entry
RGB => 'RGBColor[$val{patType} == 2 ? 1 : 0]',
# non-dithered PixPat has other stuff instead
nonDithered=> 'PixPatNonDithered[$val{patType} == 2 ? 0 : 1]',
],
PixPatNonDithered => [
pixMap => 'PixMap',
colorTable => 'ColorTable',
pixData => \ 'GetPixData($val{pixMap}, $raf)',
],
Point => [
v => 'int16s',
h => 'int16s',
],
PointText => [
txLoc => 'Point',
count => 'int8u',
text => 'string[$val{count}]',
],
Polygon => [
polySize => 'int16u',
polyBBox => 'Rect',
polyPoints => 'int16u[($val{polySize}-10)/2]',
],
Rect => [
topLeft => 'Point',
botRight => 'Point',
],
RGBColor => [
red => 'int16u',
green => 'int16u',
blue => 'int16u',
],
Rgn => [
rgnSize => 'int16u',
rgnBBox => 'Rect',
data => 'int8u[$val{rgnSize}-10]',
],
ShortLine => [
pnLoc => 'Point',
dh => 'int8s',
dv => 'int8s',
],
# http://developer.apple.com/documentation/QuickTime/INMAC/QT/iqImageCompMgr.a.htm
UncompressedQuickTime => [
size => 'int32u', # size NOT including size word
version => 'int16u',
matrix => 'int32u[9]',
matteSize => 'int32u',
matteRect => 'Rect',
matteDescr => 'Int32uData[$val{matteSize} ? 1 : 0]',
matteData => 'binary[$val{matteSize}]',
subOpcodeData => q{
binary[ $val{size} - 50 -
($val{matteSize} ? $val{mattSize} + $val{matteDescr}->{size} : 0)]
},
],
);
# PICT image opcodes
%Image::ExifTool::PICT::Main = (
VARS => { NO_LOOKUP => 1 }, # omit tags from lookup
NOTES => q{
The PICT format contains no true meta information, except for the possible
exception of the LongComment opcode. By default, only ImageWidth,
ImageHeight and X/YResolution are extracted from a PICT image. Tags in the
following table represent image opcodes. Extraction of these tags is
experimental, and is only enabled with the Verbose or Unknown options.
},
0x0000 => {
Name => 'Nop',
Description => 'No Operation',
Format => 'null',
},
0x0001 => {
Name => 'ClipRgn',
Description => 'Clipping Region',
Format => 'Rgn',
},
0x0002 => {
Name => 'BkPat',
Description => 'Background Pattern',
Format => 'int8u[8]',
},
0x0003 => {
Name => 'TxFont',
Description => 'Font Number',
Format => 'int16u',
},
0x0004 => {
Name => 'TxFace',
Description => 'Text Font Style',
Format => 'int8u',
},
0x0005 => {
Name => 'TxMode',
Description => 'Text Source Mode',
Format => 'int16u',
},
0x0006 => {
Name => 'SpExtra',
Description => 'Extra Space',
Format => 'fixed32s',
},
0x0007 => {
Name => 'PnSize',
Description => 'Pen Size',
Format => 'Point',
},
0x0008 => {
Name => 'PnMode',
Description => 'Pen Mode',
Format => 'int16u',
},
0x0009 => {
Name => 'PnPat',
Description => 'Pen Pattern',
Format => 'int8u[8]',
},
0x000a => {
Name => 'FillPat',
Description => 'Fill Pattern',
Format => 'int8u[8]',
},
0x000b => {
Name => 'OvSize',
Description => 'Oval Size',
Format => 'Point',
},
0x000c => {
Name => 'Origin',
Format => 'Point',
},
0x000d => {
Name => 'TxSize',
Description => 'Text Size',
Format => 'int16u',
},
0x000e => {
Name => 'FgColor',
Description => 'Foreground Color',
Format => 'int32u',
},
0x000f => {
Name => 'BkColor',
Description => 'Background Color',
Format => 'int32u',
},
0x0010 => {
Name => 'TxRatio',
Description => 'Text Ratio',
Format => 'Rect',
},
0x0011 => {
Name => 'VersionOp',
Description => 'Version',
Format => 'int8u',
},
0x0012 => {
Name => 'BkPixPat',
Description => 'Background Pixel Pattern',
Format => 'PixPat',
},
0x0013 => {
Name => 'PnPixPat',
Description => 'Pen Pixel Pattern',
Format => 'PixPat',
},
0x0014 => {
Name => 'FillPixPat',
Description => 'Fill Pixel Pattern',
Format => 'PixPat',
},
0x0015 => {
Name => 'PnLocHFrac',
Description => 'Fractional Pen Position',
Format => 'int16u',
},
0x0016 => {
Name => 'ChExtra',
Description => 'Added Width for NonSpace Characters',
Format => 'int16u',
},
0x0017 => {
Name => 'Reserved',
Format => 'Unknown',
},
0x001a => {
Name => 'RGBFgCol',
Description => 'Foreground Color',
Format => 'RGBColor',
},
0x001b => {
Name => 'RGBBkCol',
Description => 'Background Color',
Format => 'RGBColor',
},
0x001c => {
Name => 'HiliteMode',
Description => 'Highlight Mode Flag',
Format => 'null',
},
0x001d => {
Name => 'HiliteColor',
Description => 'Highlight Color',
Format => 'RGBColor',
},
0x001e => {
Name => 'DefHilite',
Description => 'Use Default Highlight Color',
Format => 'null',
},
0x001f => {
Name => 'OpColor',
Format => 'RGBColor',
},
0x0020 => {
Name => 'Line',
Format => 'Rect',
},
0x0021 => {
Name => 'LineFrom',
Format => 'Point',
},
0x0022 => {
Name => 'ShortLine',
Format => 'ShortLine',
},
0x0023 => {
Name => 'ShortLineFrom',
Format => 'int8u[2]',
},
0x0024 => {
Name => 'Reserved',
Format => 'Int16Data',
},
0x0028 => {
Name => 'LongText',
Format => 'PointText',
},
0x0029 => {
Name => 'DHText',
Format => 'Int8uText',
},
0x002a => {
Name => 'DVText',
Format => 'Int8uText',
},
0x002b => {
Name => 'DHDVText',
Format => 'Int8u2Text',
},
0x002c => {
Name => 'FontName',
Format => 'FontName',
},
0x002d => {
Name => 'LineJustify',
Format => 'int8u[10]',
},
0x002e => {
Name => 'GlyphState',
Format => 'int8u[8]',
},
0x002f => {
Name => 'Reserved',
Format => 'Int16Data',
},
0x0030 => {
Name => 'FrameRect',
Format => 'Rect',
},
0x0031 => {
Name => 'PaintRect',
Format => 'Rect',
},
0x0032 => {
Name => 'EraseRect',
Format => 'Rect',
},
0x0033 => {
Name => 'InvertRect',
Format => 'Rect',
},
0x0034 => {
Name => 'FillRect',
Format => 'Rect',
},
0x0035 => {
Name => 'Reserved',
Format => 'Rect',
},
0x0038 => {
Name => 'FrameSameRect',
Format => 'null',
},
0x0039 => {
Name => 'PaintSameRect',
Format => 'null',
},
0x003a => {
Name => 'EraseSameRect',
Format => 'null',
},
0x003b => {
Name => 'InvertSameRect',
Format => 'null',
},
0x003c => {
Name => 'FillSameRect',
Format => 'null',
},
0x003d => {
Name => 'Reserved',
Format => 'null',
},
0x0040 => {
Name => 'FrameRRect',
Format => 'Rect',
},
0x0041 => {
Name => 'PaintRRect',
Format => 'Rect',
},
0x0042 => {
Name => 'EraseRRect',
Format => 'Rect',
},
0x0043 => {
Name => 'InvertRRect',
Format => 'Rect',
},
0x0044 => {
Name => 'FillRRect',
Format => 'Rect',
},
0x0045 => {
Name => 'Reserved',
Format => 'Rect',
},
0x0048 => {
Name => 'FrameSameRRect',
Format => 'null',
},
0x0049 => {
Name => 'PaintSameRRect',
Format => 'null',
},
0x004a => {
Name => 'EraseSameRRect',
Format => 'null',
},
0x004b => {
Name => 'InvertSameRRect',
Format => 'null',
},
0x004c => {
Name => 'FillSameRRect',
Format => 'null',
},
0x004d => {
Name => 'Reserved',
Format => 'null',
},
0x0050 => {
Name => 'FrameOval',
Format => 'Rect',
},
0x0051 => {
Name => 'PaintOval',
Format => 'Rect',
},
0x0052 => {
Name => 'EraseOval',
Format => 'Rect',
},
0x0053 => {
Name => 'InvertOval',
Format => 'Rect',
},
0x0054 => {
Name => 'FillOval',
Format => 'Rect',
},
0x0055 => {
Name => 'Reserved',
Format => 'Rect',
},
0x0058 => {
Name => 'FrameSameOval',
Format => 'null',
},
0x0059 => {
Name => 'PaintSameOval',
Format => 'null',
},
0x005a => {
Name => 'EraseSameOval',
Format => 'null',
},
0x005b => {
Name => 'InvertSameOval',
Format => 'null',
},
0x005c => {
Name => 'FillSameOval',
Format => 'null',
},
0x005d => {
Name => 'Reserved',
Format => 'null',
},
0x0060 => {
Name => 'FrameArc',
Format => 'Arc',
},
0x0061 => {
Name => 'PaintArc',
Format => 'Arc',
},
0x0062 => {
Name => 'EraseArc',
Format => 'Arc',
},
0x0063 => {
Name => 'InvertArc',
Format => 'Arc',
},
0x0064 => {
Name => 'FillArc',
Format => 'Arc',
},
0x0065 => {
Name => 'Reserved',
Format => 'Arc',
},
0x0068 => {
Name => 'FrameSameArc',
Format => 'Point',
},
0x0069 => {
Name => 'PaintSameArc',
Format => 'Point',
},
0x006a => {
Name => 'EraseSameArc',
Format => 'Point',
},
0x006b => {
Name => 'InvertSameArc',
Format => 'Point',
},
0x006c => {
Name => 'FillSameArc',
Format => 'Point',
},
0x006d => {
Name => 'Reserved',
Format => 'int32u',
},
0x0070 => {
Name => 'FramePoly',
Format => 'Polygon',
},
0x0071 => {
Name => 'PaintPoly',
Format => 'Polygon',
},
0x0072 => {
Name => 'ErasePoly',
Format => 'Polygon',
},
0x0073 => {
Name => 'InvertPoly',
Format => 'Polygon',
},
0x0074 => {
Name => 'FillPoly',
Format => 'Polygon',
},
0x0075 => {
Name => 'Reserved',
Format => 'Polygon',
},
0x0078 => {
Name => 'FrameSamePoly',
Format => 'null',
},
0x0079 => {
Name => 'PaintSamePoly',
Format => 'null',
},
0x007a => {
Name => 'EraseSamePoly',
Format => 'null',
},
0x007b => {
Name => 'InvertSamePoly',
Format => 'null',
},
0x007c => {
Name => 'FillSamePoly',
Format => 'null',
},
0x007d => {
Name => 'Reserved',
Format => 'null',
},
0x0080 => {
Name => 'FrameRgn',
Format => 'Rgn',
},
0x0081 => {
Name => 'PaintRgn',
Format => 'Rgn',
},
0x0082 => {
Name => 'EraseRgn',
Format => 'Rgn',
},
0x0083 => {
Name => 'InvertRgn',
Format => 'Rgn',
},
0x0084 => {
Name => 'FillRgn',
Format => 'Rgn',
},
0x0085 => {
Name => 'Reserved',
Format => 'Rgn',
},
0x0088 => {
Name => 'FrameSameRgn',
Format => 'null',
},
0x0089 => {
Name => 'PaintSameRgn',
Format => 'null',
},
0x008a => {
Name => 'EraseSameRgn',
Format => 'null',
},
0x008b => {
Name => 'InvertSameRgn',
Format => 'null',
},
0x008c => {
Name => 'FillSameRgn',
Format => 'null',
},
0x008d => {
Name => 'Reserved',
Format => 'null',
},
0x0090 => {
Name => 'BitsRect',
Description => 'CopyBits with Clipped Rectangle',
Format => 'BitsRect#', # (version-dependent format)
},
0x0091 => {
Name => 'BitsRgn',
Description => 'CopyBits with Clipped Region',
Format => 'BitsRgn#', # (version-dependent format)
},
0x0092 => {
Name => 'Reserved',
Format => 'Int16Data',
},
0x0098 => {
Name => 'PackBitsRect',
Description => 'Packed CopyBits with Clipped Rectangle',
Format => 'BitsRect#', # (version-dependent format)
},
0x0099 => {
Name => 'PackBitsRgn',
Description => 'Packed CopyBits with Clipped Region',
Format => 'BitsRgn#', # (version-dependent format)
},
0x009a => {
Name => 'DirectBitsRect',
Format => 'DirectBitsRect',
},
0x009b => {
Name => 'DirectBitsRgn',
Format => 'DirectBitsRgn',
},
0x009c => {
Name => 'Reserved',
Format => 'Int16Data',
},
0x009d => {
Name => 'Reserved',
Format => 'Int16Data',
},
0x009e => {
Name => 'Reserved',
Format => 'Int16Data',
},
0x009f => {
Name => 'Reserved',
Format => 'Int16Data',
},
0x00a0 => {
Name => 'ShortComment',
Format => 'int16u',
},
0x00a1 => [
# this list for documentation only [currently not extracted]
{
# (not actually a full Photohop IRB record it appears, but it does start
# with '8BIM', and does contain resolution information at offset 0x0a)
Name => 'LongComment', # kind = 498
Format => 'LongComment',
SubDirectory => { TagTable => 'Image::ExifTool::Photoshop::Main' },
},
{
Name => 'LongComment', # kind = 224
Format => 'LongComment',
SubDirectory => {
TagTable => 'Image::ExifTool::ICC_Profile::Main',
Start => '$valuePtr + 4',
},
},
],
0x00a2 => {
Name => 'Reserved',
Format => 'Int16Data',
},
0x00b0 => {
Name => 'Reserved',
Format => 'null',
},
0x00d0 => {
Name => 'Reserved',
Format => 'Int32uData',
},
0x00ff => {
Name => 'OpEndPic',
Description => 'End of picture',
Format => 'null', # 2 for version 2!?
},
0x0100 => {
Name => 'Reserved',
Format => 'int16u',
},
0x0200 => {
Name => 'Reserved',
Format => 'int32u',
},
0x02ff => {
Name => 'Version',
Description => 'Version number of picture',
Format => 'int16u',
},
0x0300 => {
Name => 'Reserved',
Format => 'int16u',
},
0x0bff => {
Name => 'Reserved',
Format => 'int8u[22]',
},
0x0c00 => {
Name => 'HeaderOp',
Format => 'int16u[12]',
},
0x0c01 => {
Name => 'Reserved',
Format => 'int8u[24]',
},
0x7f00 => {
Name => 'Reserved',
Format => 'int8u[254]',
},
0x8000 => {
Name => 'Reserved',
Format => 'null',
},
0x8100 => {
Name => 'Reserved',
Format => 'Int32uData',
},
0x8200 => {
Name => 'CompressedQuickTime',
Format => 'CompressedQuickTime',
},
0x8201 => {
Name => 'UncompressedQuickTime',
Format => 'Int32uData',
},
0xffff => {
Name => 'Reserved',
Format => 'Int32uData',
},
);
# picture comment 'kind' codes
# http://developer.apple.com/technotes/qd/qd_10.html
my %commentKind = (
150 => 'TextBegin',
151 => 'TextEnd',
152 => 'StringBegin',
153 => 'StringEnd',
154 => 'TextCenter',
155 => 'LineLayoutOff',
156 => 'LineLayoutOn',
157 => 'ClientLineLayout',
160 => 'PolyBegin',
161 => 'PolyEnd',
163 => 'PolyIgnore',
164 => 'PolySmooth',
165 => 'PolyClose',
180 => 'DashedLine',
181 => 'DashedStop',
182 => 'SetLineWidth',
190 => 'PostScriptBegin',
191 => 'PostScriptEnd',
192 => 'PostScriptHandle',
193 => 'PostScriptFile',
194 => 'TextIsPostScript',
195 => 'ResourcePS',
196 => 'PSBeginNoSave',
197 => 'SetGrayLevel',
200 => 'RotateBegin',
201 => 'RotateEnd',
202 => 'RotateCenter',
210 => 'FormsPrinting',
211 => 'EndFormsPrinting',
224 => '<ICC Profile>',
498 => '<Photoshop Data>',
1000 => 'BitMapThinningOff',
1001 => 'BitMapThinningOn',
);
#------------------------------------------------------------------------------
# Get PixData data
# Inputs: 0) reference to PixMap, 1) RAF reference
# Returns: reference to PixData or undef on error
sub GetPixData($$)
{
my ($pixMap, $raf) = @_;
my $packType = $pixMap->{packType};
my $rowBytes = $pixMap->{rowBytes} & 0x3fff; # remove flags bits
my $height = $pixMap->{bounds}->{botRight}->{v} -
$pixMap->{bounds}->{topLeft}->{v};
my ($data, $size, $buff, $i);
if ($packType == 1 or $rowBytes < 8) { # unpacked data
$size = $rowBytes * $height;
return undef unless $raf->Read($data, $size) == $size;
} elsif ($packType == 2) { # pad byte dropped
$size = int($rowBytes * $height * 3 / 4 + 0.5);
return undef unless $raf->Read($data, $size) == $size;
} else {
$data = '';
for ($i=0; $i<$height; ++$i) {
if ($rowBytes > 250) {
$raf->Read($buff,2) == 2 or return undef;
$size = unpack('n',$buff);
} else {
$raf->Read($buff,1) == 1 or return undef;
$size = unpack('C',$buff);
}
$data .= $buff;
$raf->Read($buff,$size) == $size or return undef;
$data .= $buff;
}
}
return \$data;
}
#------------------------------------------------------------------------------
# Read value from PICT file
# Inputs: 0) RAF reference, 1) tag, 2) format, 3) optional count
# Returns: value, reference to structure hash, or undef on error
sub ReadPictValue($$$;$)
{
my ($raf, $tag, $format, $count) = @_;
return undef unless $format;
unless (defined $count) {
if ($format =~ /(.+)\[(.+)\]/s) {
$format = $1;
$count = $2;
} else {
$count = 1; # count undefined: assume 1
}
}
my $cntStr = ($count == 1) ? '' : "[$count]";
# no size if count is 0
my $size = $count ? Image::ExifTool::FormatSize($format) : 0;
if (defined $size or $format eq 'null') {
my $val;
if ($size) {
my $buff;
$size *= $count;
$raf->Read($buff, $size) == $size or return undef;
$val = ReadValue(\$buff, 0, $format, $count, $size);
} else {
$val = '';
}
if ($verbose) {
print $out "${indent}$tag ($format$cntStr)";
if ($size) {
if (not defined $val) {
print $out " = <undef>\n";
} elsif ($format eq 'binary') {
print $out " = <binary data>\n";
if ($verbose > 2) {
my %parms = ( Out => $out );
$parms{MaxLen} = 96 if $verbose < 4;
HexDump(\$val, undef, %parms);
}
} else {
print $out " = $val\n";
}
} else {
print $out "\n";
}
}
return \$val if $format eq 'binary' and defined $val;
return $val;
}
$verbose and print $out "${indent}$tag ($format$cntStr):\n";
my $struct = $structs{$format} or return undef;
my ($c, @vals);
for ($c=0; $c<$count; ++$c) {
my (%val, $i);
for ($i=0; ; $i+=2) {
my $tag = $$struct[$i] or last;
my $fmt = $$struct[$i+1];
my ($cnt, $val);
$indent .= ' ';
if (ref $fmt) {
$val = eval $$fmt;
$@ and warn $@;
if ($verbose and defined $val) {
printf $out "${indent}$tag (binary[%d]) = <binary data>\n",length($$val);
if ($verbose > 2) {
my %parms = ( Out => $out );
$parms{MaxLen} = 96 if $verbose < 4;
HexDump($val, undef, %parms);
}
}
} elsif ($fmt =~ /(.+)\[(.+)\]/s) {
$fmt = $1;
$cnt = eval $2;
$@ and warn $@;
$val = ReadPictValue($raf, $tag, $fmt, $cnt);
} else {
$val = ReadPictValue($raf, $tag, $fmt);
}
$indent = substr($indent, 2);
return undef unless defined $val;
$val{$tag} = $val;
}
return \%val if $count == 1;
push @vals, \%val;
}
return \@vals;
}
#------------------------------------------------------------------------------
# Extract meta information from a PICT image
# Inputs: 0) ExifTool object reference, 1) dirInfo reference
# Returns: 1 on success, 0 if this wasn't a valid PICT image
sub ProcessPICT($$)
{
my ($et, $dirInfo) = @_;
my $raf = $$dirInfo{RAF};
$verbose = $et->Options('Verbose');
$out = $et->Options('TextOut');
$indent = '';
my ($buff, $tried, @hdr, $op, $hRes, $vRes);
# recognize both PICT files and PICT resources (PICT files have a
# 512-byte header that we ignore, but PICT resources do not)
for (;;) {
$raf->Read($buff, 12) == 12 or return 0;
@hdr = unpack('x2n5', $buff);
$op = pop @hdr;
# check for PICT version 1 format
if ($op eq 0x1101) {
$vers = 1;
undef $extended;
last;
}
# check for PICT version 2 format
if ($op eq 0x0011) {
$raf->Read($buff, 28) == 28 or return 0;
if ($buff =~ /^\x02\xff\x0c\x00\xff\xff/) {
$vers = 2;
undef $extended;
last;
}
if ($buff =~ /^\x02\xff\x0c\x00\xff\xfe/) {
$vers = 2;
$extended = 1;
($hRes, $vRes) = unpack('x8N2', $buff);
last;
}
}
return 0 if $tried;
$tried = 1;
$raf->Seek(512, 0) or return 0;
}
# make the bounding rect signed
foreach (@hdr) {
$_ >= 0x8000 and $_ -= 0x10000;
}
my $w = $hdr[3] - $hdr[1];
my $h = $hdr[2] - $hdr[0];
return 0 unless $w > 0 and $h > 0;
SetByteOrder('MM');
if ($extended) {
# extended version 2 pictures contain resolution information
# and image bounds are in 72-dpi equivalent units
$hRes = GetFixed32s(\$buff, 8);
$vRes = GetFixed32s(\$buff, 12);
return 0 unless $hRes and $vRes;
$w = int($w * $hRes / 72 + 0.5);
$h = int($h * $vRes / 72 + 0.5);
}
$et->SetFileType();
$et->FoundTag('ImageWidth', $w);
$et->FoundTag('ImageHeight', $h);
$et->FoundTag('XResolution', $hRes) if $hRes;
$et->FoundTag('YResolution', $vRes) if $vRes;
# don't extract image opcodes unless verbose
return 1 unless $verbose or $et->Options('Unknown');
$verbose and printf $out "PICT version $vers%s\n", $extended ? ' extended' : '';
my $tagTablePtr = GetTagTable('Image::ExifTool::PICT::Main');
my $success;
for (;;) {
if ($vers == 1) {
$raf->Read($buff, 1) == 1 or last;
$op = ord($buff);
} else {
# must start version 2 opcode on an even byte
$raf->Read($buff, 1) if $raf->Tell() & 0x01;
$raf->Read($buff, 2) == 2 or last;
$op = unpack('n', $buff);
}
my $tagInfo = $et->GetTagInfo($tagTablePtr, $op);
unless ($tagInfo) {
my $i;
# search for reserved tag info
for ($i=0; $i<scalar(@reserved); $i+=2) {
next unless $op >= $reserved[$i];
last if $op > $reserved[$i+1];
$tagInfo = $et->GetTagInfo($tagTablePtr, $reserved[$i]);
last;
}
last unless $tagInfo;
}
if ($op eq 0xff) {
$verbose and print $out "End of picture\n";
$success = 1;
last;
}
my $format = $$tagInfo{Format};
unless ($format) {
$et->Warn("Missing format for $$tagInfo{Name}");
last;
}
# replace version number for version-dependent formats
$format =~ s/#$/$vers/;
my $wid = $vers * 2;
$verbose and printf $out "Tag 0x%.${wid}x, ", $op;
my $val = ReadPictValue($raf, $$tagInfo{Name}, $format);
unless (defined $val) {
$et->Warn("Error reading $$tagInfo{Name} information");
last;
}
if (ref $val eq 'HASH') {
# extract JPEG image from CompressedQuickTime imageData
if ($$tagInfo{Name} eq 'CompressedQuickTime' and
ref $val->{imageDescr} eq 'HASH' and
$val->{imageDescr}->{compressor} and
$val->{imageDescr}->{compressor} eq 'Photo - JPEG' and
ref $val->{imageData} eq 'SCALAR' and
$et->ValidateImage($val->{imageData}, 'PreviewImage'))
{
$et->FoundTag('PreviewImage', $val->{imageData});
}
} else {
# $et->FoundTag($tagInfo, $val);
}
}
$success or $et->Warn('End of picture not found');
return 1;
}
1; # end
__END__
=head1 NAME
Image::ExifTool::PICT - Read PICT meta information
=head1 SYNOPSIS
This module is used by Image::ExifTool
=head1 DESCRIPTION
This module contains routines required by Image::ExifTool to read PICT
(Apple Picture) images.
=head1 NOTES
Extraction of PICT opcodes is experimental, and is only enabled with the
Verbose or the Unknown option.
=head1 AUTHOR
Copyright 2003-2018, Phil Harvey (phil at owl.phy.queensu.ca)
This library is free software; you can redistribute it and/or modify it
under the same terms as Perl itself.
=head1 REFERENCES
=over 4
=item L<http://developer.apple.com/documentation/mac/QuickDraw/QuickDraw-2.html>
=item L<http://developer.apple.com/documentation/QuickTime/INMAC/QT/iqImageCompMgr.a.htm>
=back
=head1 SEE ALSO
L<Image::ExifTool::TagNames/PICT Tags>,
L<Image::ExifTool(3pm)|Image::ExifTool>
=cut