Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Strip parameter to encoders that support exif #1420

Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions src/Drivers/Imagick/Driver.php
Original file line number Diff line number Diff line change
Expand Up @@ -171,4 +171,21 @@ public static function version(): string

return $matches['version'];
}

/**
* @param Imagick $imagick
*
* @throws \ImagickException
* @return void
*/
public static function stripExifKeepICCProfiles(Imagick $imagick): void
olivervogel marked this conversation as resolved.
Show resolved Hide resolved
{
$profiles = $imagick->getImageProfiles('icc');

$imagick->stripImage();

if ($profiles !== []) {
$imagick->profileImage("icc", $profiles['icc']);
}
}
}
5 changes: 5 additions & 0 deletions src/Drivers/Imagick/Encoders/AvifEncoder.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
namespace Intervention\Image\Drivers\Imagick\Encoders;

use Imagick;
use Intervention\Image\Drivers\Imagick\Driver;
use Intervention\Image\EncodedImage;
use Intervention\Image\Encoders\AvifEncoder as GenericAvifEncoder;
use Intervention\Image\Interfaces\EncodedImageInterface;
Expand All @@ -26,6 +27,10 @@ public function encode(ImageInterface $image): EncodedImageInterface
$imagick->setCompressionQuality($this->quality);
$imagick->setImageCompressionQuality($this->quality);

if ($this->strip) {
Driver::stripExifKeepICCProfiles($imagick);
}

return new EncodedImage($imagick->getImagesBlob(), 'image/avif');
}
}
5 changes: 5 additions & 0 deletions src/Drivers/Imagick/Encoders/HeicEncoder.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

namespace Intervention\Image\Drivers\Imagick\Encoders;

use Intervention\Image\Drivers\Imagick\Driver;
use Intervention\Image\EncodedImage;
use Intervention\Image\Encoders\HeicEncoder as GenericHeicEncoder;
use Intervention\Image\Interfaces\EncodedImageInterface;
Expand All @@ -22,6 +23,10 @@ public function encode(ImageInterface $image): EncodedImageInterface
$imagick->setCompressionQuality($this->quality);
$imagick->setImageCompressionQuality($this->quality);

if ($this->strip) {
Driver::stripExifKeepICCProfiles($imagick);
}

return new EncodedImage($imagick->getImagesBlob(), 'image/heic');
}
}
5 changes: 5 additions & 0 deletions src/Drivers/Imagick/Encoders/Jpeg2000Encoder.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
namespace Intervention\Image\Drivers\Imagick\Encoders;

use Imagick;
use Intervention\Image\Drivers\Imagick\Driver;
use Intervention\Image\EncodedImage;
use Intervention\Image\Encoders\Jpeg2000Encoder as GenericJpeg2000Encoder;
use Intervention\Image\Interfaces\ImageInterface;
Expand All @@ -28,6 +29,10 @@ public function encode(ImageInterface $image): EncodedImageInterface
$imagick->setCompressionQuality($this->quality);
$imagick->setImageCompressionQuality($this->quality);

if ($this->strip) {
Driver::stripExifKeepICCProfiles($imagick);
}

return new EncodedImage($imagick->getImagesBlob(), 'image/jp2');
}
}
6 changes: 6 additions & 0 deletions src/Drivers/Imagick/Encoders/JpegEncoder.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
namespace Intervention\Image\Drivers\Imagick\Encoders;

use Imagick;
use Intervention\Image\Drivers\Imagick\Driver;
use Intervention\Image\EncodedImage;
use Intervention\Image\Encoders\JpegEncoder as GenericJpegEncoder;
use Intervention\Image\Interfaces\EncodedImageInterface;
Expand All @@ -30,6 +31,7 @@ public function encode(ImageInterface $image): EncodedImageInterface
// possible full transparent colors as black
$background->setColorValue(Imagick::COLOR_ALPHA, 1);

/** @var Imagick $imagick */
$imagick = $image->core()->native();
$imagick->setImageBackgroundColor($background);
$imagick->setBackgroundColor($background);
Expand All @@ -45,6 +47,10 @@ public function encode(ImageInterface $image): EncodedImageInterface
$imagick->setInterlaceScheme(Imagick::INTERLACE_PLANE);
}

if ($this->strip) {
Driver::stripExifKeepICCProfiles($imagick);
}

return new EncodedImage($imagick->getImagesBlob(), 'image/jpeg');
}
}
5 changes: 5 additions & 0 deletions src/Drivers/Imagick/Encoders/TiffEncoder.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

namespace Intervention\Image\Drivers\Imagick\Encoders;

use Intervention\Image\Drivers\Imagick\Driver;
use Intervention\Image\EncodedImage;
use Intervention\Image\Encoders\TiffEncoder as GenericTiffEncoder;
use Intervention\Image\Interfaces\ImageInterface;
Expand All @@ -24,6 +25,10 @@ public function encode(ImageInterface $image): EncodedImageInterface
$imagick->setCompressionQuality($this->quality);
$imagick->setImageCompressionQuality($this->quality);

if ($this->strip) {
Driver::stripExifKeepICCProfiles($imagick);
}

return new EncodedImage($imagick->getImagesBlob(), 'image/tiff');
}
}
5 changes: 5 additions & 0 deletions src/Drivers/Imagick/Encoders/WebpEncoder.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

use Imagick;
use ImagickPixel;
use Intervention\Image\Drivers\Imagick\Driver;
use Intervention\Image\EncodedImage;
use Intervention\Image\Encoders\WebpEncoder as GenericWebpEncoder;
use Intervention\Image\Interfaces\EncodedImageInterface;
Expand Down Expand Up @@ -36,6 +37,10 @@ public function encode(ImageInterface $image): EncodedImageInterface
$imagick->setOption('webp:lossless', 'true');
}

if ($this->strip) {
Driver::stripExifKeepICCProfiles($imagick);
}

return new EncodedImage($imagick->getImagesBlob(), 'image/webp');
}
}
7 changes: 5 additions & 2 deletions src/Encoders/AvifEncoder.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,12 @@ class AvifEncoder extends SpecializableEncoder
* Create new encoder object
*
* @param int $quality
* @param bool $strip
* @return void
*/
public function __construct(public int $quality = self::DEFAULT_QUALITY)
{
public function __construct(
public int $quality = self::DEFAULT_QUALITY,
public bool $strip = false
) {
}
}
7 changes: 5 additions & 2 deletions src/Encoders/HeicEncoder.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,12 @@ class HeicEncoder extends SpecializableEncoder
* Create new encoder object
*
* @param int $quality
* @param bool $strip
* @return void
*/
public function __construct(public int $quality = self::DEFAULT_QUALITY)
{
public function __construct(
public int $quality = self::DEFAULT_QUALITY,
public bool $strip = false
) {
}
}
7 changes: 5 additions & 2 deletions src/Encoders/Jpeg2000Encoder.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,12 @@ class Jpeg2000Encoder extends SpecializableEncoder
* Create new encoder object
*
* @param int $quality
* @param bool $strip
* @return void
*/
public function __construct(public int $quality = self::DEFAULT_QUALITY)
{
public function __construct(
public int $quality = self::DEFAULT_QUALITY,
public bool $strip = false
) {
}
}
4 changes: 3 additions & 1 deletion src/Encoders/JpegEncoder.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,13 @@ class JpegEncoder extends SpecializableEncoder
*
* @param int $quality
* @param bool $progressive
* @param bool $strip
* @return void
*/
public function __construct(
public int $quality = self::DEFAULT_QUALITY,
public bool $progressive = false
public bool $progressive = false,
public bool $strip = false
) {
}
}
7 changes: 5 additions & 2 deletions src/Encoders/TiffEncoder.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,12 @@ class TiffEncoder extends SpecializableEncoder
* Create new encoder object
*
* @param int $quality
* @param bool $strip
* @return void
*/
public function __construct(public int $quality = self::DEFAULT_QUALITY)
{
public function __construct(
public int $quality = self::DEFAULT_QUALITY,
public bool $strip = false
) {
}
}
7 changes: 5 additions & 2 deletions src/Encoders/WebpEncoder.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,12 @@ class WebpEncoder extends SpecializableEncoder
* Create new encoder object
*
* @param int $quality
* @param bool $strip
* @return void
*/
public function __construct(public int $quality = self::DEFAULT_QUALITY)
{
public function __construct(
public int $quality = self::DEFAULT_QUALITY,
public bool $strip = false
) {
}
}
13 changes: 13 additions & 0 deletions tests/Unit/Drivers/Gd/Encoders/JpegEncoderTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -37,4 +37,17 @@ public function testEncodeProgressive(): void
$this->assertEquals('image/jpeg', $result->mimetype());
$this->assertTrue($this->isProgressiveJpeg($result));
}

public function testEncodeStripExif(): void
Copy link
Member

@olivervogel olivervogel Jan 18, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The GD thing is tricky. There is no real function to delete meta data, but it is possible to create a new GDImage object and copy the data into it. The metadata is not transferred.

However, this does not lead to a consistent result. As the copy action described is used at various points in modifiers in order to achieve different results.

  • src/Drivers/Gd/Modifiers/RotateModifier.php:83
  • src/Drivers/Gd/Modifiers/ContainModifier.php:53
  • src/Drivers/Gd/Modifiers/QuantizeColorsModifier.php:45
  • src/Drivers/Gd/Modifiers/BlendTransparencyModifier.php:25
  • src/Drivers/Gd/Modifiers/CropModifier.php:46
  • src/Drivers/Gd/Modifiers/ResizeModifier.php:40
  • src/Drivers/Gd/Modifiers/CoverModifier.php:40

Basically, the metadata is (almost) always lost with GD and Intervention Image, even if this is not desired. :( This is also the reason why I have not yet integrated a “strip” feature.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Metadata is apparently not taken into account at all with GD.

$data = fopen('php://temp', 'r+');
$img = imagecreatefromjpeg('exif.jpg'); // decode
imagejpeg($img, $data); // encode
$exif = exif_read_data($data); // exif gone

{
$image = $this->readTestImage('exif.jpg');
$this->assertEquals('Oliver Vogel', $image->exif('IFD0.Artist'));

$encoder = new JpegEncoder(strip: true);
$encoder->setDriver(new Driver());
$result = $encoder->encode($image);
$this->assertMediaType('image/jpeg', $result);
$this->assertEquals('image/jpeg', $result->mimetype());
$this->assertEmpty(exif_read_data($result->toFilePointer())['IFD0.Artist'] ?? null);
}
}
31 changes: 31 additions & 0 deletions tests/Unit/Drivers/Imagick/Encoders/JpegEncoderTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

namespace Intervention\Image\Tests\Unit\Drivers\Imagick\Encoders;

use Intervention\Image\Drivers\Imagick\Decoders\FilePointerImageDecoder;
use Intervention\Image\Drivers\Imagick\Driver;
use Intervention\Image\Drivers\Imagick\Encoders\JpegEncoder;
use PHPUnit\Framework\Attributes\CoversClass;
Expand Down Expand Up @@ -37,4 +38,34 @@ public function testEncodeProgressive(): void
$this->assertEquals('image/jpeg', $result->mimetype());
$this->assertTrue($this->isProgressiveJpeg($result));
}

public function testEncodeStripExif(): void
{
$image = $this->readTestImage('exif.jpg');
$this->assertEquals('Oliver Vogel', $image->exif('IFD0.Artist'));

$encoder = new JpegEncoder(strip: true);
$encoder->setDriver(new Driver());
$result = $encoder->encode($image);
$this->assertMediaType('image/jpeg', $result);
$this->assertEquals('image/jpeg', $result->mimetype());

$this->assertEmpty(exif_read_data($result->toFilePointer())['IFD0.Artist'] ?? null);
}

public function testEncodeStripExifKeepICCProfiles(): void
{
$image = $this->readTestImage('cmyk.jpg');
$this->assertNotEmpty($image->core()->native()->getImageProfiles('icc'));

$encoder = new JpegEncoder(strip: true);
$encoder->setDriver(new Driver());
$result = $encoder->encode($image);

$decoder = new FilePointerImageDecoder();
$decoder->setDriver(new Driver());

$image = $decoder->decode($result->toFilePointer());
$this->assertNotEmpty($image->core()->native()->getImageProfiles('icc'));
}
}
Loading