diff --git a/MediaGalleryMetadata/Model/AddIptcMetadata.php b/MediaGalleryMetadata/Model/AddIptcMetadata.php index 1bdc2372eedf..337e5c8f199f 100644 --- a/MediaGalleryMetadata/Model/AddIptcMetadata.php +++ b/MediaGalleryMetadata/Model/AddIptcMetadata.php @@ -10,6 +10,10 @@ use Magento\MediaGalleryMetadataApi\Api\Data\MetadataInterface; use Magento\MediaGalleryMetadataApi\Model\FileInterface; use Magento\MediaGalleryMetadataApi\Model\SegmentInterface; +use Magento\MediaGalleryMetadata\Model\Jpeg\FileReader; +use Magento\Framework\Filesystem\DriverInterface; +use Magento\MediaGalleryMetadataApi\Model\FileInterfaceFactory; +use Magento\Framework\Exception\LocalizedException; /** * Add metadata to the IPTC data @@ -20,57 +24,141 @@ class AddIptcMetadata private const IPTC_DESCRIPTION_SEGMENT = '2#120'; private const IPTC_KEYWORDS_SEGMENT = '2#025'; + /** + * @var DriverInterface + */ + private $driver; + + /** + * @var FileReader + */ + private $fileReader; + + /** + * @var FileInterfaceFactory + */ + private $fileFactory; + + /** + * @param FileInterfaceFactory $fileFactory + * @param DriverInterface $driver + * @param FileReader $fileReader + */ + public function __construct( + FileInterfaceFactory $fileFactory, + DriverInterface $driver, + FileReader $fileReader + ) { + $this->fileFactory = $fileFactory; + $this->driver = $driver; + $this->fileReader = $fileReader; + } + /** * Write metadata * * @param FileInterface $file * @param MetadataInterface $metadata - * @param SegmentInterface $segment - * @return string + * @param null|SegmentInterface $segment */ - public function execute(FileInterface $file, MetadataInterface $metadata, SegmentInterface $segment): string + public function execute(FileInterface $file, MetadataInterface $metadata, ?SegmentInterface $segment): FileInterface { - if (is_callable('iptcembed')) { - $iptcData = iptcparse($segment->getData()); - if (!empty($metadata->getTitle())) { - $iptcData[self::IPTC_TITLE_SEGMENT][0] = $metadata->getTitle(); - } + if (!is_callable('iptcembed') && !is_callable('iptcparse')) { + throw new LocalizedException(__('iptcembed() && iptcparse() must be enabled in php configuration')); + } + + $iptcData = $segment ? iptcparse($segment->getData()) : []; - if (!empty($metadata->getDescription())) { - $iptcData[self::IPTC_DESCRIPTION_SEGMENT][0] = $metadata->getDescription(); - } + if (!empty($metadata->getTitle())) { + $iptcData[self::IPTC_TITLE_SEGMENT][0] = $metadata->getTitle(); + } - if (!empty($metadata->getKeywords())) { - foreach ($metadata->getKeywords() as $key => $keyword) { - $iptcData[self::IPTC_KEYWORDS_SEGMENT][$key] = $keyword; - } - } + if (!empty($metadata->getDescription())) { + $iptcData[self::IPTC_DESCRIPTION_SEGMENT][0] = $metadata->getDescription(); + } + + if (!empty($metadata->getKeywords())) { + $iptcData = $this->writeKeywords($metadata->getKeywords(), $iptcData); + } - $newData = ''; + $newData = ''; - foreach ($iptcData as $tag => $values) { - foreach ($values as $value) { - $newData .= $this->iptcMaketag(2, substr($tag, 2), $value); - } + foreach ($iptcData as $tag => $values) { + foreach ($values as $value) { + $newData .= $this->iptcMaketag(2, (int) substr($tag, 2), $value); } - $content = iptcembed($newData, $file->getPath()); + } + + $this->writeFile($file->getPath(), iptcembed($newData, $file->getPath())); + + $fileWithIptc = $this->fileReader->execute($file->getPath()); + + return $this->fileFactory->create([ + 'path' => $fileWithIptc->getPath(), + 'segments' => $this->getSegmentsWithIptc($fileWithIptc, $file) + ]); + } + + /** + * Return iptc segment from file. + * + * @param FileInterface $fileWithIptc + * @param FileInterface $originFile + */ + private function getSegmentsWithIptc(FileInterface $fileWithIptc, $originFile): array + { + $segments = $fileWithIptc->getSegments(); + $originFileSegments = $originFile->getSegments(); - return $content; + foreach ($segments as $key => $segment) { + if ($segment->getName() === 'APP13') { + $originFileSegments[$key] = $segments[$key]; + return $originFileSegments; + } + } + return $originFileSegments; + } + + /** + * Write keywords field to the iptc segment. + * + * @param array $keywords + * @param array $iptcData + */ + private function writeKeywords(array $keywords, array $iptcData): array + { + foreach ($keywords as $key => $keyword) { + $iptcData[self::IPTC_KEYWORDS_SEGMENT][$key] = $keyword; } + return $iptcData; + } + + /** + * Write iptc data to the image directly to the file. + * + * @param string $filePath + * @param string $content + */ + private function writeFile(string $filePath, string $content): void + { + $resource = $this->driver->fileOpen($filePath, 'wb'); + + $this->driver->fileWrite($resource, $content); + $this->driver->fileClose($resource); } /** * Create new iptc tag text * * @param int $rec - * @param string $tag + * @param int $tag * @param string $value */ - private function iptcMaketag($rec, $tag, $value) + private function iptcMaketag(int $rec, int $tag, string $value) { //phpcs:disable Magento2.Functions.DiscouragedFunction $length = strlen($value); - $retval = chr(0x1C) . chr($rec) . chr((int)$tag); + $retval = chr(0x1C) . chr($rec) . chr($tag); if ($length < 0x8000) { $retval .= chr($length >> 8) . chr($length & 0xFF); diff --git a/MediaGalleryMetadata/Model/Gif/FileReader.php b/MediaGalleryMetadata/Model/Gif/FileReader.php index e109f36f2db4..1ca3941eef1e 100644 --- a/MediaGalleryMetadata/Model/Gif/FileReader.php +++ b/MediaGalleryMetadata/Model/Gif/FileReader.php @@ -144,7 +144,7 @@ private function getSegments($resource): array if ($separator == $gifFrameSeparator) { $segments[] = $this->segmentFactory->create([ 'name' => 'frame', - 'data' => $this->readFrame($resource) + 'data' => $gifFrameSeparator . $this->readFrame($resource) ]); continue; } @@ -154,7 +154,6 @@ private function getSegments($resource): array } $segments[] = $this->getExtensionSegment($resource); - } while (!$this->driver->endOfFile($resource)); return $segments; @@ -169,6 +168,7 @@ private function getSegments($resource): array */ private function getExtensionSegment($resource): SegmentInterface { + $gifExtensionSeparator = pack("C", ord("!")); $extensionCodeBinary = $this->read($resource, 1); //phpcs:ignore Magento2.Functions.DiscouragedFunction $extensionCode = unpack('C', $extensionCodeBinary)[1]; @@ -176,21 +176,21 @@ private function getExtensionSegment($resource): SegmentInterface if ($extensionCode == 0xF9) { return $this->segmentFactory->create([ 'name' => 'Graphics Control Extension', - 'data' => $extensionCodeBinary . $this->readBlock($resource) + 'data' => $gifExtensionSeparator . $extensionCodeBinary . $this->readBlock($resource) ]); } if ($extensionCode == 0xFE) { return $this->segmentFactory->create([ 'name' => 'comment', - 'data' => $extensionCodeBinary . $this->readBlock($resource) + 'data' => $gifExtensionSeparator . $extensionCodeBinary . $this->readBlock($resource) ]); } if ($extensionCode != 0xFF) { return $this->segmentFactory->create([ - 'name' => 'unknown', - 'data' => $extensionCodeBinary . $this->readBlock($resource) + 'name' => 'Programm extension', + 'data' => $gifExtensionSeparator . $extensionCodeBinary . $this->readBlock($resource) ]); } @@ -206,14 +206,15 @@ private function getExtensionSegment($resource): SegmentInterface if ($name == 'XMP DataXMP') { return $this->segmentFactory->create([ 'name' => $name, - 'data' => $extensionCodeBinary . $blockLengthBinary + 'data' => $gifExtensionSeparator . $extensionCodeBinary . $blockLengthBinary . $name . $this->readBlockWithSubblocks($resource) ]); } return $this->segmentFactory->create([ 'name' => $name, - 'data' => $extensionCodeBinary . $blockLengthBinary . $name . $this->readBlock($resource) + 'data' => $gifExtensionSeparator . $extensionCodeBinary . $blockLengthBinary + . $name . $this->readBlock($resource) ]); } @@ -300,17 +301,13 @@ private function readBlockWithSubblocks($resource): string { $data = ''; $subLength = $this->read($resource, 1); - $blocks = 0; while ($subLength !== "\0") { - $blocks++; - $data .= $subLength; - - $data .= $this->read($resource, ord($subLength)); + $data .= $subLength . $this->read($resource, ord($subLength)); $subLength = $this->read($resource, 1); } - return $data; + return $data . $subLength; } /** @@ -327,6 +324,6 @@ private function readBlock($resource): string if ($blockLength == 0) { return ''; } - return $blockLength . $this->read($resource, $blockLength) . $this->read($resource, 1); + return $blockLengthBinary . $this->read($resource, $blockLength) . $this->read($resource, 1); } } diff --git a/MediaGalleryMetadata/Model/Gif/FileWriter.php b/MediaGalleryMetadata/Model/Gif/FileWriter.php new file mode 100644 index 000000000000..5357478e5ba0 --- /dev/null +++ b/MediaGalleryMetadata/Model/Gif/FileWriter.php @@ -0,0 +1,76 @@ +driver = $driver; + $this->segmentNames = $segmentNames; + } + + /** + * Write file object to the filesystem + * + * @param FileInterface $file + * @throws LocalizedException + * @throws FileSystemException + */ + public function execute(FileInterface $file): void + { + $resource = $this->driver->fileOpen($file->getPath(), 'wb'); + + $this->writeSegments($resource, $file->getSegments()); + $this->driver->fileClose($resource); + } + + /** + * Write gif segment + * + * @param resource $resource + * @param SegmentInterface[] $segments + */ + private function writeSegments($resource, array $segments): void + { + foreach ($segments as $segment) { + $this->driver->fileWrite( + $resource, + $segment->getData() + ); + } + $this->driver->fileWrite($resource, pack("C", ord(";"))); + } +} diff --git a/MediaGalleryMetadata/Model/Gif/Segment/XmpReader.php b/MediaGalleryMetadata/Model/Gif/Segment/XmpReader.php index 94515ca34ed2..da94a75ccca8 100644 --- a/MediaGalleryMetadata/Model/Gif/Segment/XmpReader.php +++ b/MediaGalleryMetadata/Model/Gif/Segment/XmpReader.php @@ -24,9 +24,9 @@ class XmpReader implements MetadataReaderInterface /** * see XMP Specification Part 3, 1.1.2 GIF */ - private const MAGIC_TRAILER_LENGTH = 257; + private const MAGIC_TRAILER_LENGTH = 258; private const MAGIC_TRAILER_START = "\x01\xFF\xFE"; - private const MAGIC_TRAILER_END = "\x03\x02\x01\x00"; + private const MAGIC_TRAILER_END = "\x03\x02\x01\x00\x00"; /** * @var MetadataInterfaceFactory @@ -84,10 +84,10 @@ private function isXmp(SegmentInterface $segment): bool */ private function getXmpData(SegmentInterface $segment): string { - $xmp = substr($segment->getData(), 13); + $xmp = substr($segment->getData(), 14); if (substr($xmp, -self::MAGIC_TRAILER_LENGTH, 3) !== self::MAGIC_TRAILER_START - || substr($xmp, -4) !== self::MAGIC_TRAILER_END + || substr($xmp, -5) !== self::MAGIC_TRAILER_END ) { throw new LocalizedException(__('XMP data is corrupted')); } diff --git a/MediaGalleryMetadata/Model/Gif/Segment/XmpWriter.php b/MediaGalleryMetadata/Model/Gif/Segment/XmpWriter.php new file mode 100644 index 000000000000..515bddb76f81 --- /dev/null +++ b/MediaGalleryMetadata/Model/Gif/Segment/XmpWriter.php @@ -0,0 +1,191 @@ +fileFactory = $fileFactory; + $this->segmentFactory = $segmentFactory; + $this->addXmpMetadata = $addXmpMetadata; + $this->xmpTemplate = $xmpTemplate; + } + + /** + * Add metadata to the file + * + * @param FileInterface $file + * @param MetadataInterface $metadata + * @return FileInterface + */ + public function execute(FileInterface $file, MetadataInterface $metadata): FileInterface + { + $gifSegments = $file->getSegments(); + $xmpGifSegments = []; + foreach ($gifSegments as $key => $segment) { + if ($this->isSegmentXmp($segment)) { + $xmpGifSegments[$key] = $segment; + } + } + + if (empty($xmpGifSegments)) { + return $this->fileFactory->create([ + 'path' => $file->getPath(), + 'segments' => $this->insertXmpGifSegment($gifSegments, $this->createXmpSegment($metadata)) + ]); + } + + foreach ($xmpGifSegments as $key => $segment) { + $gifSegments[$key] = $this->updateSegment($segment, $metadata); + } + + return $this->fileFactory->create([ + 'path' => $file->getPath(), + 'segments' => $gifSegments + ]); + } + + /** + * Insert XMP segment to gif image segments (at position 3) + * + * @param SegmentInterface[] $segments + * @param SegmentInterface $xmpSegment + * @return SegmentInterface[] + */ + private function insertXmpGifSegment(array $segments, SegmentInterface $xmpSegment): array + { + return array_merge(array_slice($segments, 0, 4), [$xmpSegment], array_slice($segments, 4)); + } + + /** + * Return XMP template from string + * + * @param string $string + * @param string $start + * @param string $end + */ + private function getXmpData(string $string, string $start, string $end): string + { + $string = ' ' . $string; + $ini = strpos($string, $start); + if ($ini == 0) { + return ''; + } + $ini += strlen($start); + $len = strpos($string, $end, $ini) - $ini; + + return substr($string, $ini, $len); + } + + /** + * Write new segment metadata + * + * @param MetadataInterface $metadata + * @return SegmentInterface + */ + public function createXmpSegment(MetadataInterface $metadata): SegmentInterface + { + $xmpData = $this->xmpTemplate->get(); + + $xmpSegment = pack("C", ord("!")) . pack("C", 255) . pack("C", 11). + self::XMP_SEGMENT_NAME . $this->addXmpMetadata->execute($xmpData, $metadata) . "\x01"; + + /** + * Write Magic trailer 258 bytes see XMP Specification Part 3, 1.1.2 GIF + */ + $i = 255; + while ($i > 0) { + $xmpSegment .= pack("C", $i); + $i--; + } + + return $this->segmentFactory->create([ + 'name' => self::XMP_SEGMENT_NAME, + 'data' => $xmpSegment . "\0\0" + ]); + } + + /** + * Add metadata to the segment + * + * @param SegmentInterface $segment + * @param MetadataInterface $metadata + * @return SegmentInterface + */ + public function updateSegment(SegmentInterface $segment, MetadataInterface $metadata): SegmentInterface + { + $data = $segment->getData(); + $start = substr($data, 0, self::XMP_DATA_START_POSITION); + $xmpData = $this->getXmpData($data, self::XMP_SEGMENT_NAME, "\x01"); + $end = substr($data, strpos($data, "\x01")); + + return $this->segmentFactory->create([ + 'name' => $segment->getName(), + 'data' => $start . $this->addXmpMetadata->execute($xmpData, $metadata) . $end + ]); + } + + /** + * Check if segment contains XMP data + * + * @param SegmentInterface $segment + * @return bool + */ + private function isSegmentXmp(SegmentInterface $segment): bool + { + return $segment->getName() === self::XMP_SEGMENT_NAME; + } +} diff --git a/MediaGalleryMetadata/Model/Jpeg/Segment/IptcWriter.php b/MediaGalleryMetadata/Model/Jpeg/Segment/IptcWriter.php index dcbb5b25e66c..df09ab989302 100644 --- a/MediaGalleryMetadata/Model/Jpeg/Segment/IptcWriter.php +++ b/MediaGalleryMetadata/Model/Jpeg/Segment/IptcWriter.php @@ -14,6 +14,7 @@ use Magento\MediaGalleryMetadataApi\Model\MetadataWriterInterface; use Magento\MediaGalleryMetadataApi\Model\SegmentInterface; use Magento\MediaGalleryMetadataApi\Model\SegmentInterfaceFactory; +use Magento\MediaGalleryMetadata\Model\Jpeg\FileReader; /** * Jpeg IPTC Writer @@ -21,9 +22,9 @@ class IptcWriter implements MetadataWriterInterface { private const IPTC_SEGMENT_NAME = 'APP13'; - private const IPTC_SEGMENT_START = 'Photoshop 3.0'; + private const IPTC_SEGMENT_START = 'Photoshop 3.0\0x00'; private const IPTC_DATA_START_POSITION = 0; - + /** * @var SegmentInterfaceFactory */ @@ -39,19 +40,27 @@ class IptcWriter implements MetadataWriterInterface */ private $addIptcMetadata; + /** + * @var FileReader + */ + private $fileReader; + /** * @param FileInterfaceFactory $fileFactory * @param SegmentInterfaceFactory $segmentFactory * @param AddIptcMetadata $addIptcMetadata + * @param FileReader $fileReader */ public function __construct( FileInterfaceFactory $fileFactory, SegmentInterfaceFactory $segmentFactory, - AddIptcMetadata $addIptcMetadata + AddIptcMetadata $addIptcMetadata, + FileReader $fileReader ) { $this->fileFactory = $fileFactory; $this->segmentFactory = $segmentFactory; $this->addIptcMetadata = $addIptcMetadata; + $this->fileReader = $fileReader; } /** @@ -64,36 +73,17 @@ public function __construct( public function execute(FileInterface $file, MetadataInterface $metadata): FileInterface { $segments = $file->getSegments(); + $iptcSegments = []; foreach ($segments as $key => $segment) { if ($this->isIptcSegment($segment)) { - $segments[$key] = $this->updateSegment($segment, $metadata, $file); + $iptcSegments[$key] = $segment; } } - return $this->fileFactory->create([ - 'path' => $file->getPath(), - 'segments' => $segments - ]); - } - /** - * Add metadata to the segment - * - * @param SegmentInterface $segment - * @param MetadataInterface $metadata - * @param FileInterface $file - * @return SegmentInterface - */ - public function updateSegment( - SegmentInterface $segment, - MetadataInterface $metadata, - FileInterface $file - ): SegmentInterface { - $data = $segment->getData(); - $start = substr($data, 0, self::IPTC_DATA_START_POSITION); - return $this->segmentFactory->create([ - 'name' => $segment->getName(), - 'data' => $start . $this->addIptcMetadata->execute($file, $metadata, $segment) - ]); + foreach ($iptcSegments as $segment) { + return $this->addIptcMetadata->execute($file, $metadata, $segment); + } + return $this->addIptcMetadata->execute($file, $metadata, null); } /** diff --git a/MediaGalleryMetadata/Model/Png/Segment/XmpWriter.php b/MediaGalleryMetadata/Model/Png/Segment/XmpWriter.php index 2d97aa2ed139..e5e1c787f602 100644 --- a/MediaGalleryMetadata/Model/Png/Segment/XmpWriter.php +++ b/MediaGalleryMetadata/Model/Png/Segment/XmpWriter.php @@ -14,9 +14,10 @@ use Magento\MediaGalleryMetadataApi\Model\MetadataWriterInterface; use Magento\MediaGalleryMetadataApi\Model\SegmentInterface; use Magento\MediaGalleryMetadataApi\Model\SegmentInterfaceFactory; +use Magento\MediaGalleryMetadata\Model\XmpTemplate; /** - * XMP Reader + * XMP Writer for png format */ class XmpWriter implements MetadataWriterInterface { @@ -38,23 +39,31 @@ class XmpWriter implements MetadataWriterInterface */ private $addXmpMetadata; + /** + * @var XmpTemplate + */ + private $xmpTemplate; + /** * @param FileInterfaceFactory $fileFactory * @param SegmentInterfaceFactory $segmentFactory * @param AddXmpMetadata $addXmpMetadata + * @param XmpTemplate $xmpTemplate */ public function __construct( FileInterfaceFactory $fileFactory, SegmentInterfaceFactory $segmentFactory, - AddXmpMetadata $addXmpMetadata + AddXmpMetadata $addXmpMetadata, + XmpTemplate $xmpTemplate ) { $this->fileFactory = $fileFactory; $this->segmentFactory = $segmentFactory; $this->addXmpMetadata = $addXmpMetadata; + $this->xmpTemplate = $xmpTemplate; } /** - * Add metadata to the file + * Add xmp metadata to the png file * * @param FileInterface $file * @param MetadataInterface $metadata @@ -63,11 +72,24 @@ public function __construct( public function execute(FileInterface $file, MetadataInterface $metadata): FileInterface { $segments = $file->getSegments(); + $pngXmpSegments = []; foreach ($segments as $key => $segment) { if ($this->isXmpSegment($segment)) { - $segments[$key] = $this->updateSegment($segment, $metadata); + $pngXmpSegments[$key] = $segment; } } + + if (empty($pngXmpSegments)) { + return $this->fileFactory->create([ + 'path' => $file->getPath(), + 'segments' => $this->insertPngXmpSegment($segments, $this->createPngXmpSegment($metadata)) + ]); + } + + foreach ($pngXmpSegments as $key => $segment) { + $segments[$key] = $this->updateSegment($segment, $metadata); + } + return $this->fileFactory->create([ 'path' => $file->getPath(), 'segments' => $segments @@ -75,7 +97,34 @@ public function execute(FileInterface $file, MetadataInterface $metadata): FileI } /** - * Add metadata to the segment + * Insert XMP segment to image png segments (at position 1) + * + * @param SegmentInterface[] $segments + * @param SegmentInterface $xmpSegment + * @return SegmentInterface[] + */ + private function insertPngXmpSegment(array $segments, SegmentInterface $xmpSegment): array + { + return array_merge(array_slice($segments, 0, 2), [$xmpSegment], array_slice($segments, 2)); + } + + /** + * Write new png segment metadata + * + * @param MetadataInterface $metadata + * @return SegmentInterface + */ + public function createPngXmpSegment(MetadataInterface $metadata): SegmentInterface + { + $xmpData = $this->xmpTemplate->get(); + return $this->segmentFactory->create([ + 'name' => self::XMP_SEGMENT_NAME, + 'data' => self::XMP_SEGMENT_START . $this->addXmpMetadata->execute($xmpData, $metadata) + ]); + } + + /** + * Add metadata to the png xmp segment * * @param SegmentInterface $segment * @param MetadataInterface $metadata diff --git a/MediaGalleryMetadata/Test/Integration/Model/AddMetadataTest.php b/MediaGalleryMetadata/Test/Integration/Model/AddMetadataTest.php index f23cd025e7eb..55a8ff24f07e 100644 --- a/MediaGalleryMetadata/Test/Integration/Model/AddMetadataTest.php +++ b/MediaGalleryMetadata/Test/Integration/Model/AddMetadataTest.php @@ -142,8 +142,35 @@ public function filesProvider(): array [ 'magento2', 'community' + ], + ], + [ + 'empty_xmp_image.png', + 'Title of the magento image', + 'Description of the magento image 2', + [ + 'magento2', + 'community' + ], + ], + [ + 'exiftool.gif', + 'Updated Title', + 'Updated Description', + [ + 'magento2', + 'mediagallery' + ] + ], + [ + 'empty_exiftool.gif', + 'Updated Title', + 'Updated Description', + [ + 'magento2', + 'mediagallery' ] - ] + ] ]; } } diff --git a/MediaGalleryMetadata/Test/Integration/Model/Gif/Segment/XmpTest.php b/MediaGalleryMetadata/Test/Integration/Model/Gif/Segment/XmpTest.php new file mode 100644 index 000000000000..2f51a0a0b3d6 --- /dev/null +++ b/MediaGalleryMetadata/Test/Integration/Model/Gif/Segment/XmpTest.php @@ -0,0 +1,117 @@ +xmpWriter = Bootstrap::getObjectManager()->get(XmpWriter::class); + $this->xmpReader = Bootstrap::getObjectManager()->get(XmpReader::class); + $this->fileReader = Bootstrap::getObjectManager()->get(FileReader::class); + $this->driver = Bootstrap::getObjectManager()->get(DriverInterface::class); + $this->metadataFactory = Bootstrap::getObjectManager()->get(MetadataFactory::class); + } + + /** + * Test for XMP reader and writer + * + * @dataProvider filesProvider + * @param string $fileName + * @param string $title + * @param string $description + * @param array $keywords + * @throws LocalizedException + */ + public function testWriteReadGif( + string $fileName, + string $title, + string $description, + array $keywords + ): void { + $path = realpath(__DIR__ . '/../../../../_files/' . $fileName); + $file = $this->fileReader->execute($path); + $originalGifMetadata = $this->xmpReader->execute($file); + + $this->assertEmpty($originalGifMetadata->getTitle()); + $this->assertEmpty($originalGifMetadata->getDescription()); + $this->assertEmpty($originalGifMetadata->getKeywords()); + $updatedGifFile = $this->xmpWriter->execute( + $file, + $this->metadataFactory->create([ + 'title' => $title, + 'description' => $description, + 'keywords' => $keywords + ]) + ); + $updatedGifMetadata = $this->xmpReader->execute($updatedGifFile); + $this->assertEquals($title, $updatedGifMetadata->getTitle()); + $this->assertEquals($description, $updatedGifMetadata->getDescription()); + $this->assertEquals($keywords, $updatedGifMetadata->getKeywords()); + } + + /** + * Data provider for testExecute + * + * @return array[] + */ + public function filesProvider(): array + { + return [ + [ + 'empty_exiftool.gif', + 'Title of the magento image', + 'Description of the magento image 2', + [ + 'magento2', + 'community' + ] + ] + ]; + } +} diff --git a/MediaGalleryMetadata/Test/Integration/Model/Jpeg/Segment/IptcTest.php b/MediaGalleryMetadata/Test/Integration/Model/Jpeg/Segment/IptcTest.php new file mode 100644 index 000000000000..94a9f78b65f4 --- /dev/null +++ b/MediaGalleryMetadata/Test/Integration/Model/Jpeg/Segment/IptcTest.php @@ -0,0 +1,134 @@ +varDirectory = Bootstrap::getObjectManager()->get(Filesystem::class) + ->getDirectoryWrite(DirectoryList::VAR_DIR); + $this->iptcWriter = Bootstrap::getObjectManager()->get(IptcWriter::class); + $this->iptcReader = Bootstrap::getObjectManager()->get(IptcReader::class); + $this->fileReader = Bootstrap::getObjectManager()->get(FileReader::class); + $this->driver = Bootstrap::getObjectManager()->get(DriverInterface::class); + $this->metadataFactory = Bootstrap::getObjectManager()->get(MetadataFactory::class); + } + + /** + * Test for IPTC reader and writer + * + * @dataProvider filesProvider + * @param string $fileName + * @param string $title + * @param string $description + * @param array $keywords + * @throws LocalizedException + */ + public function testWriteRead( + string $fileName, + string $title, + string $description, + array $keywords + ): void { + $path = realpath(__DIR__ . '/../../../../_files/' . $fileName); + $modifiableFilePath = $this->varDirectory->getAbsolutePath($fileName); + $this->driver->copy( + $path, + $modifiableFilePath + ); + $modifiableFilePath = $this->fileReader->execute($modifiableFilePath); + $originalMetadata = $this->iptcReader->execute($modifiableFilePath); + + $this->assertEmpty($originalMetadata->getTitle()); + $this->assertEmpty($originalMetadata->getDescription()); + $this->assertEmpty($originalMetadata->getKeywords()); + + $updatedFile = $this->iptcWriter->execute( + $modifiableFilePath, + $this->metadataFactory->create([ + 'title' => $title, + 'description' => $description, + 'keywords' => $keywords + ]) + ); + + $updatedMetadata = $this->iptcReader->execute($updatedFile); + + $this->assertEquals($title, $updatedMetadata->getTitle()); + $this->assertEquals($description, $updatedMetadata->getDescription()); + $this->assertEquals($keywords, $updatedMetadata->getKeywords()); + } + + /** + * Data provider for testExecute + * + * @return array[] + */ + public function filesProvider(): array + { + return [ + [ + 'empty_iptc.jpeg', + 'Updated Title', + 'Updated Description', + [ + 'magento2', + 'mediagallery' + ] + ] + ]; + } +} diff --git a/MediaGalleryMetadata/Test/_files/empty_exiftool.gif b/MediaGalleryMetadata/Test/_files/empty_exiftool.gif new file mode 100644 index 000000000000..14cc6026b595 Binary files /dev/null and b/MediaGalleryMetadata/Test/_files/empty_exiftool.gif differ diff --git a/MediaGalleryMetadata/Test/_files/empty_iptc.jpeg b/MediaGalleryMetadata/Test/_files/empty_iptc.jpeg new file mode 100644 index 000000000000..144a56dac2d3 Binary files /dev/null and b/MediaGalleryMetadata/Test/_files/empty_iptc.jpeg differ diff --git a/MediaGalleryMetadata/Test/_files/empty_xmp_image.png b/MediaGalleryMetadata/Test/_files/empty_xmp_image.png new file mode 100644 index 000000000000..7e81891ebc0e Binary files /dev/null and b/MediaGalleryMetadata/Test/_files/empty_xmp_image.png differ diff --git a/MediaGalleryMetadata/Test/_files/macos-photos.jpeg b/MediaGalleryMetadata/Test/_files/macos-photos.jpeg index 10c6ca9438ca..3a07b6abe788 100644 Binary files a/MediaGalleryMetadata/Test/_files/macos-photos.jpeg and b/MediaGalleryMetadata/Test/_files/macos-photos.jpeg differ diff --git a/MediaGalleryMetadata/etc/di.xml b/MediaGalleryMetadata/etc/di.xml index a35d7d38e38d..62c1407f95e3 100644 --- a/MediaGalleryMetadata/etc/di.xml +++ b/MediaGalleryMetadata/etc/di.xml @@ -25,6 +25,7 @@ Magento\MediaGalleryMetadata\Model\Jpeg\AddMetadata Magento\MediaGalleryMetadata\Model\Png\AddMetadata + Magento\MediaGalleryMetadata\Model\Gif\AddMetadata @@ -47,6 +48,15 @@ + + + Magento\MediaGalleryMetadata\Model\Gif\FileReader + Magento\MediaGalleryMetadata\Model\Gif\FileWriter + + Magento\MediaGalleryMetadata\Model\Gif\Segment\XmpWriter + + + Magento\MediaGalleryMetadata\Model\Gif\FileReader