Photoshop.pm 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650
  1. #------------------------------------------------------------------------------
  2. # File: Photoshop.pm
  3. #
  4. # Description: Read/write Photoshop IRB meta information
  5. #
  6. # Revisions: 02/06/2004 - P. Harvey Created
  7. # 02/25/2004 - P. Harvey Added hack for problem with old photoshops
  8. # 10/04/2004 - P. Harvey Added a bunch of tags (ref Image::MetaData::JPEG)
  9. # but left most of them commented out until I have enough
  10. # information to write PrintConv routines for them to
  11. # display something useful
  12. # 07/08/2005 - P. Harvey Added support for reading PSD files
  13. # 01/07/2006 - P. Harvey Added PSD write support
  14. # 11/04/2006 - P. Harvey Added handling of resource name
  15. #
  16. # References: 1) http://www.fine-view.com/jp/lab/doc/ps6ffspecsv2.pdf
  17. # 2) http://www.ozhiker.com/electronics/pjmt/jpeg_info/irb_jpeg_qual.html
  18. # 3) Matt Mueller private communication (tests with PS CS2)
  19. # 4) http://www.fileformat.info/format/psd/egff.htm
  20. # 5) http://www.telegraphics.com.au/svn/psdparse/trunk/resources.c
  21. # 6) http://libpsd.graphest.com/files/Photoshop%20File%20Formats.pdf
  22. # 7) http://www.adobe.com/devnet-apps/photoshop/fileformatashtml/
  23. #------------------------------------------------------------------------------
  24. package Image::ExifTool::Photoshop;
  25. use strict;
  26. use vars qw($VERSION $AUTOLOAD $iptcDigestInfo);
  27. use Image::ExifTool qw(:DataAccess :Utils);
  28. $VERSION = '1.47';
  29. sub ProcessPhotoshop($$$);
  30. sub WritePhotoshop($$$);
  31. # map of where information is stored in PSD image
  32. my %psdMap = (
  33. IPTC => 'Photoshop',
  34. XMP => 'Photoshop',
  35. EXIFInfo => 'Photoshop',
  36. IFD0 => 'EXIFInfo',
  37. IFD1 => 'IFD0',
  38. ICC_Profile => 'Photoshop',
  39. ExifIFD => 'IFD0',
  40. GPS => 'IFD0',
  41. SubIFD => 'IFD0',
  42. GlobParamIFD => 'IFD0',
  43. PrintIM => 'IFD0',
  44. InteropIFD => 'ExifIFD',
  45. MakerNotes => 'ExifIFD',
  46. );
  47. # tag information for PhotoshopThumbnail and PhotoshopBGRThumbnail
  48. my %thumbnailInfo = (
  49. Writable => 'undef',
  50. Protected => 1,
  51. RawConv => 'my $img=substr($val,0x1c); $self->ValidateImage(\$img,$tag)',
  52. ValueConvInv => q{
  53. my $et = new Image::ExifTool;
  54. my @tags = qw{ImageWidth ImageHeight FileType};
  55. my $info = $et->ImageInfo(\$val, @tags);
  56. my ($w, $h, $type) = @$info{@tags};
  57. $w and $h and $type eq 'JPEG' or warn("Not a valid JPEG image\n"), return undef;
  58. my $wbytes = int(($w * 24 + 31) / 32) * 4;
  59. return pack('N6n2', 1, $w, $h, $wbytes, $wbytes * $h, length($val), 24, 1) . $val;
  60. },
  61. );
  62. # Photoshop APP13 tag table
  63. # (set Unknown flag for information we don't want to display normally)
  64. %Image::ExifTool::Photoshop::Main = (
  65. GROUPS => { 2 => 'Image' },
  66. PROCESS_PROC => \&ProcessPhotoshop,
  67. WRITE_PROC => \&WritePhotoshop,
  68. 0x03e8 => { Unknown => 1, Name => 'Photoshop2Info' },
  69. 0x03e9 => { Unknown => 1, Name => 'MacintoshPrintInfo' },
  70. 0x03ea => { Unknown => 1, Name => 'XMLData', Binary => 1 }, #PH
  71. 0x03eb => { Unknown => 1, Name => 'Photoshop2ColorTable' },
  72. 0x03ed => {
  73. Name => 'ResolutionInfo',
  74. SubDirectory => {
  75. TagTable => 'Image::ExifTool::Photoshop::Resolution',
  76. },
  77. },
  78. 0x03ee => {
  79. Name => 'AlphaChannelsNames',
  80. ValueConv => 'Image::ExifTool::Photoshop::ConvertPascalString($self,$val)',
  81. },
  82. 0x03ef => { Unknown => 1, Name => 'DisplayInfo' },
  83. 0x03f0 => { Unknown => 1, Name => 'PStringCaption' },
  84. 0x03f1 => { Unknown => 1, Name => 'BorderInformation' },
  85. 0x03f2 => { Unknown => 1, Name => 'BackgroundColor' },
  86. 0x03f3 => { Unknown => 1, Name => 'PrintFlags', Format => 'int8u' },
  87. 0x03f4 => { Unknown => 1, Name => 'BW_HalftoningInfo' },
  88. 0x03f5 => { Unknown => 1, Name => 'ColorHalftoningInfo' },
  89. 0x03f6 => { Unknown => 1, Name => 'DuotoneHalftoningInfo' },
  90. 0x03f7 => { Unknown => 1, Name => 'BW_TransferFunc' },
  91. 0x03f8 => { Unknown => 1, Name => 'ColorTransferFuncs' },
  92. 0x03f9 => { Unknown => 1, Name => 'DuotoneTransferFuncs' },
  93. 0x03fa => { Unknown => 1, Name => 'DuotoneImageInfo' },
  94. 0x03fb => { Unknown => 1, Name => 'EffectiveBW', Format => 'int8u' },
  95. 0x03fc => { Unknown => 1, Name => 'ObsoletePhotoshopTag1' },
  96. 0x03fd => { Unknown => 1, Name => 'EPSOptions' },
  97. 0x03fe => { Unknown => 1, Name => 'QuickMaskInfo' },
  98. 0x03ff => { Unknown => 1, Name => 'ObsoletePhotoshopTag2' },
  99. 0x0400 => { Unknown => 1, Name => 'TargetLayerID', Format => 'int16u' }, # (LayerStateInfo)
  100. 0x0401 => { Unknown => 1, Name => 'WorkingPath' },
  101. 0x0402 => { Unknown => 1, Name => 'LayersGroupInfo', Format => 'int16u' },
  102. 0x0403 => { Unknown => 1, Name => 'ObsoletePhotoshopTag3' },
  103. 0x0404 => {
  104. Name => 'IPTCData',
  105. SubDirectory => {
  106. DirName => 'IPTC',
  107. TagTable => 'Image::ExifTool::IPTC::Main',
  108. },
  109. },
  110. 0x0405 => { Unknown => 1, Name => 'RawImageMode' },
  111. 0x0406 => { #2
  112. Name => 'JPEG_Quality',
  113. SubDirectory => {
  114. TagTable => 'Image::ExifTool::Photoshop::JPEG_Quality',
  115. },
  116. },
  117. 0x0408 => { Unknown => 1, Name => 'GridGuidesInfo' },
  118. 0x0409 => {
  119. Name => 'PhotoshopBGRThumbnail',
  120. Notes => 'this is a JPEG image, but in BGR format instead of RGB',
  121. %thumbnailInfo,
  122. Groups => { 2 => 'Preview' },
  123. },
  124. 0x040a => {
  125. Name => 'CopyrightFlag',
  126. Writable => 'int8u',
  127. Groups => { 2 => 'Author' },
  128. ValueConv => 'join(" ",unpack("C*", $val))',
  129. ValueConvInv => 'pack("C*",split(" ",$val))',
  130. PrintConv => { #3
  131. 0 => 'False',
  132. 1 => 'True',
  133. },
  134. },
  135. 0x040b => {
  136. Name => 'URL',
  137. Writable => 'string',
  138. Groups => { 2 => 'Author' },
  139. },
  140. 0x040c => {
  141. Name => 'PhotoshopThumbnail',
  142. %thumbnailInfo,
  143. Groups => { 2 => 'Preview' },
  144. },
  145. 0x040d => {
  146. Name => 'GlobalAngle',
  147. Writable => 'int32u',
  148. ValueConv => 'unpack("N",$val)',
  149. ValueConvInv => 'pack("N",$val)',
  150. },
  151. 0x040e => { Unknown => 1, Name => 'ColorSamplersResource' },
  152. 0x040f => {
  153. Name => 'ICC_Profile',
  154. SubDirectory => {
  155. TagTable => 'Image::ExifTool::ICC_Profile::Main',
  156. },
  157. },
  158. 0x0410 => { Unknown => 1, Name => 'Watermark', Format => 'int8u' },
  159. 0x0411 => { Unknown => 1, Name => 'ICC_Untagged', Format => 'int8u' },
  160. 0x0412 => { Unknown => 1, Name => 'EffectsVisible', Format => 'int8u' },
  161. 0x0413 => { Unknown => 1, Name => 'SpotHalftone' },
  162. 0x0414 => { Unknown => 1, Name => 'IDsBaseValue', Description => 'IDs Base Value', Format => 'int32u' },
  163. 0x0415 => { Unknown => 1, Name => 'UnicodeAlphaNames' },
  164. 0x0416 => { Unknown => 1, Name => 'IndexedColourTableCount', Format => 'int16u' },
  165. 0x0417 => { Unknown => 1, Name => 'TransparentIndex', Format => 'int16u' },
  166. 0x0419 => {
  167. Name => 'GlobalAltitude',
  168. Writable => 'int32u',
  169. ValueConv => 'unpack("N",$val)',
  170. ValueConvInv => 'pack("N",$val)',
  171. },
  172. 0x041a => { Unknown => 1, Name => 'Slices' },
  173. 0x041b => { Unknown => 1, Name => 'WorkflowURL' },
  174. 0x041c => { Unknown => 1, Name => 'JumpToXPEP' },
  175. 0x041d => { Unknown => 1, Name => 'AlphaIdentifiers' },
  176. 0x041e => { Unknown => 1, Name => 'URL_List' },
  177. 0x0421 => { Unknown => 1, Name => 'VersionInfo' },
  178. 0x0422 => {
  179. Name => 'EXIFInfo', #PH (Found in EPS and PSD files)
  180. SubDirectory => {
  181. TagTable=> 'Image::ExifTool::Exif::Main',
  182. ProcessProc => \&Image::ExifTool::ProcessTIFF,
  183. WriteProc => \&Image::ExifTool::WriteTIFF,
  184. },
  185. },
  186. 0x0423 => { Unknown => 1, Name => 'ExifInfo2', Binary => 1 }, #5
  187. 0x0424 => {
  188. Name => 'XMP',
  189. SubDirectory => {
  190. TagTable => 'Image::ExifTool::XMP::Main',
  191. },
  192. },
  193. 0x0425 => {
  194. Name => 'IPTCDigest',
  195. Writable => 'string',
  196. Protected => 1,
  197. Notes => q{
  198. this tag indicates provides a way for XMP-aware applications to indicate
  199. that the XMP is synchronized with the IPTC. When writing, special values of
  200. "new" and "old" represent the digests of the IPTC from the edited and
  201. original files respectively, and are undefined if the IPTC does not exist in
  202. the respective file. Set this to "new" as an indication that the XMP is
  203. synchronized with the IPTC
  204. },
  205. # also note the 'new' feature requires that the IPTC comes before this tag is written
  206. ValueConv => 'unpack("H*", $val)',
  207. ValueConvInv => q{
  208. if (lc($val) eq 'new' or lc($val) eq 'old') {
  209. {
  210. local $SIG{'__WARN__'} = sub { };
  211. return lc($val) if eval { require Digest::MD5 };
  212. }
  213. warn "Digest::MD5 must be installed\n";
  214. return undef;
  215. }
  216. return pack('H*', $val) if $val =~ /^[0-9a-f]{32}$/i;
  217. warn "Value must be 'new', 'old' or 32 hexadecimal digits\n";
  218. return undef;
  219. }
  220. },
  221. 0x0426 => { Unknown => 1, Name => 'PrintScale' }, #5
  222. 0x0428 => { Unknown => 1, Name => 'PixelAspectRatio' }, #5
  223. 0x0429 => { Unknown => 1, Name => 'LayerComps' }, #5
  224. 0x042a => { Unknown => 1, Name => 'AlternateDuotoneColors' }, #5
  225. 0x042b => { Unknown => 1, Name => 'AlternateSpotColors' }, #5
  226. 0x042d => { #7
  227. Name => 'LayerSelectionIDs',
  228. Description => 'Layer Selection IDs',
  229. Unknown => 1,
  230. ValueConv => q{
  231. my ($n, @a) = unpack("nN*",$val);
  232. $#a = $n - 1 if $n > @a;
  233. return join(' ', @a);
  234. },
  235. },
  236. 0x042e => { Unknown => 1, Name => 'HDRToningInfo' }, #7
  237. 0x042f => { Unknown => 1, Name => 'PrintInfo' }, #7
  238. 0x0430 => { Unknown => 1, Name => 'LayerGroupsEnabledID', Format => 'int8u' }, #7
  239. 0x0431 => { Unknown => 1, Name => 'ColorSamplersResource2' }, #7
  240. 0x0432 => { Unknown => 1, Name => 'MeasurementScale' }, #7
  241. 0x0433 => { Unknown => 1, Name => 'TimelineInfo' }, #7
  242. 0x0434 => { Unknown => 1, Name => 'SheetDisclosure' }, #7
  243. 0x0435 => { Unknown => 1, Name => 'DisplayInfo' }, #7
  244. 0x0436 => { Unknown => 1, Name => 'OnionSkins' }, #7
  245. 0x0438 => { Unknown => 1, Name => 'CountInfo' }, #7
  246. 0x043a => { Unknown => 1, Name => 'PrintInfo2' }, #7
  247. 0x043b => { Unknown => 1, Name => 'PrintStyle' }, #7
  248. 0x043c => { Unknown => 1, Name => 'MacintoshNSPrintInfo' }, #7
  249. 0x043d => { Unknown => 1, Name => 'WindowsDEVMODE' }, #7
  250. 0x043e => { Unknown => 1, Name => 'AutoSaveFilePath' }, #7
  251. 0x043f => { Unknown => 1, Name => 'AutoSaveFormat' }, #7
  252. 0x0440 => { Unknown => 1, Name => 'PathSelectionState' }, #7
  253. # 0x07d0-0x0bb6 Path information
  254. 0x0bb7 => {
  255. Name => 'ClippingPathName',
  256. # convert from a Pascal string (ignoring 6 bytes of unknown data after string)
  257. ValueConv => q{
  258. my $len = ord($val);
  259. $val = substr($val, 0, $len+1) if $len < length($val);
  260. return Image::ExifTool::Photoshop::ConvertPascalString($self,$val);
  261. },
  262. },
  263. 0x0bb8 => { Unknown => 1, Name => 'OriginPathInfo' }, #7
  264. # 0x0fa0-0x1387 - plug-in resources (ref 7)
  265. 0x1b58 => { Unknown => 1, Name => 'ImageReadyVariables' }, #7
  266. 0x1b59 => { Unknown => 1, Name => 'ImageReadyDataSets' }, #7
  267. 0x1f40 => { Unknown => 1, Name => 'LightroomWorkflow' }, #7
  268. 0x2710 => { Unknown => 1, Name => 'PrintFlagsInfo' },
  269. );
  270. # Photoshop JPEG quality record (ref 2)
  271. %Image::ExifTool::Photoshop::JPEG_Quality = (
  272. PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
  273. WRITE_PROC => \&Image::ExifTool::WriteBinaryData,
  274. CHECK_PROC => \&Image::ExifTool::CheckBinaryData,
  275. FORMAT => 'int16s',
  276. GROUPS => { 2 => 'Image' },
  277. 0 => {
  278. Name => 'PhotoshopQuality',
  279. Writable => 1,
  280. PrintConv => '$val + 4',
  281. PrintConvInv => '$val - 4',
  282. },
  283. 1 => {
  284. Name => 'PhotoshopFormat',
  285. PrintConv => {
  286. 0x0000 => 'Standard',
  287. 0x0001 => 'Optimised',
  288. 0x0101 => 'Progressive',
  289. },
  290. },
  291. 2 => {
  292. Name => 'ProgressiveScans',
  293. PrintConv => {
  294. 1 => '3 Scans',
  295. 2 => '4 Scans',
  296. 3 => '5 Scans',
  297. },
  298. },
  299. );
  300. # Photoshop resolution information #PH
  301. %Image::ExifTool::Photoshop::Resolution = (
  302. PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
  303. WRITE_PROC => \&Image::ExifTool::WriteBinaryData,
  304. CHECK_PROC => \&Image::ExifTool::CheckBinaryData,
  305. FORMAT => 'int16u',
  306. FIRST_ENTRY => 0,
  307. WRITABLE => 1,
  308. GROUPS => { 2 => 'Image' },
  309. 0 => {
  310. Name => 'XResolution',
  311. Format => 'int32u',
  312. Priority => 0,
  313. ValueConv => '$val / 0x10000',
  314. ValueConvInv => 'int($val * 0x10000 + 0.5)',
  315. PrintConv => 'int($val * 100 + 0.5) / 100',
  316. PrintConvInv => '$val',
  317. },
  318. 2 => {
  319. Name => 'DisplayedUnitsX',
  320. PrintConv => {
  321. 1 => 'inches',
  322. 2 => 'cm',
  323. },
  324. },
  325. 4 => {
  326. Name => 'YResolution',
  327. Format => 'int32u',
  328. Priority => 0,
  329. ValueConv => '$val / 0x10000',
  330. ValueConvInv => 'int($val * 0x10000 + 0.5)',
  331. PrintConv => 'int($val * 100 + 0.5) / 100',
  332. PrintConvInv => '$val',
  333. },
  334. 6 => {
  335. Name => 'DisplayedUnitsY',
  336. PrintConv => {
  337. 1 => 'inches',
  338. 2 => 'cm',
  339. },
  340. },
  341. );
  342. # Photoshop PSD file header
  343. %Image::ExifTool::Photoshop::Header = (
  344. PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
  345. FORMAT => 'int16u',
  346. GROUPS => { 2 => 'Image' },
  347. NOTES => 'This information is found in the PSD file header.',
  348. 6 => 'NumChannels',
  349. 7 => { Name => 'ImageHeight', Format => 'int32u' },
  350. 9 => { Name => 'ImageWidth', Format => 'int32u' },
  351. 11 => 'BitDepth',
  352. 12 => {
  353. Name => 'ColorMode',
  354. PrintConvColumns => 2,
  355. PrintConv => {
  356. 0 => 'Bitmap',
  357. 1 => 'Grayscale',
  358. 2 => 'Indexed',
  359. 3 => 'RGB',
  360. 4 => 'CMYK',
  361. 7 => 'Multichannel',
  362. 8 => 'Duotone',
  363. 9 => 'Lab',
  364. },
  365. },
  366. );
  367. # tags for unknown resource types
  368. %Image::ExifTool::Photoshop::Unknown = (
  369. GROUPS => { 2 => 'Unknown' },
  370. );
  371. # define reference to IPTCDigest tagInfo hash for convenience
  372. $iptcDigestInfo = $Image::ExifTool::Photoshop::Main{0x0425};
  373. #------------------------------------------------------------------------------
  374. # AutoLoad our writer routines when necessary
  375. #
  376. sub AUTOLOAD
  377. {
  378. return Image::ExifTool::DoAutoLoad($AUTOLOAD, @_);
  379. }
  380. #------------------------------------------------------------------------------
  381. # Convert pascal string(s) to something we can use
  382. # Inputs: 1) Pascal string data
  383. # Returns: Strings, concatenated with ', '
  384. sub ConvertPascalString($$)
  385. {
  386. my ($et, $inStr) = @_;
  387. my $outStr = '';
  388. my $len = length($inStr);
  389. my $i=0;
  390. while ($i < $len) {
  391. my $n = ord(substr($inStr, $i, 1));
  392. last if $i + $n >= $len;
  393. $i and $outStr .= ', ';
  394. $outStr .= substr($inStr, $i+1, $n);
  395. $i += $n + 1;
  396. }
  397. my $charset = $et->Options('CharsetPhotoshop') || 'Latin';
  398. return $et->Decode($outStr, $charset);
  399. }
  400. #------------------------------------------------------------------------------
  401. # Process Photoshop APP13 record
  402. # Inputs: 0) ExifTool object reference, 1) Reference to directory information
  403. # 2) Tag table reference
  404. # Returns: 1 on success
  405. sub ProcessPhotoshop($$$)
  406. {
  407. my ($et, $dirInfo, $tagTablePtr) = @_;
  408. my $dataPt = $$dirInfo{DataPt};
  409. my $pos = $$dirInfo{DirStart};
  410. my $dirEnd = $pos + $$dirInfo{DirLen};
  411. my $verbose = $et->Options('Verbose');
  412. my $success = 0;
  413. SetByteOrder('MM'); # Photoshop is always big-endian
  414. $verbose and $et->VerboseDir('Photoshop', 0, $$dirInfo{DirLen});
  415. # scan through resource blocks:
  416. # Format: 0) Type, 4 bytes - '8BIM' (or the rare 'PHUT', 'DCSR' or 'AgHg')
  417. # 1) TagID,2 bytes
  418. # 2) Name, pascal string padded to even no. bytes
  419. # 3) Size, 4 bytes - N
  420. # 4) Data, N bytes
  421. while ($pos + 8 < $dirEnd) {
  422. my $type = substr($$dataPt, $pos, 4);
  423. my ($ttPtr, $extra, $val, $name);
  424. if ($type eq '8BIM') {
  425. $ttPtr = $tagTablePtr;
  426. } elsif ($type =~ /^(PHUT|DCSR|AgHg)$/) {
  427. $ttPtr = GetTagTable('Image::ExifTool::Photoshop::Unknown');
  428. } else {
  429. $type =~ s/([^\w])/sprintf("\\x%.2x",ord($1))/ge;
  430. $et->Warn(qq{Bad Photoshop IRB resource "$type"});
  431. last;
  432. }
  433. my $tag = Get16u($dataPt, $pos + 4);
  434. $pos += 6; # point to start of name
  435. my $nameLen = Get8u($dataPt, $pos);
  436. my $namePos = ++$pos;
  437. # skip resource block name (pascal string, padded to an even # of bytes)
  438. $pos += $nameLen;
  439. ++$pos unless $nameLen & 0x01;
  440. if ($pos + 4 > $dirEnd) {
  441. $et->Warn("Bad Photoshop resource block");
  442. last;
  443. }
  444. my $size = Get32u($dataPt, $pos);
  445. $pos += 4;
  446. if ($size + $pos > $dirEnd) {
  447. $et->Warn("Bad Photoshop resource data size $size");
  448. last;
  449. }
  450. $success = 1;
  451. if ($nameLen) {
  452. $name = substr($$dataPt, $namePos, $nameLen);
  453. $extra = qq{, Name="$name"};
  454. } else {
  455. $name = '';
  456. }
  457. my $tagInfo = $et->GetTagInfo($ttPtr, $tag);
  458. # append resource name to value if requested (braced by "/#...#/")
  459. if ($tagInfo and defined $$tagInfo{SetResourceName} and
  460. $$tagInfo{SetResourceName} eq '1' and $name !~ m{/#})
  461. {
  462. $val = substr($$dataPt, $pos, $size) . '/#' . $name . '#/';
  463. }
  464. $et->HandleTag($ttPtr, $tag, $val,
  465. TagInfo => $tagInfo,
  466. Extra => $extra,
  467. DataPt => $dataPt,
  468. DataPos => $$dirInfo{DataPos},
  469. Size => $size,
  470. Start => $pos,
  471. Parent => $$dirInfo{DirName},
  472. );
  473. $size += 1 if $size & 0x01; # size is padded to an even # bytes
  474. $pos += $size;
  475. }
  476. return $success;
  477. }
  478. #------------------------------------------------------------------------------
  479. # extract information from Photoshop PSD file
  480. # Inputs: 0) ExifTool object reference, 1) dirInfo reference
  481. # Returns: 1 if this was a valid PSD file, -1 on write error
  482. sub ProcessPSD($$)
  483. {
  484. my ($et, $dirInfo) = @_;
  485. my $raf = $$dirInfo{RAF};
  486. my $outfile = $$dirInfo{OutFile};
  487. my ($data, $err, $tagTablePtr);
  488. $raf->Read($data, 30) == 30 or return 0;
  489. $data =~ /^8BPS\0([\x01\x02])/ or return 0;
  490. SetByteOrder('MM');
  491. $et->SetFileType($1 eq "\x01" ? 'PSD' : 'PSB'); # set the FileType tag
  492. my %dirInfo = (
  493. DataPt => \$data,
  494. DirStart => 0,
  495. DirName => 'Photoshop',
  496. );
  497. my $len = Get32u(\$data, 26);
  498. if ($outfile) {
  499. Write($outfile, $data) or $err = 1;
  500. $raf->Read($data, $len) == $len or return -1;
  501. Write($outfile, $data) or $err = 1; # write color mode data
  502. # initialize map of where things are written
  503. $et->InitWriteDirs(\%psdMap);
  504. } else {
  505. # process the header
  506. $tagTablePtr = GetTagTable('Image::ExifTool::Photoshop::Header');
  507. $dirInfo{DirLen} = 30;
  508. $et->ProcessDirectory(\%dirInfo, $tagTablePtr);
  509. $raf->Seek($len, 1) or $err = 1; # skip over color mode data
  510. }
  511. $raf->Read($data, 4) == 4 or $err = 1;
  512. $len = Get32u(\$data, 0);
  513. $raf->Read($data, $len) == $len or $err = 1;
  514. $tagTablePtr = GetTagTable('Image::ExifTool::Photoshop::Main');
  515. $dirInfo{DirLen} = $len;
  516. my $rtnVal = 1;
  517. if ($outfile) {
  518. # rewrite IRB resources
  519. $data = WritePhotoshop($et, \%dirInfo, $tagTablePtr);
  520. if ($data) {
  521. $len = Set32u(length $data);
  522. Write($outfile, $len, $data) or $err = 1;
  523. # look for trailer and edit if necessary
  524. my $trailInfo = Image::ExifTool::IdentifyTrailer($raf);
  525. if ($trailInfo) {
  526. my $tbuf = '';
  527. $$trailInfo{OutFile} = \$tbuf; # rewrite trailer(s)
  528. # rewrite all trailers to buffer
  529. if ($et->ProcessTrailers($trailInfo)) {
  530. my $copyBytes = $$trailInfo{DataPos} - $raf->Tell();
  531. if ($copyBytes >= 0) {
  532. # copy remaining PSD file up to start of trailer
  533. while ($copyBytes) {
  534. my $n = ($copyBytes > 65536) ? 65536 : $copyBytes;
  535. $raf->Read($data, $n) == $n or $err = 1;
  536. Write($outfile, $data) or $err = 1;
  537. $copyBytes -= $n;
  538. }
  539. # write the trailer (or not)
  540. $et->WriteTrailerBuffer($trailInfo, $outfile) or $err = 1;
  541. } else {
  542. $et->Warn('Overlapping trailer');
  543. undef $trailInfo;
  544. }
  545. } else {
  546. undef $trailInfo;
  547. }
  548. }
  549. unless ($trailInfo) {
  550. # copy over the rest of the file
  551. while ($raf->Read($data, 65536)) {
  552. Write($outfile, $data) or $err = 1;
  553. }
  554. }
  555. } else {
  556. $err = 1;
  557. }
  558. $rtnVal = -1 if $err;
  559. } elsif ($err) {
  560. $et->Warn('File format error');
  561. } else {
  562. ProcessPhotoshop($et, \%dirInfo, $tagTablePtr);
  563. # process trailers if they exist
  564. my $trailInfo = Image::ExifTool::IdentifyTrailer($raf);
  565. $et->ProcessTrailers($trailInfo) if $trailInfo;
  566. }
  567. return $rtnVal;
  568. }
  569. 1; # end
  570. __END__
  571. =head1 NAME
  572. Image::ExifTool::Photoshop - Read/write Photoshop IRB meta information
  573. =head1 SYNOPSIS
  574. This module is loaded automatically by Image::ExifTool when required.
  575. =head1 DESCRIPTION
  576. Photoshop writes its own format of meta information called a Photoshop IRB
  577. resource which is located in the APP13 record of JPEG files. This module
  578. contains the definitions to read this information.
  579. =head1 NOTES
  580. Photoshop IRB blocks may have an associated resource name. These names are
  581. usually just an empty string, but if not empty they are displayed in the
  582. verbose level 2 (or greater) output. A special C<SetResourceName> flag may
  583. be set to '1' in the tag information hash to cause the resource name to be
  584. appended to the value when extracted. If this is done, the returned value
  585. has the form "VALUE/#NAME#/". When writing, the writer routine looks for
  586. this syntax (if C<SetResourceName> is defined), and and uses the embedded
  587. name to set the name of the new resource. This allows the resource names to
  588. be preserved when copying Photoshop information via user-defined tags.
  589. =head1 AUTHOR
  590. Copyright 2003-2016, Phil Harvey (phil at owl.phy.queensu.ca)
  591. This library is free software; you can redistribute it and/or modify it
  592. under the same terms as Perl itself.
  593. =head1 REFERENCES
  594. =over 4
  595. =item L<http://www.fine-view.com/jp/lab/doc/ps6ffspecsv2.pdf>
  596. =item L<http://www.ozhiker.com/electronics/pjmt/jpeg_info/irb_jpeg_qual.html>
  597. =item L<http://www.fileformat.info/format/psd/egff.htm>
  598. =item L<http://libpsd.graphest.com/files/Photoshop%20File%20Formats.pdf>
  599. =item L<http://www.adobe.com/devnet-apps/photoshop/fileformatashtml/>
  600. =back
  601. =head1 SEE ALSO
  602. L<Image::ExifTool::TagNames/Photoshop Tags>,
  603. L<Image::ExifTool(3pm)|Image::ExifTool>,
  604. L<Image::MetaData::JPEG(3pm)|Image::MetaData::JPEG>
  605. =cut