PageRenderTime 59ms CodeModel.GetById 14ms RepoModel.GetById 0ms app.codeStats 1ms

/add-ons/pjmt/EXIF.php

https://github.com/jcplat/console-seolan
PHP | 2756 lines | 1220 code | 535 blank | 1001 comment | 345 complexity | 2353c9009d322040cc8481d3e2063908 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: EXIF.php
  5. *
  6. * Description: Provides functions for reading and writing EXIF Information
  7. * to/from an APP1 segment of a JPEG file
  8. * Unfortunately, because EXIF data may be distributed anywhere
  9. * throughout an image file, rather than just being in one block,
  10. * it is impossible to pass just a string containing only the EXIF
  11. * information. Hence it is neccessary to be able to seek to
  12. * any point in the file. This causes the HTTP and FTP wrappers
  13. * not to work - i.e. the EXIF functions will only work with local
  14. * files.
  15. * To work on an internet file, copy it locally to start with:
  16. *
  17. * $newfilename = tempnam ( $dir, "tmpexif" );
  18. * copy ( "http://whatever.com", $newfilename );
  19. *
  20. *
  21. * Author: Evan Hunter
  22. *
  23. * Date: 30/7/2004
  24. *
  25. * Project: PHP JPEG Metadata Toolkit
  26. *
  27. * Revision: 1.11
  28. *
  29. * Changes: 1.00 -> 1.10 : added function get_EXIF_TIFF to allow extracting EXIF from a TIFF file
  30. * 1.10 -> 1.11 : added functionality to allow decoding of XMP and Photoshop IRB information
  31. * embedded within the EXIF data
  32. * added checks for http and ftp wrappers, as these are not supported
  33. * changed interpret_IFD to allow thumbnail links to work when
  34. * toolkit is portable across directories
  35. *
  36. *
  37. * URL: http://electronics.ozhiker.com
  38. *
  39. * Copyright: Copyright Evan Hunter 2004
  40. *
  41. * License: This file is part of the PHP JPEG Metadata Toolkit.
  42. *
  43. * The PHP JPEG Metadata Toolkit is free software; you can
  44. * redistribute it and/or modify it under the terms of the
  45. * GNU General Public License as published by the Free Software
  46. * Foundation; either version 2 of the License, or (at your
  47. * option) any later version.
  48. *
  49. * The PHP JPEG Metadata Toolkit is distributed in the hope
  50. * that it will be useful, but WITHOUT ANY WARRANTY; without
  51. * even the implied warranty of MERCHANTABILITY or FITNESS
  52. * FOR A PARTICULAR PURPOSE. See the GNU General Public License
  53. * for more details.
  54. *
  55. * You should have received a copy of the GNU General Public
  56. * License along with the PHP JPEG Metadata Toolkit; if not,
  57. * write to the Free Software Foundation, Inc., 59 Temple
  58. * Place, Suite 330, Boston, MA 02111-1307 USA
  59. *
  60. * If you require a different license for commercial or other
  61. * purposes, please contact the author: evan@ozhiker.com
  62. *
  63. ******************************************************************************/
  64. // TODO : Thoroughly test the functions for writing EXIF segments
  65. // TODO : Figure out a way to allow EXIF to function normally with HTTP and FTP wrappers
  66. // TODO : Implement EXIF decoding of Device Setting Description field
  67. // TODO : Implement EXIF decoding of SpatialFrequencyResponse field
  68. // TODO : Implement EXIF decoding of OECF field
  69. // TODO : Implement EXIF decoding of SubjectArea field
  70. // TODO : Add a put_EXIF_TIFF function
  71. /******************************************************************************
  72. *
  73. * Initialisation
  74. *
  75. ******************************************************************************/
  76. if ( !isset( $GLOBALS['HIDE_UNKNOWN_TAGS'] ) ) $GLOBALS['HIDE_UNKNOWN_TAGS']= FALSE;
  77. if ( !isset( $GLOBALS['SHOW_BINARY_DATA_HEX'] ) ) $GLOBALS['SHOW_BINARY_DATA_HEX'] = FALSE;
  78. if ( !isset( $GLOBALS['SHOW_BINARY_DATA_TEXT'] ) ) $GLOBALS['SHOW_BINARY_DATA_TEXT'] = FALSE;
  79. include_once 'EXIF_Tags.php';
  80. include_once 'EXIF_Makernote.php';
  81. include_once 'PIM.php';
  82. include_once 'Unicode.php';
  83. include_once 'JPEG.php';
  84. include_once 'IPTC.php';
  85. include_once 'Photoshop_IRB.php'; // Change: as of version 1.11 - Required for TIFF with embedded IRB
  86. include_once 'XMP.php'; // Change: as of version 1.11 - Required for TIFF with embedded XMP
  87. include_once 'pjmt_utils.php'; // Change: as of version 1.11 - Required for directory portability
  88. /******************************************************************************
  89. *
  90. * Function: get_EXIF_JPEG
  91. *
  92. * Description: Retrieves information from a Exchangeable Image File Format (EXIF)
  93. * APP1 segment and returns it in an array.
  94. *
  95. * Parameters: filename - the filename of the JPEG image to process
  96. *
  97. * Returns: OutputArray - Array of EXIF records
  98. * FALSE - If an error occured in decoding
  99. *
  100. ******************************************************************************/
  101. function get_EXIF_JPEG( $filename )
  102. {
  103. // Change: Added as of version 1.11
  104. // Check if a wrapper is being used - these are not currently supported (see notes at top of file)
  105. if ( ( stristr ( $filename, "http://" ) != FALSE ) || ( stristr ( $filename, "ftp://" ) != FALSE ) )
  106. {
  107. // A HTTP or FTP wrapper is being used - show a warning and abort
  108. echo "HTTP and FTP wrappers are currently not supported with EXIF - See EXIF functionality documentation - a local file must be specified<br>";
  109. echo "To work on an internet file, copy it locally to start with:<br><br>\n";
  110. echo "\$newfilename = tempnam ( \$dir, \"tmpexif\" );<br>\n";
  111. echo "copy ( \"http://whatever.com\", \$newfilename );<br><br>\n";
  112. return FALSE;
  113. }
  114. // get the JPEG headers
  115. $jpeg_header_data = get_jpeg_header_data( $filename );
  116. // Flag that an EXIF segment has not been found yet
  117. $EXIF_Location = -1;
  118. //Cycle through the header segments
  119. for( $i = 0; $i < count( $jpeg_header_data ); $i++ )
  120. {
  121. // If we find an APP1 header,
  122. if ( strcmp ( $jpeg_header_data[$i]['SegName'], "APP1" ) == 0 )
  123. {
  124. // And if it has the EXIF label,
  125. if ( ( strncmp ( $jpeg_header_data[$i]['SegData'], "Exif\x00\x00", 6) == 0 ) ||
  126. ( strncmp ( $jpeg_header_data[$i]['SegData'], "Exif\x00\xFF", 6) == 0 ) ) // For some reason, some files have a faulty EXIF name which has a 0xFF in it
  127. {
  128. // Save the location of the EXIF segment
  129. $EXIF_Location = $i;
  130. }
  131. }
  132. }
  133. // Check if an EXIF segment was found
  134. if ( $EXIF_Location == -1 )
  135. {
  136. // Couldn't find any EXIF block to decode
  137. return FALSE;
  138. }
  139. $filehnd = @fopen($filename, 'rb');
  140. // Check if the file opened successfully
  141. if ( ! $filehnd )
  142. {
  143. // Could't open the file - exit
  144. echo "<p>Could not open file $filename</p>\n";
  145. return FALSE;
  146. }
  147. fseek( $filehnd, $jpeg_header_data[$EXIF_Location]['SegDataStart'] + 6 );
  148. // Decode the Exif segment into an array and return it
  149. $exif_data = process_TIFF_Header( $filehnd, "TIFF" );
  150. // Close File
  151. fclose($filehnd);
  152. return $exif_data;
  153. }
  154. /******************************************************************************
  155. * End of Function: get_EXIF_JPEG
  156. ******************************************************************************/
  157. /******************************************************************************
  158. *
  159. * Function: put_EXIF_JPEG
  160. *
  161. * Description: Stores information into a Exchangeable Image File Format (EXIF)
  162. * APP1 segment from an EXIF array.
  163. *
  164. * WARNING: Because the EXIF standard allows pointers to data
  165. * outside the APP1 segment, if there are any such pointers in
  166. * a makernote, this function will DAMAGE them since it will not
  167. * be aware that there is an external pointer. This will often
  168. * happen with Makernotes that include an embedded thumbnail.
  169. * This damage could be prevented where makernotes can be decoded,
  170. * but currently this is not implemented.
  171. *
  172. *
  173. * Parameters: exif_data - The array of EXIF data to insert into the JPEG header
  174. * jpeg_header_data - The JPEG header into which the EXIF data
  175. * should be stored, as from get_jpeg_header_data
  176. *
  177. * Returns: jpeg_header_data - JPEG header array with the EXIF segment inserted
  178. * FALSE - If an error occured
  179. *
  180. ******************************************************************************/
  181. function put_EXIF_JPEG( $exif_data, $jpeg_header_data )
  182. {
  183. // pack the EXIF data into its proper format for a JPEG file
  184. $packed_data = get_TIFF_Packed_Data( $exif_data );
  185. if ( $packed_data === FALSE )
  186. {
  187. return $jpeg_header_data;
  188. }
  189. $packed_data = "Exif\x00\x00$packed_data";
  190. //Cycle through the header segments
  191. for( $i = 0; $i < count( $jpeg_header_data ); $i++ )
  192. {
  193. // If we find an APP1 header,
  194. if ( strcmp ( $jpeg_header_data[$i]['SegName'], "APP1" ) == 0 )
  195. {
  196. // And if it has the EXIF label,
  197. if ( ( strncmp ( $jpeg_header_data[$i]['SegData'], "Exif\x00\x00", 6) == 0 ) ||
  198. ( strncmp ( $jpeg_header_data[$i]['SegData'], "Exif\x00\xFF", 6) == 0 ) ) // For some reason, some files have a faulty EXIF name which has a 0xFF in it
  199. {
  200. // Found a preexisting EXIF block - Replace it with the new one and return.
  201. $jpeg_header_data[$i]['SegData'] = $packed_data;
  202. return $jpeg_header_data;
  203. }
  204. }
  205. }
  206. // No preexisting segment segment found, insert a new one at the start of the header data.
  207. // Determine highest position of an APP segment at or below APP3, so we can put the
  208. // new APP3 at this position
  209. $highest_APP = -1;
  210. //Cycle through the header segments
  211. for( $i = 0; $i < count( $jpeg_header_data ); $i++ )
  212. {
  213. // Check if we have found an APP segment at or below APP3,
  214. if ( ( $jpeg_header_data[$i]['SegType'] >= 0xE0 ) && ( $jpeg_header_data[$i]['SegType'] <= 0xE3 ) )
  215. {
  216. // Found an APP segment at or below APP12
  217. $highest_APP = $i;
  218. }
  219. }
  220. // No preexisting EXIF block found, insert a new one at the start of the header data.
  221. array_splice($jpeg_header_data, $highest_APP + 1 , 0, array( array( "SegType" => 0xE1,
  222. "SegName" => "APP1",
  223. "SegDesc" => $GLOBALS[ "JPEG_Segment_Descriptions" ][ 0xE1 ],
  224. "SegData" => $packed_data ) ) );
  225. return $jpeg_header_data;
  226. }
  227. /******************************************************************************
  228. * End of Function: put_EXIF_JPEG
  229. ******************************************************************************/
  230. /******************************************************************************
  231. *
  232. * Function: get_Meta_JPEG
  233. *
  234. * Description: Retrieves information from a Meta APP3 segment and returns it
  235. * in an array. Uses information supplied by the
  236. * get_jpeg_header_data function.
  237. * The Meta segment has the same format as an EXIF segment, but
  238. * uses different tags
  239. *
  240. * Parameters: filename - the filename of the JPEG image to process
  241. *
  242. * Returns: OutputArray - Array of Meta records
  243. * FALSE - If an error occured in decoding
  244. *
  245. ******************************************************************************/
  246. function get_Meta_JPEG( $filename )
  247. {
  248. // Change: Added as of version 1.11
  249. // Check if a wrapper is being used - these are not currently supported (see notes at top of file)
  250. if ( ( stristr ( $filename, "http://" ) != FALSE ) || ( stristr ( $filename, "ftp://" ) != FALSE ) )
  251. {
  252. // A HTTP or FTP wrapper is being used - show a warning and abort
  253. echo "HTTP and FTP wrappers are currently not supported with Meta - See EXIF/Meta functionality documentation - a local file must be specified<br>";
  254. echo "To work on an internet file, copy it locally to start with:<br><br>\n";
  255. echo "\$newfilename = tempnam ( \$dir, \"tmpmeta\" );<br>\n";
  256. echo "copy ( \"http://whatever.com\", \$newfilename );<br><br>\n";
  257. return FALSE;
  258. }
  259. // get the JPEG headers
  260. $jpeg_header_data = get_jpeg_header_data( $filename );
  261. // Flag that an Meta segment has not been found yet
  262. $Meta_Location = -1;
  263. //Cycle through the header segments
  264. for( $i = 0; $i < count( $jpeg_header_data ); $i++ )
  265. {
  266. // If we find an APP3 header,
  267. if ( strcmp ( $jpeg_header_data[$i]['SegName'], "APP3" ) == 0 )
  268. {
  269. // And if it has the Meta label,
  270. if ( ( strncmp ( $jpeg_header_data[$i]['SegData'], "Meta\x00\x00", 6) == 0 ) ||
  271. ( strncmp ( $jpeg_header_data[$i]['SegData'], "META\x00\x00", 6) == 0 ) )
  272. {
  273. // Save the location of the Meta segment
  274. $Meta_Location = $i;
  275. }
  276. }
  277. }
  278. // Check if an EXIF segment was found
  279. if ( $Meta_Location == -1 )
  280. {
  281. // Couldn't find any Meta block to decode
  282. return FALSE;
  283. }
  284. $filehnd = @fopen($filename, 'rb');
  285. // Check if the file opened successfully
  286. if ( ! $filehnd )
  287. {
  288. // Could't open the file - exit
  289. echo "<p>Could not open file $filename</p>\n";
  290. return FALSE;
  291. }
  292. fseek( $filehnd, $jpeg_header_data[$Meta_Location]['SegDataStart'] + 6 );
  293. // Decode the Meta segment into an array and return it
  294. $meta = process_TIFF_Header( $filehnd, "Meta" );
  295. // Close File
  296. fclose($filehnd);
  297. return $meta;
  298. }
  299. /******************************************************************************
  300. * End of Function: get_Meta
  301. ******************************************************************************/
  302. /******************************************************************************
  303. *
  304. * Function: put_Meta_JPEG
  305. *
  306. * Description: Stores information into a Meta APP3 segment from a Meta array.
  307. *
  308. *
  309. * WARNING: Because the Meta (EXIF) standard allows pointers to data
  310. * outside the APP1 segment, if there are any such pointers in
  311. * a makernote, this function will DAMAGE them since it will not
  312. * be aware that there is an external pointer. This will often
  313. * happen with Makernotes that include an embedded thumbnail.
  314. * This damage could be prevented where makernotes can be decoded,
  315. * but currently this is not implemented.
  316. *
  317. *
  318. * Parameters: meta_data - The array of Meta data to insert into the JPEG header
  319. * jpeg_header_data - The JPEG header into which the Meta data
  320. * should be stored, as from get_jpeg_header_data
  321. *
  322. * Returns: jpeg_header_data - JPEG header array with the Meta segment inserted
  323. * FALSE - If an error occured
  324. *
  325. ******************************************************************************/
  326. function put_Meta_JPEG( $meta_data, $jpeg_header_data )
  327. {
  328. // pack the Meta data into its proper format for a JPEG file
  329. $packed_data = get_TIFF_Packed_Data( $meta_data );
  330. if ( $packed_data === FALSE )
  331. {
  332. return $jpeg_header_data;
  333. }
  334. $packed_data = "Meta\x00\x00$packed_data";
  335. //Cycle through the header segments
  336. for( $i = 0; $i < count( $jpeg_header_data ); $i++ )
  337. {
  338. // If we find an APP1 header,
  339. if ( strcmp ( $jpeg_header_data[$i]['SegName'], "APP3" ) == 0 )
  340. {
  341. // And if it has the Meta label,
  342. if ( ( strncmp ( $jpeg_header_data[$i]['SegData'], "Meta\x00\x00", 6) == 0 ) ||
  343. ( strncmp ( $jpeg_header_data[$i]['SegData'], "META\x00\x00", 6) == 0 ) )
  344. {
  345. // Found a preexisting Meta block - Replace it with the new one and return.
  346. $jpeg_header_data[$i]['SegData'] = $packed_data;
  347. return $jpeg_header_data;
  348. }
  349. }
  350. }
  351. // No preexisting segment segment found, insert a new one at the start of the header data.
  352. // Determine highest position of an APP segment at or below APP3, so we can put the
  353. // new APP3 at this position
  354. $highest_APP = -1;
  355. //Cycle through the header segments
  356. for( $i = 0; $i < count( $jpeg_header_data ); $i++ )
  357. {
  358. // Check if we have found an APP segment at or below APP3,
  359. if ( ( $jpeg_header_data[$i]['SegType'] >= 0xE0 ) && ( $jpeg_header_data[$i]['SegType'] <= 0xE3 ) )
  360. {
  361. // Found an APP segment at or below APP12
  362. $highest_APP = $i;
  363. }
  364. }
  365. // No preexisting Meta block found, insert a new one at the start of the header data.
  366. array_splice($jpeg_header_data, $highest_APP + 1 , 0, array( array( "SegType" => 0xE3,
  367. "SegName" => "APP3",
  368. "SegDesc" => $GLOBALS[ "JPEG_Segment_Descriptions" ][ 0xE1 ],
  369. "SegData" => $packed_data ) ) );
  370. return $jpeg_header_data;
  371. }
  372. /******************************************************************************
  373. * End of Function: put_Meta_JPEG
  374. ******************************************************************************/
  375. /******************************************************************************
  376. *
  377. * Function: get_EXIF_TIFF
  378. *
  379. * Description: Retrieves information from a Exchangeable Image File Format (EXIF)
  380. * within a TIFF file and returns it in an array.
  381. *
  382. * Parameters: filename - the filename of the TIFF image to process
  383. *
  384. * Returns: OutputArray - Array of EXIF records
  385. * FALSE - If an error occured in decoding
  386. *
  387. ******************************************************************************/
  388. function get_EXIF_TIFF( $filename )
  389. {
  390. // Change: Added as of version 1.11
  391. // Check if a wrapper is being used - these are not currently supported (see notes at top of file)
  392. if ( ( stristr ( $filename, "http://" ) != FALSE ) || ( stristr ( $filename, "ftp://" ) != FALSE ) )
  393. {
  394. // A HTTP or FTP wrapper is being used - show a warning and abort
  395. echo "HTTP and FTP wrappers are currently not supported with TIFF - See EXIF/TIFF functionality documentation - a local file must be specified<br>";
  396. echo "To work on an internet file, copy it locally to start with:<br><br>\n";
  397. echo "\$newfilename = tempnam ( \$dir, \"tmptiff\" );<br>\n";
  398. echo "copy ( \"http://whatever.com\", \$newfilename );<br><br>\n";
  399. return FALSE;
  400. }
  401. $filehnd = @fopen($filename, 'rb');
  402. // Check if the file opened successfully
  403. if ( ! $filehnd )
  404. {
  405. // Could't open the file - exit
  406. echo "<p>Could not open file $filename</p>\n";
  407. return FALSE;
  408. }
  409. // Decode the Exif segment into an array and return it
  410. $exif_data = process_TIFF_Header( $filehnd, "TIFF" );
  411. // Close File
  412. fclose($filehnd);
  413. return $exif_data;
  414. }
  415. /******************************************************************************
  416. * End of Function: get_EXIF_TIFF
  417. ******************************************************************************/
  418. /******************************************************************************
  419. *
  420. * Function: Interpret_EXIF_to_HTML
  421. *
  422. * Description: Generates html detailing the contents an APP1 EXIF array
  423. * which was retrieved with a get_EXIF_.... function.
  424. * Can also be used for APP3 Meta arrays.
  425. *
  426. * Parameters: Exif_array - the EXIF array,as read from get_EXIF_....
  427. * filename - the name of the Image file being processed ( used
  428. * by scripts which displays EXIF thumbnails)
  429. *
  430. * Returns: output_str - A string containing the HTML
  431. *
  432. ******************************************************************************/
  433. function Interpret_EXIF_to_HTML( $Exif_array, $filename )
  434. {
  435. // Create the string to receive the html output
  436. $output_str = "";
  437. // Check if the array to process is valid
  438. if ( $Exif_array === FALSE )
  439. {
  440. // Exif Array is not valid - abort processing
  441. return $output_str;
  442. }
  443. // Ouput the heading according to what type of tags were used in processing
  444. if ( $Exif_array[ 'Tags Name' ] == "TIFF" )
  445. {
  446. $output_str .= "<h2 class=\"EXIF_Main_Heading\">Contains Exchangeable Image File Format (EXIF) Information</h2>\n";
  447. }
  448. else if ( $Exif_array[ 'Tags Name' ] == "Meta" )
  449. {
  450. $output_str .= "<h2 class=\"EXIF_Main_Heading\">Contains META Information (APP3)</h2>\n";
  451. }
  452. else
  453. {
  454. $output_str .= "<h2 class=\"EXIF_Main_Heading\">Contains " . $Exif_array[ 'Tags Name' ] . " Information</h2>\n";
  455. }
  456. // Check that there are actually items to process in the array
  457. if ( count( $Exif_array ) < 1 )
  458. {
  459. // No items to process in array - abort processing
  460. return $output_str;
  461. }
  462. // Output secondary heading
  463. $output_str .= "<h3 class=\"EXIF_Secondary_Heading\">Main Image Information</h2>\n";
  464. // Interpret the zeroth IFD to html
  465. $output_str .= interpret_IFD( $Exif_array[0], $filename, $Exif_array['Byte_Align'] );
  466. // Check if there is a first IFD to process
  467. if ( array_key_exists( 1, $Exif_array ) )
  468. {
  469. // There is a first IFD for a thumbnail
  470. // Add a heading for it to the output
  471. $output_str .= "<h3 class=\"EXIF_Secondary_Heading\">Thumbnail Information</h2>\n";
  472. // Interpret the IFD to html and add it to the output
  473. $output_str .= interpret_IFD( $Exif_array[1], $filename, $Exif_array['Byte_Align'] );
  474. }
  475. // Cycle through any other IFD's
  476. $i = 2;
  477. while ( array_key_exists( $i, $Exif_array ) )
  478. {
  479. // Add a heading for the IFD
  480. $output_str .= "<h3 class=\"EXIF_Secondary_Heading\">Image File Directory (IFD) $i Information</h2>\n";
  481. // Interpret the IFD to html and add it to the output
  482. $output_str .= interpret_IFD( $Exif_array[$i], $filename, $Exif_array['Byte_Align'] );
  483. $i++;
  484. }
  485. // Return the resulting HTML
  486. return $output_str;
  487. }
  488. /******************************************************************************
  489. * End of Function: Interpret_EXIF_to_HTML
  490. ******************************************************************************/
  491. /******************************************************************************
  492. *
  493. * INTERNAL FUNCTIONS
  494. *
  495. ******************************************************************************/
  496. /******************************************************************************
  497. *
  498. * Internal Function: get_TIFF_Packed_Data
  499. *
  500. * Description: Packs TIFF IFD data from EXIF or Meta into a form ready for
  501. * either a JPEG EXIF/Meta segment or a TIFF file
  502. * This function attempts to protect the contents of an EXIF makernote,
  503. * by ensuring that it remains in the same position relative to the
  504. * TIFF header
  505. *
  506. * Parameters: tiff_data - the EXIF array,as read from get_EXIF_JPEG or get_Meta_JPEG
  507. *
  508. * Returns: packed_data - A string containing packed segment
  509. *
  510. ******************************************************************************/
  511. function get_TIFF_Packed_Data( $tiff_data )
  512. {
  513. // Check that the segment is valid
  514. if ( $tiff_data === FALSE )
  515. {
  516. return FALSE;
  517. }
  518. // Get the byte alignment
  519. $Byte_Align = $tiff_data['Byte_Align'];
  520. // Add the Byte Alignment to the Packed data
  521. $packed_data = $Byte_Align;
  522. // Add the TIFF ID to the Packed Data
  523. $packed_data .= put_IFD_Data_Type( 42, 3, $Byte_Align );
  524. // Create a string for the makernote
  525. $makernote = "";
  526. // Check if the makernote exists
  527. if ( $tiff_data[ 'Makernote_Tag' ] !== FALSE )
  528. {
  529. // A makernote exists - We need to ensure that it stays in the same position as it was
  530. // Put the Makernote before any of the IFD's by padding zeros to the correct offset
  531. $makernote .= str_repeat("\x00",( $tiff_data[ 'Makernote_Tag' ][ 'Offset' ] - 8 ) );
  532. $makernote .= $tiff_data[ 'Makernote_Tag' ]['Data'];
  533. }
  534. // Calculage where the zeroth ifd will be
  535. $ifd_offset = strlen( $makernote ) + 8;
  536. // Add the Zeroth IFD pointer to the packed data
  537. $packed_data .= put_IFD_Data_Type( $ifd_offset, 4, $Byte_Align );
  538. // Add the makernote to the packed data (if there was one)
  539. $packed_data .= $makernote;
  540. //Add the IFD's to the packed data
  541. $packed_data .= get_IFD_Array_Packed_Data( $tiff_data, $ifd_offset, $Byte_Align );
  542. // Return the result
  543. return $packed_data;
  544. }
  545. /******************************************************************************
  546. * End of Function: get_TIFF_Packed_Data
  547. ******************************************************************************/
  548. /******************************************************************************
  549. *
  550. * Internal Function: get_IFD_Array_Packed_Data
  551. *
  552. * Description: Packs a chain of IFD's from EXIF or Meta segments into a form
  553. * ready for either a JPEG EXIF/Meta segment or a TIFF file
  554. *
  555. * Parameters: ifd_data - the IFD chain array, as read from get_EXIF_JPEG or get_Meta_JPEG
  556. * Zero_IFD_offset - The offset to the first IFD from the start of the TIFF header
  557. * Byte_Align - the Byte alignment to use - "MM" or "II"
  558. *
  559. * Returns: packed_data - A string containing packed IFD's
  560. *
  561. ******************************************************************************/
  562. function get_IFD_Array_Packed_Data( $ifd_data, $Zero_IFD_offset, $Byte_Align )
  563. {
  564. // Create a string to receive the packed output
  565. $packed_data = "";
  566. // Count the IFDs
  567. $ifd_count = 0;
  568. foreach( $ifd_data as $key => $IFD )
  569. {
  570. // Make sure we only count the IFD's, not other information keys
  571. if ( is_numeric( $key ) )
  572. {
  573. $ifd_count++;
  574. }
  575. }
  576. // Cycle through each IFD,
  577. for ( $ifdno = 0; $ifdno < $ifd_count; $ifdno++ )
  578. {
  579. // Check if this IFD is the last one
  580. if ( $ifdno == $ifd_count - 1 )
  581. {
  582. // This IFD is the last one, get it's packed data
  583. $packed_data .= get_IFD_Packed_Data( $ifd_data[ $ifdno ], $Zero_IFD_offset +strlen($packed_data), $Byte_Align, FALSE );
  584. }
  585. else
  586. {
  587. // This IFD is NOT the last one, get it's packed data
  588. $packed_data .= get_IFD_Packed_Data( $ifd_data[ $ifdno ], $Zero_IFD_offset +strlen($packed_data), $Byte_Align, TRUE );
  589. }
  590. }
  591. // Return the packed output
  592. return $packed_data;
  593. }
  594. /******************************************************************************
  595. * End of Function: get_IFD_Array_Packed_Data
  596. ******************************************************************************/
  597. /******************************************************************************
  598. *
  599. * Internal Function: get_IFD_Packed_Data
  600. *
  601. * Description: Packs an IFD from EXIF or Meta segments into a form
  602. * ready for either a JPEG EXIF/Meta segment or a TIFF file
  603. *
  604. * Parameters: ifd_data - the IFD chain array, as read from get_EXIF_JPEG or get_Meta_JPEG
  605. * IFD_offset - The offset to the IFD from the start of the TIFF header
  606. * Byte_Align - the Byte alignment to use - "MM" or "II"
  607. * Another_IFD - boolean - false if this is the last IFD in the chain
  608. * - true if it is not the last
  609. *
  610. * Returns: packed_data - A string containing packed IFD's
  611. *
  612. ******************************************************************************/
  613. function get_IFD_Packed_Data( $ifd_data, $IFD_offset, $Byte_Align, $Another_IFD )
  614. {
  615. $ifd_body_str = "";
  616. $ifd_data_str = "";
  617. $Tag_Definitions_Name = $ifd_data[ 'Tags Name' ];
  618. // Count the Tags in this IFD
  619. $tag_count = 0;
  620. foreach( $ifd_data as $key => $tag )
  621. {
  622. // Make sure we only count the Tags, not other information keys
  623. if ( is_numeric( $key ) )
  624. {
  625. $tag_count++;
  626. }
  627. }
  628. // Add the Tag count to the packed data
  629. $packed_data = put_IFD_Data_Type( $tag_count, 3, $Byte_Align );
  630. // Calculate the total length of the IFD (without the offset data)
  631. $IFD_len = 2 + $tag_count * 12 + 4;
  632. // Cycle through each tag
  633. foreach( $ifd_data as $key => $tag )
  634. {
  635. // Make sure this is a tag, not another information key
  636. if ( is_numeric( $key ) )
  637. {
  638. // Add the tag number to the packed data
  639. $ifd_body_str .= put_IFD_Data_Type( $tag[ 'Tag Number' ], 3, $Byte_Align );
  640. // Add the Data type to the packed data
  641. $ifd_body_str .= put_IFD_Data_Type( $tag['Data Type'], 3, $Byte_Align );
  642. // Check if this is a Print Image Matching entry
  643. if ( $tag['Type'] == "PIM" )
  644. {
  645. // This is a Print Image Matching entry,
  646. // encode it
  647. $data = Encode_PIM( $tag, $Byte_Align );
  648. }
  649. // Check if this is a IPTC/NAA Record within the EXIF IFD
  650. else if ( ( ( $Tag_Definitions_Name == "EXIF" ) || ( $Tag_Definitions_Name == "TIFF" ) ) &&
  651. ( $tag[ 'Tag Number' ] == 33723 ) )
  652. {
  653. // This is a IPTC/NAA Record, encode it
  654. $data = put_IPTC( $tag['Data'] );
  655. }
  656. // Change: Check for embedded XMP as of version 1.11
  657. // Check if this is a XMP Record within the EXIF IFD
  658. else if ( ( ( $Tag_Definitions_Name == "EXIF" ) || ( $Tag_Definitions_Name == "TIFF" ) ) &&
  659. ( $tag[ 'Tag Number' ] == 700 ) )
  660. {
  661. // This is a XMP Record, encode it
  662. $data = write_XMP_array_to_text( $tag['Data'] );
  663. }
  664. // Change: Check for embedded IRB as of version 1.11
  665. // Check if this is a Photoshop IRB Record within the EXIF IFD
  666. else if ( ( ( $Tag_Definitions_Name == "EXIF" ) || ( $Tag_Definitions_Name == "TIFF" ) ) &&
  667. ( $tag[ 'Tag Number' ] == 34377 ) )
  668. {
  669. // This is a Photoshop IRB Record, encode it
  670. $data = pack_Photoshop_IRB_Data( $tag['Data'] );
  671. }
  672. // Exif Thumbnail Offset
  673. else if ( ( $tag[ 'Tag Number' ] == 513 ) && ( $Tag_Definitions_Name == "TIFF" ) )
  674. {
  675. // The Exif Thumbnail Offset is a pointer but of type Long, not Unknown
  676. // Hence we need to put the data into the packed string separately
  677. // Calculate the thumbnail offset
  678. $data_offset = $IFD_offset + $IFD_len + strlen($ifd_data_str);
  679. // Create the Offset for the IFD
  680. $data = put_IFD_Data_Type( $data_offset, 4, $Byte_Align );
  681. // Store the thumbnail
  682. $ifd_data_str .= $tag['Data'];
  683. }
  684. // Exif Thumbnail Length
  685. else if ( ( $tag[ 'Tag Number' ] == 514 ) && ( $Tag_Definitions_Name == "TIFF" ) )
  686. {
  687. // Encode the Thumbnail Length
  688. $data = put_IFD_Data_Type( strlen($ifd_data[513]['Data']), 4, $Byte_Align );
  689. }
  690. // Sub-IFD
  691. else if ( $tag['Type'] == "SubIFD" )
  692. {
  693. // This is a Sub-IFD
  694. // Calculate the offset to the start of the Sub-IFD
  695. $data_offset = $IFD_offset + $IFD_len + strlen($ifd_data_str);
  696. // Get the packed data for the IFD chain as the data for this tag
  697. $data = get_IFD_Array_Packed_Data( $tag['Data'], $data_offset, $Byte_Align );
  698. }
  699. else
  700. {
  701. // Not a special tag
  702. // Create a string to receive the data
  703. $data = "";
  704. // Check if this is a type Unknown tag
  705. if ( $tag['Data Type'] != 7 )
  706. {
  707. // NOT type Unknown
  708. // Cycle through each data value and add it to the data string
  709. foreach( $tag[ 'Data' ] as $data_val )
  710. {
  711. $data .= put_IFD_Data_Type( $data_val, $tag['Data Type'], $Byte_Align );
  712. }
  713. }
  714. else
  715. {
  716. // This is a type Unknown - just add the data as is to the data string
  717. $data .= $tag[ 'Data' ];
  718. }
  719. }
  720. // Pad the data string out to at least 4 bytes
  721. $data = str_pad ( $data, 4, "\x00" );
  722. // Check if the data type is an ASCII String or type Unknown
  723. if ( ( $tag['Data Type'] == 2 ) || ( $tag['Data Type'] == 7 ) )
  724. {
  725. // This is an ASCII String or type Unknown
  726. // Add the Length of the string to the packed data as the Count
  727. $ifd_body_str .= put_IFD_Data_Type( strlen($data), 4, $Byte_Align );
  728. }
  729. else
  730. {
  731. // Add the array count to the packed data as the Count
  732. $ifd_body_str .= put_IFD_Data_Type( count($tag[ 'Data' ]), 4, $Byte_Align );
  733. }
  734. // Check if the data is over 4 bytes long
  735. if ( strlen( $data ) > 4 )
  736. {
  737. // Data is longer than 4 bytes - it needs to be offset
  738. // Check if this entry is the Maker Note
  739. if ( ( $Tag_Definitions_Name == "EXIF" ) && ( $tag[ 'Tag Number' ] == 37500 ) )
  740. {
  741. // This is the makernote - It will have already been stored
  742. // at its original offset to help preserve it
  743. // all we need to do is add the Offset to the IFD packed data
  744. $data_offset = $tag[ 'Offset' ];
  745. $ifd_body_str .= put_IFD_Data_Type( $data_offset, 4, $Byte_Align );
  746. }
  747. else
  748. {
  749. // This is NOT the makernote
  750. // Calculate the data offset
  751. $data_offset = $IFD_offset + $IFD_len + strlen($ifd_data_str);
  752. // Add the offset to the IFD packed data
  753. $ifd_body_str .= put_IFD_Data_Type( $data_offset, 4, $Byte_Align );
  754. // Add the data to the offset packed data
  755. $ifd_data_str .= $data;
  756. }
  757. }
  758. else
  759. {
  760. // Data is less than or equal to 4 bytes - Add it to the packed IFD data as is
  761. $ifd_body_str .= $data;
  762. }
  763. }
  764. }
  765. // Assemble the IFD body onto the packed data
  766. $packed_data .= $ifd_body_str;
  767. // Check if there is another IFD after this one
  768. if( $Another_IFD === TRUE )
  769. {
  770. // There is another IFD after this
  771. // Calculate the Next-IFD offset so that it goes immediately after this IFD
  772. $next_ifd_offset = $IFD_offset + $IFD_len + strlen($ifd_data_str);
  773. }
  774. else
  775. {
  776. // There is NO IFD after this - indicate with offset=0
  777. $next_ifd_offset = 0;
  778. }
  779. // Add the Next-IFD offset to the packed data
  780. $packed_data .= put_IFD_Data_Type( $next_ifd_offset, 4, $Byte_Align );
  781. // Add the offset data to the packed data
  782. $packed_data .= $ifd_data_str;
  783. // Return the resulting packed data
  784. return $packed_data;
  785. }
  786. /******************************************************************************
  787. * End of Function: get_IFD_Packed_Data
  788. ******************************************************************************/
  789. /******************************************************************************
  790. *
  791. * Internal Function: process_TIFF_Header
  792. *
  793. * Description: Decodes the information stored in a TIFF header and it's
  794. * Image File Directories (IFD's). This information is returned
  795. * in an array
  796. *
  797. * Parameters: filehnd - The handle of a open image file, positioned at the
  798. * start of the TIFF header
  799. * Tag_Definitions_Name - The name of the Tag Definitions group
  800. * within the global array IFD_Tag_Definitions
  801. *
  802. *
  803. * Returns: OutputArray - Array of IFD records
  804. * FALSE - If an error occured in decoding
  805. *
  806. ******************************************************************************/
  807. function process_TIFF_Header( $filehnd, $Tag_Definitions_Name )
  808. {
  809. // Save the file position where the TIFF header starts, as offsets are relative to this position
  810. $Tiff_start_pos = ftell( $filehnd );
  811. // Read the eight bytes of the TIFF header
  812. $DataStr = network_safe_fread( $filehnd, 8 );
  813. // Check that we did get all eight bytes
  814. if ( strlen( $DataStr ) != 8 )
  815. {
  816. return FALSE; // Couldn't read the TIFF header properly
  817. }
  818. $pos = 0;
  819. // First two bytes indicate the byte alignment - should be 'II' or 'MM'
  820. // II = Intel (LSB first, MSB last - Little Endian)
  821. // MM = Motorola (MSB first, LSB last - Big Endian)
  822. $Byte_Align = substr( $DataStr, $pos, 2 );
  823. // Check the Byte Align Characters for validity
  824. if ( ( $Byte_Align != "II" ) && ( $Byte_Align != "MM" ) )
  825. {
  826. // Byte align field is invalid - we won't be able to decode file
  827. return FALSE;
  828. }
  829. // Skip over the Byte Align field which was just read
  830. $pos += 2;
  831. // Next two bytes are TIFF ID - should be value 42 with the appropriate byte alignment
  832. $TIFF_ID = substr( $DataStr, $pos, 2 );
  833. if ( get_IFD_Data_Type( $TIFF_ID, 3, $Byte_Align ) != 42 )
  834. {
  835. // TIFF header ID not found
  836. return FALSE;
  837. }
  838. // Skip over the TIFF ID field which was just read
  839. $pos += 2;
  840. // Next four bytes are the offset to the first IFD
  841. $offset_str = substr( $DataStr, $pos, 4 );
  842. $offset = get_IFD_Data_Type( $offset_str, 4, $Byte_Align );
  843. // Done reading TIFF Header
  844. // Move to first IFD
  845. if ( fseek( $filehnd, $Tiff_start_pos + $offset ) !== 0 )
  846. {
  847. // Error seeking to position of first IFD
  848. return FALSE;
  849. }
  850. // Flag that a makernote has not been found yet
  851. $GLOBALS[ "Maker_Note_Tag" ] = FALSE;
  852. // Read the IFD chain into an array
  853. $Output_Array = read_Multiple_IFDs( $filehnd, $Tiff_start_pos, $Byte_Align, $Tag_Definitions_Name );
  854. // Check if a makernote was found
  855. if ( $GLOBALS[ "Maker_Note_Tag" ] != FALSE )
  856. {
  857. // Makernote was found - Process it
  858. // The makernote needs to be processed after all other
  859. // tags as it may require some of the other tags in order
  860. // to be processed properly
  861. $GLOBALS[ "Maker_Note_Tag" ] = Read_Makernote_Tag( $GLOBALS[ "Maker_Note_Tag" ], $Output_Array, $filehnd );
  862. }
  863. $Output_Array[ 'Makernote_Tag' ] = $GLOBALS[ "Maker_Note_Tag" ];
  864. // Save the Name of the Tags used in the output array
  865. $Output_Array[ 'Tags Name' ] = $Tag_Definitions_Name;
  866. // Save the Byte alignment
  867. $Output_Array['Byte_Align'] = $Byte_Align;
  868. // Return the output array
  869. return $Output_Array ;
  870. }
  871. /******************************************************************************
  872. * End of Function: process_TIFF_Header
  873. ******************************************************************************/
  874. /******************************************************************************
  875. *
  876. * Internal Function: read_Multiple_IFDs
  877. *
  878. * Description: Reads and interprets a chain of standard Image File Directories (IFD's),
  879. * and returns the entries in an array. This chain is made up from IFD's
  880. * which have a pointer to the next IFD. IFD's are read until the next
  881. * pointer indicates there are no more
  882. *
  883. * Parameters: filehnd - a handle for the image file being read, positioned at the
  884. * start of the IFD chain
  885. * Tiff_offset - The offset of the TIFF header from the start of the file
  886. * Byte_Align - either "MM" or "II" indicating Motorola or Intel Byte alignment
  887. * Tag_Definitions_Name - The name of the Tag Definitions group within the global array IFD_Tag_Definitions
  888. * local_offsets - True indicates that offset data should be interpreted as being relative to the start of the currrent entry
  889. * False (normal) indicates offests are relative to start of Tiff header as per IFD standard
  890. * read_next_ptr - True (normal) indicates that a pointer to the next IFD should be read at the end of the IFD
  891. * False indicates that no pointer follows the IFD
  892. *
  893. *
  894. * Returns: OutputArray - Array of IFD entries
  895. *
  896. ******************************************************************************/
  897. function read_Multiple_IFDs( $filehnd, $Tiff_offset, $Byte_Align, $Tag_Definitions_Name, $local_offsets = FALSE, $read_next_ptr = TRUE )
  898. {
  899. // Start at the offset of the first IFD
  900. $Next_Offset = 0;
  901. do
  902. {
  903. // Read an IFD
  904. list($IFD_Array , $Next_Offset) = read_IFD_universal( $filehnd, $Tiff_offset, $Byte_Align, $Tag_Definitions_Name, $local_offsets, $read_next_ptr );
  905. // Move to the position of the next IFD
  906. if ( fseek( $filehnd, $Tiff_offset + $Next_Offset ) !== 0 )
  907. {
  908. // Error seeking to position of next IFD
  909. echo "<p>Error: Corrupted EXIF</p>\n";
  910. return FALSE;
  911. }
  912. $Output_Array[] = $IFD_Array;
  913. } while ( $Next_Offset != 0 ); // Until the Next IFD Offset is zero
  914. // return resulting array
  915. return $Output_Array ;
  916. }
  917. /******************************************************************************
  918. * End of Function: read_Multiple_IFDs
  919. ******************************************************************************/
  920. /******************************************************************************
  921. *
  922. * Internal Function: read_IFD_universal
  923. *
  924. * Description: Reads and interprets a standard or Non-standard Image File
  925. * Directory (IFD), and returns the entries in an array
  926. *
  927. * Parameters: filehnd - a handle for the image file being read, positioned at the start
  928. * of the IFD
  929. * Tiff_offset - The offset of the TIFF header from the start of the file
  930. * Byte_Align - either "MM" or "II" indicating Motorola or Intel Byte alignment
  931. * Tag_Definitions_Name - The name of the Tag Definitions group within the global array IFD_Tag_Definitions
  932. * local_offsets - True indicates that offset data should be interpreted as being relative to the start of the currrent entry
  933. * False (normal) indicates offests are relative to start of Tiff header as per IFD standard
  934. * read_next_ptr - True (normal) indicates that a pointer to the next IFD should be read at the end of the IFD
  935. * False indicates that no pointer follows the IFD
  936. *
  937. * Returns: OutputArray - Array of IFD entries
  938. * Next_Offset - Offset to next IFD (zero = no next IFD)
  939. *
  940. ******************************************************************************/
  941. function read_IFD_universal( $filehnd, $Tiff_offset, $Byte_Align, $Tag_Definitions_Name, $local_offsets = FALSE, $read_next_ptr = TRUE )
  942. {
  943. if ( ( $filehnd == NULL ) || ( feof( $filehnd ) ) )
  944. {
  945. return array (FALSE , 0);
  946. }
  947. // Record the Name of the Tag Group used for this IFD in the output array
  948. $OutputArray[ 'Tags Name' ] = $Tag_Definitions_Name;
  949. // Record the offset of the TIFF header in the output array
  950. $OutputArray[ 'Tiff Offset' ] = $Tiff_offset;
  951. // First 2 bytes of IFD are number of entries in the IFD
  952. $No_Entries_str = network_safe_fread( $filehnd, 2 );
  953. $No_Entries = get_IFD_Data_Type( $No_Entries_str, 3, $Byte_Align );
  954. // If the data is corrupt, the number of entries may be huge, which will cause errors
  955. // This is often caused by a lack of a Next-IFD pointer
  956. if ( $No_Entries> 10000 )
  957. {
  958. // Huge number of entries - abort
  959. echo "<p>Error: huge number of EXIF entries - EXIF is probably Corrupted</p>\n";
  960. return array ( FALSE , 0);
  961. }
  962. // If the data is corrupt or just stupid, the number of entries may zero,
  963. // Indicate this by returning false
  964. if ( $No_Entries === 0 )
  965. {
  966. // No entries - abort
  967. return array ( FALSE , 0);
  968. }
  969. // Save the file position where first IFD record starts as non-standard offsets
  970. // need to know this to calculate an absolute offset
  971. $IFD_first_rec_pos = ftell( $filehnd );
  972. // Read in the IFD structure
  973. $IFD_Data = network_safe_fread( $filehnd, 12 * $No_Entries );
  974. // Check if the entire IFD was able to be read
  975. if ( strlen( $IFD_Data ) != (12 * $No_Entries) )
  976. {
  977. // Couldn't read the IFD Data properly, Some Casio files have no Next IFD pointer, hence cause this error
  978. echo "<p>Error: EXIF Corrupted</p>\n";
  979. return array(FALSE, 0);
  980. }
  981. // Last 4 bytes of a standard IFD are the offset to the next IFD
  982. // Some NON-Standard IFD implementations do not have this, hence causing problems if it is read
  983. // If the Next IFD pointer has been requested to be read,
  984. if ( $read_next_ptr )
  985. {
  986. // Read the pointer to the next IFD
  987. $Next_Offset_str = network_safe_fread( $filehnd, 4 );
  988. $Next_Offset = get_IFD_Data_Type( $Next_Offset_str, 4, $Byte_Align );
  989. }
  990. else
  991. {
  992. // Otherwise set the pointer to zero ( no next IFD )
  993. $Next_Offset = 0;
  994. }
  995. // Initialise current position to the start
  996. $pos = 0;
  997. // Loop for reading IFD entries
  998. for ( $i = 0; $i < $No_Entries; $i++ )
  999. {
  1000. // First 2 bytes of IFD entry are the tag number ( Unsigned Short )
  1001. $Tag_No_str = substr( $IFD_Data, $pos, 2 );
  1002. $Tag_No = get_IFD_Data_Type( $Tag_No_str, 3, $Byte_Align );
  1003. $pos += 2;
  1004. // Next 2 bytes of IFD entry are the data format ( Unsigned Short )
  1005. $Data_Type_str = substr( $IFD_Data, $pos, 2 );
  1006. $Data_Type = get_IFD_Data_Type( $Data_Type_str, 3, $Byte_Align );
  1007. $pos += 2;
  1008. // If Datatype is not between 1 and 12, then skip this entry, it is probably corrupted or custom
  1009. if (( $Data_Type > 12 ) || ( $Data_Type < 1 ) )
  1010. {
  1011. $pos += 8;
  1012. continue 1; // Stop trying to process the tag any further and skip to the next one
  1013. }
  1014. // Next 4 bytes of IFD entry are the data count ( Unsigned Long )
  1015. $Data_Count_str = substr( $IFD_Data, $pos, 4 );
  1016. $Data_Count = get_IFD_Data_Type( $Data_Count_str, 4, $Byte_Align );
  1017. $pos += 4;
  1018. if ( $Data_Count > 100000 )
  1019. {
  1020. echo "<p>Error: huge EXIF data count - EXIF is probably Corrupted</p>\n";
  1021. // Some Casio files have no Next IFD pointer, hence cause errors
  1022. return array ( FALSE , 0);
  1023. }
  1024. // Total Data size is the Data Count multiplied by the size of the Data Type
  1025. $Total_Data_Size = $GLOBALS['IFD_Data_Sizes'][ $Data_Type ] * $Data_Count;
  1026. $Data_Start_pos = -1;
  1027. // If the total data size is larger than 4 bytes, then the data part is the offset to the real data
  1028. if ( $Total_Data_Size > 4 )
  1029. {
  1030. // Not enough room for data - offset provided instead
  1031. $Data_Offset_str = substr( $IFD_Data, $pos, 4 );
  1032. $Data_Start_pos = get_IFD_Data_Type( $Data_Offset_str, 4, $Byte_Align );
  1033. // In some NON-STANDARD makernotes, the offset is relative to the start of the current IFD entry
  1034. if ( $local_offsets )
  1035. {
  1036. // This is a NON-Standard IFD, seek relative to the start of the current tag
  1037. fseek( $filehnd, $IFD_first_rec_pos + $pos - 8 + $Data_Start_pos );
  1038. }
  1039. else
  1040. {
  1041. // This is a normal IFD, seek relative to the start of the TIFF header
  1042. fseek( $filehnd, $Tiff_offset + $Data_Start_pos );
  1043. }
  1044. // Read the data block from the offset position
  1045. $DataStr = network_safe_fread( $filehnd, $Total_Data_Size );
  1046. }
  1047. else
  1048. {
  1049. // The data block is less than 4 bytes, and is provided in the IFD entry, so read it
  1050. $DataStr = substr( $IFD_Data, $pos, $Total_Data_Size );
  1051. }
  1052. // Increment the position past the data
  1053. $pos += 4;
  1054. // Now create the entry for output array
  1055. $Data_Array = array( );
  1056. // Read the data items from the data block
  1057. if ( ( $Data_Type != 2 ) && ( $Data_Type != 7 ) )
  1058. {
  1059. // The data type is Numerical, Read the data items from the data block
  1060. for ( $j = 0; $j < $Data_Count; $j++ )
  1061. {
  1062. $Part_Data_Str = substr( $DataStr, $j * $GLOBALS['IFD_Data_Sizes'][ $Data_Type ], $GLOBALS['IFD_Data_Sizes'][ $Data_Type ] );
  1063. $Data_Array[] = get_IFD_Data_Type( $Part_Data_Str, $Data_Type, $Byte_Align );
  1064. }
  1065. }
  1066. elseif ( $Data_Type == 2 )
  1067. {
  1068. // The data type is String(s) (type 2)
  1069. // Strip the last terminating Null
  1070. $DataStr = substr( $DataStr, 0, strlen($DataStr)-1 );
  1071. // Split the data block into multiple strings whereever there is a Null
  1072. $Data_Array = explode( "\x00", $DataStr );
  1073. }
  1074. else
  1075. {
  1076. // The data type is Unknown (type 7)
  1077. // Do nothing to data
  1078. $Data_Array = $DataStr;
  1079. }
  1080. // If this is a Sub-IFD entry,
  1081. if ( ( array_key_exists( $Tag_No, $GLOBALS[ "IFD_Tag_Definitions" ][$Tag_Definitions_Name] ) ) &&
  1082. ( "SubIFD" == $GLOBALS[ "IFD_Tag_Definitions" ][$Tag_Definitions_Name][ $Tag_No ]['Type'] ) )
  1083. {
  1084. // This is a Sub-IFD entry, go and process the data forming Sub-IFD and use its output array as the new data for this entry
  1085. fseek( $filehnd, $Tiff_offset + $Data_Array[0] );
  1086. $Data_Array = read_Multiple_IFDs( $filehnd, $Tiff_offset, $Byte_Align, $GLOBALS[ "IFD_Tag_Definitions" ][$Tag_Definitions_Name][ $Tag_No ]['Tags Name'] );
  1087. }
  1088. $desc = "";
  1089. $units = "";
  1090. // Check if this tag exists in the list of tag definitions,
  1091. if ( array_key_exists ( $Tag_No, $GLOBALS[ "IFD_Tag_Definitions" ][$Tag_Definitions_Name]) )
  1092. {
  1093. if ( array_key_exists ( 'Description', $GLOBALS[ "IFD_Tag_Definitions" ][$Tag_Definitions_Name][ $Tag_No ] ) )
  1094. {
  1095. $desc = $GLOBALS[ "IFD_Tag_Definitions" ][$Tag_Definitions_Name][ $Tag_No ]['Description'];
  1096. }
  1097. if ( array_key_exists ( 'Units', $GLOBALS[ "IFD_Tag_Definitions" ][$Tag_Definitions_Name][ $Tag_No ] ) )
  1098. {
  1099. $units = $GLOBALS[ "IFD_Tag_Definitions" ][$Tag_Definitions_Name][ $Tag_No ]['Units'];
  1100. }
  1101. // Tag exists in definitions, append details to output array
  1102. $OutputArray[ $Tag_No ] = array ( "Tag Number" => $Tag_No,
  1103. "Tag Name" => $GLOBALS[ "IFD_Tag_Definitions" ][$Tag_Definitions_Name][ $Tag_No ]['Name'],
  1104. "Tag Description" => $desc,
  1105. "Data Type" => $Data_Type,
  1106. "Type" => $GLOBALS[ "IFD_Tag_Definitions" ][$Tag_Definitions_Name][ $Tag_No ]['Type'],
  1107. "Units" => $units,
  1108. "Data" => $Data_Array );
  1109. }
  1110. else
  1111. {
  1112. // Tag doesnt exist in definitions, append unknown details to output array
  1113. $OutputArray[ $Tag_No ] = array ( "Tag Number" => $Tag_No,
  1114. "Tag Name" => "Unknown Tag #" . $Tag_No,
  1115. "Tag Description" => "",
  1116. "Data Type" => $Data_Type,
  1117. "Type" => "Unknown",
  1118. "Units" => "",
  1119. "Data" => $Data_Array );
  1120. }
  1121. // Some information of type "Unknown" (type 7) might require information about
  1122. // how it's position and byte alignment in order to be decoded
  1123. if ( $Data_Type == 7 )
  1124. {
  1125. $OutputArray[ $Tag_No ]['Offset'] = $Data_Start_pos;
  1126. $OutputArray[ $Tag_No ]['Byte Align'] = $Byte_Align;
  1127. }
  1128. ////////////////////////////////////////////////////////////////////////
  1129. // Special Data handling
  1130. ////////////////////////////////////////////////////////////////////////
  1131. // Check if this is a Print Image Matching entry
  1132. if ( $OutputArray[ $Tag_No ]['Type'] == "PIM" )
  1133. {
  1134. // This is a Print Image Matching entry, decode it.
  1135. $OutputArray[ $Tag_No ] = Decode_PIM( $OutputArray[ $Tag_No ], $Tag_Definitions_Name );
  1136. }
  1137. // Interpret the entry into a text string using a custom interpreter
  1138. $text_val = get_Tag_Text_Value( $OutputArray[ $Tag_No ], $Tag_Definitions_Name );
  1139. // Check if a text string was generated
  1140. if ( $text_val !== FALSE )
  1141. {
  1142. // A string was generated, append it to the output array entry
  1143. $OutputArray[ $Tag_No ]['Text Value'] = $text_val;
  1144. $OutputArray[ $Tag_No ]['Decoded'] = TRUE;
  1145. }
  1146. else
  1147. {
  1148. // A string was NOT generated, append a generic string to the output array entry
  1149. $OutputArray[ $Tag_No ]['Text Value'] = get_IFD_value_as_text( $OutputArray[ $Tag_No ] ) . " " . $units;
  1150. $OutputArray[ $Tag_No ]['Decoded'] = FALSE;
  1151. }
  1152. // Check if this entry is the Maker Note
  1153. if ( ( $Tag_Definitions_Name == "EXIF" ) && ( $Tag_No == 37500 ) )
  1154. {
  1155. // Save some extra information which will allow Makernote Decoding with the output array entry
  1156. $OutputArray[ $Tag_No ]['Offset'] = $Data_Start_pos;
  1157. $OutputArray[ $Tag_No ][ 'Tiff Offset' ] = $Tiff_offset;
  1158. $OutputArray[ $Tag_No ]['ByteAlign'] = $Byte_Align;
  1159. // Save a pointer to this entry for Maker note processing later
  1160. $GLOBALS[ "Maker_Note_Tag" ] = & $OutputArray[ $Tag_No ];
  1161. }
  1162. // Check if this is a IPTC/NAA Record within the EXIF IFD
  1163. if ( ( ( $Tag_Definitions_Name == "EXIF" ) || ( $Tag_Definitions_Name == "TIFF" ) ) &&
  1164. ( $Tag_No == 33723 ) )
  1165. {
  1166. // This is a IPTC/NAA Record, interpret it and put result in the data for this entry
  1167. $OutputArray[ $Tag_No ]['Data'] = get_IPTC( $DataStr );
  1168. $OutputArray[ $Tag_No ]['Decoded'] = TRUE;
  1169. }
  1170. // Change: Check for embedded XMP as of version 1.11
  1171. // Check if this is a XMP Record within the EXIF IFD
  1172. if ( ( ( $Tag_Definitions_Name == "EXIF" ) || ( $Tag_Definitions_Name == "TIFF" ) ) &&
  1173. ( $Tag_No == 700 ) )
  1174. {
  1175. // This is a XMP Record, interpret it and put result in the data for this entry
  1176. $OutputArray[ $Tag_No ]['Data'] = read_XMP_array_from_text( $DataStr );
  1177. $OutputArray[ $Tag_No ]['Decoded'] = TRUE;
  1178. }
  1179. // Change: Check for embedded IRB as of version 1.11
  1180. // Check if this is a Photoshop IRB Record within the EXIF IFD
  1181. if ( ( ( $Tag_Definitions_Name == "EXIF" ) || ( $Tag_Definitions_Name == "TIFF" ) ) &&
  1182. ( $Tag_No == 34377 ) )
  1183. {
  1184. // This is a Photoshop IRB Record, interpret it and put result in the data for this entry
  1185. $OutputArray[ $Tag_No ]['Data'] = unpack_Photoshop_IRB_Data( $DataStr );
  1186. $OutputArray[ $Tag_No ]['Decoded'] = TRUE;
  1187. }
  1188. // Exif Thumbnail
  1189. // Check that both the thumbnail length and offset entries have been processed,
  1190. // and that this is one of them
  1191. if ( ( ( ( $Tag_No == 513 ) && ( array_key_exists( 514, $OutputArray ) ) ) ||
  1192. ( ( $Tag_No == 514 ) && ( array_key_exists( 513, $OutputArray ) ) ) ) &&
  1193. ( $Tag_Definitions_Name == "TIFF" ) )
  1194. {
  1195. // Seek to the start of the thumbnail using the offset entry
  1196. fseek( $filehnd, $Tiff_offset + $OutputArray[513]['Data'][0] );
  1197. // Read the thumbnail data, and replace the offset data with the thumbnail
  1198. $OutputArray[513]['Data'] = network_safe_fread( $filehnd, $OutputArray[514]['Data'][0] );
  1199. }
  1200. // Casio Thumbnail
  1201. // Check that both the thumbnail length and offset entries have been processed,
  1202. // and that this is one of them
  1203. if ( ( ( ( $Tag_No == 0x0004 ) && ( array_key_exists( 0x0003, $OutputArray ) ) ) ||
  1204. ( ( $Tag_No == 0x0003 ) && ( array_key_exists( 0x0004, $OutputArray ) ) ) ) &&
  1205. ( $Tag_Definitions_Name == "Casio Type 2" ) )
  1206. {
  1207. // Seek to the start of the thumbnail using the offset entry
  1208. fseek( $filehnd, $Tiff_offset + $OutputArray[0x0004]['Data'][0] );
  1209. // Read the thumbnail data, and replace the offset data with the thumbnail
  1210. $OutputArray[0x0004]['Data'] = network_safe_fread( $filehnd, $OutputArray[0x0003]['Data'][0] );
  1211. }
  1212. // Minolta Thumbnail
  1213. // Check that both the thumbnail length and offset entries have been processed,
  1214. // and that this is one of them
  1215. if ( ( ( ( $Tag_No == 0x0088 ) && ( array_key_exists( 0x0089, $OutputArray ) ) ) ||
  1216. ( ( $Tag_No == 0x0089 ) && ( array_key_exists( 0x0088, $OutputArray ) ) ) ) &&
  1217. ( $Tag_Definitions_Name == "Olympus" ) )
  1218. {
  1219. // Seek to the start of the thumbnail using the offset entry
  1220. fseek( $filehnd, $Tiff_offset + $OutputArray[0x0088]['Data'][0] );
  1221. // Read the thumbnail data, and replace the offset data with the thumbnail
  1222. $OutputArray[0x0088]['Data'] = network_safe_fread( $filehnd, $OutputArray[0x0089]['Data'][0] );
  1223. // Sometimes the minolta thumbnail data is empty (or the offset is corrupt, which results in the same thing)
  1224. // Check if the thumbnail data exists
  1225. if ( $OutputArray[0x0088]['Data'] != "" )
  1226. {
  1227. // Thumbnail exists
  1228. // Minolta Thumbnails are missing their first 0xFF for some reason,
  1229. // which is replaced with some weird character, so fix this
  1230. $OutputArray[0x0088]['Data']{0} = "\xFF";
  1231. }
  1232. else
  1233. {
  1234. // Thumbnail doesnt exist - make it obvious
  1235. $OutputArray[0x0088]['Data'] = FALSE;
  1236. }
  1237. }
  1238. }
  1239. // Return the array of IFD entries and the offset to the next IFD
  1240. return array ($OutputArray , $Next_Offset);
  1241. }
  1242. /******************************************************************************
  1243. * End of Function: read_IFD_universal
  1244. ******************************************************************************/
  1245. /******************************************************************************
  1246. *
  1247. * Internal Function: get_Tag_Text_Value
  1248. *
  1249. * Description: Attempts to interpret an IFD entry into a text string using the
  1250. * information in the IFD_Tag_Definitions global array.
  1251. *
  1252. * Parameters: Tag - The IFD entry to process
  1253. * Tag_Definitions_Name - The name of the tag definitions to use from within the IFD_Tag_Definitions global array
  1254. *
  1255. * Returns: String - if the tag was successfully decoded into a text string
  1256. * FALSE - if the tag could not be decoded using the information
  1257. * in the IFD_Tag_Definitions global array
  1258. *
  1259. ******************************************************************************/
  1260. function get_Tag_Text_Value( $Tag, $Tag_Definitions_Name )
  1261. {
  1262. // Check what format the entry is specified as
  1263. if ( $Tag['Type'] == "String" )
  1264. {
  1265. // Format is Text String
  1266. // If "Unknown" (type 7) data type,
  1267. if ( $Tag['Data Type'] == 7 )
  1268. {
  1269. // Return data as is.
  1270. return $Tag['Data'];
  1271. }
  1272. else
  1273. {
  1274. // Otherwise return the default string value of the datatype
  1275. return get_IFD_value_as_text( $Tag );
  1276. }
  1277. }
  1278. else if ( $Tag['Type'] == "Character Coded String" )
  1279. {
  1280. // Format is Character Coded String (First 8 characters indicate coding scheme)
  1281. // Convert Data to a string
  1282. if ( $Tag['Data Type'] == 7 )
  1283. {
  1284. // If it is type "Unknown" (type 7) use data as is
  1285. $data = $Tag['Data'];
  1286. }
  1287. else
  1288. {
  1289. // Otherwise use the default string value of the datatype
  1290. $data = get_IFD_value_as_text( $Tag );
  1291. }
  1292. // Some implementations allow completely data with no Coding Scheme Name,
  1293. // so we need to handle this to avoid errors
  1294. if ( trim( $data ) == "" )
  1295. {
  1296. return "";
  1297. }
  1298. // Extract the Coding Scheme Name from the first 8 characters
  1299. $char_code = substr( $data, 0, 8 );
  1300. // Extract the Data part from after the first 8 characters
  1301. $characters = substr( $data, 8 );
  1302. // Check coding scheme and interpret as neccessary
  1303. if ( $char_code === "ASCII\x00\x00\x00" )
  1304. {
  1305. // ASCII coding - return data as is.
  1306. return $characters;
  1307. }
  1308. elseif ( ( $char_code === "UNICODE\x00" ) ||
  1309. ( $char_code === "Unicode\x00" ) ) // Note lowercase is non standard
  1310. {
  1311. // Unicode coding - interpret and return result.
  1312. return xml_UTF16_clean( $characters, TRUE );
  1313. }
  1314. else
  1315. {
  1316. // Unknown coding - return string indicating this
  1317. return "Unsupported character coding : \"$char_code\"\n\"" . trim($characters) . "\"";
  1318. }
  1319. break;
  1320. }
  1321. else if ( $Tag['Type'] == "Numeric" )
  1322. {
  1323. // Format is numeric - return default text value with any required units text appended
  1324. if ( array_key_exists ( 'Units', $GLOBALS[ "IFD_Tag_Definitions" ][$Tag_Definitions_Name][ $Tag["Tag Number"] ] ) )
  1325. {
  1326. $units = $GLOBALS[ "IFD_Tag_Definitions" ][$Tag_Definitions_Name][ $Tag["Tag Number"] ]['Units'];
  1327. }
  1328. else
  1329. {
  1330. $units = "";
  1331. }
  1332. return get_IFD_value_as_text( $Tag ) . " " . $units;
  1333. }
  1334. else if ( $Tag['Type'] == "Lookup" )
  1335. {
  1336. // Format is a Lookup Table
  1337. // Get a numeric value to use in lookup
  1338. if ( is_array( $Tag['Data'] ) )
  1339. {
  1340. // If data is an array, use first element
  1341. $first_val = $Tag['Data'][0];
  1342. }
  1343. else if ( is_string( $Tag['Data'] ) )
  1344. {
  1345. // If data is a string, use the first character
  1346. $first_val = ord($Tag['Data']{0});
  1347. }
  1348. else
  1349. {
  1350. // Otherwise use the data as is
  1351. $first_val = $Tag['Data'];
  1352. }
  1353. // Check if the data value exists in the lookup table for this IFD entry
  1354. if ( array_key_exists( $first_val, $GLOBALS[ "IFD_Tag_Definitions" ][$Tag_Definitions_Name][ $Tag["Tag Number"] ] ) )
  1355. {
  1356. // Data value exists in lookup table - return the matching string
  1357. return $GLOBALS[ "IFD_Tag_Definitions" ][$Tag_Definitions_Name][ $Tag["Tag Number"] ][ $first_val ];
  1358. }
  1359. else
  1360. {
  1361. // Data value doesnt exist in lookup table - return explanation string
  1362. return "Unknown Reserved value $first_val ";
  1363. }
  1364. }
  1365. else if ( $Tag['Type'] == "Special" )
  1366. {
  1367. // Format is special - interpret to text with special handlers
  1368. return get_Special_Tag_Text_Value( $Tag, $Tag_Definitions_Name );
  1369. }
  1370. else if ( $Tag['Type'] == "PIM" )
  1371. {
  1372. // Format is Print Image Matching info - interpret with custom handler
  1373. return get_PIM_Text_Value( $Tag, $Tag_Definitions_Name );
  1374. }
  1375. else if ( $Tag['Type'] == "SubIFD" )
  1376. {
  1377. // Format is a Sub-IFD - this has no text value
  1378. return "";
  1379. }
  1380. else
  1381. {
  1382. // Unknown Format - Couldn't interpret using the IFD_Tag_Definitions global array information
  1383. return FALSE;
  1384. }
  1385. }
  1386. /******************************************************************************
  1387. * End of Function: get_Tag_Text_Value
  1388. ******************************************************************************/
  1389. /******************************************************************************
  1390. *
  1391. * Internal Function: get_Special_Tag_Text_Value
  1392. *
  1393. * Description: Interprets an IFD entry marked as "Special" in the IFD_Tag_Definitions
  1394. * global array into a text string using custom handlers
  1395. *
  1396. * Parameters: Tag - The IFD entry to process
  1397. * Tag_Definitions_Name - The name of the tag definitions to use from within the IFD_Tag_Definitions global array
  1398. *
  1399. * Returns: String - if the tag was successfully decoded into a text string
  1400. * FALSE - if the tag could not be decoded
  1401. *
  1402. ******************************************************************************/
  1403. function get_Special_Tag_Text_Value( $Tag, $Tag_Definitions_Name )
  1404. {
  1405. // Check what type of IFD is being decoded
  1406. if ( $Tag_Definitions_Name == "TIFF" )
  1407. {
  1408. // This is a TIFF IFD (bottom level)
  1409. // Check what tag number the IFD entry has.
  1410. switch ( $Tag['Tag Number'] )
  1411. {
  1412. case 530: // YCbCr Sub Sampling Entry
  1413. // Data contains two numerical values
  1414. if ( ( $Tag['Data'][0] == 2 ) && ( $Tag['Data'][1] == 1 ) )
  1415. {
  1416. // Values are 2,1 - hence YCbCr 4:2:2
  1417. return "YCbCr 4:2:2 ratio of chrominance components to the luminance components";
  1418. }
  1419. elseif ( ( $Tag['Data'][0] == 2 ) && ( $Tag['Data'][1] == 2 ) )
  1420. {
  1421. // Values are 2,2 - hence YCbCr 4:2:0
  1422. return "YCbCr 4:2:0 ratio of chrominance components to the luminance components";
  1423. }
  1424. else
  1425. {
  1426. // Other values are unknown
  1427. return "Unknown Reserved value (" . $Tag['Data'][0] . ")";
  1428. }
  1429. break;
  1430. default:
  1431. return FALSE;
  1432. }
  1433. }
  1434. else if ( $Tag_Definitions_Name == "EXIF" )
  1435. {
  1436. // This is an EXIF IFD
  1437. // Check what tag number the IFD entry has.
  1438. switch ( $Tag['Tag Number'] )
  1439. {
  1440. case 37121: // Components configuration
  1441. // Data contains 4 numerical values indicating component type
  1442. $output_str = "";
  1443. // Cycle through each component
  1444. for ( $Num = 0; $Num < 4; $Num++ )
  1445. {
  1446. // Construct first part of text string
  1447. $output_str .= "Component " . ( $Num + 1 ) . ": ";
  1448. // Construct second part of text string via
  1449. // lookup using numerical value
  1450. $value = ord( $Tag['Data']{$Num} );
  1451. switch( $value )
  1452. {
  1453. case 0:
  1454. $output_str .= "Does not exist\n";
  1455. break;
  1456. case 1:
  1457. $output_str .= "Y (Luminance)\n";
  1458. break;
  1459. case 2:
  1460. $output_str .= "Cb (Chroma minus Blue)\n";
  1461. break;
  1462. case 3:
  1463. $output_str .= "Cr (Chroma minus Red)\n";
  1464. break;
  1465. case 4:
  1466. $output_str .= "Red\n";
  1467. break;
  1468. case 5:
  1469. $output_str .= "Green\n";
  1470. break;
  1471. case 6:
  1472. $output_str .= "Blue\n";
  1473. break;
  1474. default:
  1475. $output_str .= "Unknown value $value\n";
  1476. };
  1477. }
  1478. // Return the completed string
  1479. return $output_str;
  1480. break;
  1481. case 41730: // Colour Filter Array Pattern
  1482. // The first two characters are a SHORT for Horizontal repeat pixel unit -
  1483. $n_max = get_IFD_Data_Type( substr( $Tag['Data'], 0, 2 ), 3, $Tag['Byte Align'] );
  1484. // The next two characters are a SHORT for Vertical repeat pixel unit -
  1485. $m_max = get_IFD_Data_Type( substr( $Tag['Data'], 2, 2 ), 3, $Tag['Byte Align'] );
  1486. // At least one camera type appears to have byte reversed values for N_Max and M_Max
  1487. // Check if they need reversing
  1488. if ( $n_max > 256 )
  1489. {
  1490. $n_max = $n_max/256 + 256*($n_max%256);
  1491. }
  1492. if ( $m_max > 256 )
  1493. {
  1494. $m_max = $m_max/256 + 256*($m_max%256);
  1495. }
  1496. $output_str = "";
  1497. // Cycle through all the elements in the resulting 2 dimensional array,
  1498. for( $m = 1; $m <= $m_max; $m++ )
  1499. {
  1500. for( $n = 1; $n <= $n_max; $n++ )
  1501. {
  1502. // Append text from a lookup table according to
  1503. // the value read for this element
  1504. switch ( ord($Tag['Data']{($n_max*($m-1)+$n+3)}) )
  1505. {
  1506. case 0:
  1507. $output_str .= "RED ";
  1508. break;
  1509. case 1:
  1510. $output_str .= "GREEN ";
  1511. break;
  1512. case 2:
  1513. $output_str .= "BLUE ";
  1514. break;
  1515. case 3:
  1516. $output_str .= "CYAN ";
  1517. break;
  1518. case 4:
  1519. $output_str .= "MAGENTA ";
  1520. break;
  1521. case 5:
  1522. $output_str .= "YELLOW ";
  1523. break;
  1524. case 6:
  1525. $output_str .= "WHITE ";
  1526. break;
  1527. default:
  1528. $output_str .= "Unknown ";
  1529. break;
  1530. };
  1531. };
  1532. $output_str .= "\n";
  1533. };
  1534. // Return the resulting string
  1535. return $output_str;
  1536. break;
  1537. default:
  1538. return FALSE;
  1539. }
  1540. }
  1541. else
  1542. {
  1543. // Unknown IFD type, see if it is part of a makernote
  1544. return get_Makernote_Text_Value( $Tag, $Tag_Definitions_Name );
  1545. }
  1546. }
  1547. /******************************************************************************
  1548. * End of Function: get_Tag_Text_Value
  1549. ******************************************************************************/
  1550. /******************************************************************************
  1551. *
  1552. * Function: interpret_IFD
  1553. *
  1554. * Description: Generates html detailing the contents a single IFD.
  1555. *
  1556. * Parameters: IFD_array - the array containing an IFD
  1557. * filename - the name of the Image file being processed ( used
  1558. * by scripts which displays EXIF thumbnails)
  1559. *
  1560. * Returns: output_str - A string containing the HTML
  1561. *
  1562. ******************************************************************************/
  1563. function interpret_IFD( $IFD_array, $filename )
  1564. {
  1565. // Create the output string with the table tag
  1566. $output_str = "<table class=\"EXIF_Table\" border=1>\n";
  1567. // Create an extra output string to receive any supplementary html
  1568. // which cannot go inside the table
  1569. $extra_IFD_str = "";
  1570. // Check that the IFD array is valid
  1571. if ( ( $IFD_array === FALSE ) || ( $IFD_array === NULL ) )
  1572. {
  1573. // the IFD array is NOT valid - exit
  1574. return "";
  1575. }
  1576. // Check if this is an EXIF IFD and if there is a makernote present
  1577. if ( ( $IFD_array['Tags Name'] === "EXIF" ) &&
  1578. ( ! array_key_exists( 37500, $IFD_array ) ) )
  1579. {
  1580. // This is an EXIF IFD but NO makernote is present - Add a message to the output
  1581. $extra_IFD_str .= "<h3 class=\"EXIF_Secondary_Heading\">No Makernote Present</h3>";
  1582. }
  1583. // Cycle through each tag in the IFD
  1584. foreach( $IFD_array as $Tag_ID => $Exif_Tag )
  1585. {
  1586. // Ignore the non numeric elements - they aren't tags
  1587. if ( ! is_numeric ( $Tag_ID ) )
  1588. {
  1589. // Skip Tags Name
  1590. }
  1591. // Check if the Tag has been decoded successfully
  1592. else if ( $Exif_Tag['Decoded'] == TRUE )
  1593. {
  1594. // This tag has been successfully decoded
  1595. // Table cells won't get drawn with nothing in them -
  1596. // Ensure that at least a non breaking space exists in them
  1597. if ( trim($Exif_Tag['Text Value']) == "" )
  1598. {
  1599. $Exif_Tag['Text Value'] = "&nbsp;";
  1600. }
  1601. // Check if the tag is a sub-IFD
  1602. if ( $Exif_Tag['Type'] == "SubIFD" )
  1603. {
  1604. // This is a sub-IFD tag
  1605. // Add a sub-heading for the sub-IFD
  1606. $extra_IFD_str .= "<h3 class=\"EXIF_Secondary_Heading\">" . $Exif_Tag['Tag Name'] . " contents</h3>";
  1607. // Cycle through each sub-IFD in the chain
  1608. foreach ( $Exif_Tag['Data'] as $subIFD )
  1609. {
  1610. // Interpret this sub-IFD and add the html to the secondary output
  1611. $extra_IFD_str .= interpret_IFD( $subIFD, $filename );
  1612. }
  1613. }
  1614. // Check if the tag is a makernote
  1615. else if ( $Exif_Tag['Type'] == "Maker Note" )
  1616. {
  1617. // This is a Makernote Tag
  1618. // Add a sub-heading for the Makernote
  1619. $extra_IFD_str .= "<h3 class=\"EXIF_Secondary_Heading\">Maker Note Contents</h3>";
  1620. // Interpret the Makernote and add the html to the secondary output
  1621. $extra_IFD_str .= Interpret_Makernote_to_HTML( $Exif_Tag, $filename );
  1622. }
  1623. // Check if this is a IPTC/NAA Record within the EXIF IFD
  1624. else if ( $Exif_Tag['Type'] == "IPTC" )
  1625. {
  1626. // This is a IPTC/NAA Record, interpret it and output to the secondary html
  1627. $extra_IFD_str .= "<h3 class=\"EXIF_Secondary_Heading\">Contains IPTC/NAA Embedded in EXIF</h3>";
  1628. $extra_IFD_str .=Interpret_IPTC_to_HTML( $Exif_Tag['Data'] );
  1629. }
  1630. // Change: Check for embedded XMP as of version 1.11
  1631. // Check if this is a XMP Record within the EXIF IFD
  1632. else if ( $Exif_Tag['Type'] == "XMP" )
  1633. {
  1634. // This is a XMP Record, interpret it and output to the secondary html
  1635. $extra_IFD_str .= "<h3 class=\"EXIF_Secondary_Heading\">Contains XMP Embedded in EXIF</h3>";
  1636. $extra_IFD_str .= Interpret_XMP_to_HTML( $Exif_Tag['Data'] );
  1637. }
  1638. // Change: Check for embedded IRB as of version 1.11
  1639. // Check if this is a Photoshop IRB Record within the EXIF IFD
  1640. else if ( $Exif_Tag['Type'] == "IRB" )
  1641. {
  1642. // This is a Photoshop IRB Record, interpret it and output to the secondary html
  1643. $extra_IFD_str .= "<h3 class=\"EXIF_Secondary_Heading\">Contains Photoshop IRB Embedded in EXIF</h3>";
  1644. $extra_IFD_str .= Interpret_IRB_to_HTML( $Exif_Tag['Data'], $filename );
  1645. }
  1646. // Check if the tag is Numeric
  1647. else if ( $Exif_Tag['Type'] == "Numeric" )
  1648. {
  1649. // Numeric Tag - Output text value as is.
  1650. $output_str .= "<tr class=\"EXIF_Table_Row\"><td class=\"EXIF_Caption_Cell\">" . $Exif_Tag['Tag Name'] . "</td><td class=\"EXIF_Value_Cell\">" . $Exif_Tag['Text Value'] . "</td></tr>\n";
  1651. }
  1652. else
  1653. {
  1654. // Other tag - Output text as preformatted
  1655. $output_str .= "<tr class=\"EXIF_Table_Row\"><td class=\"EXIF_Caption_Cell\">" . $Exif_Tag['Tag Name'] . "</td><td class=\"EXIF_Value_Cell\"><pre>" . trim( $Exif_Tag['Text Value']) . "</pre></td></tr>\n";
  1656. }
  1657. }
  1658. else
  1659. {
  1660. // Tag has NOT been decoded successfully
  1661. // Hence it is either an unknown tag, or one which
  1662. // requires processing at the time of html construction
  1663. // Table cells won't get drawn with nothing in them -
  1664. // Ensure that at least a non breaking space exists in them
  1665. if ( trim($Exif_Tag['Text Value']) == "" )
  1666. {
  1667. $Exif_Tag['Text Value'] = "&nbsp;";
  1668. }
  1669. // Check if this tag is the first IFD Thumbnail
  1670. if ( ( $IFD_array['Tags Name'] == "TIFF" ) &&
  1671. ( $Tag_ID == 513 ) )
  1672. {
  1673. // This is the first IFD thumbnail - Add html to the output
  1674. // Change: as of version 1.11 - Changed to make thumbnail link portable across directories
  1675. // Build the path of the thumbnail script and its filename parameter to put in a url
  1676. $link_str = get_relative_path( dirname(__FILE__) . "/get_exif_thumb.php" , getcwd ( ) );
  1677. $link_str .= "?filename=";
  1678. $link_str .= get_relative_path( $filename, dirname(__FILE__) );
  1679. // Add thumbnail link to html
  1680. $output_str .= "<tr class=\"EXIF_Table_Row\"><td class=\"EXIF_Caption_Cell\">" . $Exif_Tag['Tag Name'] . "</td><td class=\"EXIF_Value_Cell\"><a class=\"EXIF_First_IFD_Thumb_Link\" href=\"$link_str\"><img class=\"EXIF_First_IFD_Thumb\" src=\"$link_str\"></a></td></tr>\n";
  1681. }
  1682. // Check if this is the Makernote
  1683. else if ( $Exif_Tag['Type'] == "Maker Note" )
  1684. {
  1685. // This is the makernote, but has not been decoded
  1686. // Add a message to the secondary output
  1687. $extra_IFD_str .= "<h3 class=\"EXIF_Secondary_Heading\">Makernote Coding Unknown</h3>\n";
  1688. }
  1689. else
  1690. {
  1691. // This is an Unknown Tag
  1692. // Check if the user wants to hide unknown tags
  1693. if ( $GLOBALS['HIDE_UNKNOWN_TAGS'] === FALSE )
  1694. {
  1695. // User wants to display unknown tags
  1696. // Check if the Data is an ascii string
  1697. if ( $Exif_Tag['Data Type'] == 2 )
  1698. {
  1699. // This is a Ascii String field - add it preformatted to the output
  1700. $output_str .= "<tr class=\"EXIF_Table_Row\"><td class=\"EXIF_Caption_Cell\">" . $Exif_Tag['Tag Name'] . "</td><td class=\"EXIF_Value_Cell\"><pre>" . trim( $Exif_Tag['Text Value'] ) . "</pre></td></tr>\n";
  1701. }
  1702. else
  1703. {
  1704. // Not an ASCII string - add it as is to the output
  1705. $output_str .= "<tr class=\"EXIF_Table_Row\"><td class=\"EXIF_Caption_Cell\">" . $Exif_Tag['Tag Name'] . "</td><td class=\"EXIF_Value_Cell\">" . trim( $Exif_Tag['Text Value'] ) . "</td></tr>\n";
  1706. }
  1707. }
  1708. }
  1709. }
  1710. }
  1711. // Close the table in the output
  1712. $output_str .= "</table>\n";
  1713. // Add the secondary output at the end of the main output
  1714. $output_str .= "$extra_IFD_str\n";
  1715. // Return the resulting html
  1716. return $output_str;
  1717. }
  1718. /******************************************************************************
  1719. * End of Function: interpret_IFD
  1720. ******************************************************************************/
  1721. /******************************************************************************
  1722. *
  1723. * Function: get_IFD_Data_Type
  1724. *
  1725. * Description: Decodes an IFD field value from a binary data string, using
  1726. * information supplied about the data type and byte alignment of
  1727. * the stored data.
  1728. * This function should be used for all datatypes except ASCII strings
  1729. *
  1730. * Parameters: input_data - a binary data string containing the IFD value,
  1731. * must be exact length of the value
  1732. * data_type - a number representing the IFD datatype as per the
  1733. * TIFF 6.0 specification:
  1734. * 1 = Unsigned 8-bit Byte
  1735. * 2 = ASCII String
  1736. * 3 = Unsigned 16-bit Short
  1737. * 4 = Unsigned 32-bit Long
  1738. * 5 = Unsigned 2x32-bit Rational
  1739. * 6 = Signed 8-bit Byte
  1740. * 7 = Undefined
  1741. * 8 = Signed 16-bit Short
  1742. * 9 = Signed 32-bit Long
  1743. * 10 = Signed 2x32-bit Rational
  1744. * 11 = 32-bit Float
  1745. * 12 = 64-bit Double
  1746. * Byte_Align - Indicates the byte alignment of the data.
  1747. * MM = Motorola, MSB first, Big Endian
  1748. * II = Intel, LSB first, Little Endian
  1749. *
  1750. * Returns: output - the value of the data (string or numeric)
  1751. *
  1752. ******************************************************************************/
  1753. function get_IFD_Data_Type( $input_data, $data_type, $Byte_Align )
  1754. {
  1755. // Check if this is a Unsigned Byte, Unsigned Short or Unsigned Long
  1756. if (( $data_type == 1 ) || ( $data_type == 3 ) || ( $data_type == 4 ))
  1757. {
  1758. // This is a Unsigned Byte, Unsigned Short or Unsigned Long
  1759. // Check the byte alignment to see if the bytes need tp be reversed
  1760. if ( $Byte_Align == "II" )
  1761. {
  1762. // This is in Intel format, reverse it
  1763. $input_data = strrev ( $input_data );
  1764. }
  1765. // Convert the binary string to a number and return it
  1766. return hexdec( bin2hex( $input_data ) );
  1767. }
  1768. // Check if this is a ASCII string type
  1769. elseif ( $data_type == 2 )
  1770. {
  1771. // Null terminated ASCII string(s)
  1772. // The input data may represent multiple strings, as the
  1773. // 'count' field represents the total bytes, not the number of strings
  1774. // Hence this should not be processed here, as it would have
  1775. // to return multiple values instead of a single value
  1776. echo "<p>Error - ASCII Strings should not be processed in get_IFD_Data_Type</p>\n";
  1777. return "Error Should never get here"; //explode( "\x00", $input_data );
  1778. }
  1779. // Check if this is a Unsigned rational type
  1780. elseif ( $data_type == 5 )
  1781. {
  1782. // This is a Unsigned rational type
  1783. // Check the byte alignment to see if the bytes need to be reversed
  1784. if ( $Byte_Align == "MM" )
  1785. {
  1786. // Motorola MSB first byte aligment
  1787. // Unpack the Numerator and denominator and return them
  1788. return unpack( 'NNumerator/NDenominator', $input_data );
  1789. }
  1790. else
  1791. {
  1792. // Intel LSB first byte aligment
  1793. // Unpack the Numerator and denominator and return them
  1794. return unpack( 'VNumerator/VDenominator', $input_data );
  1795. }
  1796. }
  1797. // Check if this is a Signed Byte, Signed Short or Signed Long
  1798. elseif ( ( $data_type == 6 ) || ( $data_type == 8 ) || ( $data_type == 9 ) )
  1799. {
  1800. // This is a Signed Byte, Signed Short or Signed Long
  1801. // Check the byte alignment to see if the bytes need to be reversed
  1802. if ( $Byte_Align == "II" )
  1803. {
  1804. //Intel format, reverse the bytes
  1805. $input_data = strrev ( $input_data );
  1806. }
  1807. // Convert the binary string to an Unsigned number
  1808. $value = hexdec( bin2hex( $input_data ) );
  1809. // Convert to signed number
  1810. // Check if it is a Byte above 128 (i.e. a negative number)
  1811. if ( ( $data_type == 6 ) && ( $value > 128 ) )
  1812. {
  1813. // number should be negative - make it negative
  1814. return $value - 256;
  1815. }
  1816. // Check if it is a Short above 32767 (i.e. a negative number)
  1817. if ( ( $data_type == 8 ) && ( $value > 32767 ) )
  1818. {
  1819. // number should be negative - make it negative
  1820. return $value - 65536;
  1821. }
  1822. // Check if it is a Long above 2147483648 (i.e. a negative number)
  1823. if ( ( $data_type == 9 ) && ( $value > 2147483648 ) )
  1824. {
  1825. // number should be negative - make it negative
  1826. return $value - 4294967296;
  1827. }
  1828. // Return the signed number
  1829. return $value;
  1830. }
  1831. // Check if this is Undefined type
  1832. elseif ( $data_type == 7 )
  1833. {
  1834. // Custom Data - Do nothing
  1835. return $input_data;
  1836. }
  1837. // Check if this is a Signed Rational type
  1838. elseif ( $data_type == 10 )
  1839. {
  1840. // This is a Signed Rational type
  1841. // Signed Long not available with endian in unpack , use unsigned and convert
  1842. // Check the byte alignment to see if the bytes need to be reversed
  1843. if ( $Byte_Align == "MM" )
  1844. {
  1845. // Motorola MSB first byte aligment
  1846. // Unpack the Numerator and denominator
  1847. $value = unpack( 'NNumerator/NDenominator', $input_data );
  1848. }
  1849. else
  1850. {
  1851. // Intel LSB first byte aligment
  1852. // Unpack the Numerator and denominator
  1853. $value = unpack( 'VNumerator/VDenominator', $input_data );
  1854. }
  1855. // Convert the numerator to a signed number
  1856. // Check if it is above 2147483648 (i.e. a negative number)
  1857. if ( $value['Numerator'] > 2147483648 )
  1858. {
  1859. // number is negative
  1860. $value['Numerator'] -= 4294967296;
  1861. }
  1862. // Convert the denominator to a signed number
  1863. // Check if it is above 2147483648 (i.e. a negative number)
  1864. if ( $value['Denominator'] > 2147483648 )
  1865. {
  1866. // number is negative
  1867. $value['Denominator'] -= 4294967296;
  1868. }
  1869. // Return the Signed Rational value
  1870. return $value;
  1871. }
  1872. // Check if this is a Float type
  1873. elseif ( $data_type == 11 )
  1874. {
  1875. // IEEE 754 Float
  1876. // TODO - EXIF - IFD datatype Float not implemented yet
  1877. return "FLOAT NOT IMPLEMENTED YET";
  1878. }
  1879. // Check if this is a Double type
  1880. elseif ( $data_type == 12 )
  1881. {
  1882. // IEEE 754 Double
  1883. // TODO - EXIF - IFD datatype Double not implemented yet
  1884. return "DOUBLE NOT IMPLEMENTED YET";
  1885. }
  1886. else
  1887. {
  1888. // Error - Invalid Datatype
  1889. return "Invalid Datatype $data_type";
  1890. }
  1891. }
  1892. /******************************************************************************
  1893. * End of Function: get_IFD_Data_Type
  1894. ******************************************************************************/
  1895. /******************************************************************************
  1896. *
  1897. * Function: put_IFD_Data_Type
  1898. *
  1899. * Description: Encodes an IFD field from a value to a binary data string, using
  1900. * information supplied about the data type and byte alignment of
  1901. * the stored data.
  1902. *
  1903. * Parameters: input_data - an IFD data value, numeric or string
  1904. * data_type - a number representing the IFD datatype as per the
  1905. * TIFF 6.0 specification:
  1906. * 1 = Unsigned 8-bit Byte
  1907. * 2 = ASCII String
  1908. * 3 = Unsigned 16-bit Short
  1909. * 4 = Unsigned 32-bit Long
  1910. * 5 = Unsigned 2x32-bit Rational
  1911. * 6 = Signed 8-bit Byte
  1912. * 7 = Undefined
  1913. * 8 = Signed 16-bit Short
  1914. * 9 = Signed 32-bit Long
  1915. * 10 = Signed 2x32-bit Rational
  1916. * 11 = 32-bit Float
  1917. * 12 = 64-bit Double
  1918. * Byte_Align - Indicates the byte alignment of the data.
  1919. * MM = Motorola, MSB first, Big Endian
  1920. * II = Intel, LSB first, Little Endian
  1921. *
  1922. * Returns: output - the packed binary string of the data
  1923. *
  1924. ******************************************************************************/
  1925. function put_IFD_Data_Type( $input_data, $data_type, $Byte_Align )
  1926. {
  1927. // Process according to the datatype
  1928. switch ( $data_type )
  1929. {
  1930. case 1: // Unsigned Byte - return character as is
  1931. return chr($input_data);
  1932. break;
  1933. case 2: // ASCII String
  1934. // Return the string with terminating null
  1935. return $input_data . "\x00";
  1936. break;
  1937. case 3: // Unsigned Short
  1938. // Check byte alignment
  1939. if ( $Byte_Align == "II" )
  1940. {
  1941. // Intel/Little Endian - pack the short and return
  1942. return pack( "v", $input_data );
  1943. }
  1944. else
  1945. {
  1946. // Motorola/Big Endian - pack the short and return
  1947. return pack( "n", $input_data );
  1948. }
  1949. break;
  1950. case 4: // Unsigned Long
  1951. // Check byte alignment
  1952. if ( $Byte_Align == "II" )
  1953. {
  1954. // Intel/Little Endian - pack the long and return
  1955. return pack( "V", $input_data );
  1956. }
  1957. else
  1958. {
  1959. // Motorola/Big Endian - pack the long and return
  1960. return pack( "N", $input_data );
  1961. }
  1962. break;
  1963. case 5: // Unsigned Rational
  1964. // Check byte alignment
  1965. if ( $Byte_Align == "II" )
  1966. {
  1967. // Intel/Little Endian - pack the two longs and return
  1968. return pack( "VV", $input_data['Numerator'], $input_data['Denominator'] );
  1969. }
  1970. else
  1971. {
  1972. // Motorola/Big Endian - pack the two longs and return
  1973. return pack( "NN", $input_data['Numerator'], $input_data['Denominator'] );
  1974. }
  1975. break;
  1976. case 6: // Signed Byte
  1977. // Check if number is negative
  1978. if ( $input_data < 0 )
  1979. {
  1980. // Number is negative - return signed character
  1981. return chr( $input_data + 256 );
  1982. }
  1983. else
  1984. {
  1985. // Number is positive - return character
  1986. return chr( $input_data );
  1987. }
  1988. break;
  1989. case 7: // Unknown - return as is
  1990. return $input_data;
  1991. break;
  1992. case 8: // Signed Short
  1993. // Check if number is negative
  1994. if ( $input_data < 0 )
  1995. {
  1996. // Number is negative - make signed value
  1997. $input_data = $input_data + 65536;
  1998. }
  1999. // Check byte alignment
  2000. if ( $Byte_Align == "II" )
  2001. {
  2002. // Intel/Little Endian - pack the short and return
  2003. return pack( "v", $input_data );
  2004. }
  2005. else
  2006. {
  2007. // Motorola/Big Endian - pack the short and return
  2008. return pack( "n", $input_data );
  2009. }
  2010. break;
  2011. case 9: // Signed Long
  2012. // Check if number is negative
  2013. if ( $input_data < 0 )
  2014. {
  2015. // Number is negative - make signed value
  2016. $input_data = $input_data + 4294967296;
  2017. }
  2018. // Check byte alignment
  2019. if ( $Byte_Align == "II" )
  2020. {
  2021. // Intel/Little Endian - pack the long and return
  2022. return pack( "v", $input_data );
  2023. }
  2024. else
  2025. {
  2026. // Motorola/Big Endian - pack the long and return
  2027. return pack( "n", $input_data );
  2028. }
  2029. break;
  2030. case 10: // Signed Rational
  2031. // Check if numerator is negative
  2032. if ( $input_data['Numerator'] < 0 )
  2033. {
  2034. // Number is numerator - make signed value
  2035. $input_data['Numerator'] = $input_data['Numerator'] + 4294967296;
  2036. }
  2037. // Check if denominator is negative
  2038. if ( $input_data['Denominator'] < 0 )
  2039. {
  2040. // Number is denominator - make signed value
  2041. $input_data['Denominator'] = $input_data['Denominator'] + 4294967296;
  2042. }
  2043. // Check byte alignment
  2044. if ( $Byte_Align == "II" )
  2045. {
  2046. // Intel/Little Endian - pack the two longs and return
  2047. return pack( "VV", $input_data['Numerator'], $input_data['Denominator'] );
  2048. }
  2049. else
  2050. {
  2051. // Motorola/Big Endian - pack the two longs and return
  2052. return pack( "NN", $input_data['Numerator'], $input_data['Denominator'] );
  2053. }
  2054. break;
  2055. case 11: // Float
  2056. // IEEE 754 Float
  2057. // TODO - EXIF - IFD datatype Float not implemented yet
  2058. return "FLOAT NOT IMPLEMENTED YET";
  2059. break;
  2060. case 12: // Double
  2061. // IEEE 754 Double
  2062. // TODO - EXIF - IFD datatype Double not implemented yet
  2063. return "DOUBLE NOT IMPLEMENTED YET";
  2064. break;
  2065. default:
  2066. // Error - Invalid Datatype
  2067. return "Invalid Datatype $data_type";
  2068. break;
  2069. }
  2070. // Shouldn't get here
  2071. return FALSE;
  2072. }
  2073. /******************************************************************************
  2074. * End of Function: put_IFD_Data_Type
  2075. ******************************************************************************/
  2076. /******************************************************************************
  2077. *
  2078. * Function: get_IFD_value_as_text
  2079. *
  2080. * Description: Decodes an IFD field value from a binary data string, using
  2081. * information supplied about the data type and byte alignment of
  2082. * the stored data.
  2083. * This function should be used for all datatypes except ASCII strings
  2084. *
  2085. * Parameters: input_data - a binary data string containing the IFD value,
  2086. * must be exact length of the value
  2087. * data_type - a number representing the IFD datatype as per the
  2088. * TIFF 6.0 specification:
  2089. * 1 = Unsigned 8-bit Byte
  2090. * 2 = ASCII String
  2091. * 3 = Unsigned 16-bit Short
  2092. * 4 = Unsigned 32-bit Long
  2093. * 5 = Unsigned 2x32-bit Rational
  2094. * 6 = Signed 8-bit Byte
  2095. * 7 = Undefined
  2096. * 8 = Signed 16-bit Short
  2097. * 9 = Signed 32-bit Long
  2098. * 10 = Signed 2x32-bit Rational
  2099. * 11 = 32-bit Float
  2100. * 12 = 64-bit Double
  2101. * Byte_Align - Indicates the byte alignment of the data.
  2102. * MM = Motorola, MSB first, Big Endian
  2103. * II = Intel, LSB first, Little Endian
  2104. *
  2105. * Returns: output - the value of the data (string or numeric)
  2106. *
  2107. ******************************************************************************/
  2108. function get_IFD_value_as_text( $Exif_Tag )
  2109. {
  2110. // Create a string to receive the output text
  2111. $output_str = "";
  2112. // Select Processing method according to the datatype
  2113. switch ($Exif_Tag['Data Type'])
  2114. {
  2115. case 1 : // Unsigned Byte
  2116. case 3 : // Unsigned Short
  2117. case 4 : // Unsigned Long
  2118. case 6 : // Signed Byte
  2119. case 8 : // Signed Short
  2120. case 9 : // Signed Long
  2121. // Cycle through each of the values for this tag
  2122. foreach ( $Exif_Tag['Data'] as $val )
  2123. {
  2124. // Check that this isn't the first value,
  2125. if ( $output_str != "" )
  2126. {
  2127. // This isn't the first value, Add a Comma and Newline to the output
  2128. $output_str .= ",\n";
  2129. }
  2130. // Add the Value to the output
  2131. $output_str .= $val;
  2132. }
  2133. break;
  2134. case 2 : // ASCII
  2135. // Append all the strings together, separated by Newlines
  2136. $output_str .= implode ( "\n", $Exif_Tag['Data']);
  2137. break;
  2138. case 5 : // Unsigned Rational
  2139. case 10: // Signed Rational
  2140. // Cycle through each of the values for this tag
  2141. foreach ( $Exif_Tag['Data'] as $val )
  2142. {
  2143. // Check that this isn't the first value,
  2144. if ( $output_str != "" )
  2145. {
  2146. // This isn't the first value, Add a Comma and Newline to the output
  2147. $output_str .= ",\n";
  2148. }
  2149. // Add the Full Value to the output
  2150. $output_str .= $val['Numerator'] ."/" . $val['Denominator'];
  2151. // Check if division by zero might be a problem
  2152. if ( $val['Denominator'] != 0 )
  2153. {
  2154. // Denominator is not zero, Add the Decimal Value to the output text
  2155. $output_str .= " (" . ($val['Numerator'] / $val['Denominator']) . ")";
  2156. }
  2157. }
  2158. break;
  2159. case 11: // Float
  2160. case 12: // Double
  2161. // TODO - EXIF - IFD datatype Double and Float not implemented yet
  2162. $output_str .= "Float and Double not implemented yet";
  2163. break;
  2164. case 7 : // Undefined
  2165. // Unless the User has asked to see the raw binary data, this
  2166. // type should not be displayed
  2167. // Check if the user has requested to see the binary data in hex
  2168. if ( $GLOBALS['SHOW_BINARY_DATA_HEX'] == TRUE)
  2169. {
  2170. // User has requested to see the binary data in hex
  2171. // Add the value in hex
  2172. $output_str .= "( " . strlen( $Exif_Tag['Data'] ) . " bytes of binary data ): " . bin2hex( $Exif_Tag['Data'] ) ;
  2173. }
  2174. // Check if the user has requested to see the binary data as is
  2175. else if ( $GLOBALS['SHOW_BINARY_DATA_TEXT'] == TRUE)
  2176. {
  2177. // User has requested to see the binary data as is
  2178. // Add the value as is
  2179. $output_str .= "( " . strlen( $Exif_Tag['Data'] ) . " bytes of binary data ): " . $Exif_Tag['Data'] ;
  2180. }
  2181. else
  2182. {
  2183. // User has NOT requested to see binary data,
  2184. // Add a message indicating the number of bytes to the output
  2185. $output_str .= "( " . strlen( $Exif_Tag['Data'] ) . " bytes of binary data ) " ;
  2186. }
  2187. break;
  2188. default :
  2189. // Error - Unknown IFD datatype
  2190. $output_str .= "Error - Exif tag data type (" . $Exif_Tag['Data Type'] .") is invalid";
  2191. break;
  2192. }
  2193. // Return the resulting text string
  2194. return $output_str;
  2195. }
  2196. /******************************************************************************
  2197. * End of Function: get_IFD_value_as_text
  2198. ******************************************************************************/
  2199. /******************************************************************************
  2200. * Global Variable: IFD_Data_Sizes
  2201. *
  2202. * Contents: The sizes (in bytes) of each EXIF IFD Datatype, indexed by
  2203. * their datatype number
  2204. *
  2205. ******************************************************************************/
  2206. $GLOBALS['IFD_Data_Sizes'] = array( 1 => 1, // Unsigned Byte
  2207. 2 => 1, // ASCII String
  2208. 3 => 2, // Unsigned Short
  2209. 4 => 4, // Unsigned Long
  2210. 5 => 8, // Unsigned Rational
  2211. 6 => 1, // Signed Byte
  2212. 7 => 1, // Undefined
  2213. 8 => 2, // Signed Short
  2214. 9 => 4, // Signed Long
  2215. 10 => 8, // Signed Rational
  2216. 11 => 4, // Float
  2217. 12 => 8 ); // Double
  2218. /******************************************************************************
  2219. * End of Global Variable: IFD_Data_Sizes
  2220. ******************************************************************************/
  2221. ?>