For those of you who don't know, I am the owner and occassional operator (!) of CSS {Imagine}, a simple, but relatively well-visited web design gallery.  One of the requirements I had for this site when I built it was that I did NOT want to have to do any work of manually grabbing screenshots and cropping thumbnails for the sites that are submitted to the site.  So using a combination of a third-party screen-capture service and CF8's super-awesome image editing tools, I've built an entirely automated process for handling of all this less-than-fun work.  Heck, all I have to do to kick off the process is to click a link in a notification e-mail…:)

One of the downsides to using the third-party service is that the results are VERY inconsistent.  Some of the images are garbled, while others simply fail.  However, the biggest issue I have is that the service has–reasonably enough–a finite amount of servers to process all the request.  Therefore, there are several occassions (daily) that I have to wait to kick off the process until the screen shot service has reached my requests in the queue.  I know, I know, not a huge deal.  However, it would be super awesome if I could do this on my own server.

What, exactly, would doing something like this entail?  Well, for starters, you're not going to get great results with a ColdFusion-exclusive option.  A while back, Ray Camden wrote a nice post about using to accomplish this.  In fairness, it works ok, but does not give consistent results.

So the next best option is to create an executable that you can run on your server from ColdFusion that will open a browser, grab a screen shot, and save it back to the file system.  

Fortunately, this has been done.  The other day, I ran across IECapt, a tiny command-line utility that you can use to grab images in a variety of formats and save to your server.  FYI, it's available in C++ and C#, and the source code is available if you feel the need to expand its functionality.

So with all this said, let's make a screen shot!  For this example, we want to end up with a 800×600 screen shot of http://coldfusionjedi.com.

First, we'll use . If you've not used before (this example was my first exposure to it…), it basically allows you to run a process on the server where ColdFusion resides.  Here's the code:

<cfset argArray = arraynew(1)>
<cfset argArray[1] = '–url=http://coldfusionjedi.com'>
<cfset argArray[2] = '–out=C:\ColdFusion8\wwwroot\project\images\cfjedi.png'>
<cfset argArray[3] = '–min-width=960'>
<cfset argArray[4] = '–delay=1000'>
           
<cfexecute    name="C:\ColdFusion8\wwwroot\theHub\IECapt.exe"
        arguments="#argArray#"
        timeout="100" />

Pretty simple. Here, I start by building an array of arguments to pass to the executable, including the site URL, where I want the file to be saved (including filename and image type), a minimum width for the screen capture, and how long the process should delay after the web page is loading before capturing the page.  Next, I simply pass these arguments to the executable on my server.

Now before we get into some image manipulation goodness, let me make a note about the "arguments". According to the documentation, you are *supposed* to be able to pass either a string or an array to the "arguments" attribute, and ColdFusion will pass them to Windows as needed.  However, in my tests, I found that I could not get the "string" version to work.  For example, I initially tried this:

<cfset argString = "–url=http://coldfusionjedi.com –out=C:\ColdFusion8\wwwroot\project\images\cfjedi.png" />

This worked fine.  However, when I added "–min-width=960", the process borked and I gave up.

On the other hand, passing all these arguments as an array works just fine.  I'm not sure what's going on, and more than likely I'm doing something wrong.  However, I thought I'd point it out in case anyone else runs into an issue.

Ok, enough of that. Now that our image is saved to the file system, we can do some CF image juju on it and get our desired result.  Here's what my code does:

<cfscript>
     image = imageNew(myFileName);
 
     // if the image is wider than we want, trim it down to the max width; leaving height blank
     // will also keep the aspect ration, which is generally good for image πŸ˜‰
     try {
    if(image.width > 800) {
         imageResize(image,800,'');
    };
    // if the image is taller than we want, crop it down to the max height
    // since we already resized the image to the desired width, we can simply
    // crop the image down to the desired height and avoid squishing it πŸ˜‰
    if(image.height > 600) {
         imageCrop(image,0,0,800,variables.600);
    };
    // all done kiddos! write this bad boy back to the file
    imageWrite(image);
</cfscript>

Nothing terribly complicated going on here.  First, we resize the entire image to the target width–800 px.  Once that's done, we can crop the image's height to our target, which is 600px.  Now that our fancy image is resized and cropped as needed, we can simply write the changes back to the file whose instance we created by specifying the absolute path in the imageNew() call at the very start of this function.

That's all there is to it.

Let me close with a few remarks.  I think IECapt is awesome, and I'm grateful to have found it.  However, as the author himself points out, it's on the level of "works for me." There are several features which I wish it had (such as the maximum height of the screenshot or using a different browser than IE)…so down the road I'll probably have to break down and develop something of my own.  

Anyway, hope this is helpful to someone!