Classes/Utility/JpegExifOrient.php
public static function setOrientation($fileName, $orientation)
    {
        $exif_data = [];    // Buffer
        $offsetJfif = 0;

        if (($fh = fopen($fileName, 'rb+')) === false) {
            throw new \RuntimeException('Can\'t open ' . $fileName, 1363533724);
        }

        // Read file head, check for JPEG SOI + JFIF/Exif APP1
        for ($i = 0; $i < 4; $i++) {
            $exif_data[$i] = self::read_1_byte($fh);
        }
        if ($exif_data[0] !== 0xFF ||
            $exif_data[1] !== 0xD8
        ) {
            // Do nothing?
        }

        // JFIF segment: http://en.wikipedia.org/wiki/JPEG_File_Interchange_Format#JFIF_segment_format
        if ($exif_data[2] === 0xFF && $exif_data[3] === 0xE0) {
            // Get the marker parameter length count
            $length = self::read_2_bytes($fh);
            $offsetJfif = $length + 2;    // "+ 2" to skip the 2 bytes introducing this additional segment
            // Length includes itself, so must be at least 2
            // Following JFIF data length must be at least 6
            if ($length < 8) {
                return;
            }
            $length -= 8;
            // Read JFIF head, check for "JFIF"
            for ($i = 0; $i < 5; $i++) {
                $exif_data[$i] = self::read_1_byte($fh);
            }
            if ($exif_data[0] !== 0x4A ||
                $exif_data[1] !== 0x46 ||
                $exif_data[2] !== 0x49 ||
                $exif_data[3] !== 0x46 ||
                $exif_data[4] !== 0
            ) {
                return;
            }
            // Read JFIF body
            for ($i = 0; $i < $length; $i++) {
                $exif_data[$i] = self::read_1_byte($fh);
            }

            if (self::read_1_byte($fh) !== 0) {
                // Seems there is a 0 byte to separate segments...
                return;
            }
            // Read next 2 bytes in $exif_data[2] and $exif_data[3] as Exif APP1 segment
            // is now expected
            $exif_data[2] = self::read_1_byte($fh);
            $exif_data[3] = self::read_1_byte($fh);
        }

        // Exif APP1
        if ($exif_data[2] !== 0xFF || $exif_data[3] !== 0xE1) {
            return;
        }

        // Get the marker parameter length count
        $length = self::read_2_bytes($fh);
        // Length includes itself, so must be at least 2
        // Following Exif data length must be at least 6
        if ($length < 8) {
            return;
        }
        $length -= 8;
        // Read Exif head, check for "Exif"
        for ($i = 0; $i < 6; $i++) {
            $exif_data[$i] = self::read_1_byte($fh);
        }
        if ($exif_data[0] !== 0x45 ||
            $exif_data[1] !== 0x78 ||
            $exif_data[2] !== 0x69 ||
            $exif_data[3] !== 0x66 ||
            $exif_data[4] !== 0 ||
            $exif_data[5] !== 0
        ) {
            return;
        }
        // Read Exif body
        for ($i = 0; $i < $length; $i++) {
            $exif_data[$i] = self::read_1_byte($fh);
        }

        if ($length < 12) {    // Length of an IFD entry
            return;
        }

        // Discover byte order
        if ($exif_data[0] === 0x49 && $exif_data[1] === 0x49) {
            $is_motorola = false;
        } elseif ($exif_data[0] === 0x4D && $exif_data[1] === 0x4D) {
            $is_motorola = true;
        } else {
            return;
        }

        // Check Tag mark
        if ($is_motorola) {
            if ($exif_data[2] !== 0) return;
            if ($exif_data[3] !== 0x2A) return;
        } else {
            if ($exif_data[3] !== 0) return;
            if ($exif_data[2] !== 0x2A) return;
        }

        // Get first IFD offset (offset to IFD0)
        if ($is_motorola) {
            if ($exif_data[4] !== 0) return;
            if ($exif_data[5] !== 0) return;
            $offset = $exif_data[6];
            $offset <<= 8;
            $offset += $exif_data[7];
        } else {
            if ($exif_data[7] !== 0) return;
            if ($exif_data[6] !== 0) return;
            $offset = $exif_data[5];
            $offset <<= 8;
            $offset += $exif_data[4];
        }
        // Check end of data segment
        if ($offset > $length - 2) return;

        // Get the number of directory entries contained in this IFD
        if ($is_motorola) {
            $number_of_tags = $exif_data[$offset];
            $number_of_tags <<= 8;
            $number_of_tags += $exif_data[$offset + 1];
        } else {
            $number_of_tags = $exif_data[$offset + 1];
            $number_of_tags <<= 8;
            $number_of_tags += $exif_data[$offset];
        }
        if ($number_of_tags === 0) return;
        $offset += 2;

        // Search for Orientation Tag in IFD0
        while (true) {
            // Check end of data segment
            if ($offset > $length - 12) return;
            // Get Tag number
            if ($is_motorola) {
                $tagnum = $exif_data[$offset];
                $tagnum <<= 8;
                $tagnum += $exif_data[$offset + 1];
            } else {
                $tagnum = $exif_data[$offset + 1];
                $tagnum <<= 8;
                $tagnum += $exif_data[$offset];
            }
            // Found Orientation Tag
            if ($tagnum === 0x0112) break;
            if (--$number_of_tags === 0) return;
            $offset += 12;
        }

        // Set the Orientation value
        if ($is_motorola) {
            $exif_data[$offset + 2] = 0;    // Format = unsigned short (2 octets)
            $exif_data[$offset + 3] = 3;
            $exif_data[$offset + 4] = 0;    // Number of Components = 1
            $exif_data[$offset + 5] = 0;
            $exif_data[$offset + 6] = 0;
            $exif_data[$offset + 7] = 1;
            $exif_data[$offset + 8] = 0;
            $exif_data[$offset + 9] = $orientation;
            $exif_data[$offset + 10] = 0;
            $exif_data[$offset + 11] = 0;
        } else {
            $exif_data[$offset + 2] = 3;    // Format = unsigned short (2 octets)
            $exif_data[$offset + 3] = 0;
            $exif_data[$offset + 4] = 1;    // Number of Components = 1
            $exif_data[$offset + 5] = 0;
            $exif_data[$offset + 6] = 0;
            $exif_data[$offset + 7] = 0;
            $exif_data[$offset + 8] = $orientation;
            $exif_data[$offset + 9] = 0;
            $exif_data[$offset + 10] = 0;
            $exif_data[$offset + 11] = 0;
        }
        fseek($fh, (4 + 2 + 6 + 2) + $offsetJfif + $offset, SEEK_SET);
        $data = '';
        for ($i = 0; $i < 10; $i++) {
            $data .= chr($exif_data[$i + $offset + 2]);
        }
        fwrite($fh, $data);
        fclose($fh);
    }
public static function setOrientation($fileName, $orientation)
    {
        $exif_data = [];    // Buffer
        $offsetJfif = 0;

        if (($fh = fopen($fileName, 'rb+')) === false) {
            throw new \RuntimeException('Can\'t open ' . $fileName, 1363533724);
        }

        // Read file head, check for JPEG SOI + JFIF/Exif APP1
        for ($i = 0; $i < 4; $i++) {
            $exif_data[$i] = self::read_1_byte($fh);
        }
        if ($exif_data[0] !== 0xFF ||
            $exif_data[1] !== 0xD8
        ) {
            // Do nothing?
        }

        // JFIF segment: http://en.wikipedia.org/wiki/JPEG_File_Interchange_Format#JFIF_segment_format
        if ($exif_data[2] === 0xFF && $exif_data[3] === 0xE0) {
            // Get the marker parameter length count
            $length = self::read_2_bytes($fh);
            $offsetJfif = $length + 2;    // "+ 2" to skip the 2 bytes introducing this additional segment
            // Length includes itself, so must be at least 2
            // Following JFIF data length must be at least 6
            if ($length < 8) {
                return;
            }
            $length -= 8;
            // Read JFIF head, check for "JFIF"
            for ($i = 0; $i < 5; $i++) {
                $exif_data[$i] = self::read_1_byte($fh);
            }
            if ($exif_data[0] !== 0x4A ||
                $exif_data[1] !== 0x46 ||
                $exif_data[2] !== 0x49 ||
                $exif_data[3] !== 0x46 ||
                $exif_data[4] !== 0
            ) {
                return;
            }
            // Read JFIF body
            for ($i = 0; $i < $length; $i++) {
                $exif_data[$i] = self::read_1_byte($fh);
            }

            if (self::read_1_byte($fh) !== 0) {
                // Seems there is a 0 byte to separate segments...
                return;
            }
            // Read next 2 bytes in $exif_data[2] and $exif_data[3] as Exif APP1 segment
            // is now expected
            $exif_data[2] = self::read_1_byte($fh);
            $exif_data[3] = self::read_1_byte($fh);
        }

        // Exif APP1
        if ($exif_data[2] !== 0xFF || $exif_data[3] !== 0xE1) {
            return;
        }

        // Get the marker parameter length count
        $length = self::read_2_bytes($fh);
        // Length includes itself, so must be at least 2
        // Following Exif data length must be at least 6
        if ($length < 8) {
            return;
        }
        $length -= 8;
        // Read Exif head, check for "Exif"
        for ($i = 0; $i < 6; $i++) {
            $exif_data[$i] = self::read_1_byte($fh);
        }
        if ($exif_data[0] !== 0x45 ||
            $exif_data[1] !== 0x78 ||
            $exif_data[2] !== 0x69 ||
            $exif_data[3] !== 0x66 ||
            $exif_data[4] !== 0 ||
            $exif_data[5] !== 0
        ) {
            return;
        }
        // Read Exif body
        for ($i = 0; $i < $length; $i++) {
            $exif_data[$i] = self::read_1_byte($fh);
        }

        if ($length < 12) {    // Length of an IFD entry
            return;
        }

        // Discover byte order
        if ($exif_data[0] === 0x49 && $exif_data[1] === 0x49) {
            $is_motorola = false;
        } elseif ($exif_data[0] === 0x4D && $exif_data[1] === 0x4D) {
            $is_motorola = true;
        } else {
            return;
        }

        // Check Tag mark
        if ($is_motorola) {
            if ($exif_data[2] !== 0) return;
            if ($exif_data[3] !== 0x2A) return;
        } else {
            if ($exif_data[3] !== 0) return;
            if ($exif_data[2] !== 0x2A) return;
        }

        // Get first IFD offset (offset to IFD0)
        if ($is_motorola) {
            if ($exif_data[4] !== 0) return;
            if ($exif_data[5] !== 0) return;
            $offset = $exif_data[6];
            $offset <<= 8;
            $offset += $exif_data[7];
        } else {
            if ($exif_data[7] !== 0) return;
            if ($exif_data[6] !== 0) return;
            $offset = $exif_data[5];
            $offset <<= 8;
            $offset += $exif_data[4];
        }
        // Check end of data segment
        if ($offset > $length - 2) return;

        // Get the number of directory entries contained in this IFD
        if ($is_motorola) {
            $number_of_tags = $exif_data[$offset];
            $number_of_tags <<= 8;
            $number_of_tags += $exif_data[$offset + 1];
        } else {
            $number_of_tags = $exif_data[$offset + 1];
            $number_of_tags <<= 8;
            $number_of_tags += $exif_data[$offset];
        }
        if ($number_of_tags === 0) return;
        $offset += 2;

        // Search for Orientation Tag in IFD0
        while (true) {
            // Check end of data segment
            if ($offset > $length - 12) return;
            // Get Tag number
            if ($is_motorola) {
                $tagnum = $exif_data[$offset];
                $tagnum <<= 8;
                $tagnum += $exif_data[$offset + 1];
            } else {
                $tagnum = $exif_data[$offset + 1];
                $tagnum <<= 8;
                $tagnum += $exif_data[$offset];
            }
            // Found Orientation Tag
            if ($tagnum === 0x0112) break;
            if (--$number_of_tags === 0) return;
            $offset += 12;
        }

        // Set the Orientation value
        if ($is_motorola) {
            $exif_data[$offset + 2] = 0;    // Format = unsigned short (2 octets)
            $exif_data[$offset + 3] = 3;
            $exif_data[$offset + 4] = 0;    // Number of Components = 1
            $exif_data[$offset + 5] = 0;
            $exif_data[$offset + 6] = 0;
            $exif_data[$offset + 7] = 1;
            $exif_data[$offset + 8] = 0;
            $exif_data[$offset + 9] = $orientation;
            $exif_data[$offset + 10] = 0;
            $exif_data[$offset + 11] = 0;
        } else {
            $exif_data[$offset + 2] = 3;    // Format = unsigned short (2 octets)
            $exif_data[$offset + 3] = 0;
            $exif_data[$offset + 4] = 1;    // Number of Components = 1
            $exif_data[$offset + 5] = 0;
            $exif_data[$offset + 6] = 0;
            $exif_data[$offset + 7] = 0;
            $exif_data[$offset + 8] = $orientation;
            $exif_data[$offset + 9] = 0;
            $exif_data[$offset + 10] = 0;
            $exif_data[$offset + 11] = 0;
        }
        fseek($fh, (4 + 2 + 6 + 2) + $offsetJfif + $offset, SEEK_SET);
        $data = '';
        for ($i = 0; $i < 10; $i++) {
            $data .= chr($exif_data[$i + $offset + 2]);
        }
        fwrite($fh, $data);
        fclose($fh);
    }
public static function setOrientation ( $ fileName , $ orientation ) { $ exif_data = [ ] ; // Buffer $ offsetJfif = 0 ; if ( ( $ fh = fopen ( $ fileName , 'rb+' ) ) === false ) { throw new \ RuntimeException ( 'Can\'t open ' . $ fileName , 1363533724 ) ; } // Read file head, check for JPEG SOI + JFIF/Exif APP1 for ( $ i = 0 ; $ i < 4 ; $ i ++ ) { $ exif_data [ $ i ] = self :: read_1_byte ( $ fh ) ; } if ( $ exif_data [ 0 ] !== 0xFF || $ exif_data [ 1 ] !== 0xD8 ) { // Do nothing? } // JFIF segment: http://en.wikipedia.org/wiki/JPEG_File_Interchange_Format#JFIF_segment_format if ( $ exif_data [ 2 ] === 0xFF && $ exif_data [ 3 ] === 0xE0 ) { // Get the marker parameter length count $ length = self :: read_2_bytes ( $ fh ) ; $ offsetJfif = $ length + 2 ; // "+ 2" to skip the 2 bytes introducing this additional segment // Length includes itself, so must be at least 2 // Following JFIF data length must be at least 6 if ( $ length < 8 ) { return ; } $ length -= 8 ; // Read JFIF head, check for "JFIF" for ( $ i = 0 ; $ i < 5 ; $ i ++ ) { $ exif_data [ $ i ] = self :: read_1_byte ( $ fh ) ; } if ( $ exif_data [ 0 ] !== 0x4A || $ exif_data [ 1 ] !== 0x46 || $ exif_data [ 2 ] !== 0x49 || $ exif_data [ 3 ] !== 0x46 || $ exif_data [ 4 ] !== 0 ) { return ; } // Read JFIF body for ( $ i = 0 ; $ i < $ length ; $ i ++ ) { $ exif_data [ $ i ] = self :: read_1_byte ( $ fh ) ; } if ( self :: read_1_byte ( $ fh ) !== 0 ) { // Seems there is a 0 byte to separate segments... return ; } // Read next 2 bytes in $exif_data[2] and $exif_data[3] as Exif APP1 segment // is now expected $ exif_data [ 2 ] = self :: read_1_byte ( $ fh ) ; $ exif_data [ 3 ] = self :: read_1_byte ( $ fh ) ; } // Exif APP1 if ( $ exif_data [ 2 ] !== 0xFF || $ exif_data [ 3 ] !== 0xE1 ) { return ; } // Get the marker parameter length count $ length = self :: read_2_bytes ( $ fh ) ; // Length includes itself, so must be at least 2 // Following Exif data length must be at least 6 if ( $ length < 8 ) { return ; } $ length -= 8 ; // Read Exif head, check for "Exif" for ( $ i = 0 ; $ i < 6 ; $ i ++ ) { $ exif_data [ $ i ] = self :: read_1_byte ( $ fh ) ; } if ( $ exif_data [ 0 ] !== 0x45 || $ exif_data [ 1 ] !== 0x78 || $ exif_data [ 2 ] !== 0x69 || $ exif_data [ 3 ] !== 0x66 || $ exif_data [ 4 ] !== 0 || $ exif_data [ 5 ] !== 0 ) { return ; } // Read Exif body for ( $ i = 0 ; $ i < $ length ; $ i ++ ) { $ exif_data [ $ i ] = self :: read_1_byte ( $ fh ) ; } if ( $ length < 12 ) { // Length of an IFD entry return ; } // Discover byte order if ( $ exif_data [ 0 ] === 0x49 && $ exif_data [ 1 ] === 0x49 ) { $ is_motorola = false ; } elseif ( $ exif_data [ 0 ] === 0x4D && $ exif_data [ 1 ] === 0x4D ) { $ is_motorola = true ; } else { return ; } // Check Tag mark if ( $ is_motorola ) { if ( $ exif_data [ 2 ] !== 0 ) return ; if ( $ exif_data [ 3 ] !== 0x2A ) return ; } else { if ( $ exif_data [ 3 ] !== 0 ) return ; if ( $ exif_data [ 2 ] !== 0x2A ) return ; } // Get first IFD offset (offset to IFD0) if ( $ is_motorola ) { if ( $ exif_data [ 4 ] !== 0 ) return ; if ( $ exif_data [ 5 ] !== 0 ) return ; $ offset = $ exif_data [ 6 ] ; $ offset <<= 8 ; $ offset += $ exif_data [ 7 ] ; } else { if ( $ exif_data [ 7 ] !== 0 ) return ; if ( $ exif_data [ 6 ] !== 0 ) return ; $ offset = $ exif_data [ 5 ] ; $ offset <<= 8 ; $ offset += $ exif_data [ 4 ] ; } // Check end of data segment if ( $ offset > $ length - 2 ) return ; // Get the number of directory entries contained in this IFD if ( $ is_motorola ) { $ number_of_tags = $ exif_data [ $ offset ] ; $ number_of_tags <<= 8 ; $ number_of_tags += $ exif_data [ $ offset + 1 ] ; } else { $ number_of_tags = $ exif_data [ $ offset + 1 ] ; $ number_of_tags <<= 8 ; $ number_of_tags += $ exif_data [ $ offset ] ; } if ( $ number_of_tags === 0 ) return ; $ offset += 2 ; // Search for Orientation Tag in IFD0 while ( true ) { // Check end of data segment if ( $ offset > $ length - 12 ) return ; // Get Tag number if ( $ is_motorola ) { $ tagnum = $ exif_data [ $ offset ] ; $ tagnum <<= 8 ; $ tagnum += $ exif_data [ $ offset + 1 ] ; } else { $ tagnum = $ exif_data [ $ offset + 1 ] ; $ tagnum <<= 8 ; $ tagnum += $ exif_data [ $ offset ] ; } // Found Orientation Tag if ( $ tagnum === 0x0112 ) break ; if ( -- $ number_of_tags === 0 ) return ; $ offset += 12 ; } // Set the Orientation value if ( $ is_motorola ) { $ exif_data [ $ offset + 2 ] = 0 ; // Format = unsigned short (2 octets) $ exif_data [ $ offset + 3 ] = 3 ; $ exif_data [ $ offset + 4 ] = 0 ; // Number of Components = 1 $ exif_data [ $ offset + 5 ] = 0 ; $ exif_data [ $ offset + 6 ] = 0 ; $ exif_data [ $ offset + 7 ] = 1 ; $ exif_data [ $ offset + 8 ] = 0 ; $ exif_data [ $ offset + 9 ] = $ orientation ; $ exif_data [ $ offset + 10 ] = 0 ; $ exif_data [ $ offset + 11 ] = 0 ; } else { $ exif_data [ $ offset + 2 ] = 3 ; // Format = unsigned short (2 octets) $ exif_data [ $ offset + 3 ] = 0 ; $ exif_data [ $ offset + 4 ] = 1 ; // Number of Components = 1 $ exif_data [ $ offset + 5 ] = 0 ; $ exif_data [ $ offset + 6 ] = 0 ; $ exif_data [ $ offset + 7 ] = 0 ; $ exif_data [ $ offset + 8 ] = $ orientation ; $ exif_data [ $ offset + 9 ] = 0 ; $ exif_data [ $ offset + 10 ] = 0 ; $ exif_data [ $ offset + 11 ] = 0 ; } fseek ( $ fh , ( 4 + 2 + 6 + 2 ) + $ offsetJfif + $ offset , SEEK_SET ) ; $ data = '' ; for ( $ i = 0 ; $ i < 10 ; $ i ++ ) { $ data .= chr ( $ exif_data [ $ i + $ offset + 2 ] ) ; } fwrite ( $ fh , $ data ) ; fclose ( $ fh ) ; }
Sets the Exif Orientation for a given JPG file.

@param string $fileName
@param integer $orientation
@return void
@throws \RuntimeException
Sets the Exif Orientation for a given JPG file .