<?php

/******************************************************************************
*
* Filename:     JFIF.php
*
* Description:  Provides functions for reading and writing information to/from
*               JPEG File Interchange Format (JFIF) segments and
*               JFIF Extension (JFXX) segments within a JPEG file.
*
* Author:       Evan Hunter
*
* Date:         24/7/2004
*
* Project:      PHP JPEG Metadata Toolkit
*
* Revision:     1.00
*
* 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_JFIF
*
* Description:  Retrieves information from a JPEG File Interchange Format (JFIF)
*               segment and returns it in an array. Uses information supplied by
*               the get_jpeg_header_data function
*
* Parameters:   jpeg_header_data - a JPEG header data array in the same format
*                                  as from get_jpeg_header_data
*
* Returns:      JFIF_data - an array of JFIF data
*               FALSE - if a JFIF segment could not be found
*
******************************************************************************/

function get_JFIF( $jpeg_header_data )
{
        //Cycle through the header segments
        for( $i = 0; $i < count( $jpeg_header_data ); $i++ )
        {
                // If we find an APP0 header,
                if ( strcmp ( $jpeg_header_data[$i]['SegName'], "APP0" ) == 0 )
                {
                        // And if it has the JFIF label,
                        if( strncmp ( $jpeg_header_data[$i]['SegData'], "JFIF\x00", 5) == 0 )
                        {
                                // Found a JPEG File Interchange Format (JFIF) Block

                                // unpack the JFIF data from the incoming string
                                // First is the JFIF label string
                                // Then a two byte version number
                                // Then a byte, units identifier, ( 0 = aspect ration, 1 = dpi, 2 = dpcm)
                                // Then a two byte int X-Axis pixel Density (resolution)
                                // Then a two byte int Y-Axis pixel Density (resolution)
                                // Then a byte X-Axis JFIF thumbnail size
                                // Then a byte Y-Axis JFIF thumbnail size
                                // Then the uncompressed RGB JFIF thumbnail data

                                $JFIF_data = unpack( 'a5JFIF/C2Version/CUnits/nXDensity/nYDensity/CThumbX/CThumbY/a*ThumbData', $jpeg_header_data[$i]['SegData'] );

                                return $JFIF_data;
                        }
                }
        }
        return FALSE;
}

/******************************************************************************
* End of Function:     get_JFIF
******************************************************************************/




/******************************************************************************
*
* Function:     put_JFIF
*
* Description:  Creates a new JFIF segment from an array of JFIF data in the
*               same format as would be retrieved from get_JFIF, 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 JFIF segment will be put
*               new_JFIF_array - a JFIF information array in the same format as
*                                from get_JFIF, to create the new segment
*
* Returns:      jpeg_header_data - the JPEG header data array with the new
*                                  JFIF segment added
*
******************************************************************************/

function put_JFIF( $jpeg_header_data, $new_JFIF_array )
{
        // pack the JFIF data into its proper format for a JPEG file
        $packed_data = pack( 'a5CCCnnCCa*',"JFIF\x00", $new_JFIF_array['Version1'], $new_JFIF_array['Version2'], $new_JFIF_array['Units'], $new_JFIF_array['XDensity'], $new_JFIF_array['YDensity'], $new_JFIF_array['ThumbX'], $new_JFIF_array['ThumbY'], $new_JFIF_array['ThumbData'] );

        //Cycle through the header segments
        for( $i = 0; $i < count( $jpeg_header_data ); $i++ )
        {
                // If we find an APP0 header,
                if ( strcmp ( $jpeg_header_data[$i]['SegName'], "APP0" ) == 0 )
                {
                        // And if it has the JFIF label,
                        if( strncmp ( $jpeg_header_data[$i]['SegData'], "JFIF\x00", 5) == 0 )
                        {
                                // Found a preexisting JFIF block - Replace it with the new one and return.
                                $jpeg_header_data[$i]['SegData'] = $packed_data;
                                return $jpeg_header_data;
                        }
                }
        }

        // No preexisting JFIF block found, insert a new one at the start of the header data.
        array_splice($jpeg_header_data, 0 , 0, array( array(   "SegType" => 0xE0,
                                                                "SegName" => "APP0",
                                                                "SegDesc" => $GLOBALS[ "JPEG_Segment_Descriptions" ][ 0xE0 ],
                                                                "SegData" => $packed_data ) ) );
        return $jpeg_header_data;
}

/******************************************************************************
* End of Function:     put_JFIF
******************************************************************************/








/******************************************************************************
*
* Function:     Interpret_JFIF_to_HTML
*
* Description:  Generates html showing the JFIF information contained in
*               a JFIF data array, as retrieved with get_JFIF
*
* Parameters:   JFIF_array - a JFIF data array, as from get_JFIF
*               filename - the name of the JPEG file being processed ( used
*                          by the script which displays the JFIF thumbnail)
*
*
* Returns:      output - the HTML string
*
******************************************************************************/

function Interpret_JFIF_to_HTML( $JFIF_array, $filename )
{
        $output = "";
        if ( $JFIF_array !== FALSE )
        {
                $output .= "<H2 class=\"JFIF_Main_Heading\">Contains JPEG File Interchange Format (JFIF) Information</H2>\n";
                $output .= "\n<table class=\"JFIF_Table\" border=1>\n";
                $output .= "<tr class=\"JFIF_Table_Row\"><td class=\"JFIF_Caption_Cell\">JFIF version: </td><td class=\"JFIF_Value_Cell\">". sprintf( "%d.%02d", $JFIF_array['Version1'], $JFIF_array['Version2'] ) . "</td></tr>\n";
                if ( $JFIF_array['Units'] == 0 )
                {
                        $output .= "<tr class=\"JFIF_Table_Row\"><td class=\"JFIF_Caption_Cell\">Pixel Aspect Ratio: </td><td class=\"JFIF_Value_Cell\">" . $JFIF_array['XDensity'] ." x " . $JFIF_array['YDensity'] . "</td></tr>\n";
                }
                elseif ( $JFIF_array['Units'] == 1 )
                {
                        $output .= "<tr class=\"JFIF_Table_Row\"><td class=\"JFIF_Caption_Cell\">Resolution: </td><td class=\"JFIF_Value_Cell\">" . $JFIF_array['XDensity'] ." x " . $JFIF_array['YDensity'] . " pixels per inch</td></tr>\n";
                }
                elseif ( $JFIF_array['Units'] == 2 )
                {
                        $output .= "<tr class=\"JFIF_Table_Row\"><td class=\"JFIF_Caption_Cell\">Resolution: </td><td class=\"JFIF_Value_Cell\">" . $JFIF_array['XDensity'] ." x " . $JFIF_array['YDensity'] . " pixels per cm</td></tr>\n";
                }

                $output .= "<tr class=\"JFIF_Table_Row\"><td class=\"JFIF_Caption_Cell\">JFIF (uncompressed) thumbnail: </td><td class=\"JFIF_Value_Cell\">";
                if ( ( $JFIF_array['ThumbX'] != 0 ) && ( $JFIF_array['ThumbY'] != 0 ) )
                {
                        $output .= $JFIF_array['ThumbX'] ." x " . $JFIF_array['ThumbY'] . " pixels, Thumbnail Display Not Yet Implemented</td></tr>\n";
                        // TODO Implement JFIF Thumbnail display
                }
                else
                {
                        $output .= "None</td></tr>\n";
                }

                $output .= "</table><br>\n";
        }

        return $output;

}


/******************************************************************************
* End of Function:     Interpret_JFIF_to_HTML
******************************************************************************/










/******************************************************************************
*
* Function:     get_JFXX
*
* Description:  Retrieves information from a JPEG File Interchange Format Extension (JFXX)
*               segment and returns it in an array. Uses information supplied by
*               the get_jpeg_header_data function
*
* Parameters:   jpeg_header_data - a JPEG header data array in the same format
*                                  as from get_jpeg_header_data
*
* Returns:      JFXX_data - an array of JFXX data
*               FALSE - if a JFXX segment could not be found
*
******************************************************************************/

function get_JFXX( $jpeg_header_data )
{
        //Cycle through the header segments
        for( $i = 0; $i < count( $jpeg_header_data ); $i++ )
        {
                // If we find an APP0 header,
                if ( strcmp ( $jpeg_header_data[$i]['SegName'], "APP0" ) == 0 )
                {
                        // And if it has the JFIF label,
                        if( strncmp ( $jpeg_header_data[$i]['SegData'], "JFXX\x00", 5) == 0 )
                        {
                                // Found a JPEG File Interchange Format Extension (JFXX) Block

                                // unpack the JFXX data from the incoming string
                                // First is the 5 byte JFXX label string
                                // Then a 1 byte Extension code, indicating Thumbnail Format
                                // Then the thumbnail data

                                $JFXX_data = unpack( 'a5JFXX/CExtension_Code/a*ThumbData', $jpeg_header_data[$i]['SegData'] );
                                return $JFXX_data;
                        }
                }
        }
        return FALSE;
}

/******************************************************************************
* End of Function:     get_JFXX
******************************************************************************/




/******************************************************************************
*
* Function:     put_JFXX
*
* Description:  Creates a new JFXX segment from an array of JFXX data in the
*               same format as would be retrieved from get_JFXX, 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 JFXX segment will be put
*               new_JFXX_array - a JFXX information array in the same format as
*                                from get_JFXX, to create the new segment
*
* Returns:      jpeg_header_data - the JPEG header data array with the new
*                                  JFXX segment added
*
******************************************************************************/

function put_JFXX( $jpeg_header_data, $new_JFXX_array )
{
        // pack the JFXX data into its proper format for a JPEG file
        $packed_data = pack( 'a5Ca*',"JFXX\x00", $new_JFXX_array['Extension_Code'], $new_JFXX_array['ThumbData'] );

        $JFIF_pos = -1;

        //Cycle through the header segments
        for( $i = 0; $i < count( $jpeg_header_data ); $i++ )
        {
                // If we find an APP0 header,
                if ( strcmp ( $jpeg_header_data[$i]['SegName'], "APP0" ) == 0 )
                {
                        // And if it has the JFXX label,
                        if( strncmp ( $jpeg_header_data[$i]['SegData'], "JFXX\x00", 5) == 0 )
                        {
                                // Found a preexisting JFXX block - Replace it with the new one and return.
                                $jpeg_header_data[$i]['SegData'] = $packed_data;
                                return $jpeg_header_data;
                        }

                        // if it has the JFIF label,
                        if( strncmp ( $jpeg_header_data[$i][SegData], "JFIF\x00", 5) == 0 )
                        {
                                // Found a preexisting JFIF block - Mark it in case we need to insert the JFXX after it
                                $JFIF_pos = $i;
                        }
                }
        }


        // No preexisting JFXX block found

        // Check if a JFIF segment was found,
        if ( $JFIF_pos !== -1 )
        {
                // A pre-existing JFIF segment was found,
                // insert the new JFXX segment after it.
                array_splice($jpeg_header_data, $JFIF_pos +1 , 0, array ( array(        "SegType" => 0xE0,
                                                                                        "SegName" => "APP0",
                                                                                        "SegDesc" => $GLOBALS[ "JPEG_Segment_Descriptions" ][ 0xE0 ],
                                                                                        "SegData" => $packed_data ) ) );

        }
        else
        {
                // No pre-existing JFIF segment was found,
                // insert a new JFIF and the new JFXX segment at the start of the array.

                // Insert new JFXX segment
                array_splice($jpeg_header_data, 0 , 0, array( array(   "SegType" => 0xE0,
                                                                        "SegName" => "APP0",
                                                                        "SegDesc" => $GLOBALS[ "JPEG_Segment_Descriptions" ][ 0xE0 ],
                                                                        "SegData" => $packed_data ) ) );

                // Create a new JFIF to be inserted at the start of
                // the array, with generic values
                $packed_data = pack( 'a5CCCnnCCa*',"JFIF\x00", 1, 2, 1, 72, 72, 0, 0, "" );

                array_splice($jpeg_header_data, 0 , 0, array( array(   "SegType" => 0xE0,
                                                                        "SegName" => "APP0",
                                                                        "SegDesc" => $GLOBALS[ "JPEG_Segment_Descriptions" ][ 0xE0 ],
                                                                        "SegData" => $packed_data ) ) );
        }


        return $jpeg_header_data;
}

/******************************************************************************
* End of Function:     put_JFIF
******************************************************************************/



/******************************************************************************
*
* Function:     Interpret_JFXX_to_HTML
*
* Description:  Generates html showing the JFXX thumbnail contained in
*               a JFXX data array, as retrieved with get_JFXX
*
* Parameters:   JFXX_array - a JFXX information array in the same format as
*                            from get_JFXX, to create the new segment
*               filename - the name of the JPEG file being processed ( used
*                          by the script which displays the JFXX thumbnail)
*
* Returns:      output - the Html string
*
******************************************************************************/

function Interpret_JFXX_to_HTML( $JFXX_array, $filename )
{
        $output = "";
        if ( $JFXX_array !== FALSE )
        {
                $output .= "<H2 class=\"JFXX_Main_Heading\">Contains JPEG File Interchange Extension Format  (JFXX) Thumbnail</H2>\n";
                switch ( $JFXX_array['Extension_Code'] )
                {
                        case 0x10 :     $output .= "<p class=\"JFXX_Text\">JFXX Thumbnail is JPEG Encoded</p>\n";
                                        $output .= "<a class=\"JFXX_Thumbnail_Link\" href=\"get_jfxx_thumb.php?filename=$filename\"><img  class=\"JFXX_Thumbnail\" src=\"get_jfxx_thumb.php?filename=$filename\"></a>\n";
                                        break;
                        case 0x11 :     $output .= "<p class=\"JFXX_Text\">JFXX Thumbnail is Encoded 1 byte/pixel</p>\n";
                                        $output .= "<p class=\"JFXX_Text\">Thumbnail Display Not Implemented Yet</p>\n";
                                        break;
                        case 0x13 :     $output .= "<p class=\"JFXX_Text\">JFXX Thumbnail is Encoded 3 bytes/pixel</p>\n";
                                        $output .= "<p class=\"JFXX_Text\">Thumbnail Display Not Implemented Yet</p>\n";
                                        break;
                        default :       $output .= "<p class=\"JFXX_Text\">JFXX Thumbnail is Encoded with Unknown format</p>\n";
                                        break;

                        // TODO: Implement JFXX one and three bytes per pixel thumbnail decoding
                }

        }

        return $output;

}

/******************************************************************************
* End of Function:     Interpret_JFXX_to_HTML
******************************************************************************/




?>