How to add support for HEIC images with ImageMagick in PHP

How to add support for HEIC images with ImageMagick in PHP

We're adding server support for HEIC images by executing ImageMagick shell scripts. This way, we're bypassing the GD or Imagick libraries.

·

3 min read

Apple released a new image format called HEIC/HEIF, and it broke many apps that handle image processing. The PHP library Intervention Image caught up to it and added support for the new image format, but somehow, it isn't working at the time of this writing. So we had to come up with a solution.

As a prerequisite, you'll need to have the ImageMagick software installed with HEIC support on your server. See how to do that in this awesome post.

We will be using neither the GD library nor Imagick for this, as we're not relying on PHP libraries at all. We're going a level deeper and going to execute ImageMagick shell scripts. Why? Because, somehow, even though the Intervention Image library says that it has support for HEIC image formats, it failed every time we tried it. This may be fixed on a later release, or, it might not even be a bug at all. Maybe it's us, the developers, who are doing something wrong. But anyway, if you're stuck with this too, bear with me.

We wanted to convert the HEIC images into JPG. That is because, we want to render them in a browser, so they have to be converted. No modern browser supports HEIC images so far. We're using Intervention Image to process our images, so aside from other code related to validation, processing, etc., here's the trick:

...

private const TEMP_HEIC_STORAGE_DIR = 'app/heic_images/';

protected function convertHeic(UploadedFile $file): \Intervention\Image\Image
{
    $extension = $file->getClientOriginalExtension();

    // Generating a random filename, and not using the image's
    // original filename to handle cases
    // that contain spaces or other weird characters
    $randomFilename = bin2hex(random_bytes(8));

    // Path for a temporary file from the upload
    // storage/app/heic_images/sample1.heic
    $tmpFilepath = storage_path(
        self::TEMP_HEIC_STORAGE_DIR .
        $randomFilename . ".$extension"
    );

    // Path for a converted temporary file
    // storage/app/heic_images/sample1.jpg
    $convertedFilepath = storage_path(
        self::TEMP_HEIC_STORAGE_DIR .
        $randomFilename . '.jpg'
    );

    // Store the uploaded HEIC file on the server
    File::put($tmpFilepath, $file->getContent());

    // Run a shell command to execute ImageMagick conversion
    exec('magick convert ' . $tmpFilepath . ' ' . $convertedFilepath);

    // Make the image from the new converted file
    $image = Image::make($convertedFilepath);

    // Remove the temporary files from storage
    unlink($tmpFilepath);
    unlink($convertedFilepath);

    return $image;
}

Hacky, I know. It could use some refactoring, make the method a bit shorter, but the idea is there. By using the magick command you avoid using PHP libraries and go straight to ImageMagick. It may not be the most performant solution out there, but it works. It's alive and well and you can see it working on production on OLM. You can sign up and upload a HEIC image from your iPhone, it needs to be geotagged.

What about extracting image data? (EXIF)

Yeah, you probably can't extract EXIF data from HEIC images at this time, but the conversion we did above does copy the image data. To our surprise, it works only if we're converting to JPG images. It didn't work with PNG, and we haven't tried with other formats. So once you have the converted image in PHP, just go with $image->exif() and you should be just fine.

The code used for illustration is taken from the OpenLitterMap project. They're doing a great job creating the world's most advanced open database on litter, brands & plastic pollution. The project is open-sourced and would love your contributions, both as users and developers.