PageRenderTime 56ms CodeModel.GetById 15ms RepoModel.GetById 0ms app.codeStats 1ms

/add-ons/pjmt/Photoshop_IRB.php

https://github.com/jcplat/console-seolan
PHP | 1514 lines | 924 code | 214 blank | 376 comment | 88 complexity | e58043abc2d91c4d79fa8f5f487ca4ef MD5 | raw file
Possible License(s): LGPL-2.0, LGPL-2.1, GPL-3.0, Apache-2.0, BSD-3-Clause
  1. <?
  2. /******************************************************************************
  3. *
  4. * Filename: Photoshop_IRB.php
  5. *
  6. * Description: Provides functions for reading and writing information to/from
  7. * the 'App 13' Photoshop Information Resource Block segment of
  8. * JPEG format files
  9. *
  10. * Author: Evan Hunter
  11. *
  12. * Date: 23/7/2004
  13. *
  14. * Project: PHP JPEG Metadata Toolkit
  15. *
  16. * Revision: 1.11
  17. *
  18. * Changes: 1.00 -> 1.02 : changed get_Photoshop_IRB to work with corrupted
  19. * resource names which Photoshop can still read
  20. * 1.02 -> 1.03 : Fixed put_Photoshop_IRB to output "Photoshop 3.0\x00"
  21. * string with every APP13 segment, not just the first one
  22. * 1.03 -> 1.10 : changed get_Photoshop_IRB to fix processing of embedded resource names,
  23. * after discovering that Photoshop does not process
  24. * resource names according to the standard :
  25. * "Adobe Photoshop 6.0 File Formats Specification, Version 6.0, Release 2, November 2000"
  26. * This is an update to the change 1.00 -> 1.02, which was not fully correct
  27. * changed put_Photoshop_IRB to fix the writing of embedded resource name,
  28. * to avoid creating blank resources, and to fix a problem
  29. * causing the IRB block to be incorrectly positioned if no APP segments existed.
  30. * changed get_Photoshop_IPTC to initialise the output array correctly.
  31. * 1.10 -> 1.11 : Moved code out of get_Photoshop_IRB into new function unpack_Photoshop_IRB_Data
  32. * to allow reading of IRB blocks embedded within EXIF (for TIFF Files)
  33. * Moved code out of put_Photoshop_IRB into new function pack_Photoshop_IRB_Data
  34. * to allow writing of IRB blocks embedded within EXIF (for TIFF Files)
  35. * Enabled the usage of $GLOBALS['HIDE_UNKNOWN_TAGS'] to hide unknown resources
  36. * changed Interpret_IRB_to_HTML to allow thumbnail links to work when
  37. * toolkit is portable across directories
  38. *
  39. *
  40. * URL: http://electronics.ozhiker.com
  41. *
  42. * Copyright: Copyright Evan Hunter 2004
  43. *
  44. * License: This file is part of the PHP JPEG Metadata Toolkit.
  45. *
  46. * The PHP JPEG Metadata Toolkit is free software; you can
  47. * redistribute it and/or modify it under the terms of the
  48. * GNU General Public License as published by the Free Software
  49. * Foundation; either version 2 of the License, or (at your
  50. * option) any later version.
  51. *
  52. * The PHP JPEG Metadata Toolkit is distributed in the hope
  53. * that it will be useful, but WITHOUT ANY WARRANTY; without
  54. * even the implied warranty of MERCHANTABILITY or FITNESS
  55. * FOR A PARTICULAR PURPOSE. See the GNU General Public License
  56. * for more details.
  57. *
  58. * You should have received a copy of the GNU General Public
  59. * License along with the PHP JPEG Metadata Toolkit; if not,
  60. * write to the Free Software Foundation, Inc., 59 Temple
  61. * Place, Suite 330, Boston, MA 02111-1307 USA
  62. *
  63. * If you require a different license for commercial or other
  64. * purposes, please contact the author: evan@ozhiker.com
  65. *
  66. ******************************************************************************/
  67. // Change: as of version 1.11 - added to ensure the HIDE_UNKNOWN_TAGS variable is set even if EXIF.php is not included
  68. if ( !isset( $GLOBALS['HIDE_UNKNOWN_TAGS'] ) ) $GLOBALS['HIDE_UNKNOWN_TAGS']= FALSE;
  69. include_once 'IPTC.php';
  70. include_once 'Unicode.php';
  71. // TODO: Many Photoshop IRB resources not interpeted
  72. // TODO: Obtain a copy of the Photoshop CS File Format Specification
  73. // TODO: Find out what Photoshop IRB resources 1061, 1062 & 1064 are
  74. // TODO: Test get_Photoshop_IRB and put_Photoshop_IRB with multiple APP13 segments
  75. /******************************************************************************
  76. *
  77. * Function: get_Photoshop_IRB
  78. *
  79. * Description: Retrieves the Photoshop Information Resource Block (IRB) information
  80. * from an App13 JPEG segment and returns it as an array. This may
  81. * include IPTC-NAA IIM Information. Uses information
  82. * supplied by the get_jpeg_header_data function
  83. *
  84. * Parameters: jpeg_header_data - a JPEG header data array in the same format
  85. * as from get_jpeg_header_data
  86. *
  87. * Returns: IRBdata - The array of Photoshop IRB records
  88. * FALSE - if an APP 13 Photoshop IRB segment could not be found,
  89. * or if an error occured
  90. *
  91. ******************************************************************************/
  92. function get_Photoshop_IRB( $jpeg_header_data )
  93. {
  94. // Photoshop Image Resource blocks can span several JPEG APP13 segments, so we need to join them up if there are more than one
  95. $joined_IRB = "";
  96. //Cycle through the header segments
  97. for( $i = 0; $i < count( $jpeg_header_data ); $i++ )
  98. {
  99. // If we find an APP13 header,
  100. if ( strcmp ( $jpeg_header_data[$i]['SegName'], "APP13" ) == 0 )
  101. {
  102. // And if it has the photoshop label,
  103. if( strncmp ( $jpeg_header_data[$i]['SegData'], "Photoshop 3.0\x00", 14) == 0 )
  104. {
  105. // join it to the other previous IRB data
  106. $joined_IRB .= substr ( $jpeg_header_data[$i]['SegData'], 14 );
  107. }
  108. }
  109. }
  110. // If there was some Photoshop IRB information found,
  111. if ( $joined_IRB != "" )
  112. {
  113. // Found a Photoshop Image Resource Block - extract it.
  114. // Change: Moved code into unpack_Photoshop_IRB_Data to allow TIFF reading as of 1.11
  115. return unpack_Photoshop_IRB_Data( $joined_IRB );
  116. }
  117. else
  118. {
  119. // No Photoshop IRB found
  120. return FALSE;
  121. }
  122. }
  123. /******************************************************************************
  124. * End of Function: get_Photoshop_IRB
  125. ******************************************************************************/
  126. /******************************************************************************
  127. *
  128. * Function: put_Photoshop_IRB
  129. *
  130. * Description: Adds or modifies the Photoshop Information Resource Block (IRB)
  131. * information from an App13 JPEG segment. If a Photoshop IRB already
  132. * exists, it is replaced, otherwise a new one is inserted, using the
  133. * supplied data. Uses information supplied by the get_jpeg_header_data
  134. * function
  135. *
  136. * Parameters: jpeg_header_data - a JPEG header data array in the same format
  137. * as from get_jpeg_header_data
  138. * new_IRB_data - an array of the data to be stored in the Photoshop
  139. * IRB segment. Should be in the same format as received
  140. * from get_Photoshop_IRB
  141. *
  142. * Returns: jpeg_header_data - the JPEG header data array with the
  143. * Photoshop IRB added.
  144. * FALSE - if an error occured
  145. *
  146. ******************************************************************************/
  147. function put_Photoshop_IRB( $jpeg_header_data, $new_IRB_data )
  148. {
  149. // Delete all existing Photoshop IRB blocks - the new one will replace them
  150. //Cycle through the header segments
  151. for( $i = 0; $i < count( $jpeg_header_data ) ; $i++ )
  152. {
  153. // If we find an APP13 header,
  154. if ( strcmp ( $jpeg_header_data[$i]['SegName'], "APP13" ) == 0 )
  155. {
  156. // And if it has the photoshop label,
  157. if( strncmp ( $jpeg_header_data[$i]['SegData'], "Photoshop 3.0\x00", 14) == 0 )
  158. {
  159. // Delete the block information - it needs to be rebuilt
  160. array_splice( $jpeg_header_data, $i, 1 );
  161. }
  162. }
  163. }
  164. // Now we have deleted the pre-existing blocks
  165. // Retrieve the Packed Photoshop IRB Data
  166. // Change: Moved code into pack_Photoshop_IRB_Data to allow TIFF writing as of 1.11
  167. $packed_IRB_data = pack_Photoshop_IRB_Data( $new_IRB_data );
  168. // Change : This section changed to fix incorrect positioning of IRB segment, as of revision 1.10
  169. // when there are no APP segments present
  170. //Cycle through the header segments in reverse order (to find where to put the APP13 block - after any APP0 to APP12 blocks)
  171. $i = count( $jpeg_header_data ) - 1;
  172. while (( $i >= 0 ) && ( ( $jpeg_header_data[$i]['SegType'] > 0xED ) || ( $jpeg_header_data[$i]['SegType'] < 0xE0 ) ) )
  173. {
  174. $i--;
  175. }
  176. // Cycle through the packed output data until it's size is less than 32000 bytes, outputting each 32000 byte block to an APP13 segment
  177. while ( strlen( $packed_IRB_data ) > 32000 )
  178. {
  179. // Change: Fixed put_Photoshop_IRB to output "Photoshop 3.0\x00" string with every APP13 segment, not just the first one, as of 1.03
  180. // Write a 32000 byte APP13 segment
  181. array_splice($jpeg_header_data, $i +1 , 0, array( "SegType" => 0xED,
  182. "SegName" => "APP13",
  183. "SegDesc" => $GLOBALS[ "JPEG_Segment_Descriptions" ][ 0xED ],
  184. "SegData" => "Photoshop 3.0\x00" . substr( $packed_IRB_data,0,32000) ) );
  185. // Delete the 32000 bytes from the packed output data, that were just output
  186. $packed_IRB_data = substr_replace($packed_IRB_data, '', 0, 32000);
  187. $i++;
  188. }
  189. // Write the last block of packed output data to an APP13 segment - Note array_splice doesn't work with multidimensional arrays, hence inserting a blank string
  190. array_splice($jpeg_header_data, $i + 1 , 0, "" );
  191. $jpeg_header_data[$i + 1] = array( "SegType" => 0xED,
  192. "SegName" => "APP13",
  193. "SegDesc" => $GLOBALS[ "JPEG_Segment_Descriptions" ][ 0xED ],
  194. "SegData" => "Photoshop 3.0\x00" . $packed_IRB_data );
  195. return $jpeg_header_data;
  196. }
  197. /******************************************************************************
  198. * End of Function: put_Photoshop_IRB
  199. ******************************************************************************/
  200. /******************************************************************************
  201. *
  202. * Function: get_Photoshop_IPTC
  203. *
  204. * Description: Retrieves IPTC-NAA IIM information from within a Photoshop
  205. * IRB (if it is present) and returns it in an array. Uses
  206. * information supplied by the get_jpeg_header_data function
  207. *
  208. * Parameters: Photoshop_IRB_data - an array of Photoshop IRB records, as
  209. * returned from get_Photoshop_IRB
  210. *
  211. * Returns: IPTC_Data_Out - The array of IPTC-NAA IIM records
  212. * FALSE - if an IPTC-NAA IIM record could not be found, or if
  213. * an error occured
  214. *
  215. ******************************************************************************/
  216. function get_Photoshop_IPTC( $Photoshop_IRB_data )
  217. {
  218. // Change: Initialise array correctly, as of revision 1.10
  219. $IPTC_Data_Out = array();
  220. //Cycle through the Photoshop 8BIM records looking for the IPTC-NAA record
  221. for( $i = 0; $i < count( $Photoshop_IRB_data ); $i++ )
  222. {
  223. // Check if each record is a IPTC record (which has id 0x0404)
  224. if ( $Photoshop_IRB_data[$i]['ResID'] == 0x0404 )
  225. {
  226. // We've found an IPTC block - Decode it
  227. $IPTC_Data_Out = get_IPTC( $Photoshop_IRB_data[$i]['ResData'] );
  228. }
  229. }
  230. // If there was no records put into the output array,
  231. if ( count( $IPTC_Data_Out ) == 0 )
  232. {
  233. // Then return false
  234. return FALSE;
  235. }
  236. else
  237. {
  238. // Otherwise return the array
  239. return $IPTC_Data_Out;
  240. }
  241. }
  242. /******************************************************************************
  243. * End of Function: get_Photoshop_IPTC
  244. ******************************************************************************/
  245. /******************************************************************************
  246. *
  247. * Function: put_Photoshop_IPTC
  248. *
  249. * Description: Inserts a new IPTC-NAA IIM resource into a Photoshop
  250. * IRB, or replaces an the existing resource if one is present.
  251. * Uses information supplied by the get_Photoshop_IRB function
  252. *
  253. * Parameters: Photoshop_IRB_data - an array of Photoshop IRB records, as
  254. * returned from get_Photoshop_IRB, into
  255. * which the IPTC-NAA IIM record will be inserted
  256. * new_IPTC_block - an array of IPTC-NAA records in the same format
  257. * as those returned by get_Photoshop_IPTC
  258. *
  259. * Returns: Photoshop_IRB_data - The Photoshop IRB array with the
  260. * IPTC-NAA IIM resource inserted
  261. *
  262. ******************************************************************************/
  263. function put_Photoshop_IPTC( $Photoshop_IRB_data, $new_IPTC_block )
  264. {
  265. $iptc_block_pos = -1;
  266. //Cycle through the 8BIM records looking for the IPTC-NAA record
  267. for( $i = 0; $i < count( $Photoshop_IRB_data ); $i++ )
  268. {
  269. // Check if each record is a IPTC record (which has id 0x0404)
  270. if ( $Photoshop_IRB_data[$i]['ResID'] == 0x0404 )
  271. {
  272. // We've found an IPTC block - save the position
  273. $iptc_block_pos = $i;
  274. }
  275. }
  276. // If no IPTC block was found, create a new one
  277. if ( $iptc_block_pos == -1 )
  278. {
  279. // New block position will be at the end of the array
  280. $iptc_block_pos = count( $Photoshop_IRB_data );
  281. }
  282. // Write the new IRB resource to the Photoshop IRB array with no data
  283. $Photoshop_IRB_data[$iptc_block_pos] = array( "ResID" => 0x0404,
  284. "ResName" => $GLOBALS['Photoshop_ID_Names'][ 0x0404 ],
  285. "ResDesc" => $GLOBALS[ "Photoshop_ID_Descriptions" ][ 0x0404 ],
  286. "ResEmbeddedName" => "\x00\x00",
  287. "ResData" => put_IPTC( $new_IPTC_block ) );
  288. // Return the modified IRB
  289. return $Photoshop_IRB_data;
  290. }
  291. /******************************************************************************
  292. * End of Function: put_Photoshop_IPTC
  293. ******************************************************************************/
  294. /******************************************************************************
  295. *
  296. * Function: Interpret_IRB_to_HTML
  297. *
  298. * Description: Generates html showing the information contained in a Photoshop
  299. * IRB data array, as retrieved with get_Photoshop_IRB, including
  300. * any IPTC-NAA IIM records found.
  301. *
  302. * Please note that the following resource numbers are not currently
  303. * decoded: ( Many of these do not apply to JPEG images)
  304. * 0x03E9, 0x03EE, 0x03EF, 0x03F0, 0x03F1, 0x03F2, 0x03F6, 0x03F9,
  305. * 0x03FA, 0x03FB, 0x03FD, 0x03FE, 0x0400, 0x0401, 0x0402, 0x0405,
  306. * 0x040E, 0x040F, 0x0410, 0x0412, 0x0413, 0x0415, 0x0416, 0x0417,
  307. * 0x041B, 0x041C, 0x041D, 0x0BB7
  308. *
  309. * ( Also these Obsolete resource numbers)
  310. * 0x03E8, 0x03EB, 0x03FC, 0x03FF, 0x0403
  311. *
  312. *
  313. * Parameters: IRB_array - a Photoshop IRB data array as from get_Photoshop_IRB
  314. * filename - the name of the JPEG file being processed ( used
  315. * by the script which displays the Photoshop thumbnail)
  316. *
  317. *
  318. * Returns: output_str - the HTML string
  319. *
  320. ******************************************************************************/
  321. function Interpret_IRB_to_HTML( $IRB_array, $filename )
  322. {
  323. // Create a string to receive the HTML
  324. $output_str = "";
  325. // Check if the Photoshop IRB array is valid
  326. if ( $IRB_array !== FALSE )
  327. {
  328. // Create another string to receive secondary HTML to be appended at the end
  329. $secondary_output_str = "";
  330. // Add the Heading to the HTML
  331. $output_str .= "<h2 class=\"Photoshop_Main_Heading\">Contains Photoshop Information Resource Block (IRB)</h2>";
  332. // Add Table to the HTML
  333. $output_str .= "<table class=\"Photoshop_Table\" border=1>\n";
  334. // Cycle through each of the Photoshop IRB records, creating HTML for each
  335. foreach( $IRB_array as $IRB_Resource )
  336. {
  337. // Check if the entry is a known Photoshop IRB resource
  338. // Get the Name of the Resource
  339. if ( array_key_exists( $IRB_Resource['ResID'], $GLOBALS[ "Photoshop_ID_Names" ] ) )
  340. {
  341. $Resource_Name = $GLOBALS['Photoshop_ID_Names'][ $IRB_Resource['ResID'] ];
  342. }
  343. else
  344. {
  345. // Change: Added check for $GLOBALS['HIDE_UNKNOWN_TAGS'] to allow hiding of unknown resources as of 1.11
  346. if ( $GLOBALS['HIDE_UNKNOWN_TAGS'] == TRUE )
  347. {
  348. continue;
  349. }
  350. else
  351. {
  352. // Unknown Resource - Make appropriate name
  353. $Resource_Name = "Unknown Resource (". $IRB_Resource['ResID'] .")";
  354. }
  355. }
  356. // Add HTML for the resource as appropriate
  357. switch ( $IRB_Resource['ResID'] )
  358. {
  359. case 0x0404 : // IPTC-NAA IIM Record
  360. $secondary_output_str .= Interpret_IPTC_to_HTML( get_IPTC( $IRB_Resource['ResData'] ) );
  361. break;
  362. case 0x040B : // URL
  363. $output_str .= "<tr class=\"Photoshop_Table_Row\"><td class=\"Photoshop_Caption_Cell\">$Resource_Name</td><td class=\"Photoshop_Value_Cell\"><a href=\"" . $IRB_Resource['ResData'] . "\">" . htmlentities( $IRB_Resource['ResData'] ) ."</a></td></tr>\n";
  364. break;
  365. case 0x040A : // Copyright Marked
  366. if ( hexdec( bin2hex( $IRB_Resource['ResData'] ) ) == 1 )
  367. {
  368. $output_str .= "<tr class=\"Photoshop_Table_Row\"><td class=\"Photoshop_Caption_Cell\">$Resource_Name</td><td class=\"Photoshop_Value_Cell\"><pre>Image is Copyrighted Material</pre></td></tr>\n";
  369. }
  370. else
  371. {
  372. $output_str .= "<tr class=\"Photoshop_Table_Row\"><td class=\"Photoshop_Caption_Cell\">$Resource_Name</td><td class=\"Photoshop_Value_Cell\"><pre>Image is Not Copyrighted Material</pre></td></tr>\n";
  373. }
  374. break;
  375. case 0x040D : // Global Lighting Angle
  376. $output_str .= "<tr class=\"Photoshop_Table_Row\"><td class=\"Photoshop_Caption_Cell\">$Resource_Name</td><td class=\"Photoshop_Value_Cell\"><pre>Global lighting angle for effects layer = " . hexdec( bin2hex( $IRB_Resource['ResData'] ) ) . " degrees</pre></td></tr>\n";
  377. break;
  378. case 0x0419 : // Global Altitude
  379. $output_str .= "<tr class=\"Photoshop_Table_Row\"><td class=\"Photoshop_Caption_Cell\">$Resource_Name</td><td class=\"Photoshop_Value_Cell\"><pre>Global Altitude = " . hexdec( bin2hex( $IRB_Resource['ResData'] ) ) . "</pre></td></tr>\n";
  380. break;
  381. case 0x0421 : // Version Info
  382. $output_str .= "<tr class=\"Photoshop_Table_Row\"><td class=\"Photoshop_Caption_Cell\">$Resource_Name</td><td class=\"Photoshop_Value_Cell\"><pre>\n";
  383. $output_str .= "Version = " . hexdec( bin2hex( substr( $IRB_Resource['ResData'], 0, 4 ) ) ) . "\n";
  384. $output_str .= "Has Real Merged Data = " . ord( $IRB_Resource['ResData']{4} ) . "\n";
  385. $writer_size = hexdec( bin2hex( substr( $IRB_Resource['ResData'], 5, 4 ) ) ) * 2;
  386. $output_str .= "Writer Name = " . HTML_UTF16_Escape( substr( $IRB_Resource['ResData'], 9, $writer_size ), TRUE ) . "\n";
  387. $reader_size = hexdec( bin2hex( substr( $IRB_Resource['ResData'], 9 + $writer_size , 4 ) ) ) * 2;
  388. $output_str .= "Reader Name = " . HTML_UTF16_Escape( substr( $IRB_Resource['ResData'], 13 + $writer_size, $reader_size ), TRUE ) . "\n";
  389. $output_str .= "File Version = " . hexdec( bin2hex( substr( $IRB_Resource['ResData'], 13 + $writer_size + $reader_size, 4 ) ) ) . "\n";
  390. $output_str .= "</pre></td></tr>\n";
  391. break;
  392. case 0x0411 : // ICC Untagged
  393. if ( $IRB_Resource['ResData'] == "\x01" )
  394. {
  395. $output_str .= "<tr class=\"Photoshop_Table_Row\"><td class=\"Photoshop_Caption_Cell\">$Resource_Name</td><td class=\"Photoshop_Value_Cell\"><pre>Intentionally untagged - any assumed ICC profile handling disabled</pre></td></tr>\n";
  396. }
  397. else
  398. {
  399. $output_str .= "<tr class=\"Photoshop_Table_Row\"><td class=\"Photoshop_Caption_Cell\">$Resource_Name</td><td class=\"Photoshop_Value_Cell\"><pre>Unknown value (0x" .bin2hex( $IRB_Resource['ResData'] ). ")</pre></td></tr>\n";
  400. }
  401. break;
  402. case 0x041A : // Slices
  403. $output_str .= "<tr class=\"Photoshop_Table_Row\"><td class=\"Photoshop_Caption_Cell\">$Resource_Name</td><td class=\"Photoshop_Value_Cell\">";
  404. // Unpack the first 24 bytes
  405. $Slices_Info = unpack("NVersion/NBound_top/NBound_left/NBound_bottom/NBound_right/NStringlen", $IRB_Resource['ResData'] );
  406. $output_str .= "Version = " . $Slices_Info['Version'] . "<br>\n";
  407. $output_str .= "Bounding Rectangle = Top:" . $Slices_Info['Bound_top'] . ", Left:" . $Slices_Info['Bound_left'] . ", Bottom:" . $Slices_Info['Bound_bottom'] . ", Right:" . $Slices_Info['Bound_right'] . " (Pixels)<br>\n";
  408. $Slicepos = 24;
  409. // Extract a Unicode String
  410. $output_str .= "Text = '" . HTML_UTF16_Escape( substr( $IRB_Resource['ResData'], 24, $Slices_Info['Stringlen']*2), TRUE ) . "'<br>\n";
  411. $Slicepos += $Slices_Info['Stringlen'] * 2;
  412. // Unpack the number of Slices
  413. $Num_Slices = hexdec( bin2hex( substr( $IRB_Resource['ResData'], $Slicepos, 4 ) ) );
  414. $output_str .= "Number of Slices = " . $Num_Slices . "\n";
  415. $Slicepos += 4;
  416. // Cycle through the slices
  417. for( $i = 1; $i <= $Num_Slices; $i++ )
  418. {
  419. $output_str .= "<br><br>Slice $i:<br>\n";
  420. // Unpack the first 16 bytes of the slice
  421. $SliceA = unpack("NID/NGroupID/NOrigin/NStringlen", substr($IRB_Resource['ResData'], $Slicepos ) );
  422. $Slicepos += 16;
  423. $output_str .= "ID = " . $SliceA['ID'] . "<br>\n";
  424. $output_str .= "Group ID = " . $SliceA['GroupID'] . "<br>\n";
  425. $output_str .= "Origin = " . $SliceA['Origin'] . "<br>\n";
  426. // Extract a Unicode String
  427. $output_str .= "Text = '" . HTML_UTF16_Escape( substr( $IRB_Resource['ResData'], $Slicepos, $SliceA['Stringlen']*2), TRUE ) . "'<br>\n";
  428. $Slicepos += $SliceA['Stringlen'] * 2;
  429. // Unpack the next 24 bytes of the slice
  430. $SliceB = unpack("NType/NLeftPos/NTopPos/NRightPos/NBottomPos/NURLlen", substr($IRB_Resource['ResData'], $Slicepos ) );
  431. $Slicepos += 24;
  432. $output_str .= "Type = " . $SliceB['Type'] . "<br>\n";
  433. $output_str .= "Position = Top:" . $SliceB['TopPos'] . ", Left:" . $SliceB['LeftPos'] . ", Bottom:" . $SliceB['BottomPos'] . ", Right:" . $SliceB['RightPos'] . " (Pixels)<br>\n";
  434. // Extract a Unicode String
  435. $output_str .= "URL = <a href='" . substr( $IRB_Resource['ResData'], $Slicepos, $SliceB['URLlen']*2) . "'>" . HTML_UTF16_Escape( substr( $IRB_Resource['ResData'], $Slicepos, $SliceB['URLlen']*2), TRUE ) . "</a><br>\n";
  436. $Slicepos += $SliceB['URLlen'] * 2;
  437. // Unpack the length of a Unicode String
  438. $Targetlen = hexdec( bin2hex( substr( $IRB_Resource['ResData'], $Slicepos, 4 ) ) );
  439. $Slicepos += 4;
  440. // Extract a Unicode String
  441. $output_str .= "Target = '" . HTML_UTF16_Escape( substr( $IRB_Resource['ResData'], $Slicepos, $Targetlen*2), TRUE ) . "'<br>\n";
  442. $Slicepos += $Targetlen * 2;
  443. // Unpack the length of a Unicode String
  444. $Messagelen = hexdec( bin2hex( substr( $IRB_Resource['ResData'], $Slicepos, 4 ) ) );
  445. $Slicepos += 4;
  446. // Extract a Unicode String
  447. $output_str .= "Message = '" . HTML_UTF16_Escape( substr( $IRB_Resource['ResData'], $Slicepos, $Messagelen*2), TRUE ) . "'<br>\n";
  448. $Slicepos += $Messagelen * 2;
  449. // Unpack the length of a Unicode String
  450. $AltTaglen = hexdec( bin2hex( substr( $IRB_Resource['ResData'], $Slicepos, 4 ) ) );
  451. $Slicepos += 4;
  452. // Extract a Unicode String
  453. $output_str .= "Alt Tag = '" . HTML_UTF16_Escape( substr( $IRB_Resource['ResData'], $Slicepos, $AltTaglen*2), TRUE ) . "'<br>\n";
  454. $Slicepos += $AltTaglen * 2;
  455. // Unpack the HTML flag
  456. if ( ord( $IRB_Resource['ResData']{ $Slicepos } ) === 0x01 )
  457. {
  458. $output_str .= "Cell Text is HTML<br>\n";
  459. }
  460. else
  461. {
  462. $output_str .= "Cell Text is NOT HTML<br>\n";
  463. }
  464. $Slicepos++;
  465. // Unpack the length of a Unicode String
  466. $CellTextlen = hexdec( bin2hex( substr( $IRB_Resource['ResData'], $Slicepos, 4 ) ) );
  467. $Slicepos += 4;
  468. // Extract a Unicode String
  469. $output_str .= "Cell Text = '" . HTML_UTF16_Escape( substr( $IRB_Resource['ResData'], $Slicepos, $CellTextlen*2), TRUE ) . "'<br>\n";
  470. $Slicepos += $CellTextlen * 2;
  471. // Unpack the last 12 bytes of the slice
  472. $SliceC = unpack("NAlignH/NAlignV/CAlpha/CRed/CGreen/CBlue", substr($IRB_Resource['ResData'], $Slicepos ) );
  473. $Slicepos += 12;
  474. $output_str .= "Alignment = Horizontal:" . $SliceC['AlignH'] . ", Vertical:" . $SliceC['AlignV'] . "<br>\n";
  475. $output_str .= "Alpha Colour = " . $SliceC['Alpha'] . "<br>\n";
  476. $output_str .= "Red = " . $SliceC['Red'] . "<br>\n";
  477. $output_str .= "Green = " . $SliceC['Green'] . "<br>\n";
  478. $output_str .= "Blue = " . $SliceC['Blue'] . "\n";
  479. }
  480. $output_str .= "</td></tr>\n";
  481. break;
  482. case 0x0408 : // Grid and Guides information
  483. $output_str .= "<tr class=\"Photoshop_Table_Row\"><td class=\"Photoshop_Caption_Cell\">$Resource_Name</td><td class=\"Photoshop_Value_Cell\">";
  484. // Unpack the Grids info
  485. $Grid_Info = unpack("NVersion/NGridCycleH/NGridCycleV/NGuideCount", $IRB_Resource['ResData'] );
  486. $output_str .= "Version = " . $Grid_Info['Version'] . "<br>\n";
  487. $output_str .= "Grid Cycle = " . $Grid_Info['GridCycleH']/32 . " Pixel(s) x " . $Grid_Info['GridCycleV']/32 . " Pixel(s)<br>\n";
  488. $output_str .= "Number of Guides = " . $Grid_Info['GuideCount'] . "\n";
  489. // Cycle through the Guides
  490. for( $i = 0; $i < $Grid_Info['GuideCount']; $i++ )
  491. {
  492. // Unpack the info for this guide
  493. $Guide_Info = unpack("NLocation/CDirection", substr($IRB_Resource['ResData'],16+$i*5,5) );
  494. $output_str .= "<br>Guide $i : Location = " . $Guide_Info['Location']/32 . " Pixel(s) from edge";
  495. if ( $Guide_Info['Direction'] === 0 )
  496. {
  497. $output_str .= ", Vertical\n";
  498. }
  499. else
  500. {
  501. $output_str .= ", Horizontal\n";
  502. }
  503. }
  504. break;
  505. $output_str .= "</td></tr>\n";
  506. case 0x0406 : // JPEG Quality
  507. $Qual_Info = unpack("nQuality/nFormat/nScans/Cconst", $IRB_Resource['ResData'] );
  508. $output_str .= "<tr class=\"Photoshop_Table_Row\"><td class=\"Photoshop_Caption_Cell\">$Resource_Name</td><td class=\"Photoshop_Value_Cell\">";
  509. switch ( $Qual_Info['Quality'] )
  510. {
  511. case 0xFFFD:
  512. $output_str .= "Quality 1 (Low)<br>\n";
  513. break;
  514. case 0xFFFE:
  515. $output_str .= "Quality 2 (Low)<br>\n";
  516. break;
  517. case 0xFFFF:
  518. $output_str .= "Quality 3 (Low)<br>\n";
  519. break;
  520. case 0x0000:
  521. $output_str .= "Quality 4 (Low)<br>\n";
  522. break;
  523. case 0x0001:
  524. $output_str .= "Quality 5 (Medium)<br>\n";
  525. break;
  526. case 0x0002:
  527. $output_str .= "Quality 6 (Medium)<br>\n";
  528. break;
  529. case 0x0003:
  530. $output_str .= "Quality 7 (Medium)<br>\n";
  531. break;
  532. case 0x0004:
  533. $output_str .= "Quality 8 (High)<br>\n";
  534. break;
  535. case 0x0005:
  536. $output_str .= "Quality 9 (High)<br>\n";
  537. break;
  538. case 0x0006:
  539. $output_str .= "Quality 10 (Maximum)<br>\n";
  540. break;
  541. case 0x0007:
  542. $output_str .= "Quality 11 (Maximum)<br>\n";
  543. break;
  544. case 0x0008:
  545. $output_str .= "Quality 12 (Maximum)<br>\n";
  546. break;
  547. default:
  548. $output_str .= "Unknown Quality (" . $Qual_Info['Quality'] . ")<br>\n";
  549. break;
  550. }
  551. switch ( $Qual_Info['Format'] )
  552. {
  553. case 0x0000:
  554. $output_str .= "Standard Format\n";
  555. break;
  556. case 0x0001:
  557. $output_str .= "Optimised Format\n";
  558. break;
  559. case 0x0101:
  560. $output_str .= "Progressive Format<br>\n";
  561. break;
  562. default:
  563. $output_str .= "Unknown Format (" . $Qual_Info['Format'] .")\n";
  564. break;
  565. }
  566. if ( $Qual_Info['Format'] == 0x0101 )
  567. {
  568. switch ( $Qual_Info['Scans'] )
  569. {
  570. case 0x0001:
  571. $output_str .= "3 Scans\n";
  572. break;
  573. case 0x0002:
  574. $output_str .= "4 Scans\n";
  575. break;
  576. case 0x0003:
  577. $output_str .= "5 Scans\n";
  578. break;
  579. default:
  580. $output_str .= "Unknown number of scans (" . $Qual_Info['Scans'] .")\n";
  581. break;
  582. }
  583. }
  584. $output_str .= "</td></tr>\n";
  585. break;
  586. case 0x0409 : // Thumbnail Resource
  587. case 0x040C : // Thumbnail Resource
  588. $thumb_data = unpack("NFormat/NWidth/NHeight/NWidthBytes/NSize/NCompressedSize/nBitsPixel/nPlanes", $IRB_Resource['ResData'] );
  589. $output_str .= "<tr class=\"Photoshop_Table_Row\"><td class=\"Photoshop_Caption_Cell\">$Resource_Name</td><td class=\"Photoshop_Value_Cell\"><pre>\n";
  590. $output_str .= "Format = " . (( $thumb_data['Format'] == 1 ) ? "JPEG RGB\n" : "Raw RGB\n");
  591. $output_str .= "Width = " . $thumb_data['Width'] . "\n";
  592. $output_str .= "Height = " . $thumb_data['Height'] . "\n";
  593. $output_str .= "Padded Row Bytes = " . $thumb_data['WidthBytes'] . " bytes\n";
  594. $output_str .= "Total Size = " . $thumb_data['Size'] . " bytes\n";
  595. $output_str .= "Compressed Size = " . $thumb_data['CompressedSize'] . " bytes\n";
  596. $output_str .= "Bits per Pixel = " . $thumb_data['BitsPixel'] . " bits\n";
  597. $output_str .= "Number of planes = " . $thumb_data['Planes'] . " bytes\n";
  598. // Change: as of version 1.11 - Changed to make thumbnail link portable across directories
  599. // Build the path of the thumbnail script and its filename parameter to put in a url
  600. $link_str = get_relative_path( dirname(__FILE__) . "/get_ps_thumb.php" , getcwd ( ) );
  601. $link_str .= "?filename=";
  602. $link_str .= get_relative_path( $filename, dirname(__FILE__) );
  603. // Add thumbnail link to html
  604. $output_str .= "Thumbnail Data:</pre><a class=\"Photoshop_Thumbnail_Link\" href=\"$link_str\"><img class=\"Photoshop_Thumbnail_Link\" src=\"$link_str\"></a>\n";
  605. $output_str .= "</td></tr>\n";
  606. break;
  607. case 0x0414 : // Document Specific ID's
  608. $output_str .= "<tr class=\"Photoshop_Table_Row\"><td class=\"Photoshop_Caption_Cell\">$Resource_Name</td><td class=\"Photoshop_Value_Cell\"><pre>" . hexdec( bin2hex( $IRB_Resource['ResData'] ) ) . "</pre></td></tr>\n";
  609. break;
  610. case 0x041E : // URL List
  611. $URL_count = hexdec( bin2hex( substr( $IRB_Resource['ResData'], 0, 4 ) ) );
  612. $output_str .= "<tr class=\"Photoshop_Table_Row\"><td class=\"Photoshop_Caption_Cell\">$Resource_Name</td><td class=\"Photoshop_Value_Cell\">\n";
  613. $output_str .= "$URL_count URL's in list<br>\n";
  614. $urlstr = substr( $IRB_Resource['ResData'], 4 );
  615. // TODO: Check if URL List in Photoshop IRB works
  616. for( $i = 0; $i < $URL_count; $i++ )
  617. {
  618. $url_data = unpack( "NLong/NID/NURLSize", $urlstr );
  619. $output_str .= "URL $i info: long = " . $url_data['Long'] .", ";
  620. $output_str .= "ID = " . $url_data['ID'] . ", ";
  621. $urlstr = substr( $urlstr, 12 );
  622. $url = substr( $urlstr, 0, $url_data['URLSize'] );
  623. $output_str .= "URL = <a href=\"" . xml_UTF16_clean( $url, TRUE ) . "\">" . HTML_UTF16_Escape( $url, TRUE ) . "</a><br>\n";
  624. }
  625. $output_str .= "</td></tr>\n";
  626. break;
  627. case 0x03F4 : // Grayscale and multichannel halftoning information.
  628. $output_str .= "<tr class=\"Photoshop_Table_Row\"><td class=\"Photoshop_Caption_Cell\">$Resource_Name</td><td class=\"Photoshop_Value_Cell\"><pre>\n";
  629. $output_str .= Interpret_Halftone( $IRB_Resource['ResData'] );
  630. $output_str .= "</pre></td></tr>\n";
  631. break;
  632. case 0x03F5 : // Color halftoning information
  633. $output_str .= "<tr class=\"Photoshop_Table_Row\"><td class=\"Photoshop_Caption_Cell\">$Resource_Name</td><td class=\"Photoshop_Value_Cell\"><pre>\n";
  634. $output_str .= "Cyan Halftoning Info:\n" . Interpret_Halftone( substr( $IRB_Resource['ResData'], 0, 18 ) ) . "\n\n";
  635. $output_str .= "Magenta Halftoning Info:\n" . Interpret_Halftone( substr( $IRB_Resource['ResData'], 18, 18 ) ) . "\n\n";
  636. $output_str .= "Yellow Halftoning Info:\n" . Interpret_Halftone( substr( $IRB_Resource['ResData'], 36, 18 ) ) . "\n";
  637. $output_str .= "Black Halftoning Info:\n" . Interpret_Halftone( substr( $IRB_Resource['ResData'], 54, 18 ) ) . "\n";
  638. $output_str .= "</pre></td></tr>\n";
  639. break;
  640. case 0x03F7 : // Grayscale and multichannel transfer function.
  641. $output_str .= "<tr class=\"Photoshop_Table_Row\"><td class=\"Photoshop_Caption_Cell\">$Resource_Name</td><td class=\"Photoshop_Value_Cell\"><pre>\n";
  642. $output_str .= Interpret_Transfer_Function( substr( $IRB_Resource['ResData'], 0, 28 ) ) ;
  643. $output_str .= "</pre></td></tr>\n";
  644. break;
  645. case 0x03F8 : // Color transfer functions
  646. $output_str .= "<tr class=\"Photoshop_Table_Row\"><td class=\"Photoshop_Caption_Cell\">$Resource_Name</td><td class=\"Photoshop_Value_Cell\"><pre>\n";
  647. $output_str .= "Red Transfer Function: \n" . Interpret_Transfer_Function( substr( $IRB_Resource['ResData'], 0, 28 ) ) . "\n\n";
  648. $output_str .= "Green Transfer Function: \n" . Interpret_Transfer_Function( substr( $IRB_Resource['ResData'], 28, 28 ) ) . "\n\n";
  649. $output_str .= "Blue Transfer Function: \n" . Interpret_Transfer_Function( substr( $IRB_Resource['ResData'], 56, 28 ) ) . "\n";
  650. $output_str .= "</pre></td></tr>\n";
  651. break;
  652. case 0x03F3 : // Print Flags
  653. $output_str .= "<tr class=\"Photoshop_Table_Row\"><td class=\"Photoshop_Caption_Cell\">$Resource_Name</td><td class=\"Photoshop_Value_Cell\"><pre>\n";
  654. if ( $IRB_Resource['ResData']{0} == "\x01" )
  655. {
  656. $output_str .= "Labels Selected\n";
  657. }
  658. else
  659. {
  660. $output_str .= "Labels Not Selected\n";
  661. }
  662. if ( $IRB_Resource['ResData']{1} == "\x01" )
  663. {
  664. $output_str .= "Crop Marks Selected\n";
  665. }
  666. else
  667. {
  668. $output_str .= "Crop Marks Not Selected\n";
  669. }
  670. if ( $IRB_Resource['ResData']{2} == "\x01" )
  671. {
  672. $output_str .= "Color Bars Selected\n";
  673. }
  674. else
  675. {
  676. $output_str .= "Color Bars Not Selected\n";
  677. }
  678. if ( $IRB_Resource['ResData']{3} == "\x01" )
  679. {
  680. $output_str .= "Registration Marks Selected\n";
  681. }
  682. else
  683. {
  684. $output_str .= "Registration Marks Not Selected\n";
  685. }
  686. if ( $IRB_Resource['ResData']{4} == "\x01" )
  687. {
  688. $output_str .= "Negative Selected\n";
  689. }
  690. else
  691. {
  692. $output_str .= "Negative Not Selected\n";
  693. }
  694. if ( $IRB_Resource['ResData']{5} == "\x01" )
  695. {
  696. $output_str .= "Flip Selected\n";
  697. }
  698. else
  699. {
  700. $output_str .= "Flip Not Selected\n";
  701. }
  702. if ( $IRB_Resource['ResData']{6} == "\x01" )
  703. {
  704. $output_str .= "Interpolate Selected\n";
  705. }
  706. else
  707. {
  708. $output_str .= "Interpolate Not Selected\n";
  709. }
  710. if ( $IRB_Resource['ResData']{7} == "\x01" )
  711. {
  712. $output_str .= "Caption Selected";
  713. }
  714. else
  715. {
  716. $output_str .= "Caption Not Selected";
  717. }
  718. $output_str .= "</pre></td></tr>\n";
  719. break;
  720. case 0x2710 : // Print Flags Information
  721. $PrintFlags = unpack( "nVersion/CCentCrop/Cjunk/NBleedWidth/nBleedWidthScale", $IRB_Resource['ResData'] );
  722. $output_str .= "<tr class=\"Photoshop_Table_Row\"><td class=\"Photoshop_Caption_Cell\">$Resource_Name</td><td class=\"Photoshop_Value_Cell\"><pre>\n";
  723. $output_str .= "Version = " . $PrintFlags['Version'] . "\n";
  724. $output_str .= "Centre Crop Marks = " . $PrintFlags['CentCrop'] . "\n";
  725. $output_str .= "Bleed Width = " . $PrintFlags['BleedWidth'] . "\n";
  726. $output_str .= "Bleed Width Scale = " . $PrintFlags['BleedWidthScale'];
  727. $output_str .= "</pre></td></tr>\n";
  728. break;
  729. case 0x03ED : // Resolution Info
  730. $ResInfo = unpack( "nhRes_int/nhResdec/nhResUnit/nwidthUnit/nvRes_int/nvResdec/nvResUnit/nheightUnit", $IRB_Resource['ResData'] );
  731. $output_str .= "<tr class=\"Photoshop_Table_Row\"><td class=\"Photoshop_Caption_Cell\">$Resource_Name</td><td class=\"Photoshop_Value_Cell\"><pre>\n";
  732. $output_str .= "Horizontal Resolution = " . ($ResInfo['hRes_int'] + $ResInfo['hResdec']/65536) . " pixels per Inch\n";
  733. $output_str .= "Vertical Resolution = " . ($ResInfo['vRes_int'] + $ResInfo['vResdec']/65536) . " pixels per Inch\n";
  734. if ( $ResInfo['hResUnit'] == 1 )
  735. {
  736. $output_str .= "Display units for Horizontal Resolution = Pixels per Inch\n";
  737. }
  738. elseif ( $ResInfo['hResUnit'] == 2 )
  739. {
  740. $output_str .= "Display units for Horizontal Resolution = Pixels per Centimetre\n";
  741. }
  742. else
  743. {
  744. $output_str .= "Display units for Horizontal Resolution = Unknown Value (". $ResInfo['hResUnit'] .")\n";
  745. }
  746. if ( $ResInfo['vResUnit'] == 1 )
  747. {
  748. $output_str .= "Display units for Vertical Resolution = Pixels per Inch\n";
  749. }
  750. elseif ( $ResInfo['vResUnit'] == 2 )
  751. {
  752. $output_str .= "Display units for Vertical Resolution = Pixels per Centimetre\n";
  753. }
  754. else
  755. {
  756. $output_str .= "Display units for Vertical Resolution = Unknown Value (". $ResInfo['vResUnit'] .")\n";
  757. }
  758. if ( $ResInfo['widthUnit'] == 1 )
  759. {
  760. $output_str .= "Display units for Image Width = Inches\n";
  761. }
  762. elseif ( $ResInfo['widthUnit'] == 2 )
  763. {
  764. $output_str .= "Display units for Image Width = Centimetres\n";
  765. }
  766. elseif ( $ResInfo['widthUnit'] == 3 )
  767. {
  768. $output_str .= "Display units for Image Width = Points\n";
  769. }
  770. elseif ( $ResInfo['widthUnit'] == 4 )
  771. {
  772. $output_str .= "Display units for Image Width = Picas\n";
  773. }
  774. elseif ( $ResInfo['widthUnit'] == 5 )
  775. {
  776. $output_str .= "Display units for Image Width = Columns\n";
  777. }
  778. else
  779. {
  780. $output_str .= "Display units for Image Width = Unknown Value (". $ResInfo['widthUnit'] .")\n";
  781. }
  782. if ( $ResInfo['heightUnit'] == 1 )
  783. {
  784. $output_str .= "Display units for Image Height = Inches";
  785. }
  786. elseif ( $ResInfo['heightUnit'] == 2 )
  787. {
  788. $output_str .= "Display units for Image Height = Centimetres";
  789. }
  790. elseif ( $ResInfo['heightUnit'] == 3 )
  791. {
  792. $output_str .= "Display units for Image Height = Points";
  793. }
  794. elseif ( $ResInfo['heightUnit'] == 4 )
  795. {
  796. $output_str .= "Display units for Image Height = Picas";
  797. }
  798. elseif ( $ResInfo['heightUnit'] == 5 )
  799. {
  800. $output_str .= "Display units for Image Height = Columns";
  801. }
  802. else
  803. {
  804. $output_str .= "Display units for Image Height = Unknown Value (". $ResInfo['heightUnit'] .")";
  805. }
  806. $output_str .= "</pre></td></tr>\n";
  807. break;
  808. default : // All other records
  809. $output_str .= "<tr class=\"Photoshop_Table_Row\"><td class=\"Photoshop_Caption_Cell\">$Resource_Name</td><td class=\"Photoshop_Value_Cell\">RESOURCE DECODING NOT IMPLEMENTED YET<BR>" . strlen( $IRB_Resource['ResData'] ) . " bytes</td></tr>\n";
  810. }
  811. }
  812. // Add the table end to the HTML
  813. $output_str .= "</table>\n";
  814. // Add any secondary output to the HTML
  815. $output_str .= $secondary_output_str;
  816. }
  817. // Return the HTML
  818. return $output_str;
  819. }
  820. /******************************************************************************
  821. * End of Function: Interpret_IRB_to_HTML
  822. ******************************************************************************/
  823. /******************************************************************************
  824. *
  825. * INTERNAL FUNCTIONS
  826. *
  827. ******************************************************************************/
  828. /******************************************************************************
  829. *
  830. * Function: unpack_Photoshop_IRB_Data
  831. *
  832. * Description: Extracts Photoshop Information Resource Block (IRB) information
  833. * from a binary string containing the IRB, as read from a file
  834. *
  835. * Parameters: IRB_Data - The binary string containing the IRB
  836. *
  837. * Returns: IRBdata - The array of Photoshop IRB records
  838. *
  839. ******************************************************************************/
  840. function unpack_Photoshop_IRB_Data( $IRB_Data )
  841. {
  842. $pos = 0;
  843. // Cycle through the IRB and extract its records - Records are started with 8BIM, so cycle until no more instances of 8BIM can be found
  844. while ( ( $pos < strlen( $IRB_Data ) ) && ( ($pos = strpos( $IRB_Data, "8BIM", $pos) ) !== FALSE ) )
  845. {
  846. // Skip the position over the 8BIM characters
  847. $pos += 4;
  848. // Next two characters are the record ID - denoting what type of record it is.
  849. $ID = ord( $IRB_Data{ $pos } ) * 256 + ord( $IRB_Data{ $pos +1 } );
  850. // Skip the positionover the two record ID characters
  851. $pos += 2;
  852. // Next comes a Record Name - usually not used, but it should be a null terminated string, padded with 0x00 to be an even length
  853. $namestartpos = $pos;
  854. // Change: Fixed processing of embedded resource names, as of revision 1.10
  855. // NOTE: Photoshop does not process resource names according to the standard :
  856. // "Adobe Photoshop 6.0 File Formats Specification, Version 6.0, Release 2, November 2000"
  857. //
  858. // The resource name is actually formatted as follows:
  859. // One byte name length, followed by the null terminated ascii name string.
  860. // The field is then padded with a Null character if required, to ensure that the
  861. // total length of the name length and name is even.
  862. // Name - process it
  863. // Get the length
  864. $namelen = ord ( $IRB_Data{ $namestartpos } );
  865. // Total length of name and length info must be even, hence name length must be odd
  866. // Check if the name length is even,
  867. if ( $namelen % 2 == 0 )
  868. {
  869. // add one to length to make it odd
  870. $namelen ++;
  871. }
  872. // Extract the name
  873. $resembeddedname = trim( substr ( $IRB_Data, $namestartpos+1, $namelen) );
  874. $pos += $namelen + 1;
  875. // Next is a four byte size field indicating the size in bytes of the record's data - MSB first
  876. $datasize = ord( $IRB_Data{ $pos } ) * 16777216 + ord( $IRB_Data{ $pos + 1 } ) * 65536 +
  877. ord( $IRB_Data{ $pos + 2 } ) * 256 + ord( $IRB_Data{ $pos + 3 } );
  878. $pos += 4;
  879. // The record is stored padded with 0x00 characters to make the size even, so we need to calculate the stored size
  880. $storedsize = $datasize + ($datasize % 2);
  881. $resdata = substr ( $IRB_Data, $pos, $datasize );
  882. // Get the description for this resource
  883. // Check if this is a Path information Resource, since they have a range of ID's
  884. if ( ( $ID >= 0x07D0 ) && ( $ID <= 0x0BB6 ) )
  885. {
  886. $ResDesc = "ID Info : Path Information (saved paths).";
  887. }
  888. else
  889. {
  890. if ( array_key_exists( $ID, $GLOBALS[ "Photoshop_ID_Descriptions" ] ) )
  891. {
  892. $ResDesc = $GLOBALS[ "Photoshop_ID_Descriptions" ][ $ID ];
  893. }
  894. else
  895. {
  896. $ResDesc = "";
  897. }
  898. }
  899. // Get the Name of the Resource
  900. if ( array_key_exists( $ID, $GLOBALS[ "Photoshop_ID_Names" ] ) )
  901. {
  902. $ResName = $GLOBALS['Photoshop_ID_Names'][ $ID ];
  903. }
  904. else
  905. {
  906. $ResName = "";
  907. }
  908. // Store the Resource in the array to be returned
  909. $IRB_Array[] = array( "ResID" => $ID,
  910. "ResName" => $ResName,
  911. "ResDesc" => $ResDesc,
  912. "ResEmbeddedName" => $resembeddedname,
  913. "ResData" => $resdata );
  914. // Jump over the data to the next record
  915. $pos += $storedsize;
  916. }
  917. // Return the array created
  918. return $IRB_Array;
  919. }
  920. /******************************************************************************
  921. * End of Function: unpack_Photoshop_IRB_Data
  922. ******************************************************************************/
  923. /******************************************************************************
  924. *
  925. * Function: pack_Photoshop_IRB_Data
  926. *
  927. * Description: Packs a Photoshop Information Resource Block (IRB) array into it's
  928. * binary form, which can be written to a file
  929. *
  930. * Parameters: IRB_data - an Photoshop IRB array to be converted. Should be in
  931. * the same format as received from get_Photoshop_IRB
  932. *
  933. * Returns: packed_IRB_data - the binary string of packed IRB data
  934. *
  935. ******************************************************************************/
  936. function pack_Photoshop_IRB_Data( $IRB_data )
  937. {
  938. $packed_IRB_data = "";
  939. // Cycle through each resource in the IRB,
  940. foreach ($IRB_data as $resource)
  941. {
  942. // Change: Fix to avoid creating blank resources, as of revision 1.10
  943. // Check if there is actually any data for this resource
  944. if( strlen( $resource['ResData'] ) == 0 )
  945. {
  946. // No data for resource - skip it
  947. continue;
  948. }
  949. // Append the 8BIM tag, and resource ID to the packed output data
  950. $packed_IRB_data .= pack("a4n", "8BIM", $resource['ResID'] );
  951. // Change: Fixed processing of embedded resource names, as of revision 1.10
  952. // NOTE: Photoshop does not process resource names according to the standard :
  953. // "Adobe Photoshop 6.0 File Formats Specification, Version 6.0, Release 2, November 2000"
  954. //
  955. // The resource name is actually formatted as follows:
  956. // One byte name length, followed by the null terminated ascii name string.
  957. // The field is then padded with a Null character if required, to ensure that the
  958. // total length of the name length and name is even.
  959. // Append Name Size
  960. $packed_IRB_data .= pack( "c", strlen(trim($resource['ResEmbeddedName'])));
  961. // Append the Resource Name to the packed output data
  962. $packed_IRB_data .= trim($resource['ResEmbeddedName']);
  963. // If the resource name is even length, then with the addition of
  964. // the size it becomes odd and needs to be padded to an even number
  965. if ( strlen( trim($resource['ResEmbeddedName']) ) % 2 == 0 )
  966. {
  967. // then it needs to be evened up by appending another null
  968. $packed_IRB_data .= "\x00";
  969. }
  970. // Append the resource data size to the packed output data
  971. $packed_IRB_data .= pack("N", strlen( $resource['ResData'] ) );
  972. // Append the resource data to the packed output data
  973. $packed_IRB_data .= $resource['ResData'];
  974. // If the resource data is odd length,
  975. if ( strlen( $resource['ResData'] ) % 2 == 1 )
  976. {
  977. // then it needs to be evened up by appending another null
  978. $packed_IRB_data .= "\x00";
  979. }
  980. }
  981. // Return the packed data string
  982. return $packed_IRB_data;
  983. }
  984. /******************************************************************************
  985. * End of Function: pack_Photoshop_IRB_Data
  986. ******************************************************************************/
  987. /******************************************************************************
  988. *
  989. * Internal Function: Interpret_Transfer_Function
  990. *
  991. * Description: Used by Interpret_IRB_to_HTML to interpret Color transfer functions
  992. * for Photoshop IRB resource 0x03F8. Converts the transfer function
  993. * information to a human readable version.
  994. *
  995. * Parameters: Transfer_Function_Binary - a 28 byte Ink curves structure string
  996. *
  997. * Returns: output_str - the text string containing the transfer function
  998. * information
  999. *
  1000. ******************************************************************************/
  1001. function Interpret_Transfer_Function( $Transfer_Function_Binary )
  1002. {
  1003. // Unpack the Transfer function information
  1004. $Trans_vals = unpack ( "n13Curve/nOverride", $Transfer_Function_Binary );
  1005. $output_str = "Transfer Function Points: ";
  1006. // Cycle through each of the Transfer function array values
  1007. foreach ( $Trans_vals as $Key => $val )
  1008. {
  1009. // Check if the value should be negative
  1010. if ($val > 32768 )
  1011. {
  1012. // Value should be negative - make it so
  1013. $val = $val - 65536;
  1014. }
  1015. // Check that the Override item is not getting in this list, and
  1016. // that the value is not -1, which means ignored
  1017. if ( ( $Key != "Override" ) && ( $val != -1 ) )
  1018. {
  1019. // This is a valid transfer function point, output it
  1020. $output_str .= $val/10 . "%, ";
  1021. }
  1022. }
  1023. // Output the override info
  1024. if ( $Trans_vals['Override'] == 0 )
  1025. {
  1026. $output_str .= "\nOverride: Let printer supply curve";
  1027. }
  1028. else
  1029. {
  1030. $output_str .= "\nOverride: Override printer’s default transfer curve";
  1031. }
  1032. // Return the result
  1033. return $output_str;
  1034. }
  1035. /******************************************************************************
  1036. * End of Function: Interpret_Transfer_Function
  1037. ******************************************************************************/
  1038. /******************************************************************************
  1039. *
  1040. * Internal Function: Interpret_Halftone
  1041. *
  1042. * Description: Used by Interpret_IRB_to_HTML to interpret Color halftoning information
  1043. * for Photoshop IRB resource 0x03F5. Converts the halftoning info
  1044. * to a human readable version.
  1045. *
  1046. * Parameters: Transfer_Function_Binary - a 18 byte Halftone screen parameter
  1047. & structure string
  1048. *
  1049. * Returns: output_str - the text string containing the transfer function
  1050. * information
  1051. *
  1052. ******************************************************************************/
  1053. function Interpret_Halftone( $Halftone_Binary )
  1054. {
  1055. // Create a string to receive the output
  1056. $output_str = "";
  1057. // Unpack the binary data into an array
  1058. $HalftoneInfo = unpack( "nFreqVal_int/nFreqVal_dec/nFreqScale/nAngle_int/nAngle_dec/nShapeCode/NMisc/CAccurate/CDefault", $Halftone_Binary );
  1059. // Interpret Ink Screen Frequency
  1060. $output_str .= "Ink Screen Frequency = " . ($HalftoneInfo['FreqVal_int'] + $HalftoneInfo['FreqVal_dec']/65536) . " lines per Inch\n";
  1061. if ( $HalftoneInfo['FreqScale'] == 1 )
  1062. {
  1063. $output_str .= "Display units for Ink Screen Frequency = Inches\n";
  1064. }
  1065. else
  1066. {
  1067. $output_str .= "Display units for Ink Screen Frequency = Centimetres\n";
  1068. }
  1069. // Interpret Angle for screen
  1070. $output_str .= "Angle for screen = " . ($HalftoneInfo['Angle_int'] + $HalftoneInfo['Angle_dec']/65536) . " degrees\n";
  1071. // Interpret Shape of Halftone Dots
  1072. if ($HalftoneInfo['ShapeCode'] > 32768 )
  1073. {
  1074. $HalftoneInfo['ShapeCode'] = $HalftoneInfo['ShapeCode'] - 65536;
  1075. }
  1076. if ( $HalftoneInfo['ShapeCode'] == 0 )
  1077. {
  1078. $output_str .= "Shape of Halftone Dots = Round\n";
  1079. }
  1080. elseif ( $HalftoneInfo['ShapeCode'] == 1 )
  1081. {
  1082. $output_str .= "Shape of Halftone Dots = Ellipse\n";
  1083. }
  1084. elseif ( $HalftoneInfo['ShapeCode'] == 2 )
  1085. {
  1086. $output_str .= "Shape of Halftone Dots = Line\n";
  1087. }
  1088. elseif ( $HalftoneInfo['ShapeCode'] == 3 )
  1089. {
  1090. $output_str .= "Shape of Halftone Dots = Square\n";
  1091. }
  1092. elseif ( $HalftoneInfo['ShapeCode'] == 4 )
  1093. {
  1094. $output_str .= "Shape of Halftone Dots = Cross\n";
  1095. }
  1096. elseif ( $HalftoneInfo['ShapeCode'] == 6 )
  1097. {
  1098. $output_str .= "Shape of Halftone Dots = Diamond\n";
  1099. }
  1100. else
  1101. {
  1102. $output_str .= "Shape of Halftone Dots = Unknown shape (" . $HalftoneInfo['ShapeCode'] . ")\n";
  1103. }
  1104. // Interpret Accurate Screens
  1105. if ( $HalftoneInfo['Accurate'] == 1 )
  1106. {
  1107. $output_str .= "Use Accurate Screens Selected\n";
  1108. }
  1109. else
  1110. {
  1111. $output_str .= "Use Other (not Accurate) Screens Selected\n";
  1112. }
  1113. // Interpret Printer Default Screens
  1114. if ( $HalftoneInfo['Default'] == 1 )
  1115. {
  1116. $output_str .= "Use printer’s default screens\n";
  1117. }
  1118. else
  1119. {
  1120. $output_str .= "Use Other (not Printer Default) Screens Selected\n";
  1121. }
  1122. // Return Text
  1123. return $output_str;
  1124. }
  1125. /******************************************************************************
  1126. * End of Global Variable: Interpret_Halftone
  1127. ******************************************************************************/
  1128. /******************************************************************************
  1129. * Global Variable: Photoshop_ID_Names
  1130. *
  1131. * Contents: The Names of the Photoshop IRB resources, indexed by their
  1132. * resource number
  1133. *
  1134. ******************************************************************************/
  1135. $GLOBALS[ "Photoshop_ID_Names" ] = array(
  1136. 0x03E8 => "Number of channels, rows, columns, depth, and mode. (Obsolete)",
  1137. 0x03E9 => "Macintosh print manager info ",
  1138. 0x03EB => "Indexed color table (Obsolete)",
  1139. 0x03ED => "Resolution Info",
  1140. 0x03EE => "Alpha Channel Names",
  1141. 0x03EF => "Display Info",
  1142. 0x03F0 => "Caption String",
  1143. 0x03F1 => "Border information",
  1144. 0x03F2 => "Background color",
  1145. 0x03F3 => "Print flags",
  1146. 0x03F4 => "Grayscale and multichannel halftoning information",
  1147. 0x03F5 => "Color halftoning information",
  1148. 0x03F6 => "Duotone halftoning information",
  1149. 0x03F7 => "Grayscale and multichannel transfer function",
  1150. 0x03F8 => "Color transfer functions",
  1151. 0x03F9 => "Duotone transfer functions",
  1152. 0x03FA => "Duotone image information",
  1153. 0x03FB => "Black and white values",
  1154. 0x03FC => "Obsolete Resource.",
  1155. 0x03FD => "EPS options",
  1156. 0x03FE => "Quick Mask information",
  1157. 0x03FF => "Obsolete Resource",
  1158. 0x0400 => "Layer state information",
  1159. 0x0401 => "Working path (not saved)",
  1160. 0x0402 => "Layers group information",
  1161. 0x0403 => "Obsolete Resource",
  1162. 0x0404 => "IPTC-NAA record",
  1163. 0x0405 => "Raw Format Image mode",
  1164. 0x0406 => "JPEG quality",
  1165. 0x0408 => "Grid and guides information",
  1166. 0x0409 => "Thumbnail resource",
  1167. 0x040A => "Copyright flag",
  1168. 0x040B => "URL",
  1169. 0x040C => "Thumbnail resource",
  1170. 0x040D => "Global Angle",
  1171. 0x040E => "Color samplers resource",
  1172. 0x040F => "ICC Profile",
  1173. 0x0410 => "Watermark",
  1174. 0x0411 => "ICC Untagged",
  1175. 0x0412 => "Effects visible",
  1176. 0x0413 => "Spot Halftone",
  1177. 0x0414 => "Document Specific IDs",
  1178. 0x0415 => "Unicode Alpha Names",
  1179. 0x0416 => "Indexed Color Table Count",
  1180. 0x0417 => "Tansparent Index. Index of transparent color, if any.",
  1181. 0x0419 => "Global Altitude",
  1182. 0x041A => "Slices",
  1183. 0x041B => "Workflow URL",
  1184. 0x041C => "Jump To XPEP",
  1185. 0x041D => "Alpha Identifiers",
  1186. 0x041E => "URL List",
  1187. 0x0421 => "Version Info",
  1188. 0x0BB7 => "Name of clipping path.",
  1189. 0x2710 => "Print flags information"
  1190. );
  1191. /******************************************************************************
  1192. * End of Global Variable: Photoshop_ID_Names
  1193. ******************************************************************************/
  1194. /******************************************************************************
  1195. * Global Variable: Photoshop_ID_Descriptions
  1196. *
  1197. * Contents: The Descriptions of the Photoshop IRB resources, indexed by their
  1198. * resource number
  1199. *
  1200. ******************************************************************************/
  1201. $GLOBALS[ "Photoshop_ID_Descriptions" ] = array(
  1202. 0x03E8 => "Obsolete—Photoshop 2.0 only. number of channels, rows, columns, depth, and mode.",
  1203. 0x03E9 => "Optional. Macintosh print manager print info record.",
  1204. 0x03EB => "Obsolete—Photoshop 2.0 only. Contains the indexed color table.",
  1205. 0x03ED => "ResolutionInfo structure. See Appendix A in Photoshop SDK Guide.pdf",
  1206. 0x03EE => "Names of the alpha channels as a series of Pascal strings.",
  1207. 0x03EF => "DisplayInfo structure. See Appendix A in Photoshop SDK Guide.pdf",
  1208. 0x03F0 => "Optional. The caption as a Pascal string.",
  1209. 0x03F1 => "Border information. border width, border units",
  1210. 0x03F2 => "Background color.",
  1211. 0x03F3 => "Print flags. labels, crop marks, color bars, registration marks, negative, flip, interpolate, caption.",
  1212. 0x03F4 => "Grayscale and multichannel halftoning information.",
  1213. 0x03F5 => "Color halftoning information.",
  1214. 0x03F6 => "Duotone halftoning information.",
  1215. 0x03F7 => "Grayscale and multichannel transfer function.",
  1216. 0x03F8 => "Color transfer functions.",
  1217. 0x03F9 => "Duotone transfer functions.",
  1218. 0x03FA => "Duotone image information.",
  1219. 0x03FB => "Effective black and white values for the dot range.",
  1220. 0x03FC => "Obsolete Resource.",
  1221. 0x03FD => "EPS options.",
  1222. 0x03FE => "Quick Mask information. Quick Mask channel ID, Mask initially empty.",
  1223. 0x03FF => "Obsolete Resource.",
  1224. 0x0400 => "Layer state information. Index of target layer.",
  1225. 0x0401 => "Working path (not saved).",
  1226. 0x0402 => "Layers group information. Group ID for the dragging groups. Layers in a group have the same group ID.",
  1227. 0x0403 => "Obsolete Resource.",
  1228. 0x0404 => "IPTC-NAA record. This contains the File Info... information. See the IIMV4.pdf document.",
  1229. 0x0405 => "Image mode for raw format files.",
  1230. 0x0406 => "JPEG quality. Private.",
  1231. 0x0408 => "Grid and guides information.",
  1232. 0x0409 => "Thumbnail resource.",
  1233. 0x040A => "Copyright flag. Boolean indicating whether image is copyrighted. Can be set via Property suite or by user in File Info...",
  1234. 0x040B => "URL. Handle of a text string with uniform resource locator. Can be set via Property suite or by user in File Info...",
  1235. 0x040C => "Thumbnail resource.",
  1236. 0x040D => "Global Angle. Global lighting angle for effects layer.",
  1237. 0x040E => "Color samplers resource.",
  1238. 0x040F => "ICC Profile. The raw bytes of an ICC format profile, see the ICC34.pdf and ICC34.h files from the Internation Color Consortium.",
  1239. 0x0410 => "Watermark.",
  1240. 0x0411 => "ICC Untagged. Disables any assumed profile handling when opening the file. 1 = intentionally untagged.",
  1241. 0x0412 => "Effects visible. Show/hide all the effects layer.",
  1242. 0x0413 => "Spot Halftone. Version, length, variable length data.",
  1243. 0x0414 => "Document specific IDs for layer identification",
  1244. 0x0415 => "Unicode Alpha Names. Length and the string",
  1245. 0x0416 => "Indexed Color Table Count. Number of colors in table that are actually defined",
  1246. 0x0417 => "Transparent Index. Index of transparent color, if any.",
  1247. 0x0419 => "Global Altitude.",
  1248. 0x041A => "Slices.",
  1249. 0x041B => "Workflow URL. Length, string.",
  1250. 0x041C => "Jump To XPEP. Major version, Minor version, Count. Table which can include: Dirty flag, Mod date.",
  1251. 0x041D => "Alpha Identifiers.",
  1252. 0x041E => "URL List. Count of URLs, IDs, and strings",
  1253. 0x0421 => "Version Info. Version, HasRealMergedData, string of writer name, string of reader name, file version.",
  1254. 0x0BB7 => "Name of clipping path.",
  1255. 0x2710 => "Print flags information. Version, Center crop marks, Bleed width value, Bleed width scale."
  1256. );
  1257. /******************************************************************************
  1258. * End of Global Variable: Photoshop_ID_Descriptions
  1259. ******************************************************************************/
  1260. ?>