PHP Imagick extension on Windows

Imagick is very nice PHP extension which allow to create or modify images in simpler way than GD. One of disadvantage of this extension is lack of ready dll for Windows. You have to compile it by yourself. In place where I work, no one was able to compile it with success (and as I read some other blogs, it is common problem). So is there any other way to use this extension in project and be able to develop it on Windows? Yes!

I’ve created simple class which has similar interface to orginal Imagick class, but it use normal ImageMagick exec’s (They are available in binary version, so you do not have to compile them). Then in Windows (developing) environment all calls to Imagick library are handled by such mock class, and on Unix (production) calls are normally handled by native Imagick extension.

How to use it:

  1. Install ImageMagick (windows-dll version).
  2. Open command line and type ‚convert‚. If you see list of possible settings of ImageMagick, go to step 4.
  3. On Windows, there is other program called convert (used for converting file systems). You have to change your system path variable, so path to ImageMagick will be before c:/windows/system32 (how to do it). When you change this variable, open command line once again and type convert. You should see list of possible settings of ImageMagick.
  4. Now you have to switch off your server, and then start it again, so new path will be reloaded. Sometimes you have to restart your computer.
  5. Copy below class to new file (you can name it Imagick.php) to some place in your project (ie. to vendors/ directory when you are using CakePHP).
  6. Add to some file, which is always executed (like bootstrap.php in CakePHP), something like that:
if (!class_exists('Imagick') && file_exists('../vendors/Imagick.php'))
    require_once '../vendors/Imagick.php';

Done. Now you should be able to use basic functions from Imagick extensions. My mock class doesn’t contain all functions from orginal class, but for me it was enough. If you need some other function from this extension, which is not in my class, you can just add it 😉

Content of Imagic.php:

<?php

/**
 * Substitute for Imagick class for windows
 * It's wrapping 'convert' command.
 * It doesn't implement all methods and propably never will.
 *
 * @author Miron Pawlik
 * @version 1.0
 */
class Imagick {

    /**
     * Name of working file
     * @var string
     */
    private $filename;
    /**
     * Queue for commands for image
     */
    private $commandQueue = array();
    /**
     * List of temporary files (to remove in destructor)
     * @var array
     */
    private $tmpFiles = array();
    /**
     * Foramt of image
     * @var string
     */
    private $imageFormat = null;


    const COMPOSITE_OVER = 0;

    /**
     *
     * @param string $filename
     */
    public function __construct($filename = null) {
        if ($filename && !file_exists($filename)) {
            throw new ImagickException($filename . ' not found', 1);
        } else {
            $this->filename = $filename;
        }
    }

    public function __destruct() {
        foreach ($this->tmpFiles as $filename) {
            @unlink($filename);
        }
    }

    public function __call($name, $arguments) {
        //some hack for clone method
        if ($name == 'clone') {
            return clone $this;
        }
        throw new Exception('Method ' . $name . ' is not yet implemented :(', 1);
    }

    public static function __callStatic($name, $arguments) {
        throw new Exception('Static method ' . $name . ' is not yet implemented :(', 2);
    }

    public function getFilename() {
        return $this->filename;
    }

    public function setFilename($filename) {
        $this->filename = $filename;
    }

    public function getImageWidth() {
        $tmp = getimagesize($this->filename);
        return $tmp[0];
    }

    public function getImageHeight() {
        $tmp = getimagesize($this->filename);
        return $tmp[1];
    }

    /**
     * Get images properties
     * @param string $pattern
     * @param boolean $only_names NOT SUPPORTED
     */
    public function getImageProperties($pattern = "*", $only_names = true) {
        $results = array();
        $properties = array();
        exec('identify -format "%[' . $pattern . ']" ' . $this->filename, $results);
        foreach ($results as $line) {
            if (trim($line)) {
                $tmp = explode('=', $line, 2);
                if (is_array($tmp) && count($tmp) == 2) {
                    $properties[$tmp[0]] = $tmp[1];
                }
            }
        }
        return $properties;
    }

    /**
     * Generate thumbinail of image
     * @param int $columns
     * @param int $rows
     * @param bool $bestfit
     */
    public function thumbnailImage($columns, $rows, $bestfit = false) {
        $command = '-thumbnail ';
        if ($columns > 0 && $rows == 0) {
            $command .= $columns;
        } elseif ($columns == 0 && $rows > 0) {
            $command .= 'x' . $rows;
        } elseif ($columns > 0 && $rows > 0) {
            if ($bestfit) {
                $command .= $columns . 'x' . $rows;
            } else {
                $command .= $columns . 'x' . $rows . '!';
            }
        } else {
            return false;
        }
        $this->commandQueue[] = $command;
        return true;
    }

    /**
     * Crop image
     * @param int $width
     * @param int $height
     * @param int $x
     * @param int $y
     */
    public function cropImage($width, $height, $x, $y) {
        $command = '-crop ';
        if ($width > 0) {
            $command .= $width;
        }
        $command .= 'x';
        if ($height > 0) {
            $command .= $height;
        }
        $command .= '+' . $x . '+' . $y;
        $this->commandQueue[] = $command;

        return true; //for now...
    }

    /**
     * Write image to file
     * @param string $filename
     */
    public function writeImage($filename = null) {

        if (!$filename) {
            $filename = $this->filename;
        }
        $exec = 'convert ' . $this->filename;
        foreach ($this->commandQueue as $command) {
            $exec .= ' ' . $command;
        }
        $exec .= ' ';
        if ($this->imageFormat) {
            $exec .= $this->imageFormat . ':';
        }

        $exec .= $filename;

        //echo $exec;
        $arr = array();
        exec($exec, $arr);
        return true;
    }

    /**
     * Read image from string
     * @param string $image
     */
    public function readImageBlob($image) {
        $file = tempnam(sys_get_temp_dir(), 'img');
        $this->tmpFiles[] = $file;
        file_put_contents($file, $image);
        $this->filename = $file;
    }

    /**
     * Get image width and height
     * @return <type>
     */
    public function getImageGeometry() {
        $return = array(
            'width' => $this->getImageWidth(),
            'height' => $this->getImageHeight()
        );
        return $return;
    }

    /**
     * Lets do nothing!
     * @return null
     */
    public function coalesceImages() {
        return null;
    }

    public function getImageSize() {
        if (file_exists($this->filename)) {
            return filesize($this->filename);
        }
        return null;
    }

    /**
     * Create new image
     * @param int $cols
     * @param int $rows
     * @param int $background
     * @param string $format
     */
    public function newImage($cols, $rows, $background, $format = null) {
        $this->commandQueue = '-size ' . $cols . 'x' . $rows . ' xc:transparent';
    }

    /**
     * set image format
     * @param string $format
     */
    public function setImageFormat($format) {
        $this->imageFormat = $format;
    }

    /**
     * Ugly mock. Copy one file into other.
     * @param Imagick $composite_object other mock
     * @param int $composite
     * @param int $x
     * @param int $y
     * @param int $channel
     */
    public function compositeImage($composite_object, $composite, $x, $y, $channel = 0) {
        $this->commandQueue = $composite_object->clone();
    }

}

/**
 * Simple mock for ImagickPixel.
 * This class do completly nothing
 */
class ImagickPixel {

    public function __construct($name) {

    }

}

/**
 * Exception class
 */
class ImagickException extends Exception {

}

?>

Jedno przemyślenie nt. „PHP Imagick extension on Windows”

  1. Of course, you could also just use PHP’s built-in ftunniocs for this:
    <?php function file_ext ($name){
    $info = pathinfo($name);
    return $info['extension'];
    }
    function file_ext_strip ($name){
    $info = pathinfo($name);
    return $info['filename'];
    }#
    DEMO
    $filename = "/path/to/some.file"
    ;echo file_ext($filename).'’
    ;echo file_ext_strip($filename).”;
    ?>

Dodaj komentarz

Twój adres email nie zostanie opublikowany. Pola, których wypełnienie jest wymagane, są oznaczone symbolem *