15

I having some problems with an image that has EXIF/IPTC data stored in it.
When I use imageCreateFromJpeg (to rotate/crop or etc) the newly stored file doesn't preserve the EXIF/IPTC data.

My current code looks like this:

<?php
// Before executing - EXIF/IPTC data is there (checked)
$image = "/path/to/my/image.jpg";
$source = imagecreatefromjpeg($image);
$rotate = imagerotate($source,90,0);
imageJPEG($rotate,$image);
// After executing  - EXIF/IPTC data doesn't exist anymore. 
?>

Am I doing something wrong?

tftd
  • 16,203
  • 11
  • 62
  • 106

3 Answers3

9

You aren't doing anything wrong, but GD doesn't deal with Exif of IPTC data at all as its beyond the scope of what GD does.

You will have to use a 3rd party library or other PHP extension to read the data from the source image and re-insert it to the output image created by imagejpeg.

Here are some libraries of interest: pel (php exif library), an example on php.net showing how to use pel to do what you want, php metadata toolkit, iptcembed() function.

drew010
  • 68,777
  • 11
  • 134
  • 162
  • 1
    Ahh... So in other words I have to copy the EXIF/IPTC data and store in the new image? – tftd Apr 16 '12 at 23:55
  • 3
    Correct, either before or after creating the image you will have to extract the meta data from the source image. Since you are using `imagejpeg` to output the final image, you have to write it to the final image after you have saved it out. – drew010 Apr 17 '12 at 00:04
  • 1
    It is a big pitty GD doesn't do it, that should be a default! – Tomas Apr 17 '12 at 00:51
  • 3
    Why the library needs to understand exif? Why it is not enough to just blindy copy the particular block of bytes to new image? (I suppose EXIF comes in a single block...) – Tomas Apr 17 '12 at 00:51
  • 2
    @Tomas I though the same thing. I was amazed to find it doesn't copy it! And because EXIF can't be embedded via php functions you need to write yourself a class to put the headers into the image - what a waste of time! – tftd Apr 17 '12 at 02:37
  • 1
    @tftd, yes, you're right! Could you please provide the source of your solution? Just post a link or something. – Tomas Apr 17 '12 at 07:31
  • 1
    I don't have any source yet... working on a solution. But if I eventually come up with something I would definitely post it :) – tftd Apr 17 '12 at 11:08
  • Check out the solution by Thiago Barcala. – cazort Aug 10 '21 at 23:00
9

Here is an example of image scaling using gd, and copying Exif and ICC color profile using PEL:

function scaleImage($inputPath, $outputPath, $scale) {
    $inputImage = imagecreatefromjpeg($inputPath);
    list($width, $height) = getimagesize($inputPath);
    $outputImage = imagecreatetruecolor($width * $scale, $height * $scale);
    imagecopyresampled($outputImage, $inputImage, 0, 0, 0, 0, $width * $scale, $height * $scale, $width, $height);
    imagejpeg($outputImage, $outputPath, 100);
}

function copyMeta($inputPath, $outputPath) {
    $inputPel = new \lsolesen\pel\PelJpeg($inputPath);
    $outputPel = new \lsolesen\pel\PelJpeg($outputPath);
    if ($exif = $inputPel->getExif()) {
        $outputPel->setExif($exif);
    }
    if ($icc = $inputPel->getIcc()) {
        $outputPel->setIcc($icc);
    }
    $outputPel->saveFile($outputPath);
}

copy('https://i.stack.imgur.com/p42W6.jpg', 'input.jpg');
scaleImage('input.jpg', 'without_icc.jpg', 0.2);
scaleImage('input.jpg', 'with_icc.jpg', 0.2);
copyMeta('input.jpg', 'with_icc.jpg');

Output images:

Output without ICC Output with copied ICC

Input image:

Original Image

Thiago Barcala
  • 6,463
  • 2
  • 20
  • 23
1

The answer by @drew010 is correct in that this cannot be done without an external library or other program. However, that answer is quite old and there are now at least two good ways of doing it. @Thiago Barcala gave one answer, using PEL.

Here is a completely different one using a different one, the PERL-script exiftool by Paul Harvey. I prefer this solution because exiftool has a longer history of development and use, is better documented, and seems more stable and reliable to me. PEL is newer by nearly 10 years, has an unstable API, a history of the project changing hands, and has yet to reach version 1.0. I tried setting it up and ran into some roadblocks, and found no documentation for overcoming them, whereas setting up exiftool worked out-of-the-box.

Install exiftool, then, after saving the old image to a new path run:

exec('exiftool -TagsFromFile /full/path/to/original_image.jpg /full/path/to/newly_saved_image.jpg');

You must leave both files in existence for this to work; if you overwrite the file as your original code does, the EXIF data will be lost.

Make sure your php.ini allows for the exec() call; sometimes it is disallowed for security reasons. Also, take great care that you do not allow any user-generated input in any parameters you pass to that call because it could allow an attacker to execute an arbitrary command under the privileges of the web server. The exec call is most secure if your script generates the filenames according to some formula, such as alphanumeric characters only, with a fixed directory path, and then feed them into the exec call.

If you don't want to install exiftool globally, you can just replace exiftool by the full path to it. If you are using SELinux, make sure to set the context on the file for the exiftool script to httpd_exec_t to allow it to be executed by the web server, and make sure the directory the whole script is in has context httpd_sys_content_t or some other context that allows access by the webserver.

cazort
  • 516
  • 6
  • 19
  • 1
    Awesome! Thanks for sharing your experience and tips on how to do things in 2021. Unfortunately I have already selected an anwer back in 2012 and the closest I can go is upvoting new answers... :) – tftd Aug 12 '21 at 18:45