1261 lines
33 KiB
Perl
1261 lines
33 KiB
Perl
#------------------------------------------------------------------------------
|
|
# 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
|
|
|