ImageMagick v6 Examples --
API & Scripting

Index
ImageMagick Examples Preface and Index
API and other IM usage methods
Security Issues
Hints for Better ImageMagick Shell/PHP Scripts
Why do you use multiple "convert" commands
Making IM faster (in general)
Creating linux RPMs from SRPMs
The Command Line Interface (CLI) of ImageMgaick which this examples deals with is only one method by which you can use, modify and control images with the core library of ImageMagick functions (MagickCore). It is basically the 'shell' API interface. There are lots of other Application Programming Interfaces (API's) which you can use more directly from many programming languages, see ImageMagick APIs.

Here I look at ways of improving your IM scripting and programming, differences between Windows and Unix scripting, and look at basics of using IM from other API's and programming languages.


APIs and other IM usage methods

Windows Batch Scripts

Window users should specifically note that...

Thorsten Röllich pointed out that you can use a FOR ... DO sequence in a windows batch script to pass text output of one "convert" command to another. For example...

  FOR /F "tokens=*" %i IN ('convert ...') DO convert ... %i ...

Basically the output of the single quoted "convert" command in parenthesis is saved into the special batch script "%i" variable and substitued at the "%i" location in the second "convert" command. Normally percent characters need to be doubled in window batch scripts, this is the exception.

This could for example take the output of the convert options "-format %wx%h info:" to a "-crop" operator in the second convert command.

PHP (IM commands from "system()" functions)

PHP users have three ways of using ImageMagick, The "imagick PECL interface, the "MagicWand" interface, and a seeming common method of making system calls directly to the command line interface. As IM Examples is mostly about using the command line, that is the form I will look at here.

PHP using Shell Commands

The best source of information on using this technique is the IM forum user 'Bonzo' and his web site RubbleWebs.

The following is the recommend procedure for initial tests of an ISP command line IM interface, assuming you do not have direct command line 'shell' access on the remote system. That is you can only upload web files for execution.

So the first thing we need to do is try and find and run the 'convert' command.

<?php
  header('Content-Type: text/plain');
  system("exec 2>&1; type convert");
  system("exec 2>&1; convert -version");
  system("exec 2>&1; convert -list type"); <!-- before IM v6.3.5-7 -->
  system("exec 2>&1; convert -list font");
?>

Upload the above PHP and look at it via the web.

This will run three commands to see what is present. If convert on the command PATH, and if so where is it. What version is it. And what font does IM thing it has access to.

If you only see errors, then the "convert" is not on the command line path, and your ISP provider did NOT initialise the web server PATH properly.

If this is the case you will need to find out exactly where it is located and use something like this for you PHP scripts, which makes your script less portable.

<?php
  $im_path="/opt/php5extras/ImageMagick/bin"
  header('Content-Type: text/plain');
  system("exec 2>&1; $im_path/convert -version");
  system("exec 2>&1; $im_path/convert -list type");
  system("exec 2>&1; $im_path/convert -list font");
?>

If you get "ldd" library errors, the LD_LIBRARY_PATH is wrong, and the ISP has definitely fallen down on the job, and you need to report the error, and have them fix the web servers LD_LIBRARY_PATH environment variable.

After that try some of the simpler examples from IM Examples and try to get them working. EG: output the IM 'rose' image as a JPEG.

<?php
  header( 'Content-Type: image/jpeg' );
  system("convert rose:  jpg:-");
?>

Or try one of the fonts listed from the first test page. On my Solaris Test Server I noticed that the 'Utopia' font set was available so I can try to create a label with it.

<?php
  header('Content-Type: image/gif');
  system("convert -pointsize 72 -font Utopia-Italic label:'Font Test' gif:-");
?>

Watch the extra quotes Note that typically the IM commands are wrapped by an extra set of quotes (usually double quotes), as such care must be taken to remove 'line-continuation' backslashes, and to double up other backslashes.

For example... This command

  convert -background none -fill red -gravity center \
          -font Candice -size 70x46 caption:"A Rose by any Name" \
          \( rose: -negate -flip \) +swap -composite \
          output.gif

Will become something like this PHP equivalent...

<?php
  header('Content-Type: image/gif');

  $color="red";
  $image="rose:";
  $size="70x46";
  $string="A Rose by any Name";

  passthru("convert -background none -fill '$color' -gravity center" .
            " -font Candice -size '$size' caption:'$string'" .
            " \\( '$image' -negate -flip \\) +swap -composite" .
            " gif:-" );
?>

Note how I still split up the lines to make the image processing sequence easier to follow, but using appended strings rather than a shell line continuation. Especially note the extra space at the start of later lines. And double up the other backslashes that was present in the original command. Alternatively you can protect those options by using single quotes.

I also use PHP variables to better adjust the PHP script, and control the results, but insert those options using single quotes to protect them from further modification by the shell. Watchout for single quotes within those inserted strings.

Also you can perform multiple shell commands within the same system call string. In fact a single system call can contain a complete shell script if you want! So you can do shell loops and multiple commands (with cleanups) all in the one system call. Something not may people realise.

Basically if you are careful you can make good use of the mathematics provided by PHP, and the scripting abilities of the shell. All at the same time. Just watch the quotes.

I recomend you at least read the PHP manuals on PHP exec(), system(), and passthru() and understand there differences between them.

For various examples of calling ImageMagick commands from PHP see Rubble Web, Writing IM code in PHP which describes about four different techniques.

The more secure method...

Also if you what to avoid the shell parsing the arguments and the need for most of the quoting requirements, (you separate the arguments yourself), such as for security reasions, or specific user input, then use the pcntl_exec() PHP function. This basically avoids all the quoting or other shell escaping, and is thus much more secure that using a 'all in one string' type command. Note you will also

However you will also have to fork a sub-process for the funtion to run in as well, ir it replaces the PHP being executed. This can thus get fairly complex. It is just a shame that PHP has not provided any other 'shell-less' command execution function, such as provided in other languages like perl.

PHP PECL 'IMagick'

To test if the PHP PECL imagick module is actually working upload a simple test "image.jpg" image and this PHP script to the same web assessable directory.

<?php
  $handle = imagick_readimage( getcwd() . "image.jpg" );
  if ( imagick_iserror( $handle ) ) {
    $reason      = imagick_failedreason( $handle ) ;
    $description = imagick_faileddescription( $handle ) ;

    print "Handle Read failed!<BR>\n";
    print "Reason: $reason<BR>\n";
    print "Description: $description<BR>\n";
    exit ;
  }
  header( "Content-type: " . imagick_getmimetype( $handle ) );
  print imagick_image2blob( $handle );
?>

PHP 'MagickWand'

You can check if the PHP MagickWand module is part of the PHP installation (and switch to command line or other fallbacks) using...

<?php
  if (extension_loaded('magickwand')) {
    echo "PHP MagickWand is available!";
  }
?>

But to check that it is actually working properly, upload some test "image.png" and this script...

<?php
  $image = NewMagickWand();
  if( MagickReadImage( $image, 'image.png' ) ) {
    header( 'Content-Type: image/jpeg' );
    MagickSetImageFormat( $image, 'JPEG' );
    MagickEchoImageBlob( $image );
  } else {
    echo "Error in MagickReadImage()";
    echo MagickGetExceptionString($image);
  }
?>

No guarantees with the above, though more feedback welcome. I do not generally program in PHP, but used the above for testing a SunONE-PHP5 test installation (with all three methods: command-line, magick, MagickWand).

Complex PHP scripts...

If you need to generate and output both HTML and IMAGES, consider designing your PHP script so that separate HTML requests or input options, generate the various parts you need on your web document, from either the same, or different PHP scripts.

That is a top level PHP script can output HTML with appropriate <IMG> tags, that call itself (or another PHP script) with appropriate options, to create or modify the images displayed on the first top level PHP script. This is in what a lot of photo album, and graphing PHP scripts do. All controlled by the GET, and PATH_INFO extensions to the URL calls. Note that you can not use POST within an IMG tag.

By doing things in this way you should be able to completely avoid the need to both generate, save, and clean-up, temporary images for PHP generated web pages. A solution that is full of problems, such as resource limitations and garbage collection, making it a very bad programming technique.


Security Warnings

When writing a script for public use, especially a web-based PHP script where ANYONE in the world could be running it, it is vitally important to check everything that comes from a unkown (or even a known) user. And I mean EVERYTHING, from arguments, filenames, URLs, and images too.

Until you verify some input argument, that argument it could contains letters, numbers, spaces, puncutation, or even 'null' and control characters.

It does not matter that you are using some web controled input form. A slightly knowledgeable person can easilly call your PHP with his own arguments without using that input form at all. An don't believe they won't do it, robots are out there, reading input forms and creating there own 'hacked' argments to try an break into random scripts.

Meta-characters in file names

As a security issue, you should watch out for filenames that contain spaces, quotes, punctuation, control-characters, or other meta-characters as both IM and Shells may try to expand them.

The problem is that a file called '*?@${&) .jpg' is actually a perfectly legal filename under UNIX, but a LOT of programs will have trouble handling it if that program (like shell and IM) also do filename expansion. Even when you quote the file name correctly some filenames can still break your quoting.

As a security measure it is often a good idea to error and abort if a filename has some unknown or unusual characters in it (like white space, control, or other meta-chars). Before passing such a filename to a shell command, or IM. It is better to be lightly to restrictive when security is involved than allowing something bad through.


Hints for Better ImageMagick Shell/PHP Scripts

These were some basic script programming points I made about a contributed shell script that was sent to the IM mail list for others to use. I originally sent these to the author privately (and who will remain anonymous), for which he was grateful. They are not all IM specific, but should be applied anyway, as standard programming practice. Especially if you plan to have someone else, use, look at, and/or bugfix your program or script. It will in turn make your script more useful.

These things basically gives the user using your program more freedom to do what THEY want rather than what YOU want. Don't limit them or yourself by making assumptions on what the script will be used for.

PS: One of my main expertise is in UNIX script writing, over lots of different architures and 'flavors' of UNIX, LINUX, and other UNIX-like systems, with more than 15 years experience behind me. I should know what I am talking about with regard to the above.


Why do you use multiple "convert" commands

Willem on Wed, 25 Oct 2006 wrote...
I was wondering; sometimes I see in your examples you're invoking Convert more then once to obtain the desired result. In general, I would expect that invoking convert more then once isn't needed; it should all be possible in 1 invocation (but the command would be more complex then). Do you agree with this statement?

I agree totally. You should be able to do all your processing in one single step.

I use multiple commands for a number possible reasons. Typically in the example pages, I do it so I can get and display the intermediate image result, so as to better demonstrate the intermediate processing stages that are involved. Often later in the same example area I repeat the process but using a single command. As such in principle, yes a single command can do all image processing.

The exception to this is in cases where I need to extract information and later insert that info into another command. An example of this is the Fuzzy Trim technique which requires you to extract the results of a trim on a blurred copy of the image. This result is then used to crop the original image. I also did this in the update to Thumbnail Rounded Corners example, where I used IM itself to generate a draw command using an images size for the next command.

However there is a proposal that will allow options to be generated from image that have been previously read into memory.

You are most welcome to combine the image processing techniques all into a single command. I do this all the time myself.

In scripts, such as the 'jigsaw' script (see Advanced Techniques, Jigsaw Pieces) I commonly end up using multiple commands for a different reason -- optional processing. That is the user may or may not what a particular step, so I used a separate command for each stage of processing which is executed based on the options user provided the script.

In such a case a temporary file is basically unavoidable. However I typically only need at most one or two temporary images, and each step processes the image back into the same temporary filename, for the next optional processing step to continue with. In this can a MPC file can speed up the reading of intermediate files to a near instant, though this file format actually needs two files to save the image.

EG: convert /tmp/image1.png ..operations.. /tmp/image1.png

And finally you may need to change your processing style based on the results of previous processing steps.

For example in image comparisons, I often need to change techniques based on the images being compared. Comparing a diagram or cartoon can require vary different techniques to say a real image.

If multiple commands is becoming a problem, perhaps it is time to go to an API interface such as PerlMagick, where multiple image sequences can all be held in memory so as to avoid unnecessary disk IO.


Making IM Faster (in general)

There are many ways of making IM work faster. Here are the most important aspects to keep in mind.


Building ImageMagick RPMs for linux from SRPMs

You do NOT need root to actually build the RPM's though you do need root to install the RPMs.

First get the latest source RPM release from Linux Source RPMs.

Create a working temporary directory in which to build IM

  tmp=/tmp/im_build; rm -rf $tmp; mkdir $tmp

Now build the RPMs... All the defines restrict the build to the temporary directory. If you remove all the defines the RPMs will be built in the system /usr/src area which is usually root owned.

  nice rpmbuild --define="_sourcedir $tmp" --define="_specdir $tmp" \
      --define="_rpmdir $tmp" --define="_builddir $tmp" \
      --nodeps --rebuild   ImageMagick*.src.rpm

Get the just built RPMs for the magick core and PerlMagick...

  cp -p $tmp/*/ImageMagick-[6p]*.i386.rpm

Clean up the build areas...

  rm -rf $tmp /var/tmp/rpm-tmp.*

Now you can install the RPM packages you build specifically for your linux system... You will need to be root for this...

  rpm -ihv --force --nodeps  ImageMagick-*.i386.rpm

To upgrade an existing one do this

  rpm -Uhv --force --nodeps  ImageMagick-*.i386.rpm

To later remove IM, do this

  rpm -e --force --nodeps  ImageMagick\*

Warning, other packages may need an IM installed, so if you remove it update your computer system to install the, default old version supplied by your linux system. This generally involved using a GUI Update package or "yum".

Enjoy.


Created: 26 October 2006
Updated: 20 March 2008
Author: Anthony Thyssen, <A.Thyssen_AT_griffith.edu.au>
Examples Generated with: [version image]
URL: http://www.imagemagick.org/Usage/api/