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);
    }
Sets the Exif Orientation for a given JPG file.

@param string $fileName
@param integer $orientation
@return void
@throws \RuntimeException