diff --git a/Classes/Domain/Extractor/ExifExtractor.php b/Classes/Domain/Extractor/ExifExtractor.php index 8a726a1..d25ad1e 100644 --- a/Classes/Domain/Extractor/ExifExtractor.php +++ b/Classes/Domain/Extractor/ExifExtractor.php @@ -127,94 +127,100 @@ class ExifExtractor extends AbstractExtractor 'GPSDestLatitude', ]; + /** + * @var array + */ + protected static $subSecondProperties = [ + 'SubSecTime' => 'DateTime', + 'SubSecTimeOriginal' => 'DateTimeOriginal', + 'SubSecTimeDigitized' => 'DateTimeDigitized', + ]; + + /** + * @var array + */ + protected static $timeOffsetProperties = [ + 'OffsetTime' => 'DateTime', + 'OffsetTimeOriginal' => 'DateTimeOriginal', + 'OffsetTimeDigitized' => 'DateTimeDigitized', + ]; + /** * @inheritdoc */ public function extractMetaData(FlowResource $resource, MetaDataCollection $metaDataCollection) { - $convertedExifData = exif_read_data($resource->createTemporaryLocalCopy(), 'EXIF'); - if ($convertedExifData === false) { + $exifData = exif_read_data($resource->createTemporaryLocalCopy(), 'EXIF'); + if ($exifData === false) { throw new ExtractorException(sprintf('EXIF data of flow resource %s could not be extracted.', $resource->getSha1()), 1486675463); } foreach (static::$deprecatedOrUnmappedProperties as $deprecatedOrUnmappedProperty => $newProperty) { - if (isset($convertedExifData[$deprecatedOrUnmappedProperty])) { - $convertedExifData[$newProperty] = $convertedExifData[$deprecatedOrUnmappedProperty]; - unset($convertedExifData[$deprecatedOrUnmappedProperty]); + if (isset($exifData[$deprecatedOrUnmappedProperty])) { + $exifData[$newProperty] = $exifData[$deprecatedOrUnmappedProperty]; + unset($exifData[$deprecatedOrUnmappedProperty]); } } foreach (static::$rationalProperties as $rationalProperty) { - if (isset($convertedExifData[$rationalProperty])) { - $convertedExifData[$rationalProperty] = NumberConverter::convertRationalToFloat($convertedExifData[$rationalProperty]); + if (isset($exifData[$rationalProperty])) { + $exifData[$rationalProperty] = NumberConverter::convertRationalToFloat($exifData[$rationalProperty]); } } foreach (static::$rationalArrayProperties as $rationalArrayProperty) { - if (isset($convertedExifData[$rationalArrayProperty])) { - foreach ($convertedExifData[$rationalArrayProperty] as $key => $value) { - $convertedExifData[$rationalArrayProperty][$key] = NumberConverter::convertRationalToFloat($value); + if (isset($exifData[$rationalArrayProperty])) { + foreach ($exifData[$rationalArrayProperty] as $key => $value) { + $exifData[$rationalArrayProperty][$key] = NumberConverter::convertRationalToFloat($value); } } } - if (isset($convertedExifData['GPSVersionID'])) { - $convertedExifData['GPSVersionID'] = NumberConverter::convertBinaryToVersion($convertedExifData['GPSVersionID']); + if (isset($exifData['GPSVersionID'])) { + $exifData['GPSVersionID'] = NumberConverter::convertBinaryToVersion($exifData['GPSVersionID']); } - if (isset($convertedExifData['GPSAltitudeRef'], $convertedExifData['GPSAltitude'])) { - if ($convertedExifData['GPSAltitudeRef'] === 1) { - $convertedExifData['GPSAltitude'] = -$convertedExifData['GPSAltitude']; + if (isset($exifData['GPSAltitudeRef'], $exifData['GPSAltitude'])) { + if ($exifData['GPSAltitudeRef'] === 1) { + $exifData['GPSAltitude'] = -$exifData['GPSAltitude']; } - unset($convertedExifData['GPSAltitudeRef']); + unset($exifData['GPSAltitudeRef']); } foreach (static::$gpsProperties as $gpsProperty) { - if (isset($convertedExifData[$gpsProperty])) { - $convertedExifData[$gpsProperty] = CoordinatesConverter::convertDmsToDd($convertedExifData[$gpsProperty], isset($convertedExifData[$gpsProperty . 'Ref']) ? $convertedExifData[$gpsProperty . 'Ref'] : null); - unset($convertedExifData[$gpsProperty . 'Ref']); + if (isset($exifData[$gpsProperty])) { + $exifData[$gpsProperty] = CoordinatesConverter::convertDmsToDd($exifData[$gpsProperty], isset($exifData[$gpsProperty . 'Ref']) ? $exifData[$gpsProperty . 'Ref'] : null); + unset($exifData[$gpsProperty . 'Ref']); } } - if (isset($convertedExifData['GPSTimeStamp'], $convertedExifData['GPSDateStamp'])) { - $convertedExifData['GPSDateTimeStamp'] = DateConverter::convertGpsDateAndTime($convertedExifData['GPSDateStamp'], $convertedExifData['GPSTimeStamp']); - unset($convertedExifData['GPSTimeStamp'], $convertedExifData['GPSDateStamp']); + if (isset($exifData['GPSTimeStamp'], $exifData['GPSDateStamp'])) { + $exifData['GPSDateTimeStamp'] = DateConverter::convertGpsDateAndTime($exifData['GPSDateStamp'], $exifData['GPSTimeStamp']); + unset($exifData['GPSTimeStamp'], $exifData['GPSDateStamp']); } - foreach ($convertedExifData as $property => $value) { - $convertedExifData[$property] = Exif::interpretValue($property, $value); + foreach ($exifData as $property => $value) { + $exifData[$property] = Exif::interpretValue($property, $value); } - $subSecondProperties = [ - 'SubSecTime' => 'DateTime', - 'SubSecTimeOriginal' => 'DateTimeOriginal', - 'SubSecTimeDigitized' => 'DateTimeDigitized' - ]; - - foreach ($subSecondProperties as $subSecondProperty => $dateTimeProperty) { - if (isset($convertedExifData[$subSecondProperty], $convertedExifData[$dateTimeProperty])) { - $convertedExifData[$dateTimeProperty] = \DateTime::createFromFormat('Y-m-d H:i:s.u', $convertedExifData[$dateTimeProperty]->format('Y-m-d H:i:s.') . $convertedExifData[$subSecondProperty]); - unset($convertedExifData[$subSecondProperty]); + foreach (static::$subSecondProperties as $subSecondProperty => $dateTimeProperty) { + if (isset($exifData[$subSecondProperty], $exifData[$dateTimeProperty])) { + $exifData[$dateTimeProperty] = \DateTime::createFromFormat('Y-m-d H:i:s.u', $exifData[$dateTimeProperty]->format('Y-m-d H:i:s.') . $exifData[$subSecondProperty]); + unset($exifData[$subSecondProperty]); } } - $timeOffsetProperties = [ - 'OffsetTime' => 'DateTime', - 'OffsetTimeOriginal' => 'DateTimeOriginal', - 'OffsetTimeDigitized' => 'DateTimeDigitized', - ]; - - foreach ($timeOffsetProperties as $timeOffsetProperty => $dateTimeProperty) { - if (isset($convertedExifData[$timeOffsetProperty], $convertedExifData[$dateTimeProperty])) { - $convertedExifData[$dateTimeProperty] = \DateTime::createFromFormat('Y-m-d H:i:s.uP', $convertedExifData[$dateTimeProperty]->format('Y-m-d H:i:s.u') . $convertedExifData[$timeOffsetProperty]); - unset($convertedExifData[$timeOffsetProperty]); + foreach (static::$timeOffsetProperties as $timeOffsetProperty => $dateTimeProperty) { + if (isset($exifData[$timeOffsetProperty], $exifData[$dateTimeProperty])) { + $exifData[$dateTimeProperty] = \DateTime::createFromFormat('Y-m-d H:i:s.uP', $exifData[$dateTimeProperty]->format('Y-m-d H:i:s.u') . $exifData[$timeOffsetProperty]); + unset($exifData[$timeOffsetProperty]); } } // wrongly encoded UserComment breaks saving of the whole data set, so check for correct encoding and remove if necessary - if (isset($convertedExifData['UserComment'])) { - $characterCode = substr($convertedExifData['UserComment'], 0, 8); - $value = substr($convertedExifData['UserComment'], 8); + if (isset($exifData['UserComment'])) { + $characterCode = substr($exifData['UserComment'], 0, 8); + $value = substr($exifData['UserComment'], 8); switch ($characterCode) { case chr(0x41) . chr(0x53) . chr(0x43) . chr(0x49) . chr(0x49) . chr(0x0) . chr(0x0) . chr(0x0): // ASCII $encoding = 'US-ASCII'; @@ -229,15 +235,15 @@ public function extractMetaData(FlowResource $resource, MetaDataCollection $meta case chr(0x0) . chr(0x0) . chr(0x0) . chr(0x0) . chr(0x0) . chr(0x0) . chr(0x0) . chr(0x0): // Undefined // try it with ASCII anyway $encoding = 'ASCII'; - $value = $convertedExifData['UserComment']; + $value = $exifData['UserComment']; } if (mb_check_encoding($value, $encoding)) { - $convertedExifData['UserComment'] = $value; + $exifData['UserComment'] = $value; } else { - unset($convertedExifData['UserComment']); + unset($exifData['UserComment']); } } - $metaDataCollection->set('exif', new Dto\Exif($convertedExifData)); + $metaDataCollection->set('exif', new Dto\Exif($exifData)); } } diff --git a/Classes/Domain/Extractor/IptcIimExtractor.php b/Classes/Domain/Extractor/IptcIimExtractor.php index 50c64a0..2213897 100644 --- a/Classes/Domain/Extractor/IptcIimExtractor.php +++ b/Classes/Domain/Extractor/IptcIimExtractor.php @@ -44,6 +44,47 @@ class IptcIimExtractor extends AbstractExtractor 'image/vnd.microsoft.icon', ]; + /** + * @var array + */ + protected static $mapping = [ + 'IntellectualGenres' => Iptc\Iim::OBJECT_ATTRIBUTE_REFERENCE, + 'Title' => Iptc\Iim::OBJECT_NAME, + 'SubjectCodes' => Iptc\Iim::SUBJECT_REFERENCE, + 'Keywords' => Iptc\Iim::KEYWORDS, + 'Instructions' => Iptc\Iim::SPECIAL_INSTRUCTIONS, + 'Creator' => Iptc\Iim::BYLINE, + 'CreatorTitle' => Iptc\Iim::BYLINE_TITLE, + 'City' => Iptc\Iim::CITY, + 'Sublocation' => Iptc\Iim::SUBLOCATION, + 'State' => Iptc\Iim::PROVINCE_STATE, + 'CountryCode' => Iptc\Iim::COUNTRY_PRIMARY_LOCATION_CODE, + 'Country' => Iptc\Iim::COUNTRY_PRIMARY_LOCATION_NAME, + 'JobId' => Iptc\Iim::ORIGINAL_TRANSMISSION_REFERENCE, + 'Headline' => Iptc\Iim::HEADLINE, + 'CreditLine' => Iptc\Iim::CREDIT, + 'Source' => Iptc\Iim::SOURCE, + 'CopyrightNotice' => Iptc\Iim::COPYRIGHT_NOTICE, + 'Contact' => Iptc\Iim::CONTACT, + 'Description' => Iptc\Iim::CAPTION_ABSTRACT, + 'DescriptionWriter' => Iptc\Iim::WRITER_EDITOR, + ]; + + /** + * @var array + */ + protected static $dateTimeMapping = [ + 'CreationDate' => [ + 'date' => Iptc\Iim::DATE_CREATED, + 'time' => Iptc\Iim::TIME_CREATED, + ], + // sometimes used but not really specified in IPTC MetaData + 'DigitalCreationDate' => [ + 'date' => Iptc\Iim::DIGITAL_CREATION_DATE, + 'time' => Iptc\Iim::DIGITAL_CREATION_TIME, + ], + ]; + /** * @inheritdoc */ @@ -52,9 +93,19 @@ public function extractMetaData(FlowResource $resource, MetaDataCollection $meta $iim = new Iptc\Iim($this->readRawIptcData($resource)); $iptcData = []; - $iptcData['IntellectualGenres'] = $iim->getProperty(Iptc\Iim::OBJECT_ATTRIBUTE_REFERENCE); - $iptcData['Title'] = $iim->getProperty(Iptc\Iim::OBJECT_NAME); - $iptcData['SubjectCodes'] = $iim->getProperty(Iptc\Iim::SUBJECT_REFERENCE); + + foreach (static::$mapping as $iptcProperty => $iimProperty) { + $iptcData[$iptcProperty] = $iim->getProperty($iimProperty); + } + + foreach (static::$dateTimeMapping as $iptcProperty => $iimProperties) { + $dateString = $iim->getProperty($iimProperties['date']); + if (!empty($dateString)) { + $timeString = $iim->getProperty($iimProperties['time']); + $dateTimeString = $dateString . (empty($timeString) ? '000000+0000' : $timeString); + $iptcData[$iptcProperty] = \DateTime::createFromFormat('YmdHisO', $dateTimeString); + } + } //caring for deprecated (supplemental) category /** @var array $categories */ @@ -83,29 +134,6 @@ public function extractMetaData(FlowResource $resource, MetaDataCollection $meta $iptcData['DeprecatedCategories'] = $deprecatedCategories; } - $iptcData['Keywords'] = $iim->getProperty(Iptc\Iim::KEYWORDS); - $iptcData['Instructions'] = $iim->getProperty(Iptc\Iim::SPECIAL_INSTRUCTIONS); - - $iptcData['CreationDate'] = DateConverter::convertIso8601DateAndTimeString($iim->getProperty(Iptc\Iim::DATE_CREATED), $iim->getProperty(Iptc\Iim::TIME_CREATED)); - - $iptcData['Creator'] = $iim->getProperty(Iptc\Iim::BYLINE); - $iptcData['CreatorTitle'] = $iim->getProperty(Iptc\Iim::BYLINE_TITLE); - $iptcData['City'] = $iim->getProperty(Iptc\Iim::CITY); - $iptcData['Sublocation'] = $iim->getProperty(Iptc\Iim::SUBLOCATION); - $iptcData['State'] = $iim->getProperty(Iptc\Iim::PROVINCE_STATE); - $iptcData['CountryCode'] = $iim->getProperty(Iptc\Iim::COUNTRY_PRIMARY_LOCATION_CODE); - $iptcData['Country'] = $iim->getProperty(Iptc\Iim::COUNTRY_PRIMARY_LOCATION_NAME); - $iptcData['JobId'] = $iim->getProperty(Iptc\Iim::ORIGINAL_TRANSMISSION_REFERENCE); - $iptcData['Headline'] = $iim->getProperty(Iptc\Iim::HEADLINE); - $iptcData['CreditLine'] = $iim->getProperty(Iptc\Iim::CREDIT); - $iptcData['Source'] = $iim->getProperty(Iptc\Iim::SOURCE); - $iptcData['CopyrightNotice'] = $iim->getProperty(Iptc\Iim::COPYRIGHT_NOTICE); - $iptcData['Contact'] = $iim->getProperty(Iptc\Iim::CONTACT); - $iptcData['Description'] = $iim->getProperty(Iptc\Iim::CAPTION_ABSTRACT); - $iptcData['DescriptionWriter'] = $iim->getProperty(Iptc\Iim::WRITER_EDITOR); - - // sometimes used but not really specified in IPTC MetaData - $iptcData['DigitalCreationDate'] = DateConverter::convertIso8601DateAndTimeString($iim->getProperty(Iptc\Iim::DIGITAL_CREATION_DATE), $iim->getProperty(Iptc\Iim::DIGITAL_CREATION_TIME)); $metaDataCollection->set('iptc', new Dto\Iptc($iptcData)); }