Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Copy an image and preserve its EXIF/IPTC data with PHP imageCreateFromJpeg?

Tags:

php

exif

gd

iptc

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?

like image 369
tftd Avatar asked Apr 16 '12 22:04

tftd


3 Answers

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.

like image 157
drew010 Avatar answered Nov 15 '22 17:11

drew010


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

like image 23
Thiago Barcala Avatar answered Nov 15 '22 17:11

Thiago Barcala


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.

like image 37
cazort Avatar answered Nov 15 '22 16:11

cazort