From c3185ba5ef2c36257dcae8f78e8edcd1cadc0445 Mon Sep 17 00:00:00 2001 From: Marek Szymczuk Date: Wed, 12 Aug 2015 13:05:00 +0200 Subject: [PATCH 1/4] Fixed normalization of "ExposureTime" (prevent division by zero) --- lib/PHPExif/Mapper/Exiftool.php | 9 ++++++++- lib/PHPExif/Mapper/Native.php | 23 ++++++++++++++--------- 2 files changed, 22 insertions(+), 10 deletions(-) diff --git a/lib/PHPExif/Mapper/Exiftool.php b/lib/PHPExif/Mapper/Exiftool.php index ab6fd4d..43af664 100644 --- a/lib/PHPExif/Mapper/Exiftool.php +++ b/lib/PHPExif/Mapper/Exiftool.php @@ -143,7 +143,14 @@ public function mapRawData(array $data) } break; case self::EXPOSURETIME: - $value = '1/' . round(1 / $value); + // Based on the source code of Exiftool (PrintExposureTime subroutine): + // http://cpansearch.perl.org/src/EXIFTOOL/Image-ExifTool-9.90/lib/Image/ExifTool/Exif.pm + if ($value < 0.25001 && $value > 0) { + $value = sprintf('1/%d', intval(0.5 + 1 / $value)); + } else { + $value = sprintf('%.1f', $value); + $value = preg_replace('/.0$/', '', $value); + } break; case self::FOCALLENGTH: $focalLengthParts = explode(' ', $value); diff --git a/lib/PHPExif/Mapper/Native.php b/lib/PHPExif/Mapper/Native.php index 96eecad..0268cfd 100644 --- a/lib/PHPExif/Mapper/Native.php +++ b/lib/PHPExif/Mapper/Native.php @@ -150,13 +150,18 @@ public function mapRawData(array $data) } break; case self::EXPOSURETIME: - // normalize ExposureTime - // on one test image, it reported "10/300" instead of "1/30" - list($counter, $denominator) = explode('/', $value); - if (intval($counter) !== 1) { - $denominator /= $counter; + if (!is_float($value)) { + $value = $this->normalizeComponent($value); + } + + // Based on the source code of Exiftool (PrintExposureTime subroutine): + // http://cpansearch.perl.org/src/EXIFTOOL/Image-ExifTool-9.90/lib/Image/ExifTool/Exif.pm + if ($value < 0.25001 && $value > 0) { + $value = sprintf('1/%d', intval(0.5 + 1 / $value)); + } else { + $value = sprintf('%.1f', $value); + $value = preg_replace('/.0$/', '', $value); } - $value = '1/' . round($denominator); break; case self::FOCALLENGTH: $parts = explode('/', $value); @@ -217,7 +222,7 @@ protected function isSection($field) */ protected function extractGPSCoordinate(array $components) { - $components = array_map(array($this, 'normalizeGPSComponent'), $components); + $components = array_map(array($this, 'normalizeComponent'), $components); if (count($components) > 2) { return intval($components[0]) + (intval($components[1]) / 60) + (floatval($components[2]) / 3600); @@ -227,12 +232,12 @@ protected function extractGPSCoordinate(array $components) } /** - * Normalize GPS coordinates components + * Normalize component * * @param mixed $component * @return int|float */ - protected function normalizeGPSComponent($component) + protected function normalizeComponent($component) { $parts = explode('/', $component); From 9cd8186012e378efa3cc263fed1d3e380426f8c2 Mon Sep 17 00:00:00 2001 From: Marek Szymczuk Date: Wed, 12 Aug 2015 13:05:22 +0200 Subject: [PATCH 2/4] Updated tests --- tests/PHPExif/Mapper/ExiftoolMapperTest.php | 13 ++++++-- tests/PHPExif/Mapper/NativeMapperTest.php | 36 +++++++++++++++++++-- 2 files changed, 43 insertions(+), 6 deletions(-) diff --git a/tests/PHPExif/Mapper/ExiftoolMapperTest.php b/tests/PHPExif/Mapper/ExiftoolMapperTest.php index e4c1e87..92224b5 100644 --- a/tests/PHPExif/Mapper/ExiftoolMapperTest.php +++ b/tests/PHPExif/Mapper/ExiftoolMapperTest.php @@ -138,12 +138,19 @@ public function testMapRawDataCorrectlyIgnoresIncorrectCreationDate() public function testMapRawDataCorrectlyFormatsExposureTime() { $rawData = array( - \PHPExif\Mapper\Exiftool::EXPOSURETIME => 1/400, + '1/30' => 10/300, + '1/400' => 2/800, + '1/400' => 1/400, + '0' => 0, ); - $mapped = $this->mapper->mapRawData($rawData); + foreach ($rawData as $expected => $value) { + $mapped = $this->mapper->mapRawData(array( + \PHPExif\Mapper\Exiftool::EXPOSURETIME => $value, + )); - $this->assertEquals('1/400', reset($mapped)); + $this->assertEquals($expected, reset($mapped)); + } } /** diff --git a/tests/PHPExif/Mapper/NativeMapperTest.php b/tests/PHPExif/Mapper/NativeMapperTest.php index 5eaff51..a18bea4 100644 --- a/tests/PHPExif/Mapper/NativeMapperTest.php +++ b/tests/PHPExif/Mapper/NativeMapperTest.php @@ -108,12 +108,19 @@ public function testMapRawDataCorrectlyIgnoresIncorrectDateTimeOriginal() public function testMapRawDataCorrectlyFormatsExposureTime() { $rawData = array( - \PHPExif\Mapper\Native::EXPOSURETIME => '2/800', + '1/30' => 10/300, + '1/400' => 2/800, + '1/400' => 1/400, + '0' => 0, ); - $mapped = $this->mapper->mapRawData($rawData); + foreach ($rawData as $expected => $value) { + $mapped = $this->mapper->mapRawData(array( + \PHPExif\Mapper\Native::EXPOSURETIME => $value, + )); - $this->assertEquals('1/400', reset($mapped)); + $this->assertEquals($expected, reset($mapped)); + } } /** @@ -251,4 +258,27 @@ public function testMapRawDataCorrectlyIgnoresInvalidCreateDate() $result ); } + + /** + * @group mapper + * @covers \PHPExif\Mapper\Native::normalizeComponent + */ + public function testNormalizeComponentCorrectly() + { + $reflMethod = new \ReflectionMethod('\PHPExif\Mapper\Native', 'normalizeComponent'); + $reflMethod->setAccessible(true); + + $rawData = array( + '2/800' => 0.0025, + '1/400' => 0.0025, + '0/1' => 0, + '0' => 0, + ); + + foreach ($rawData as $value => $expected) { + $normalized = $reflMethod->invoke($this->mapper, $value); + + $this->assertEquals($expected, $normalized); + } + } } From eb5a528f936042f93b5dc62b141c7e176701622a Mon Sep 17 00:00:00 2001 From: Marek Szymczuk Date: Wed, 12 Aug 2015 13:38:44 +0200 Subject: [PATCH 3/4] Added check for case when exposure time is already a decimal value. --- lib/PHPExif/Exif.php | 4 ++++ tests/PHPExif/ExifTest.php | 17 +++++++++++++---- 2 files changed, 17 insertions(+), 4 deletions(-) diff --git a/lib/PHPExif/Exif.php b/lib/PHPExif/Exif.php index 126e2b1..1f7e2ce 100755 --- a/lib/PHPExif/Exif.php +++ b/lib/PHPExif/Exif.php @@ -346,6 +346,10 @@ public function getExposureMilliseconds() return false; } + if (is_float($this->data[self::EXPOSURE])) { + return $this->data[self::EXPOSURE]; + } + $exposureParts = explode('/', $this->data[self::EXPOSURE]); return (int) reset($exposureParts) / (int) end($exposureParts); diff --git a/tests/PHPExif/ExifTest.php b/tests/PHPExif/ExifTest.php index 964a917..5191814 100755 --- a/tests/PHPExif/ExifTest.php +++ b/tests/PHPExif/ExifTest.php @@ -219,10 +219,19 @@ public function testGetExposure() */ public function testGetExposureMilliseconds() { - $expected = 1/320; - $data[\PHPExif\Exif::EXPOSURE] = '1/320'; - $this->exif->setData($data); - $this->assertEquals($expected, $this->exif->getExposureMilliseconds()); + $rawData = array( + array(1/300, '1/300'), + array(0.0025, 0.0025), + ); + + foreach ($rawData as $data) { + $expected = reset($data); + $value = end($data); + + $data[\PHPExif\Exif::EXPOSURE] = $value; + $this->exif->setData($data); + $this->assertEquals($expected, $this->exif->getExposureMilliseconds()); + } } /** From 689a0ac582a3ab4d80417a9f7d2539b9269d8fdd Mon Sep 17 00:00:00 2001 From: Marek Szymczuk Date: Wed, 12 Aug 2015 13:52:39 +0200 Subject: [PATCH 4/4] Cast exposure time to float --- lib/PHPExif/Exif.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/PHPExif/Exif.php b/lib/PHPExif/Exif.php index 1f7e2ce..e6614e4 100755 --- a/lib/PHPExif/Exif.php +++ b/lib/PHPExif/Exif.php @@ -346,8 +346,8 @@ public function getExposureMilliseconds() return false; } - if (is_float($this->data[self::EXPOSURE])) { - return $this->data[self::EXPOSURE]; + if (is_numeric($this->data[self::EXPOSURE])) { + return $this->data[self::EXPOSURE] + 0; } $exposureParts = explode('/', $this->data[self::EXPOSURE]);