ColdFusion 8: URL Thumbnails

A reader asked me this morning if ColdFusion 8 can create images from URLs. This is often used to provide a snap shot of a remote site. Turns out this is relatively easy. Damon Cooper of Adobe showed an example of this a few weeks ago. It takes all of two tags:

<cfdocument src="http://www.raymondcamden.com" name="pdfdata" format="pdf" /> <cfpdf source="pdfdata" pages="1" action="thumbnail" destination="." format="jpg" overwrite="true" resolution="high" scale="25">

The first line simply uses cfdocument with the src attribute. I point to a URL (in this case, my blog) and store the result in a PDF variable.

Next I use the cfpdf tag to create a thumbnail. I specify the JPG format, use a high resolution, and set a scale to 25% just for the heck of it. Also note I only do page 1. By default the cfpdf/action="thumbnail" tag will create a thumbnail for each page of the PDF, but all we really want is the first page.

That's it. Done. Complete. Simple as pie. But of course I had to go a bit crazy and make a UDF out of it. The code below allows you to pass a URL (and an optional scale). It will then handle making the image, reading it into a CF8 Image object, deleting the file, and returning the object. You can then save it, or do whatever. For my tests, I did:

<cfset myimage = getThumbnail("http://www.coldfusionjedi.com",30)> <cfimage action="writeToBrowser" source="#myimage#">

The "writeToBrowser" action lets me test without actually saving a file, but I believe it doesn't work in IE. (Not that I care.) Enjoy, and let me know how it works for you. I'll probably add options to let you specify an image type as well.

The image quality is pretty good I think. It is not the same as what you see from Firefox, but for a thumbnail, I think it works ok:

<cffunction name="getThumbnail" returnType="any" output="false"> <cfargument name="url" type="string" required="true"> <cfargument name="scale" type="numeric" required="false" default="25">

<cfset var pdfdata = ""> <cfset var prefix = replace(createUUID(),"-","_","all")> <cfset var myimage = "">

<!--- make the pdf ---> <cfdocument src="#arguments.url#" name="pdfdata" format="pdf" />

<!--- write out the image ---> <cfpdf source="pdfdata" pages="1" action="thumbnail" destination="." format="jpg" overwrite="true" resolution="high" scale="#arguments.scale#" imagePrefix="#prefix#">

<!--- read it in ---> <cfset myimage = imageNew(expandPath('./#prefix#_page_1.jpg'))>

<!--- clean it up ---> <cffile action="delete" file="#expandPath('./#prefix#_page_1.jpg')#"> <cfreturn myimage> </cffunction>

Archived Comments

Comment 1 by Scott Stroz posted on 6/13/2007 at 10:03 PM

What in the name of all that is holy is a 'snap snot'?

Comment 2 by Raymond Camden posted on 6/13/2007 at 10:06 PM

Dude I have no idea what you are talking about. (Whistles innocently)

Comment 3 by Damien McKenna posted on 6/13/2007 at 10:56 PM

Is the CFDocument generation of HTML any better than previous editions? IIRC it didn't support some/many CSS attributes.

Comment 4 by Damien McKenna posted on 6/13/2007 at 10:57 PM

Ray: idea for riaforge, automated application screenshots - give it a URL to where your app is set up as a demo, have it automatically grab a screenshot and store in the project. For a rainy day.

Comment 5 by Clark posted on 6/13/2007 at 11:49 PM

Cool! So maybe...then a Flash remoting CFC could take a screenshot of a site...and return that as a bytearray so it could be rendered in the Flash player? That would rock.

Comment 6 by David Harris posted on 6/14/2007 at 12:01 AM

great!
I've been wanting to be able to do that for ages!
BD has a "jpg" format on their cfdocument, I was hoping Adobe would think this was a good idea too!

Comment 7 by Raymond Camden posted on 6/14/2007 at 12:09 AM

@Damien: I don't really know. I definitely saw oddities with my site being rendered - BUT - I could still read the site - I could still recognize it - so it was good enough I think.

Comment 8 by Dale Fraser posted on 6/14/2007 at 2:20 AM

Thanks Ray,

I knew if there was a way you would know how.

I think the quality is prety good, and while the CFDocument might not render every element perfectly, which browser does?

If you add in some IMAGE tags to dress up the final result, crop, put a border etc, would work well in lots of situations.

Comment 9 by Justin Lewis posted on 6/14/2007 at 7:12 AM

Nice solution. Just reading this, I have to wonder what the server cpu overhead would be if you are running that on every load. Maybe doing it once and saving the image if NOT fileExists.

I'll try it out and check out how much megahurtz it steels.

Comment 10 by Raul Riera posted on 6/14/2007 at 4:57 PM

Great, this should be a new question on the coldfusion cookbook

Comment 11 by Tom Jordahl posted on 6/15/2007 at 12:44 AM

Note that the writetobrowser action now writes the file to a disk cache and emits an img tag that references the CF file caching servlet.

Should work in any browser.

Comment 12 by Raymond Camden posted on 6/15/2007 at 12:48 AM

Thanks Tom. I wasn't sure about the IE thing.

Comment 13 by Dwayne Anderson posted on 6/21/2007 at 2:02 AM

why would the src attribute not be available to me

error:Attribute validation error for tag CFDOCUMENT.

Comment 14 by Raymond Camden posted on 6/21/2007 at 2:06 AM

You are using CF8, right?

Comment 15 by Dwayne Anderson posted on 6/21/2007 at 2:16 AM

MX7 - damn, soes this mean it does not work...I've been struggling with this for 2 days.

Comment 16 by Dwayne Anderson posted on 6/21/2007 at 2:18 AM

but even in Dreamweaver 8, the attribute doesn't show up. ) i have CFMX7 on the server and 8 on my machine.

Comment 17 by Raymond Camden posted on 6/21/2007 at 2:22 AM

Correct. Sorry. FYI, to make it work with DWMX8, I mean the editing, you need to get the updated tag library from labs. I believe. I'm not a DW users. (*cough*CFEclipse*cough*)

Comment 18 by BL posted on 7/10/2007 at 7:47 AM

Wow, that's really cool. I didn't even think about the possibilities of a format="jpg" on the cfpdf tag. Good call Adobe.

Comment 19 by Dave Phipps posted on 7/11/2007 at 4:52 PM

What css media type does cfdocument use/recognise. I use a media type of screen and all I see is an unstyled page in the my thumbnail. Try pointing your code to: www.abingdon.org.uk

Comment 20 by CV posted on 9/4/2007 at 12:07 AM

Ray, when I use your code, some thumbs have missing "sections" in them (they are blank or white). It seems that Flash does not get rendered in the thumbnail either. It also seems that some photos on simple HTML do not get rendered as well. Any idea as to why or how to improve on the accuracy of the "grab"? Thanks!

Here are a few sample URLs that get a little funky:
http://www.caraccessories.com/
http://www.partstrain.com/

Is this worthy of another blog entry?

Comment 21 by Raymond Camden posted on 9/4/2007 at 10:55 PM

That is all done behind the scenes. You would need to talk to Adobe about that.

Comment 22 by Brett posted on 9/17/2007 at 7:44 PM

Ray, when you say talk to adobe, is there a specific place to go to talk to them. I have a whole series of PDF files that when I run the code, like CV stated above, I get nothing but a JPG with a blank page. After a day of searching and trying different work-arounds, I still can't seem to get this to work correctly. By the way, your site is by far the best CF 8 resource I have found. Thank you.

Comment 23 by Raymond Camden posted on 9/17/2007 at 7:48 PM

You have two choices.

One is support. I don't know if that is free or not. I've only ever called support when I needed help with my CS3 install (oh god, the pain, the pain!). You would use that option if you need something fixed now.

If you just want to file a bug report, then go to www.adobe.com/go/wish. You can also use that URL to request a new feature.

Comment 24 by Solomon posted on 10/1/2007 at 12:22 PM

Please can someone tell me if it is possible to possible to create a PDF with updateable fields, then use ColdFusion & pdf forms to merge the data.

Comment 25 by Doug Jones posted on 4/22/2008 at 11:52 PM

For those of you that are wondering why the thumbnail doesn't look 100% like your browser window rendering, I think cfpdf reads your page like it would look if you were to print it. So it's not going to look 100% like your browser window unless you have created a stylesheet for media type "print".

At least that's been my observation. Still very cool.

Comment 26 by Mike posted on 8/29/2008 at 1:16 PM

Hey Ray,

I just tried this, and using the exact code in your eg there im seeing this exception

writeToBrowser:

coldfusion.image.Image$ReadImageMetadataException: Unable to read image source properly. D:\Domains\xxxxxx.com\wwwroot\0DBCEC5F_19B9_F849_B29064C59F3AC984_page_1.jpg (The system cannot find the file specified)

any ideas why this wouldnt work?

Comment 27 by Raymond Camden posted on 8/30/2008 at 6:53 PM

Interesting. It definitely worked before. This is an old blog entry, so it must be something that changed in 801.

Comment 28 by Raymond Camden posted on 8/30/2008 at 7:04 PM

Ok, I got it fixed. Just change the last line of the UDF to:

<cfreturn imageGetBlob(myimage)>

This makes the UDF return the binary data of the image, not a 'native cf image' which is barfing when you try to use it. It barfs because we delete the original temp source we used. As I said, something must have changed in 801.

Anyway, the binary data CAN be used with writeToBrowser, or you can save it, whatever. Either way, it's fixed now.

Comment 29 by Jeff Horne posted on 9/11/2008 at 6:38 PM

I know this is late in the game on this topic but is there any reason the site image can not be placed in a cftooltip tag so the user will see the image in a tooltip box before clicking on the actual link. This would avoid having to use some of the ad supported apps like <a href="http://www.snap.com">Snap Shots</a>.

Comment 30 by Raymond Camden posted on 9/11/2008 at 6:40 PM

You can do it - if you do an IMG tag pointing to a CFM that makes the snapshot. What you have to watch out for is speed. You would definitely want to cache your snapshots.

Comment 31 by Tim Mortimer posted on 9/15/2008 at 1:36 AM

Hi Ray

Always find your blogs useful. A related question to this thread - whats the easiest way to convert CFML output or text to an image in CF?

Tim

Comment 32 by Raymond Camden posted on 9/15/2008 at 6:12 AM

Check the CF8 docs for imageDrawText()

Comment 33 by Freemont posted on 2/6/2009 at 11:20 PM

Was having trouble with creating thumbnails from a pdf with cfpdf. Looked at this site, than I found that saving the pdf to Acrobat 8, than running the code, rendered the pdf correctly.

Comment 34 by Jibu posted on 5/28/2010 at 12:45 PM

Its not working in cf9
error :
An error: ERROR encountered loading http://www.xxxx.com/: java.net.ConnectException: Connection timed out: connect occured during the rendering process of this document.

Comment 35 by Asish posted on 5/28/2010 at 3:33 PM

Works fine in cf8 but getting Connection timed out error using cf9 what to do??

Comment 36 by Raymond Camden posted on 5/28/2010 at 7:11 PM

Are you sure the machine can hit the URL at all? Try a simple test w/ cfhttp.

Comment 37 by Asish posted on 5/29/2010 at 9:32 AM

when i tried to call the url using cfhttp in cf9 i got dis result
but is working fine in cf8
Charset [empty string]
ErrorDetail Connect Exception: Connection timed out: connect
Filecontent Connection Failure
Header [empty string]
Mimetype Unable to determine MIME type of file.
Responseheader
struct [empty]
Statuscode Connection Failure. Status code unavailable.
Text YES

any other method of creating thumbnail without using cfdocument and cfpdf ??

Comment 38 by Raymond Camden posted on 5/30/2010 at 5:53 PM

Interesting - what's the URL again?

Comment 39 by Asish posted on 5/31/2010 at 8:43 AM

Tried to call the url as given. It worked fine in CF8

<cfhttp
timeout="120"
throwonerror="false"
url="http://www.barclaybrowning.com"
method="get"
useragent="Mozilla/5.0 (Windows; U; Windows NT 6.0; en-US; rv:1.8.1.12) Gecko/20080201 Firefox/2.0.0.12"
getasbinary="yes"
result="pdfdata"
>

Comment 40 by Raymond Camden posted on 5/31/2010 at 6:08 PM

This, to me, sounds like a DNS issue. Are the CF8 and CF9 servers on the same physical box? If not - try this. RDP to the box. Open a browser. And then try to hit the URL. If it fails, it means your box can't reach the remote URL. Check your network settings.

Comment 41 by Asish posted on 6/1/2010 at 2:07 PM

Ok thanks.. will try it

Comment 42 by Jibu posted on 6/3/2010 at 10:45 AM
Comment 43 by Raymond Camden posted on 6/3/2010 at 3:15 PM

You miss my point. It doesn't matter if it works for me. If your server can't hit those URLs, it is a network issue on the box. The fact that some other tool (webshot?) also fails seems to imply the same.

Comment 44 by ColdFusion Developer posted on 7/9/2010 at 4:10 PM

Wow! Much better than the buggy CFX tag I was using before! Thank you!

Comment 45 by Todd M Hebert posted on 11/10/2010 at 9:36 PM

Another url that gets funky httpp://www.barretire.com

Comment 46 by stofke72 posted on 9/26/2011 at 3:04 PM

<cfdocument src="http://www.coldfusionjedi.com" name="pdfdata" format="pdf" />
<cfpdf source="#pdfdata#" pages="1" action="thumbnail" destination="." format="jpg" overwrite="true" resolution="high" scale="25">

Railo seems to require # # around pdfdata when set as source in cfpdf.

Comment 47 by Michael Ryan posted on 11/9/2011 at 1:29 AM

Ray great sample. I was wondering if you had a suggestion how to possibly postpone the the thumbnail snap shot until the page is fully loaded. Thanks

Comment 48 by Raymond Camden posted on 11/9/2011 at 1:32 AM

If you are talking about things that happen via JS or otherwise, then no, that can't be done.

Comment 49 by Vishal posted on 2/15/2012 at 2:12 PM

Hi

Can we have a screen shot of the current page where I first need to login.
I have tried for your above code but every time it gives me screen shot of login page.

Comment 50 by Raymond Camden posted on 2/15/2012 at 4:46 PM

If you try to use a screen shot of a page that requires you to login, this is not going to work. The CFHTTP call starts it's own session to the remote URL.

You can, however, make it work with a page that requires http authentication. Just add username/password to the cfhttp tag.

Comment 51 by Misty posted on 3/11/2012 at 12:29 PM

Hi ray, I know i may be asking a weird question here, but the thing is i have provided users an interface to upload videos. The issue is that how can i generate the thumbnails of the videos to show to the users so when they click, they can see it else a static type. I know this question seems stupid but i thought let's share with you, if we can come up with some idea,

i am using CF 8

Comment 52 by Raymond Camden posted on 3/11/2012 at 9:26 PM

You have a couple options.

One is to use ffmpeg, a command line tool that is a) free, b) very powerful, and c) a bit hard to use. It can make thumbnails from various points of the video.

The other option is ZenCoder. It isn't free, but they have an incredibly easy to use API.

Comment 53 by dani posted on 4/16/2012 at 9:06 PM

Hello Ray. I'm using CFPDF to create thumbnails from a directory with a bunch of PDFs. Each of these PDFs is some artwork done. I may have 10 or i may Have 40 PDFs on that directory.

So I use the CFPDF within a CFFTP tag to loop trough the directory where the PDFs are and then create the thumbnails.
My problem is that when I have 40 PDFs, since I'm using a shared server, my script gets timed out. Is there any way to improve the performance of the thumbnails creation? Am I using more server resources by setting the "scale" property to, let say "10"? Should I reduce the size of my source PDF?

Thank you!

Comment 54 by Raymond Camden posted on 4/16/2012 at 10:45 PM

While you could possibly simply the operation of each generation, what if you simply did 10 at a time? Ie, your code runs on a schedule, hits the directory, and only does the first 10 it finds that do NOT have thumbnails generated?

Comment 55 by dani posted on 4/16/2012 at 11:13 PM

Basically this works for a billing system.
My client is a photographer / packager. When he bills his clients, he always needs to send a contract sheet: a PDF with the thumbnails of what he is billing.
I can't think of any other way to do it other than at the time he creates the invoice. I could use CFPDF action = "optimize" to reduce its size, but I guess I'd be asking for more resources to the server. So far, my only solution (the one I can think of) is to create the thumbnails of already optimized PDFs. That means uploading lower res PDFs before the contract sheet is created.

Comment 56 by Mike posted on 4/16/2012 at 11:23 PM

@Dani,

I have the same time out issue with my host and did like Ray suggest. Added a field to database (ThumbCreated) looped though what I figure out I could before timeout if the Records were GT the timeout I would <cflocation to start again to start the timer again.

Comment 57 by Raymond Camden posted on 4/16/2012 at 11:30 PM

How do the PDFs end up in the directory though? Can't you take _that_ process and make the thumbnails right then?

Comment 58 by dani posted on 4/16/2012 at 11:42 PM

@Mike: what you mean is to split the process? For example, I get the amount of PDFs I have in the directory and then through a loop a split the thumbnail creation process?

@Ray: my client uploads the PDF manually via FTP since is what he's use to. I couldn't find a multiple upload via CFFTP (mind that those PDFs are about 3 to 5 megs each).

Comment 59 by Raymond Camden posted on 4/17/2012 at 12:42 AM

Is he FTPing to your server? If so - you can use an Event Gateway to notice changes, or just use a scheduled task to scan the directory every few minutes.

Comment 60 by dani posted on 4/17/2012 at 12:55 AM

I'll try that!! Thank you!
On another subject, I just noticed that my PDF created by cfdocument gets huge when embedding images on it.
Do you know of any fix or trick? I'm using CF 9.

Comment 61 by Raymond Camden posted on 4/17/2012 at 12:57 AM

None outside of running the optimize action.

Comment 62 by dani posted on 4/17/2012 at 1:02 AM

Yup, it drops the size from 9.3 to 2.8 megs, but it does not give me the open in browser option.
So if I don't optimize, and I don't put a filename attribute, then Firefox asks me what to do.
As soon as I put the CFPDF tag to optimize, it saves the file, I don't get the Firefox window.

I'm a bit stuck.

Comment 63 by Raymond Camden posted on 4/17/2012 at 6:56 AM

"Yup, it drops the size from 9.3 to 2.8 megs, but it does not give me the open in browser option."

Err - why would it? It's modifying a file. If you want the user to download it, then you would serve it like normally do - with cfheader/cfcontent.

Comment 64 by dani posted on 4/17/2012 at 3:02 PM

It's weird, when I try it, I have the Firefox windows that asks me to choose, but it is a 0 kb file (I didin't want to reduce it that much). What the code does is it saves the file on the server and then, in theory, in optimizes it and tell FF to ask me what to do.

Here is the code (i put it right after my closing cfdocument tag):

</cfdocument>

<cfheader name="Content-Disposition" value="inline; filename=thumbs_#URL.id#.pdf">
<cfcontent type="application/x-pdf">

<cfpdf action = "optimize"
source = "invoice/thumbnail_#URL.id#.pdf"
overwrite = "yes"
algo = "bilinear"
pages = "*" />

Comment 65 by Raymond Camden posted on 4/17/2012 at 3:24 PM

Oh no - that's not going to work. You are using cfdocument to generate a PDF in a variable thats served to the user immediately. You need to save the document to the file system. You can use the filename attribute of cfdocument to save it. You then use cfpdf on that file. You then 'serve' it up using cfheader/cfcontent.

Comment 66 by dani posted on 4/17/2012 at 3:26 PM

but...that's what I'm doing...

<cfdocument format="pdf"
margintop = "0"
marginbottom = "0"
marginleft = "0"
marginright = "0"
fontembed="no"
overwrite="yes"
filename="invoice/thumbnail_#url.id#.pdf">

Comment 67 by Raymond Camden posted on 4/17/2012 at 3:30 PM

Weird. Maybe you can show the entire page of code? Please use pastebin.

Comment 68 by dani posted on 4/17/2012 at 3:31 PM

How do I use pastebin?

Comment 69 by Raymond Camden posted on 4/17/2012 at 3:35 PM
Comment 70 by dani posted on 4/17/2012 at 3:39 PM
Comment 71 by dani posted on 4/17/2012 at 3:41 PM
Comment 72 by Raymond Camden posted on 4/17/2012 at 3:58 PM

You forgot to actually serve the data:

<cfheader name="Content-Disposition" value="attachment; filename=thumbnail_#URL.id#.pdf">
<cfcontent type="application/x-pdf">

This doesn't actually include the real data. Add file=".." to your cfcontent tag with the value being the same as filename.

Comment 73 by Dani posted on 4/17/2012 at 6:17 PM

Thank you Ray, but it keep throwing me a "file not found" error.

So here are my steps:

1) Generate PDF (9 megs)
2) Optimize it with CFPDF action optimize. (at this point I see it being saved on the server with 2.8 megs)
3) Serve the file using the following code:

<cfpdf action = "optimize"
source = "invoice/thumbnail_#URL.id#.pdf"
overwrite = "yes"
algo = "bilinear"
pages = "*" />

<cfheader name="Content-Disposition" value="attachment; filename=thumbnail_#URL.id#.pdf">
<cfcontent type="application/x-pdf" file="invoice/thumbnail_#URL.id#.pdf">

Comment 74 by Dani posted on 4/17/2012 at 6:19 PM

Sorry, the code in my previous comment should start with <cfheader

Comment 75 by Mike posted on 4/17/2012 at 6:20 PM

Looks like the path in <cfheader FileName is wrong.

"thumbnail_#URL.id#.pdf"

Comment 76 by Raymond Camden posted on 4/17/2012 at 6:27 PM

@Mike: No - the filename in cfheader is made up - it can be anything.

@Dani - try using a full path, not relative.

Comment 77 by Dani posted on 4/17/2012 at 6:29 PM

Thanks to both!

@Ray, yes, I just read the documentation. It did work!

Thanks for your constant help.

Comment 78 by Raju posted on 9/9/2013 at 12:36 PM

Hi,
I have issue with client variables in cf10, when i creating client variables in one page it was effecting in that page only and i am not able to access that variables in another page in application. here is code in application CFC

this.Name = "test";
this.ApplicationTimeout = CreateTimeSpan(0,0,0,0);
this.ClientManagement= "yes";
this.ClientStorage = "clientstorage";
this.SessionManagement = true;
this.SessionTimeout = CreateTimeSpan( 0, 0, 20, 0 );
this.SetClientCookies = "yes";
this.SetDomainCookies = "no";
this.ScriptProtect = "all";

Comment 79 by Raymond Camden posted on 9/9/2013 at 2:23 PM

Raju, this comment is not on topic. I see you have already emailed me the same question and I will try to reply.

Comment 80 by misty posted on 9/9/2013 at 3:57 PM

is this: >> this.ApplicationTimeout = CreateTimeSpan(0,0,0,0);

a mistake or you did it on your own, take a look