Serving up CFIMages via Image Tags (and a Non-CF Friday contest!)

Todd Sharp recently pinged me about an interesting problem. He was working with Images generated in ColdFusion 8 and was trying to use this imaga via the HTML IMG tag, like so:

<img src="foo.cfm">

The code in foo.cfm generated the graphic and he couldn't quite figure out how to display it. One thing he tried was the WriteToBrowser action:

<cfimage action="writeToBrowser" ...>

This didn't work - and if you view source on a file using this action you will see why. The WriteToBrowser action actually generates HTML that points to a 'magic' URL to serve the image.

He also tried cfcontent:

<cfimage action="read" source="paris.jpg" name="paris"> <cfcontent type="image/jpg" variable="#paris#">

This also failed as well. Why? The cfcontent tag doesn't like the Image variable. ColdFusion will throw the following error:

coldfusion.image.Image is not a supported variable type. The variable is expected to contain binary data.

If you think about it - this makes sense. Image variables aren't binary data in ColdFusion. While they may store binary data, ColdFusion wraps up the data in a new "Image" variable type. Now personally I think cfcontent should be smart enough to recognize a image variable and deal with it - but what do you in the meantime?

ColdFusion provides a ImageGetBlob function. This returns the binary data of the image and can be safely used with cfcontent like so:

<cfimage action="read" source="paris.jpg" name="paris"> <cfcontent type="image/jpg" variable="#imageGetBlob(paris)#">

But wait - there is a catch! This works ok as long as you begin your image with a real image. Notice above I have a image file I'm beginning with. If I use a 100% virtual image then it doesn't work. Consider:

<cfset canvas = imageNew("", 100, 100, "rgb", "white")>

If you run imageGetBlob on this, you get:

The source file should contain an extension,so that ColdFusion can determine the image format.

So I'm not 100% sure - but this is how I read this. I had created an image of a certain type of image type. But there was no file type for the image. Obviously I could write the variable out to a file, but there is no way to go directly from an "pure" Image variable to a blob. I'm going to file an enhancement request to add support for this. In the meantime if you needed to do something like this, I'd recommend creating a "canvas" graphic in your desired format and seed your dynamic image with that.

Ok - so now for the contest part. I whipped up a quick demo to show the code described above in action. I call it the Paris-Talkometer. Let me show you the code and then I'll link to the application.

<cfparam name="url.caption" default="">

<cfajaxproxy bind="javascript:updImage({caption})">

<script> function updImage(str) { if(str != '') document.getElementById('myimg').src = 'imgs3.cfm?caption='+escape(str); } </script>

<h2>Paris-Talkometer</h2>

<cfform name="form"> <cfinput type="text" name="caption" value="#url.caption#" > <cfinput name="mybutton" type="button" value="Update"><br /> <cfinput type="image" name="myimage" src="imgs3.cfm?caption=#url.caption#" id="myimg"> </cfform>

What we have is is a basic form. There is a text box for a caption, and a cfinput image type that points to imgs3.cfm. Notice though that it passes a URL variable. Go back up to the top of the file and see how I use cfajaxproxy to bind to the caption. Whenever the caption changes, I use a bit of JavaScript to the change the SRC of the image. Here is the code behind imgs3.cfm:

<cfparam name="url.caption" default="">

<cfimage action="read" source="paris.jpg" name="paris">

<cfif len(trim(url.caption))>

<cfset imageSetDrawingColor(paris, "black")> <cfset imageDrawRect(paris, 1, 1, paris.width, 40, true)>

<cfset imageSetAntialiasing(paris, "on")> <cfset imageSetDrawingColor(paris, "white")> <cfset text = { style='bold', size=12, font='verdana' }> <cfset imageDrawText(paris, url.caption, 10, 25, text)>

</cfif>

<cfcontent type="image/jpg" variable="#imageGetBlob(paris)#">

All I'm really doing here is looking for a URL.caption value. If it exists, I draw some text over the picture. The last thing I do is serve up the image using cfcontent and imageGetBlob.

You can see this in action here:
http://www.coldfusionjedi.com/demos/imagecfcontent/test3.cfm

Now for the contest. If you look at the code for test3.cfm, you will notice that you can seed the caption value via the URL. Here is an example.

Your content is to make Paris intelligent - or at least witty. While this is a somewhat herculean task - I'm sure some of you can do it. Just post your URL as a comment (and of course, you can comment on the main part of this blog post as well). Please keep your captions work safe. Work safe doesn't mean boring - just keep it safe please.

Archived Comments

Comment 1 by Hem posted on 9/14/2007 at 5:11 PM

Ray,

If you save the image file to the HDD and use a server side redirect or a cflocation redirect to the image file I believe it should work.

Cheers!

Comment 2 by Raymond Camden posted on 9/14/2007 at 5:16 PM

Hem - I believe I mentioned that - if you make it a real file, then it definitely works - but I was trying to be 100% virtual.

Actually, I mentioned if you _start_ with a real file you are good. So yes - this is an alternative as well.

Comment 3 by Lola LB posted on 9/14/2007 at 5:51 PM

Okay . . . here's my comment:

http://www.coldfusionjedi.c...

Anyone who's been to a sheep & wool festival will see the absurdity and irony in that comment, considering who Paris Hilton is. ;-)

Comment 4 by Lola LB posted on 9/14/2007 at 5:54 PM

Hmm . . . that exclamation mark _is_ supposed to be part of the comment . . .

Comment 5 by Pablo Vos posted on 9/14/2007 at 5:56 PM
Comment 6 by Pablo Vos posted on 9/14/2007 at 6:01 PM
Comment 7 by Pablo Vos posted on 9/14/2007 at 6:03 PM

I hope the last one is SFW... where I work it is ;-)

Comment 8 by Gary Gilbert posted on 9/14/2007 at 6:26 PM

@Pablo

"Did you know they named a city after me" LMFAO awesome!

Comment 9 by Pablo Vos posted on 9/14/2007 at 7:15 PM

Thx, can't wait to see other posts...

Comment 10 by Jeff posted on 9/14/2007 at 7:34 PM

Is there anything in the CFIMAGE arsenal of tools that will allow for a nice wrap when having LOOOooooong text??

Comment 11 by Raymond Camden posted on 9/14/2007 at 7:38 PM

No. I asked Adobe about this - but didn't get a response. (It was towards the endof the beta.)

As far as I know, the no way to "measure" your text. Let me kick some people around and see if I can get any info on that.

Comment 12 by Jeff posted on 9/14/2007 at 7:47 PM

Thanks for looking into. As you can tell, I have paragraphs and paragraphs of information to write about regarding my hero, Paris :)

Comment 13 by Mat Evans posted on 9/14/2007 at 7:53 PM

hehe, good competition..

http://www.coldfusionjedi.c... me! I'm trapped in your computer!

http://www.coldfusionjedi.c... I do look pretty fine in here

(to be viewed one after the other) ;)

enjoy..

Comment 14 by Mat Evans posted on 9/14/2007 at 7:54 PM

grr.. sorry my bad.. assumed it would code the URLs

i feel silly now :)

Comment 15 by Pablo Vos posted on 9/14/2007 at 8:00 PM
Comment 16 by Pablo Vos posted on 9/14/2007 at 8:02 PM

@Raymond: your url parser sucks kinda...

Let's try again: http://www.coldfusionjedi.c...

Comment 17 by Pablo Vos posted on 9/14/2007 at 8:04 PM
Comment 18 by Pablo Vos posted on 9/14/2007 at 8:04 PM

@this url parser: GRRR!

http://www.coldfusionjedi.c...

Comment 19 by Raymond Camden posted on 9/14/2007 at 8:08 PM

If you want to avoid the URL Parser, remove the http from your url.

Comment 20 by Rupesh kumar posted on 9/14/2007 at 8:10 PM

Ray,
I know you don't like using undocumented features :-)
But here is something very simple you can use which will work in all the cases. You can use getImageBytes() method on image object.

<cfset canvas = imageNew("", 100, 100, "rgb", "red")>
<cfset bytes = canvas.getImageBytes("jpg")>
<cfcontent type="image/jpg" variable="#bytes#">

Cheers !
Rupesh.

Comment 21 by Raymond Camden posted on 9/14/2007 at 8:13 PM

Nice!

Rupesh - I didn't get a chance to file a ER. I'll do so now.

Comment 22 by JC posted on 9/14/2007 at 8:44 PM

http://www.coldfusionjedi.c... read two books in jail...the bible and CFWACK...and I found God

Comment 23 by Chris posted on 9/14/2007 at 9:07 PM
Comment 24 by CeeVee posted on 9/14/2007 at 9:48 PM
Comment 25 by CeeVee posted on 9/14/2007 at 9:49 PM

Ugh. Supposed to read, "That's Hot..."

Comment 26 by West posted on 9/15/2007 at 12:36 AM
Comment 27 by TJ Downes posted on 9/16/2007 at 5:01 PM
Comment 28 by Dan Wilson posted on 9/16/2007 at 5:18 PM
Comment 29 by Dan Wilson posted on 9/16/2007 at 5:37 PM

As usual, TJ schools me:

Here is the above link in tiny form.

http://tinyurl.com/34qs3u

Comment 30 by Raymond Camden posted on 9/16/2007 at 7:19 PM

Tj - you know me too well. ;)

Comment 31 by George Bridgeman posted on 9/17/2007 at 1:42 PM

Ray,

Regarding the text measuring... there's no way in CF8 to do it natively but you can write a few lines of Java code to do it for you and call that from your CF code. I wrote a Font CFC to do this, as well as determining whether or not text will fit in a given rect size, etc. It doesn't reformat the text to fit inside a rect (it's on the TODO list) but it's quite accurate and supports different font families, sizes and styles.

Once I get home this evening I can upload it someplace and give the URL here if you're interested. The code is still pretty dirty as it's still in development, but it works.

George.

Comment 32 by Raymond Camden posted on 9/17/2007 at 5:45 PM

Sure - post the code. It does sound interesting.

Comment 33 by George Bridgeman posted on 9/17/2007 at 11:31 PM

I've put the code up here:

http://homepage.mac.com/geo...

I'll finish it off if I get some time. You should be able to get the jist of it though, even though it's quite mucky.

Comment 34 by Phil posted on 7/19/2008 at 2:10 PM

When serving the image with your code:

<cfcontent type="image/jpg" variable="#imageGetBlob(myimage)#">

the quality of the rendered image is quite bad. Is there any way on how to render a image with a better quality?

Comment 35 by TJ Downes posted on 3/28/2009 at 9:36 PM

Ray, thanks for this. I know it's been a while since you posted it, but I had remember you posted it and easily found it via Google when I needed it. I was able to create a proof of concept in about 30 minutes, for a client, using the Flex-AJAX bridge to dynamic generate images based upon user feedback in my Flex app. thanks!