PageRenderTime 55ms CodeModel.GetById 21ms 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

Large files files are truncated, but you can click here to view the full file

  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 (FA

Large files files are truncated, but you can click here to view the full file