/add-ons/pjmt/EXIF.php
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
- <?
-
- /******************************************************************************
- *
- * Filename: EXIF.php
- *
- * Description: Provides functions for reading and writing EXIF Information
- * to/from an APP1 segment of a JPEG file
- * Unfortunately, because EXIF data may be distributed anywhere
- * throughout an image file, rather than just being in one block,
- * it is impossible to pass just a string containing only the EXIF
- * information. Hence it is neccessary to be able to seek to
- * any point in the file. This causes the HTTP and FTP wrappers
- * not to work - i.e. the EXIF functions will only work with local
- * files.
- * To work on an internet file, copy it locally to start with:
- *
- * $newfilename = tempnam ( $dir, "tmpexif" );
- * copy ( "http://whatever.com", $newfilename );
- *
- *
- * Author: Evan Hunter
- *
- * Date: 30/7/2004
- *
- * Project: PHP JPEG Metadata Toolkit
- *
- * Revision: 1.11
- *
- * Changes: 1.00 -> 1.10 : added function get_EXIF_TIFF to allow extracting EXIF from a TIFF file
- * 1.10 -> 1.11 : added functionality to allow decoding of XMP and Photoshop IRB information
- * embedded within the EXIF data
- * added checks for http and ftp wrappers, as these are not supported
- * changed interpret_IFD to allow thumbnail links to work when
- * toolkit is portable across directories
- *
- *
- * URL: http://electronics.ozhiker.com
- *
- * Copyright: Copyright Evan Hunter 2004
- *
- * License: This file is part of the PHP JPEG Metadata Toolkit.
- *
- * The PHP JPEG Metadata Toolkit is free software; you can
- * redistribute it and/or modify it under the terms of the
- * GNU General Public License as published by the Free Software
- * Foundation; either version 2 of the License, or (at your
- * option) any later version.
- *
- * The PHP JPEG Metadata Toolkit is distributed in the hope
- * that it will be useful, but WITHOUT ANY WARRANTY; without
- * even the implied warranty of MERCHANTABILITY or FITNESS
- * FOR A PARTICULAR PURPOSE. See the GNU General Public License
- * for more details.
- *
- * You should have received a copy of the GNU General Public
- * License along with the PHP JPEG Metadata Toolkit; if not,
- * write to the Free Software Foundation, Inc., 59 Temple
- * Place, Suite 330, Boston, MA 02111-1307 USA
- *
- * If you require a different license for commercial or other
- * purposes, please contact the author: evan@ozhiker.com
- *
- ******************************************************************************/
-
-
- // TODO : Thoroughly test the functions for writing EXIF segments
- // TODO : Figure out a way to allow EXIF to function normally with HTTP and FTP wrappers
- // TODO : Implement EXIF decoding of Device Setting Description field
- // TODO : Implement EXIF decoding of SpatialFrequencyResponse field
- // TODO : Implement EXIF decoding of OECF field
- // TODO : Implement EXIF decoding of SubjectArea field
- // TODO : Add a put_EXIF_TIFF function
-
- /******************************************************************************
- *
- * Initialisation
- *
- ******************************************************************************/
-
-
- if ( !isset( $GLOBALS['HIDE_UNKNOWN_TAGS'] ) ) $GLOBALS['HIDE_UNKNOWN_TAGS']= FALSE;
- if ( !isset( $GLOBALS['SHOW_BINARY_DATA_HEX'] ) ) $GLOBALS['SHOW_BINARY_DATA_HEX'] = FALSE;
- if ( !isset( $GLOBALS['SHOW_BINARY_DATA_TEXT'] ) ) $GLOBALS['SHOW_BINARY_DATA_TEXT'] = FALSE;
-
-
- include_once 'EXIF_Tags.php';
- include_once 'EXIF_Makernote.php';
- include_once 'PIM.php';
- include_once 'Unicode.php';
- include_once 'JPEG.php';
- include_once 'IPTC.php';
- include_once 'Photoshop_IRB.php'; // Change: as of version 1.11 - Required for TIFF with embedded IRB
- include_once 'XMP.php'; // Change: as of version 1.11 - Required for TIFF with embedded XMP
- include_once 'pjmt_utils.php'; // Change: as of version 1.11 - Required for directory portability
-
-
-
-
-
-
-
-
- /******************************************************************************
- *
- * Function: get_EXIF_JPEG
- *
- * Description: Retrieves information from a Exchangeable Image File Format (EXIF)
- * APP1 segment and returns it in an array.
- *
- * Parameters: filename - the filename of the JPEG image to process
- *
- * Returns: OutputArray - Array of EXIF records
- * FALSE - If an error occured in decoding
- *
- ******************************************************************************/
-
- function get_EXIF_JPEG( $filename )
- {
- // Change: Added as of version 1.11
- // Check if a wrapper is being used - these are not currently supported (see notes at top of file)
- if ( ( stristr ( $filename, "http://" ) != FALSE ) || ( stristr ( $filename, "ftp://" ) != FALSE ) )
- {
- // A HTTP or FTP wrapper is being used - show a warning and abort
- echo "HTTP and FTP wrappers are currently not supported with EXIF - See EXIF functionality documentation - a local file must be specified<br>";
- echo "To work on an internet file, copy it locally to start with:<br><br>\n";
- echo "\$newfilename = tempnam ( \$dir, \"tmpexif\" );<br>\n";
- echo "copy ( \"http://whatever.com\", \$newfilename );<br><br>\n";
- return FALSE;
- }
-
- // get the JPEG headers
- $jpeg_header_data = get_jpeg_header_data( $filename );
-
-
- // Flag that an EXIF segment has not been found yet
- $EXIF_Location = -1;
-
- //Cycle through the header segments
- for( $i = 0; $i < count( $jpeg_header_data ); $i++ )
- {
- // If we find an APP1 header,
- if ( strcmp ( $jpeg_header_data[$i]['SegName'], "APP1" ) == 0 )
- {
- // And if it has the EXIF label,
- if ( ( strncmp ( $jpeg_header_data[$i]['SegData'], "Exif\x00\x00", 6) == 0 ) ||
- ( 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
- {
- // Save the location of the EXIF segment
- $EXIF_Location = $i;
- }
- }
-
- }
-
- // Check if an EXIF segment was found
- if ( $EXIF_Location == -1 )
- {
- // Couldn't find any EXIF block to decode
- return FALSE;
- }
-
- $filehnd = @fopen($filename, 'rb');
-
- // Check if the file opened successfully
- if ( ! $filehnd )
- {
- // Could't open the file - exit
- echo "<p>Could not open file $filename</p>\n";
- return FALSE;
- }
-
- fseek( $filehnd, $jpeg_header_data[$EXIF_Location]['SegDataStart'] + 6 );
-
- // Decode the Exif segment into an array and return it
- $exif_data = process_TIFF_Header( $filehnd, "TIFF" );
-
-
-
- // Close File
- fclose($filehnd);
- return $exif_data;
- }
-
- /******************************************************************************
- * End of Function: get_EXIF_JPEG
- ******************************************************************************/
-
-
-
- /******************************************************************************
- *
- * Function: put_EXIF_JPEG
- *
- * Description: Stores information into a Exchangeable Image File Format (EXIF)
- * APP1 segment from an EXIF array.
- *
- * WARNING: Because the EXIF standard allows pointers to data
- * outside the APP1 segment, if there are any such pointers in
- * a makernote, this function will DAMAGE them since it will not
- * be aware that there is an external pointer. This will often
- * happen with Makernotes that include an embedded thumbnail.
- * This damage could be prevented where makernotes can be decoded,
- * but currently this is not implemented.
- *
- *
- * Parameters: exif_data - The array of EXIF data to insert into the JPEG header
- * jpeg_header_data - The JPEG header into which the EXIF data
- * should be stored, as from get_jpeg_header_data
- *
- * Returns: jpeg_header_data - JPEG header array with the EXIF segment inserted
- * FALSE - If an error occured
- *
- ******************************************************************************/
-
- function put_EXIF_JPEG( $exif_data, $jpeg_header_data )
- {
- // pack the EXIF data into its proper format for a JPEG file
- $packed_data = get_TIFF_Packed_Data( $exif_data );
- if ( $packed_data === FALSE )
- {
- return $jpeg_header_data;
- }
-
- $packed_data = "Exif\x00\x00$packed_data";
-
- //Cycle through the header segments
- for( $i = 0; $i < count( $jpeg_header_data ); $i++ )
- {
- // If we find an APP1 header,
- if ( strcmp ( $jpeg_header_data[$i]['SegName'], "APP1" ) == 0 )
- {
- // And if it has the EXIF label,
- if ( ( strncmp ( $jpeg_header_data[$i]['SegData'], "Exif\x00\x00", 6) == 0 ) ||
- ( 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
- {
- // Found a preexisting EXIF block - Replace it with the new one and return.
- $jpeg_header_data[$i]['SegData'] = $packed_data;
- return $jpeg_header_data;
- }
- }
- }
-
- // No preexisting segment segment found, insert a new one at the start of the header data.
-
- // Determine highest position of an APP segment at or below APP3, so we can put the
- // new APP3 at this position
-
-
- $highest_APP = -1;
-
- //Cycle through the header segments
- for( $i = 0; $i < count( $jpeg_header_data ); $i++ )
- {
- // Check if we have found an APP segment at or below APP3,
- if ( ( $jpeg_header_data[$i]['SegType'] >= 0xE0 ) && ( $jpeg_header_data[$i]['SegType'] <= 0xE3 ) )
- {
- // Found an APP segment at or below APP12
- $highest_APP = $i;
- }
- }
-
- // No preexisting EXIF block found, insert a new one at the start of the header data.
- array_splice($jpeg_header_data, $highest_APP + 1 , 0, array( array( "SegType" => 0xE1,
- "SegName" => "APP1",
- "SegDesc" => $GLOBALS[ "JPEG_Segment_Descriptions" ][ 0xE1 ],
- "SegData" => $packed_data ) ) );
- return $jpeg_header_data;
-
- }
-
- /******************************************************************************
- * End of Function: put_EXIF_JPEG
- ******************************************************************************/
-
-
-
-
- /******************************************************************************
- *
- * Function: get_Meta_JPEG
- *
- * Description: Retrieves information from a Meta APP3 segment and returns it
- * in an array. Uses information supplied by the
- * get_jpeg_header_data function.
- * The Meta segment has the same format as an EXIF segment, but
- * uses different tags
- *
- * Parameters: filename - the filename of the JPEG image to process
- *
- * Returns: OutputArray - Array of Meta records
- * FALSE - If an error occured in decoding
- *
- ******************************************************************************/
-
- function get_Meta_JPEG( $filename )
- {
- // Change: Added as of version 1.11
- // Check if a wrapper is being used - these are not currently supported (see notes at top of file)
- if ( ( stristr ( $filename, "http://" ) != FALSE ) || ( stristr ( $filename, "ftp://" ) != FALSE ) )
- {
- // A HTTP or FTP wrapper is being used - show a warning and abort
- echo "HTTP and FTP wrappers are currently not supported with Meta - See EXIF/Meta functionality documentation - a local file must be specified<br>";
- echo "To work on an internet file, copy it locally to start with:<br><br>\n";
- echo "\$newfilename = tempnam ( \$dir, \"tmpmeta\" );<br>\n";
- echo "copy ( \"http://whatever.com\", \$newfilename );<br><br>\n";
- return FALSE;
- }
-
- // get the JPEG headers
- $jpeg_header_data = get_jpeg_header_data( $filename );
-
-
- // Flag that an Meta segment has not been found yet
- $Meta_Location = -1;
-
- //Cycle through the header segments
- for( $i = 0; $i < count( $jpeg_header_data ); $i++ )
- {
- // If we find an APP3 header,
- if ( strcmp ( $jpeg_header_data[$i]['SegName'], "APP3" ) == 0 )
- {
- // And if it has the Meta label,
- if ( ( strncmp ( $jpeg_header_data[$i]['SegData'], "Meta\x00\x00", 6) == 0 ) ||
- ( strncmp ( $jpeg_header_data[$i]['SegData'], "META\x00\x00", 6) == 0 ) )
- {
- // Save the location of the Meta segment
- $Meta_Location = $i;
- }
- }
- }
-
- // Check if an EXIF segment was found
- if ( $Meta_Location == -1 )
- {
- // Couldn't find any Meta block to decode
- return FALSE;
- }
-
-
- $filehnd = @fopen($filename, 'rb');
-
- // Check if the file opened successfully
- if ( ! $filehnd )
- {
- // Could't open the file - exit
- echo "<p>Could not open file $filename</p>\n";
- return FALSE;
- }
-
- fseek( $filehnd, $jpeg_header_data[$Meta_Location]['SegDataStart'] + 6 );
-
- // Decode the Meta segment into an array and return it
- $meta = process_TIFF_Header( $filehnd, "Meta" );
-
- // Close File
- fclose($filehnd);
-
- return $meta;
- }
-
- /******************************************************************************
- * End of Function: get_Meta
- ******************************************************************************/
-
-
-
-
-
-
-
- /******************************************************************************
- *
- * Function: put_Meta_JPEG
- *
- * Description: Stores information into a Meta APP3 segment from a Meta array.
- *
- *
- * WARNING: Because the Meta (EXIF) standard allows pointers to data
- * outside the APP1 segment, if there are any such pointers in
- * a makernote, this function will DAMAGE them since it will not
- * be aware that there is an external pointer. This will often
- * happen with Makernotes that include an embedded thumbnail.
- * This damage could be prevented where makernotes can be decoded,
- * but currently this is not implemented.
- *
- *
- * Parameters: meta_data - The array of Meta data to insert into the JPEG header
- * jpeg_header_data - The JPEG header into which the Meta data
- * should be stored, as from get_jpeg_header_data
- *
- * Returns: jpeg_header_data - JPEG header array with the Meta segment inserted
- * FALSE - If an error occured
- *
- ******************************************************************************/
-
- function put_Meta_JPEG( $meta_data, $jpeg_header_data )
- {
- // pack the Meta data into its proper format for a JPEG file
- $packed_data = get_TIFF_Packed_Data( $meta_data );
- if ( $packed_data === FALSE )
- {
- return $jpeg_header_data;
- }
-
- $packed_data = "Meta\x00\x00$packed_data";
-
- //Cycle through the header segments
- for( $i = 0; $i < count( $jpeg_header_data ); $i++ )
- {
- // If we find an APP1 header,
- if ( strcmp ( $jpeg_header_data[$i]['SegName'], "APP3" ) == 0 )
- {
- // And if it has the Meta label,
- if ( ( strncmp ( $jpeg_header_data[$i]['SegData'], "Meta\x00\x00", 6) == 0 ) ||
- ( strncmp ( $jpeg_header_data[$i]['SegData'], "META\x00\x00", 6) == 0 ) )
- {
- // Found a preexisting Meta block - Replace it with the new one and return.
- $jpeg_header_data[$i]['SegData'] = $packed_data;
- return $jpeg_header_data;
- }
- }
- }
- // No preexisting segment segment found, insert a new one at the start of the header data.
-
- // Determine highest position of an APP segment at or below APP3, so we can put the
- // new APP3 at this position
-
-
- $highest_APP = -1;
-
- //Cycle through the header segments
- for( $i = 0; $i < count( $jpeg_header_data ); $i++ )
- {
- // Check if we have found an APP segment at or below APP3,
- if ( ( $jpeg_header_data[$i]['SegType'] >= 0xE0 ) && ( $jpeg_header_data[$i]['SegType'] <= 0xE3 ) )
- {
- // Found an APP segment at or below APP12
- $highest_APP = $i;
- }
- }
-
- // No preexisting Meta block found, insert a new one at the start of the header data.
- array_splice($jpeg_header_data, $highest_APP + 1 , 0, array( array( "SegType" => 0xE3,
- "SegName" => "APP3",
- "SegDesc" => $GLOBALS[ "JPEG_Segment_Descriptions" ][ 0xE1 ],
- "SegData" => $packed_data ) ) );
- return $jpeg_header_data;
-
- }
-
- /******************************************************************************
- * End of Function: put_Meta_JPEG
- ******************************************************************************/
-
-
-
- /******************************************************************************
- *
- * Function: get_EXIF_TIFF
- *
- * Description: Retrieves information from a Exchangeable Image File Format (EXIF)
- * within a TIFF file and returns it in an array.
- *
- * Parameters: filename - the filename of the TIFF image to process
- *
- * Returns: OutputArray - Array of EXIF records
- * FALSE - If an error occured in decoding
- *
- ******************************************************************************/
-
- function get_EXIF_TIFF( $filename )
- {
- // Change: Added as of version 1.11
- // Check if a wrapper is being used - these are not currently supported (see notes at top of file)
- if ( ( stristr ( $filename, "http://" ) != FALSE ) || ( stristr ( $filename, "ftp://" ) != FALSE ) )
- {
- // A HTTP or FTP wrapper is being used - show a warning and abort
- echo "HTTP and FTP wrappers are currently not supported with TIFF - See EXIF/TIFF functionality documentation - a local file must be specified<br>";
- echo "To work on an internet file, copy it locally to start with:<br><br>\n";
- echo "\$newfilename = tempnam ( \$dir, \"tmptiff\" );<br>\n";
- echo "copy ( \"http://whatever.com\", \$newfilename );<br><br>\n";
- return FALSE;
- }
-
-
- $filehnd = @fopen($filename, 'rb');
-
- // Check if the file opened successfully
- if ( ! $filehnd )
- {
- // Could't open the file - exit
- echo "<p>Could not open file $filename</p>\n";
- return FALSE;
- }
-
- // Decode the Exif segment into an array and return it
- $exif_data = process_TIFF_Header( $filehnd, "TIFF" );
-
- // Close File
- fclose($filehnd);
- return $exif_data;
- }
-
- /******************************************************************************
- * End of Function: get_EXIF_TIFF
- ******************************************************************************/
-
-
-
-
- /******************************************************************************
- *
- * Function: Interpret_EXIF_to_HTML
- *
- * Description: Generates html detailing the contents an APP1 EXIF array
- * which was retrieved with a get_EXIF_.... function.
- * Can also be used for APP3 Meta arrays.
- *
- * Parameters: Exif_array - the EXIF array,as read from get_EXIF_....
- * filename - the name of the Image file being processed ( used
- * by scripts which displays EXIF thumbnails)
- *
- * Returns: output_str - A string containing the HTML
- *
- ******************************************************************************/
-
- function Interpret_EXIF_to_HTML( $Exif_array, $filename )
- {
- // Create the string to receive the html output
- $output_str = "";
-
- // Check if the array to process is valid
- if ( $Exif_array === FALSE )
- {
- // Exif Array is not valid - abort processing
- return $output_str;
- }
-
- // Ouput the heading according to what type of tags were used in processing
- if ( $Exif_array[ 'Tags Name' ] == "TIFF" )
- {
- $output_str .= "<h2 class=\"EXIF_Main_Heading\">Contains Exchangeable Image File Format (EXIF) Information</h2>\n";
- }
- else if ( $Exif_array[ 'Tags Name' ] == "Meta" )
- {
- $output_str .= "<h2 class=\"EXIF_Main_Heading\">Contains META Information (APP3)</h2>\n";
- }
- else
- {
- $output_str .= "<h2 class=\"EXIF_Main_Heading\">Contains " . $Exif_array[ 'Tags Name' ] . " Information</h2>\n";
- }
-
-
- // Check that there are actually items to process in the array
- if ( count( $Exif_array ) < 1 )
- {
- // No items to process in array - abort processing
- return $output_str;
- }
-
- // Output secondary heading
- $output_str .= "<h3 class=\"EXIF_Secondary_Heading\">Main Image Information</h2>\n";
-
- // Interpret the zeroth IFD to html
- $output_str .= interpret_IFD( $Exif_array[0], $filename, $Exif_array['Byte_Align'] );
-
- // Check if there is a first IFD to process
- if ( array_key_exists( 1, $Exif_array ) )
- {
- // There is a first IFD for a thumbnail
- // Add a heading for it to the output
- $output_str .= "<h3 class=\"EXIF_Secondary_Heading\">Thumbnail Information</h2>\n";
-
- // Interpret the IFD to html and add it to the output
- $output_str .= interpret_IFD( $Exif_array[1], $filename, $Exif_array['Byte_Align'] );
- }
-
- // Cycle through any other IFD's
- $i = 2;
- while ( array_key_exists( $i, $Exif_array ) )
- {
- // Add a heading for the IFD
- $output_str .= "<h3 class=\"EXIF_Secondary_Heading\">Image File Directory (IFD) $i Information</h2>\n";
-
- // Interpret the IFD to html and add it to the output
- $output_str .= interpret_IFD( $Exif_array[$i], $filename, $Exif_array['Byte_Align'] );
- $i++;
- }
-
- // Return the resulting HTML
- return $output_str;
- }
-
- /******************************************************************************
- * End of Function: Interpret_EXIF_to_HTML
- ******************************************************************************/
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- /******************************************************************************
- *
- * INTERNAL FUNCTIONS
- *
- ******************************************************************************/
-
-
-
-
-
-
-
-
-
-
-
- /******************************************************************************
- *
- * Internal Function: get_TIFF_Packed_Data
- *
- * Description: Packs TIFF IFD data from EXIF or Meta into a form ready for
- * either a JPEG EXIF/Meta segment or a TIFF file
- * This function attempts to protect the contents of an EXIF makernote,
- * by ensuring that it remains in the same position relative to the
- * TIFF header
- *
- * Parameters: tiff_data - the EXIF array,as read from get_EXIF_JPEG or get_Meta_JPEG
- *
- * Returns: packed_data - A string containing packed segment
- *
- ******************************************************************************/
-
- function get_TIFF_Packed_Data( $tiff_data )
- {
- // Check that the segment is valid
- if ( $tiff_data === FALSE )
- {
- return FALSE;
- }
-
- // Get the byte alignment
- $Byte_Align = $tiff_data['Byte_Align'];
-
- // Add the Byte Alignment to the Packed data
- $packed_data = $Byte_Align;
-
- // Add the TIFF ID to the Packed Data
- $packed_data .= put_IFD_Data_Type( 42, 3, $Byte_Align );
-
- // Create a string for the makernote
- $makernote = "";
-
- // Check if the makernote exists
- if ( $tiff_data[ 'Makernote_Tag' ] !== FALSE )
- {
- // A makernote exists - We need to ensure that it stays in the same position as it was
- // Put the Makernote before any of the IFD's by padding zeros to the correct offset
- $makernote .= str_repeat("\x00",( $tiff_data[ 'Makernote_Tag' ][ 'Offset' ] - 8 ) );
- $makernote .= $tiff_data[ 'Makernote_Tag' ]['Data'];
- }
-
- // Calculage where the zeroth ifd will be
- $ifd_offset = strlen( $makernote ) + 8;
-
- // Add the Zeroth IFD pointer to the packed data
- $packed_data .= put_IFD_Data_Type( $ifd_offset, 4, $Byte_Align );
-
- // Add the makernote to the packed data (if there was one)
- $packed_data .= $makernote;
-
- //Add the IFD's to the packed data
- $packed_data .= get_IFD_Array_Packed_Data( $tiff_data, $ifd_offset, $Byte_Align );
-
- // Return the result
- return $packed_data;
- }
-
- /******************************************************************************
- * End of Function: get_TIFF_Packed_Data
- ******************************************************************************/
-
-
-
-
- /******************************************************************************
- *
- * Internal Function: get_IFD_Array_Packed_Data
- *
- * Description: Packs a chain of IFD's from EXIF or Meta segments into a form
- * ready for either a JPEG EXIF/Meta segment or a TIFF file
- *
- * Parameters: ifd_data - the IFD chain array, as read from get_EXIF_JPEG or get_Meta_JPEG
- * Zero_IFD_offset - The offset to the first IFD from the start of the TIFF header
- * Byte_Align - the Byte alignment to use - "MM" or "II"
- *
- * Returns: packed_data - A string containing packed IFD's
- *
- ******************************************************************************/
-
- function get_IFD_Array_Packed_Data( $ifd_data, $Zero_IFD_offset, $Byte_Align )
- {
- // Create a string to receive the packed output
- $packed_data = "";
-
- // Count the IFDs
- $ifd_count = 0;
- foreach( $ifd_data as $key => $IFD )
- {
- // Make sure we only count the IFD's, not other information keys
- if ( is_numeric( $key ) )
- {
- $ifd_count++;
- }
- }
-
-
- // Cycle through each IFD,
- for ( $ifdno = 0; $ifdno < $ifd_count; $ifdno++ )
- {
- // Check if this IFD is the last one
- if ( $ifdno == $ifd_count - 1 )
- {
- // This IFD is the last one, get it's packed data
- $packed_data .= get_IFD_Packed_Data( $ifd_data[ $ifdno ], $Zero_IFD_offset +strlen($packed_data), $Byte_Align, FALSE );
- }
- else
- {
- // This IFD is NOT the last one, get it's packed data
- $packed_data .= get_IFD_Packed_Data( $ifd_data[ $ifdno ], $Zero_IFD_offset +strlen($packed_data), $Byte_Align, TRUE );
- }
-
- }
-
- // Return the packed output
- return $packed_data;
- }
-
- /******************************************************************************
- * End of Function: get_IFD_Array_Packed_Data
- ******************************************************************************/
-
-
-
- /******************************************************************************
- *
- * Internal Function: get_IFD_Packed_Data
- *
- * Description: Packs an IFD from EXIF or Meta segments into a form
- * ready for either a JPEG EXIF/Meta segment or a TIFF file
- *
- * Parameters: ifd_data - the IFD chain array, as read from get_EXIF_JPEG or get_Meta_JPEG
- * IFD_offset - The offset to the IFD from the start of the TIFF header
- * Byte_Align - the Byte alignment to use - "MM" or "II"
- * Another_IFD - boolean - false if this is the last IFD in the chain
- * - true if it is not the last
- *
- * Returns: packed_data - A string containing packed IFD's
- *
- ******************************************************************************/
-
- function get_IFD_Packed_Data( $ifd_data, $IFD_offset, $Byte_Align, $Another_IFD )
- {
-
- $ifd_body_str = "";
- $ifd_data_str = "";
-
- $Tag_Definitions_Name = $ifd_data[ 'Tags Name' ];
-
-
- // Count the Tags in this IFD
- $tag_count = 0;
- foreach( $ifd_data as $key => $tag )
- {
- // Make sure we only count the Tags, not other information keys
- if ( is_numeric( $key ) )
- {
- $tag_count++;
- }
- }
-
- // Add the Tag count to the packed data
- $packed_data = put_IFD_Data_Type( $tag_count, 3, $Byte_Align );
-
- // Calculate the total length of the IFD (without the offset data)
- $IFD_len = 2 + $tag_count * 12 + 4;
-
-
- // Cycle through each tag
- foreach( $ifd_data as $key => $tag )
- {
- // Make sure this is a tag, not another information key
- if ( is_numeric( $key ) )
- {
-
- // Add the tag number to the packed data
- $ifd_body_str .= put_IFD_Data_Type( $tag[ 'Tag Number' ], 3, $Byte_Align );
-
- // Add the Data type to the packed data
- $ifd_body_str .= put_IFD_Data_Type( $tag['Data Type'], 3, $Byte_Align );
-
- // Check if this is a Print Image Matching entry
- if ( $tag['Type'] == "PIM" )
- {
- // This is a Print Image Matching entry,
- // encode it
- $data = Encode_PIM( $tag, $Byte_Align );
- }
- // Check if this is a IPTC/NAA Record within the EXIF IFD
- else if ( ( ( $Tag_Definitions_Name == "EXIF" ) || ( $Tag_Definitions_Name == "TIFF" ) ) &&
- ( $tag[ 'Tag Number' ] == 33723 ) )
- {
- // This is a IPTC/NAA Record, encode it
- $data = put_IPTC( $tag['Data'] );
- }
- // Change: Check for embedded XMP as of version 1.11
- // Check if this is a XMP Record within the EXIF IFD
- else if ( ( ( $Tag_Definitions_Name == "EXIF" ) || ( $Tag_Definitions_Name == "TIFF" ) ) &&
- ( $tag[ 'Tag Number' ] == 700 ) )
- {
- // This is a XMP Record, encode it
- $data = write_XMP_array_to_text( $tag['Data'] );
- }
- // Change: Check for embedded IRB as of version 1.11
- // Check if this is a Photoshop IRB Record within the EXIF IFD
- else if ( ( ( $Tag_Definitions_Name == "EXIF" ) || ( $Tag_Definitions_Name == "TIFF" ) ) &&
- ( $tag[ 'Tag Number' ] == 34377 ) )
- {
- // This is a Photoshop IRB Record, encode it
- $data = pack_Photoshop_IRB_Data( $tag['Data'] );
- }
- // Exif Thumbnail Offset
- else if ( ( $tag[ 'Tag Number' ] == 513 ) && ( $Tag_Definitions_Name == "TIFF" ) )
- {
- // The Exif Thumbnail Offset is a pointer but of type Long, not Unknown
- // Hence we need to put the data into the packed string separately
- // Calculate the thumbnail offset
- $data_offset = $IFD_offset + $IFD_len + strlen($ifd_data_str);
-
- // Create the Offset for the IFD
- $data = put_IFD_Data_Type( $data_offset, 4, $Byte_Align );
-
- // Store the thumbnail
- $ifd_data_str .= $tag['Data'];
- }
- // Exif Thumbnail Length
- else if ( ( $tag[ 'Tag Number' ] == 514 ) && ( $Tag_Definitions_Name == "TIFF" ) )
- {
- // Encode the Thumbnail Length
- $data = put_IFD_Data_Type( strlen($ifd_data[513]['Data']), 4, $Byte_Align );
- }
- // Sub-IFD
- else if ( $tag['Type'] == "SubIFD" )
- {
- // This is a Sub-IFD
- // Calculate the offset to the start of the Sub-IFD
- $data_offset = $IFD_offset + $IFD_len + strlen($ifd_data_str);
- // Get the packed data for the IFD chain as the data for this tag
- $data = get_IFD_Array_Packed_Data( $tag['Data'], $data_offset, $Byte_Align );
- }
- else
- {
- // Not a special tag
-
- // Create a string to receive the data
- $data = "";
-
- // Check if this is a type Unknown tag
- if ( $tag['Data Type'] != 7 )
- {
- // NOT type Unknown
- // Cycle through each data value and add it to the data string
- foreach( $tag[ 'Data' ] as $data_val )
- {
- $data .= put_IFD_Data_Type( $data_val, $tag['Data Type'], $Byte_Align );
- }
- }
- else
- {
- // This is a type Unknown - just add the data as is to the data string
- $data .= $tag[ 'Data' ];
- }
- }
-
- // Pad the data string out to at least 4 bytes
- $data = str_pad ( $data, 4, "\x00" );
-
-
- // Check if the data type is an ASCII String or type Unknown
- if ( ( $tag['Data Type'] == 2 ) || ( $tag['Data Type'] == 7 ) )
- {
- // This is an ASCII String or type Unknown
- // Add the Length of the string to the packed data as the Count
- $ifd_body_str .= put_IFD_Data_Type( strlen($data), 4, $Byte_Align );
- }
- else
- {
- // Add the array count to the packed data as the Count
- $ifd_body_str .= put_IFD_Data_Type( count($tag[ 'Data' ]), 4, $Byte_Align );
- }
-
-
- // Check if the data is over 4 bytes long
- if ( strlen( $data ) > 4 )
- {
- // Data is longer than 4 bytes - it needs to be offset
- // Check if this entry is the Maker Note
- if ( ( $Tag_Definitions_Name == "EXIF" ) && ( $tag[ 'Tag Number' ] == 37500 ) )
- {
- // This is the makernote - It will have already been stored
- // at its original offset to help preserve it
- // all we need to do is add the Offset to the IFD packed data
- $data_offset = $tag[ 'Offset' ];
-
- $ifd_body_str .= put_IFD_Data_Type( $data_offset, 4, $Byte_Align );
- }
- else
- {
- // This is NOT the makernote
- // Calculate the data offset
- $data_offset = $IFD_offset + $IFD_len + strlen($ifd_data_str);
-
- // Add the offset to the IFD packed data
- $ifd_body_str .= put_IFD_Data_Type( $data_offset, 4, $Byte_Align );
-
- // Add the data to the offset packed data
- $ifd_data_str .= $data;
- }
- }
- else
- {
- // Data is less than or equal to 4 bytes - Add it to the packed IFD data as is
- $ifd_body_str .= $data;
- }
-
- }
- }
-
- // Assemble the IFD body onto the packed data
- $packed_data .= $ifd_body_str;
-
- // Check if there is another IFD after this one
- if( $Another_IFD === TRUE )
- {
- // There is another IFD after this
- // Calculate the Next-IFD offset so that it goes immediately after this IFD
- $next_ifd_offset = $IFD_offset + $IFD_len + strlen($ifd_data_str);
- }
- else
- {
- // There is NO IFD after this - indicate with offset=0
- $next_ifd_offset = 0;
- }
-
- // Add the Next-IFD offset to the packed data
- $packed_data .= put_IFD_Data_Type( $next_ifd_offset, 4, $Byte_Align );
-
- // Add the offset data to the packed data
- $packed_data .= $ifd_data_str;
-
- // Return the resulting packed data
- return $packed_data;
- }
-
- /******************************************************************************
- * End of Function: get_IFD_Packed_Data
- ******************************************************************************/
-
-
-
-
-
- /******************************************************************************
- *
- * Internal Function: process_TIFF_Header
- *
- * Description: Decodes the information stored in a TIFF header and it's
- * Image File Directories (IFD's). This information is returned
- * in an array
- *
- * Parameters: filehnd - The handle of a open image file, positioned at the
- * start of the TIFF header
- * Tag_Definitions_Name - The name of the Tag Definitions group
- * within the global array IFD_Tag_Definitions
- *
- *
- * Returns: OutputArray - Array of IFD records
- * FALSE - If an error occured in decoding
- *
- ******************************************************************************/
-
- function process_TIFF_Header( $filehnd, $Tag_Definitions_Name )
- {
-
-
- // Save the file position where the TIFF header starts, as offsets are relative to this position
- $Tiff_start_pos = ftell( $filehnd );
-
-
-
- // Read the eight bytes of the TIFF header
- $DataStr = network_safe_fread( $filehnd, 8 );
-
- // Check that we did get all eight bytes
- if ( strlen( $DataStr ) != 8 )
- {
- return FALSE; // Couldn't read the TIFF header properly
- }
-
- $pos = 0;
- // First two bytes indicate the byte alignment - should be 'II' or 'MM'
- // II = Intel (LSB first, MSB last - Little Endian)
- // MM = Motorola (MSB first, LSB last - Big Endian)
- $Byte_Align = substr( $DataStr, $pos, 2 );
-
-
-
- // Check the Byte Align Characters for validity
- if ( ( $Byte_Align != "II" ) && ( $Byte_Align != "MM" ) )
- {
- // Byte align field is invalid - we won't be able to decode file
- return FALSE;
- }
-
- // Skip over the Byte Align field which was just read
- $pos += 2;
-
- // Next two bytes are TIFF ID - should be value 42 with the appropriate byte alignment
- $TIFF_ID = substr( $DataStr, $pos, 2 );
-
- if ( get_IFD_Data_Type( $TIFF_ID, 3, $Byte_Align ) != 42 )
- {
- // TIFF header ID not found
- return FALSE;
- }
-
- // Skip over the TIFF ID field which was just read
- $pos += 2;
-
-
- // Next four bytes are the offset to the first IFD
- $offset_str = substr( $DataStr, $pos, 4 );
- $offset = get_IFD_Data_Type( $offset_str, 4, $Byte_Align );
-
- // Done reading TIFF Header
-
-
- // Move to first IFD
-
- if ( fseek( $filehnd, $Tiff_start_pos + $offset ) !== 0 )
- {
- // Error seeking to position of first IFD
- return FALSE;
- }
-
-
-
- // Flag that a makernote has not been found yet
- $GLOBALS[ "Maker_Note_Tag" ] = FALSE;
-
- // Read the IFD chain into an array
- $Output_Array = read_Multiple_IFDs( $filehnd, $Tiff_start_pos, $Byte_Align, $Tag_Definitions_Name );
-
- // Check if a makernote was found
- if ( $GLOBALS[ "Maker_Note_Tag" ] != FALSE )
- {
- // Makernote was found - Process it
- // The makernote needs to be processed after all other
- // tags as it may require some of the other tags in order
- // to be processed properly
- $GLOBALS[ "Maker_Note_Tag" ] = Read_Makernote_Tag( $GLOBALS[ "Maker_Note_Tag" ], $Output_Array, $filehnd );
-
- }
-
- $Output_Array[ 'Makernote_Tag' ] = $GLOBALS[ "Maker_Note_Tag" ];
-
- // Save the Name of the Tags used in the output array
- $Output_Array[ 'Tags Name' ] = $Tag_Definitions_Name;
-
-
-
- // Save the Byte alignment
- $Output_Array['Byte_Align'] = $Byte_Align;
-
-
- // Return the output array
- return $Output_Array ;
- }
-
- /******************************************************************************
- * End of Function: process_TIFF_Header
- ******************************************************************************/
-
-
-
-
-
-
- /******************************************************************************
- *
- * Internal Function: read_Multiple_IFDs
- *
- * Description: Reads and interprets a chain of standard Image File Directories (IFD's),
- * and returns the entries in an array. This chain is made up from IFD's
- * which have a pointer to the next IFD. IFD's are read until the next
- * pointer indicates there are no more
- *
- * Parameters: filehnd - a handle for the image file being read, positioned at the
- * start of the IFD chain
- * Tiff_offset - The offset of the TIFF header from the start of the file
- * Byte_Align - either "MM" or "II" indicating Motorola or Intel Byte alignment
- * Tag_Definitions_Name - The name of the Tag Definitions group within the global array IFD_Tag_Definitions
- * local_offsets - True indicates that offset data should be interpreted as being relative to the start of the currrent entry
- * False (normal) indicates offests are relative to start of Tiff header as per IFD standard
- * read_next_ptr - True (normal) indicates that a pointer to the next IFD should be read at the end of the IFD
- * False indicates that no pointer follows the IFD
- *
- *
- * Returns: OutputArray - Array of IFD entries
- *
- ******************************************************************************/
-
- function read_Multiple_IFDs( $filehnd, $Tiff_offset, $Byte_Align, $Tag_Definitions_Name, $local_offsets = FALSE, $read_next_ptr = TRUE )
- {
- // Start at the offset of the first IFD
- $Next_Offset = 0;
-
- do
- {
- // Read an IFD
- list($IFD_Array , $Next_Offset) = read_IFD_universal( $filehnd, $Tiff_offset, $Byte_Align, $Tag_Definitions_Name, $local_offsets, $read_next_ptr );
-
- // Move to the position of the next IFD
- if ( fseek( $filehnd, $Tiff_offset + $Next_Offset ) !== 0 )
- {
- // Error seeking to position of next IFD
- echo "<p>Error: Corrupted EXIF</p>\n";
- return FALSE;
- }
-
- $Output_Array[] = $IFD_Array;
-
-
- } while ( $Next_Offset != 0 ); // Until the Next IFD Offset is zero
-
-
- // return resulting array
-
- return $Output_Array ;
- }
-
- /******************************************************************************
- * End of Function: read_Multiple_IFDs
- ******************************************************************************/
-
-
-
-
-
-
-
- /******************************************************************************
- *
- * Internal Function: read_IFD_universal
- *
- * Description: Reads and interprets a standard or Non-standard Image File
- * Directory (IFD), and returns the entries in an array
- *
- * Parameters: filehnd - a handle for the image file being read, positioned at the start
- * of the IFD
- * Tiff_offset - The offset of the TIFF header from the start of the file
- * Byte_Align - either "MM" or "II" indicating Motorola or Intel Byte alignment
- * Tag_Definitions_Name - The name of the Tag Definitions group within the global array IFD_Tag_Definitions
- * local_offsets - True indicates that offset data should be interpreted as being relative to the start of the currrent entry
- * False (normal) indicates offests are relative to start of Tiff header as per IFD standard
- * read_next_ptr - True (normal) indicates that a pointer to the next IFD should be read at the end of the IFD
- * False indicates that no pointer follows the IFD
- *
- * Returns: OutputArray - Array of IFD entries
- * Next_Offset - Offset to next IFD (zero = no next IFD)
- *
- ******************************************************************************/
-
- function read_IFD_universal( $filehnd, $Tiff_offset, $Byte_Align, $Tag_Definitions_Name, $local_offsets = FALSE, $read_next_ptr = TRUE )
- {
- if ( ( $filehnd == NULL ) || ( feof( $filehnd ) ) )
- {
- return array (FA…
Large files files are truncated, but you can click here to view the full file