1.10 : changed put_jpeg_header_data to check if the data * being written exists * * 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 * ******************************************************************************/ /****************************************************************************** * * Function: get_jpeg_header_data * * Description: Reads all the JPEG header segments from an JPEG image file into an * array * * Parameters: filename - the filename of the file to JPEG file to read * * Returns: headerdata - Array of JPEG header segments * FALSE - if headers could not be read * ******************************************************************************/ function get_jpeg_header_data( $filename ) { // prevent refresh from aborting file operations and hosing file ignore_user_abort(true); // Attempt to open the jpeg file - the at symbol supresses the error message about // not being able to open files. The file_exists would have been used, but it // does not work with files fetched over http or ftp. $filehnd = @fopen($filename, 'rb'); // Check if the file opened successfully if ( ! $filehnd ) { // Could't open the file - exit echo "

Could not open file $filename

\n"; return FALSE; } // Read the first two characters $data = network_safe_fread( $filehnd, 2 ); // Check that the first two characters are 0xFF 0xDA (SOI - Start of image) if ( $data != "\xFF\xD8" ) { // No SOI (FF D8) at start of file - This probably isn't a JPEG file - close file and return; echo "

This probably is not a JPEG file

\n"; fclose($filehnd); return FALSE; } // Read the third character $data = network_safe_fread( $filehnd, 2 ); // Check that the third character is 0xFF (Start of first segment header) if ( $data[0] != "\xFF" ) { // NO FF found - close file and return - JPEG is probably corrupted fclose($filehnd); return FALSE; } // Flag that we havent yet hit the compressed image data $hit_compressed_image_data = FALSE; // Cycle through the file until, one of: 1) an EOI (End of image) marker is hit, // 2) we have hit the compressed image data (no more headers are allowed after data) // 3) or end of file is hit while ( ( $data[1] != "\xD9" ) && (! $hit_compressed_image_data) && ( ! feof( $filehnd ) )) { // Found a segment to look at. // Check that the segment marker is not a Restart marker - restart markers don't have size or data after them if ( ( ord($data[1]) < 0xD0 ) || ( ord($data[1]) > 0xD7 ) ) { // Segment isn't a Restart marker // Read the next two bytes (size) $sizestr = network_safe_fread( $filehnd, 2 ); // convert the size bytes to an integer $decodedsize = unpack ("nsize", $sizestr); // Save the start position of the data $segdatastart = ftell( $filehnd ); // Read the segment data with length indicated by the previously read size $segdata = network_safe_fread( $filehnd, $decodedsize['size'] - 2 ); // Store the segment information in the output array $headerdata[] = array( "SegType" => ord($data[1]), "SegName" => $GLOBALS[ "JPEG_Segment_Names" ][ ord($data[1]) ], "SegDesc" => $GLOBALS[ "JPEG_Segment_Descriptions" ][ ord($data[1]) ], "SegDataStart" => $segdatastart, "SegData" => $segdata ); } // If this is a SOS (Start Of Scan) segment, then there is no more header data - the compressed image data follows if ( $data[1] == "\xDA" ) { // Flag that we have hit the compressed image data - exit loop as no more headers available. $hit_compressed_image_data = TRUE; } else { // Not an SOS - Read the next two bytes - should be the segment marker for the next segment $data = network_safe_fread( $filehnd, 2 ); // Check that the first byte of the two is 0xFF as it should be for a marker if ( $data[0] != "\xFF" ) { // NO FF found - close file and return - JPEG is probably corrupted fclose($filehnd); return FALSE; } } } // Close File fclose($filehnd); // Alow the user to abort from now on ignore_user_abort(false); // Return the header data retrieved return $headerdata; } /****************************************************************************** * End of Function: get_jpeg_header_data ******************************************************************************/ /****************************************************************************** * * Function: put_jpeg_header_data * * Description: Writes JPEG header data into a JPEG file. Takes an array in the * same format as from get_jpeg_header_data, and combines it with * the image data of an existing JPEG file, to create a new JPEG file * WARNING: As this function will replace all JPEG headers, * including SOF etc, it is best to read the jpeg headers * from a file, alter them, then put them back on the same * file. If a SOF segment wer to be transfered from one * file to another, the image could become unreadable unless * the images were idenical size and configuration * * * Parameters: old_filename - the JPEG file from which the image data will be retrieved * new_filename - the name of the new JPEG to create (can be same as old_filename) * jpeg_header_data - a JPEG header data array in the same format * as from get_jpeg_header_data * * Returns: TRUE - on Success * FALSE - on Failure * ******************************************************************************/ function put_jpeg_header_data( $old_filename, $new_filename, $jpeg_header_data ) { // Change: added check to ensure data exists, as of revision 1.10 // Check if the data to be written exists if ( $jpeg_header_data == FALSE ) { // Data to be written not valid - abort return FALSE; } // extract the compressed image data from the old file $compressed_image_data = get_jpeg_image_data( $old_filename ); // Check if the extraction worked if ( ( $compressed_image_data === FALSE ) || ( $compressed_image_data === NULL ) ) { // Couldn't get image data from old file return FALSE; } // Cycle through new headers foreach ($jpeg_header_data as $segno => $segment) { // Check that this header is smaller than the maximum size if ( strlen($segment['SegData']) > 0xfffd ) { // Could't open the file - exit echo "

A Header is too large to fit in JPEG segment

\n"; return FALSE; } } ignore_user_abort(true); ## prevent refresh from aborting file operations and hosing file // Attempt to open the new jpeg file $newfilehnd = @fopen($new_filename, 'wb'); // Check if the file opened successfully if ( ! $newfilehnd ) { // Could't open the file - exit echo "

Could not open file $new_filename

\n"; return FALSE; } // Write SOI fwrite( $newfilehnd, "\xFF\xD8" ); // Cycle through new headers, writing them to the new file foreach ($jpeg_header_data as $segno => $segment) { // Write segment marker fwrite( $newfilehnd, sprintf( "\xFF%c", $segment['SegType'] ) ); // Write segment size fwrite( $newfilehnd, pack( "n", strlen($segment['SegData']) + 2 ) ); // Write segment data fwrite( $newfilehnd, $segment['SegData'] ); } // Write the compressed image data fwrite( $newfilehnd, $compressed_image_data ); // Write EOI fwrite( $newfilehnd, "\xFF\xD9" ); // Close File fclose($newfilehnd); // Alow the user to abort from now on ignore_user_abort(false); return TRUE; } /****************************************************************************** * End of Function: put_jpeg_header_data ******************************************************************************/ /****************************************************************************** * * Function: get_jpeg_Comment * * Description: Retreives the contents of the JPEG Comment (COM = 0xFFFE) segment if one * exists * * Parameters: jpeg_header_data - the JPEG header data, as retrieved * from the get_jpeg_header_data function * * Returns: string - Contents of the Comment segement * FALSE - if the comment segment couldnt be found * ******************************************************************************/ function get_jpeg_Comment( $jpeg_header_data ) { //Cycle through the header segments until COM is found or we run out of segments $i = 0; while ( ( $i < count( $jpeg_header_data) ) && ( $jpeg_header_data[$i]['SegName'] != "COM" ) ) { $i++; } // Check if a COM segment has been found if ( $i < count( $jpeg_header_data) ) { // A COM segment was found, return it's contents return $jpeg_header_data[$i]['SegData']; } else { // No COM segment found return FALSE; } } /****************************************************************************** * End of Function: get_jpeg_Comment ******************************************************************************/ /****************************************************************************** * * Function: put_jpeg_Comment * * Description: Creates a new JPEG Comment segment from a string, and inserts * this segment into the supplied JPEG header array * * Parameters: jpeg_header_data - a JPEG header data array in the same format * as from get_jpeg_header_data, into which the * new Comment segment will be put * $new_Comment - a string containing the new Comment * * Returns: jpeg_header_data - the JPEG header data array with the new * JPEG Comment segment added * ******************************************************************************/ function put_jpeg_Comment( $jpeg_header_data, $new_Comment ) { //Cycle through the header segments for( $i = 0; $i < count( $jpeg_header_data ); $i++ ) { // If we find an COM header, if ( strcmp ( $jpeg_header_data[$i]['SegName'], "COM" ) == 0 ) { // Found a preexisting Comment block - Replace it with the new one and return. $jpeg_header_data[$i]['SegData'] = $new_Comment; return $jpeg_header_data; } } // No preexisting Comment block found, find where to put it by searching for the highest app segment $i = 0; while ( ( $i < count( $jpeg_header_data ) ) && ( $jpeg_header_data[$i]["SegType"] >= 0xE0 ) ) { $i++; } // insert a Comment segment new at the position found of the header data. array_splice($jpeg_header_data, $i , 0, array( array( "SegType" => 0xFE, "SegName" => $GLOBALS[ "JPEG_Segment_Names" ][ 0xFE ], "SegDesc" => $GLOBALS[ "JPEG_Segment_Descriptions" ][ 0xFE ], "SegData" => $new_Comment ) ) ); return $jpeg_header_data; } /****************************************************************************** * End of Function: put_jpeg_Comment ******************************************************************************/ /****************************************************************************** * * Function: Interpret_Comment_to_HTML * * Description: Generates html showing the contents of any JPEG Comment segment * * Parameters: jpeg_header_data - the JPEG header data, as retrieved * from the get_jpeg_header_data function * * Returns: output - the HTML * ******************************************************************************/ function Interpret_Comment_to_HTML( $jpeg_header_data ) { // Create a string to receive the output $output = ""; // read the comment segment $comment = get_jpeg_Comment( $jpeg_header_data ); // Check if the comment segment was valid if ( $comment !== FALSE ) { // Comment exists - add it to the output $output .= "

JPEG Comment

\n"; $output .= "

$comment

\n"; } // Return the result return $output; } /****************************************************************************** * End of Function: Interpret_Comment_to_HTML ******************************************************************************/ /****************************************************************************** * * Function: get_jpeg_intrinsic_values * * Description: Retreives information about the intrinsic characteristics of the * jpeg image, such as Bits per Component, Height and Width. * * Parameters: jpeg_header_data - the JPEG header data, as retrieved * from the get_jpeg_header_data function * * Returns: array - An array containing the intrinsic JPEG values * FALSE - if the comment segment couldnt be found * ******************************************************************************/ function get_jpeg_intrinsic_values( $jpeg_header_data ) { // Create a blank array for the output $Outputarray = array( ); //Cycle through the header segments until Start Of Frame (SOF) is found or we run out of segments $i = 0; while ( ( $i < count( $jpeg_header_data) ) && ( substr( $jpeg_header_data[$i]['SegName'], 0, 3 ) != "SOF" ) ) { $i++; } // Check if a SOF segment has been found if ( substr( $jpeg_header_data[$i]['SegName'], 0, 3 ) == "SOF" ) { // SOF segment was found, extract the information $data = $jpeg_header_data[$i]['SegData']; // First byte is Bits per component $Outputarray['Bits per Component'] = ord( $data[0] ); // Second and third bytes are Image Height $Outputarray['Image Height'] = ord( $data[1] ) * 256 + ord( $data[2] ); // Forth and fifth bytes are Image Width $Outputarray['Image Width'] = ord( $data[3] ) * 256 + ord( $data[4] ); // Sixth byte is number of components $numcomponents = ord( $data[5] ); // Following this is a table containing information about the components for( $i = 0; $i < $numcomponents; $i++ ) { $Outputarray['Components'][] = array ( 'Component Identifier' => ord( $data{ 6 + $i * 3 } ), 'Horizontal Sampling Factor' => ( ord( $data{ 7 + $i * 3 } ) & 0xF0 ) / 16, 'Vertical Sampling Factor' => ( ord( $data{ 7 + $i * 3 } ) & 0x0F ), 'Quantization table destination selector' => ord( $data{ 8 + $i * 3 } ) ); } } else { // Couldn't find Start Of Frame segment, hence can't retrieve info return FALSE; } return $Outputarray; } /****************************************************************************** * End of Function: get_jpeg_intrinsic_values ******************************************************************************/ /****************************************************************************** * * Function: Interpret_intrinsic_values_to_HTML * * Description: Generates html showing some of the intrinsic JPEG values which * were retrieved with the get_jpeg_intrinsic_values function * * Parameters: values - the JPEG intrinsic values, as read from get_jpeg_intrinsic_values * * Returns: OutputStr - A string containing the HTML * ******************************************************************************/ function Interpret_intrinsic_values_to_HTML( $values ) { // Check values are valid if ( $values != FALSE ) { // Write Heading $OutputStr = "

Intrinsic JPEG Information

\n"; // Create Table $OutputStr .= "\n"; // Put image height and width into table $OutputStr .= "\n"; $OutputStr .= "\n"; // Put colour depth into table if ( count( $values['Components'] ) == 1 ) { $OutputStr .= "\n"; } else { $OutputStr .= "\n"; } // Close Table $OutputStr .= "
Image Height" . $values['Image Height'] . " pixels
Image Width" . $values['Image Width'] . " pixels
Colour Depth" . $values['Bits per Component'] . " bit Monochrome
Colour Depth" . ($values['Bits per Component'] * count( $values['Components'] ) ) . " bit
\n"; // Return html return $OutputStr; } } /****************************************************************************** * End of Function: Interpret_intrinsic_values_to_HTML ******************************************************************************/ /****************************************************************************** * * Function: get_jpeg_image_data * * Description: Retrieves the compressed image data part of the JPEG file * * Parameters: filename - the filename of the JPEG file to read * * Returns: compressed_data - A string containing the compressed data * FALSE - if retrieval failed * ******************************************************************************/ function get_jpeg_image_data( $filename ) { // prevent refresh from aborting file operations and hosing file ignore_user_abort(true); // Attempt to open the jpeg file $filehnd = @fopen($filename, 'rb'); // Check if the file opened successfully if ( ! $filehnd ) { // Could't open the file - exit return FALSE; } // Read the first two characters $data = network_safe_fread( $filehnd, 2 ); // Check that the first two characters are 0xFF 0xDA (SOI - Start of image) if ( $data != "\xFF\xD8" ) { // No SOI (FF D8) at start of file - close file and return; fclose($filehnd); return FALSE; } // Read the third character $data = network_safe_fread( $filehnd, 2 ); // Check that the third character is 0xFF (Start of first segment header) if ( $data[0] != "\xFF" ) { // NO FF found - close file and return fclose($filehnd); return; } // Flag that we havent yet hit the compressed image data $hit_compressed_image_data = FALSE; // Cycle through the file until, one of: 1) an EOI (End of image) marker is hit, // 2) we have hit the compressed image data (no more headers are allowed after data) // 3) or end of file is hit while ( ( $data[1] != "\xD9" ) && (! $hit_compressed_image_data) && ( ! feof( $filehnd ) )) { // Found a segment to look at. // Check that the segment marker is not a Restart marker - restart markers don't have size or data after them if ( ( ord($data[1]) < 0xD0 ) || ( ord($data[1]) > 0xD7 ) ) { // Segment isn't a Restart marker // Read the next two bytes (size) $sizestr = network_safe_fread( $filehnd, 2 ); // convert the size bytes to an integer $decodedsize = unpack ("nsize", $sizestr); // Read the segment data with length indicated by the previously read size $segdata = network_safe_fread( $filehnd, $decodedsize['size'] - 2 ); } // If this is a SOS (Start Of Scan) segment, then there is no more header data - the compressed image data follows if ( $data[1] == "\xDA" ) { // Flag that we have hit the compressed image data - exit loop after reading the data $hit_compressed_image_data = TRUE; // read the rest of the file in // Can't use the filesize function to work out // how much to read, as it won't work for files being read by http or ftp // So instead read 1Mb at a time till EOF $compressed_data = ""; do { $compressed_data .= network_safe_fread( $filehnd, 1048576 ); } while( ! feof( $filehnd ) ); // Strip off EOI and anything after $EOI_pos = strpos( $compressed_data, "\xFF\xD9" ); $compressed_data = substr( $compressed_data, 0, $EOI_pos ); } else { // Not an SOS - Read the next two bytes - should be the segment marker for the next segment $data = network_safe_fread( $filehnd, 2 ); // Check that the first byte of the two is 0xFF as it should be for a marker if ( $data[0] != "\xFF" ) { // Problem - NO FF foundclose file and return"; fclose($filehnd); return; } } } // Close File fclose($filehnd); // Alow the user to abort from now on ignore_user_abort(false); // Return the compressed data if it was found if ( $hit_compressed_image_data ) { return $compressed_data; } else { return FALSE; } } /****************************************************************************** * End of Function: get_jpeg_image_data ******************************************************************************/ /****************************************************************************** * * Function: Generate_JPEG_APP_Segment_HTML * * Description: Generates html showing information about the Application (APP) * segments which are present in the JPEG file * * Parameters: jpeg_header_data - the JPEG header data, as retrieved * from the get_jpeg_header_data function * * Returns: output - A string containing the HTML * ******************************************************************************/ function Generate_JPEG_APP_Segment_HTML( $jpeg_header_data ) { if ( $jpeg_header_data == FALSE ) { return ""; } // Write Heading $output = "

Application Metadata Segments

\n"; // Create table $output .= "\n"; // Cycle through each segment in the array foreach( $jpeg_header_data as $jpeg_header ) { // Check if the segment is a APP segment if ( ( $jpeg_header['SegType'] >= 0xE0 ) && ( $jpeg_header['SegType'] <= 0xEF ) ) { // This is an APP segment // Read APP Segment Name - a Null terminated string at the start of the segment $seg_name = strtok($jpeg_header['SegData'], "\x00"); // Some Segment names are either too long or not meaningfull, so // we should clean them up if ( $seg_name == "http://ns.adobe.com/xap/1.0/" ) { $seg_name = "XAP/RDF (\"http://ns.adobe.com/xap/1.0/\")"; } elseif ( $seg_name == "Photoshop 3.0" ) { $seg_name = "Photoshop IRB (\"Photoshop 3.0\")"; } elseif ( ( strncmp ( $seg_name, "[picture info]", 14) == 0 ) || ( strncmp ( $seg_name, "\x0a\x09\x09\x09\x09[picture info]", 19) == 0 ) ) { $seg_name = "[picture info]"; } elseif ( strncmp ( $seg_name, "Type=", 5) == 0 ) { $seg_name = "Epson Info"; } elseif ( ( strncmp ( $seg_name, "HHHHHHHHHHHHHHH", 15) == 0 ) || ( strncmp ( $seg_name, "@s33", 5) == 0 ) ) { $seg_name = "HP segment full of \"HHHHH\""; } // Clean the segment name so it doesn't cause problems with HTML $seg_name = htmlentities( $seg_name, ENT_COMPAT, LANG_CHARSET ); // Output a Table row containing this APP segment $output .= "\n"; } } // Close the table $output .= "
$seg_name" . $jpeg_header['SegName'] . "" . strlen( $jpeg_header['SegData']). " bytes
\n"; // Return the HTML return $output; } /****************************************************************************** * End of Function: Generate_JPEG_APP_Segment_HTML ******************************************************************************/ /****************************************************************************** * * Function: network_safe_fread * * Description: Retrieves data from a file. This function is required since * the fread function will not always return the requested number * of characters when reading from a network stream or pipe * * Parameters: file_handle - the handle of a file to read from * length - the number of bytes requested * * Returns: data - the data read from the file. may be less than the number * requested if EOF was hit * ******************************************************************************/ function network_safe_fread( $file_handle, $length ) { // Create blank string to receive data $data = ""; // Keep reading data from the file until either EOF occurs or we have // retrieved the requested number of bytes while ( ( !feof( $file_handle ) ) && ( strlen($data) < $length ) ) { $data .= fread( $file_handle, $length-strlen($data) ); } // return the data read return $data; } /****************************************************************************** * End of Function: network_safe_fread ******************************************************************************/ /****************************************************************************** * Global Variable: JPEG_Segment_Names * * Contents: The names of the JPEG segment markers, indexed by their marker number * ******************************************************************************/ $GLOBALS[ "JPEG_Segment_Names" ] = array( 0xC0 => "SOF0", 0xC1 => "SOF1", 0xC2 => "SOF2", 0xC3 => "SOF4", 0xC5 => "SOF5", 0xC6 => "SOF6", 0xC7 => "SOF7", 0xC8 => "JPG", 0xC9 => "SOF9", 0xCA => "SOF10", 0xCB => "SOF11", 0xCD => "SOF13", 0xCE => "SOF14", 0xCF => "SOF15", 0xC4 => "DHT", 0xCC => "DAC", 0xD0 => "RST0", 0xD1 => "RST1", 0xD2 => "RST2", 0xD3 => "RST3", 0xD4 => "RST4", 0xD5 => "RST5", 0xD6 => "RST6", 0xD7 => "RST7", 0xD8 => "SOI", 0xD9 => "EOI", 0xDA => "SOS", 0xDB => "DQT", 0xDC => "DNL", 0xDD => "DRI", 0xDE => "DHP", 0xDF => "EXP", 0xE0 => "APP0", 0xE1 => "APP1", 0xE2 => "APP2", 0xE3 => "APP3", 0xE4 => "APP4", 0xE5 => "APP5", 0xE6 => "APP6", 0xE7 => "APP7", 0xE8 => "APP8", 0xE9 => "APP9", 0xEA => "APP10", 0xEB => "APP11", 0xEC => "APP12", 0xED => "APP13", 0xEE => "APP14", 0xEF => "APP15", 0xF0 => "JPG0", 0xF1 => "JPG1", 0xF2 => "JPG2", 0xF3 => "JPG3", 0xF4 => "JPG4", 0xF5 => "JPG5", 0xF6 => "JPG6", 0xF7 => "JPG7", 0xF8 => "JPG8", 0xF9 => "JPG9", 0xFA => "JPG10", 0xFB => "JPG11", 0xFC => "JPG12", 0xFD => "JPG13", 0xFE => "COM", 0x01 => "TEM", 0x02 => "RES", ); /****************************************************************************** * End of Global Variable: JPEG_Segment_Names ******************************************************************************/ /****************************************************************************** * Global Variable: JPEG_Segment_Descriptions * * Contents: The descriptions of the JPEG segment markers, indexed by their marker number * ******************************************************************************/ $GLOBALS[ "JPEG_Segment_Descriptions" ] = array( /* JIF Marker byte pairs in JPEG Interchange Format sequence */ 0xC0 => "Start Of Frame (SOF) Huffman - Baseline DCT", 0xC1 => "Start Of Frame (SOF) Huffman - Extended sequential DCT", 0xC2 => "Start Of Frame Huffman - Progressive DCT (SOF2)", 0xC3 => "Start Of Frame Huffman - Spatial (sequential) lossless (SOF3)", 0xC5 => "Start Of Frame Huffman - Differential sequential DCT (SOF5)", 0xC6 => "Start Of Frame Huffman - Differential progressive DCT (SOF6)", 0xC7 => "Start Of Frame Huffman - Differential spatial (SOF7)", 0xC8 => "Start Of Frame Arithmetic - Reserved for JPEG extensions (JPG)", 0xC9 => "Start Of Frame Arithmetic - Extended sequential DCT (SOF9)", 0xCA => "Start Of Frame Arithmetic - Progressive DCT (SOF10)", 0xCB => "Start Of Frame Arithmetic - Spatial (sequential) lossless (SOF11)", 0xCD => "Start Of Frame Arithmetic - Differential sequential DCT (SOF13)", 0xCE => "Start Of Frame Arithmetic - Differential progressive DCT (SOF14)", 0xCF => "Start Of Frame Arithmetic - Differential spatial (SOF15)", 0xC4 => "Define Huffman Table(s) (DHT)", 0xCC => "Define Arithmetic coding conditioning(s) (DAC)", 0xD0 => "Restart with modulo 8 count 0 (RST0)", 0xD1 => "Restart with modulo 8 count 1 (RST1)", 0xD2 => "Restart with modulo 8 count 2 (RST2)", 0xD3 => "Restart with modulo 8 count 3 (RST3)", 0xD4 => "Restart with modulo 8 count 4 (RST4)", 0xD5 => "Restart with modulo 8 count 5 (RST5)", 0xD6 => "Restart with modulo 8 count 6 (RST6)", 0xD7 => "Restart with modulo 8 count 7 (RST7)", 0xD8 => "Start of Image (SOI)", 0xD9 => "End of Image (EOI)", 0xDA => "Start of Scan (SOS)", 0xDB => "Define quantization Table(s) (DQT)", 0xDC => "Define Number of Lines (DNL)", 0xDD => "Define Restart Interval (DRI)", 0xDE => "Define Hierarchical progression (DHP)", 0xDF => "Expand Reference Component(s) (EXP)", 0xE0 => "Application Field 0 (APP0) - usually JFIF or JFXX", 0xE1 => "Application Field 1 (APP1) - usually EXIF or XMP/RDF", 0xE2 => "Application Field 2 (APP2) - usually Flashpix", 0xE3 => "Application Field 3 (APP3)", 0xE4 => "Application Field 4 (APP4)", 0xE5 => "Application Field 5 (APP5)", 0xE6 => "Application Field 6 (APP6)", 0xE7 => "Application Field 7 (APP7)", 0xE8 => "Application Field 8 (APP8)", 0xE9 => "Application Field 9 (APP9)", 0xEA => "Application Field 10 (APP10)", 0xEB => "Application Field 11 (APP11)", 0xEC => "Application Field 12 (APP12) - usually [picture info]", 0xED => "Application Field 13 (APP13) - usually photoshop IRB / IPTC", 0xEE => "Application Field 14 (APP14)", 0xEF => "Application Field 15 (APP15)", 0xF0 => "Reserved for JPEG extensions (JPG0)", 0xF1 => "Reserved for JPEG extensions (JPG1)", 0xF2 => "Reserved for JPEG extensions (JPG2)", 0xF3 => "Reserved for JPEG extensions (JPG3)", 0xF4 => "Reserved for JPEG extensions (JPG4)", 0xF5 => "Reserved for JPEG extensions (JPG5)", 0xF6 => "Reserved for JPEG extensions (JPG6)", 0xF7 => "Reserved for JPEG extensions (JPG7)", 0xF8 => "Reserved for JPEG extensions (JPG8)", 0xF9 => "Reserved for JPEG extensions (JPG9)", 0xFA => "Reserved for JPEG extensions (JPG10)", 0xFB => "Reserved for JPEG extensions (JPG11)", 0xFC => "Reserved for JPEG extensions (JPG12)", 0xFD => "Reserved for JPEG extensions (JPG13)", 0xFE => "Comment (COM)", 0x01 => "For temp private use arith code (TEM)", 0x02 => "Reserved (RES)", ); /****************************************************************************** * End of Global Variable: JPEG_Segment_Descriptions ******************************************************************************/ ?>