PICT.pm 33 KB

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