$length ) { return self::NORMAL; } if ( 0xE1 === $marker && "Exif\0\0" === substr( $jpeg, $offset + 4, 6 ) ) { return self::from_tiff( substr( $jpeg, $offset + 10, $segment_length - 8 ) ); } $offset += 2 + $segment_length; } return self::NORMAL; } /** * Reads the tag from IFD0 of a TIFF block. * * @param string $tiff TIFF header and IFDs. */ private static function from_tiff( string $tiff ): int { $length = strlen( $tiff ); if ( $length < 8 ) { return self::NORMAL; } $order = substr( $tiff, 0, 2 ); if ( 'MM' !== $order && 'II' !== $order ) { return self::NORMAL; } $big_endian = 'MM' === $order; $ifd = self::u32( $tiff, 4, $big_endian ); if ( $ifd + 2 > $length ) { return self::NORMAL; } $entries = self::u16( $tiff, $ifd, $big_endian ); for ( $i = 0; $i < $entries; $i++ ) { $entry = $ifd + 2 + $i * 12; if ( $entry + 12 > $length ) { return self::NORMAL; } if ( self::TAG_ORIENTATION === self::u16( $tiff, $entry, $big_endian ) ) { $value = self::u16( $tiff, $entry + 8, $big_endian ); return ( $value >= 1 && $value <= 8 ) ? $value : self::NORMAL; } } return self::NORMAL; } /** * Unsigned 16-bit integer at $offset. * * @param string $bytes Data. * @param int $offset Byte offset. * @param bool $big_endian Byte order. */ private static function u16( string $bytes, int $offset, bool $big_endian ): int { $unpacked = unpack( $big_endian ? 'n' : 'v', $bytes, $offset ); return false === $unpacked ? 0 : (int) $unpacked[1]; } /** * Unsigned 32-bit integer at $offset. * * @param string $bytes Data. * @param int $offset Byte offset. * @param bool $big_endian Byte order. */ private static function u32( string $bytes, int $offset, bool $big_endian ): int { $unpacked = unpack( $big_endian ? 'N' : 'V', $bytes, $offset ); return false === $unpacked ? 0 : (int) $unpacked[1]; } }