I am currently trying to convert an ICO file to a 16×16 px PNG, using PHP-Imagick. What I’ve tried so far:
<?php if (empty(Imagick::queryFormats("ICO"))) { throw new Exception('Unsupported format'); } $sourceFile = __DIR__ . '/favicon.ico'; $targetFile = __DIR__ . '/favicon.png'; $im = new Imagick($sourceFile); $im->writeImages($targetFile, true);
This works partially. The problem is, that an ICO file may contain multiple images, so the code above creates multiple PNG files
- favicon-0.png
- favicon-1.png
- …
for every size. This is okay, but then, I need the possibility to find the one that is close to 16×16 px, scale it down (if needed) and delete all others. For this, I already tried some things and this is where I’m stuck currently:
<?php if (empty(Imagick::queryFormats("ICO"))) { throw new Exception('Unsupported format'); } $sourceFile = __DIR__ . '/favicon.ico'; $targetFile = __DIR__ . '/favicon.png'; $im = new Imagick($sourceFile); $count = $im->getNumberImages(); if ($count > 1) { for ($x = 1; $x <= $count; $x++) { $im->previousImage(); $tmpImageWidth = $im->getImageWidth(); $tmpImageHeight = $im->getImageHeight(); // ??? } } $im->writeImages($targetFile, true);
I guess, i would find a working way with some trial and error. But I would like to know, if there’s an easier way to achieve this.
TL;DR: I need a simple way to convert an ICO file of any size to a 16×16 px PNG, using PHP-Imagick (using GD isn’t an option).
Update:
My (currently working but maybe suboptimal) solution:
<?php if (empty(Imagick::queryFormats("ICO"))) { throw new Exception('Unsupported format'); } $sourceFile = __DIR__ . '/favicon.ico'; $targetFile = __DIR__ . '/favicon.png'; $im = new Imagick($sourceFile); $count = $im->getNumberImages(); $targetWidth = $targetHeight = 16; if ($count > 1) { $images = []; for ($x = 1; $x <= $count; $x++) { $im->previousImage(); $images[] = [ 'object' => $im, 'size' => $im->getImageWidth() + $im->getImageHeight() ]; } $minSize = min(array_column($images, 'size')); $image = array_values(array_filter($images, function($image) use ($minSize) { return $minSize === $image['size']; }))[0]; $im = $image['object']; if ($image['size'] <> $targetWidth + $targetHeight) { $im->cropThumbnailImage($targetWidth, $targetHeight); } } else { if ($im->getImageWidth() <> $targetWidth || $im->getImageHeight() <> $targetHeight) { $im->cropThumbnailImage($targetWidth, $targetHeight); } } $im->writeImage($targetFile);
Advertisement
Answer
My final solution:
<?php if (empty(Imagick::queryFormats("ICO"))) { throw new Exception('Unsupported format'); } $sourceFile = __DIR__ . '/favicon.ico'; $targetFile = __DIR__ . '/favicon.png'; $im = new Imagick($sourceFile); $count = $im->getNumberImages(); $targetWidth = $targetHeight = 16; if ($count > 1) { $images = []; for ($x = 1; $x <= $count; $x++) { $im->previousImage(); $images[] = [ 'object' => $im, 'size' => $im->getImageWidth() + $im->getImageHeight() ]; } $minSize = min(array_column($images, 'size')); $image = array_values(array_filter($images, function($image) use ($minSize) { return $minSize === $image['size']; }))[0]; $im = $image['object']; if ($image['size'] <> $targetWidth + $targetHeight) { $im->cropThumbnailImage($targetWidth, $targetHeight); } } else { if ($im->getImageWidth() <> $targetWidth || $im->getImageHeight() <> $targetHeight) { $im->cropThumbnailImage($targetWidth, $targetHeight); } } $im->writeImage($targetFile);