Tuesday, September 19, 2006

A workaround for cfdocument missing images

This is with reference to the post Missing images in CFDocument. There are some cases when the images are locally on the machine running ColdFusion but even then cfdocument is not able to show the images. The reasons could be
1) ColdFusion is behind a firewall because of which it is not able to send any HTTP request (even though to itself).
2) The images are under a protected directory which needs authentication. Since cfdocument can not send authentication information currently, it is not able to fetch the image.
3) ColdFusion is using HTTPS and it is not configured properly to trust itself. So cfdocument can not send a https request to itself.
4) Any other reason which is preventing CFDocument from sending request to the local server.

If the images are on local machine, it is possible to use the file url for images (or CSS,javascripts, etc). CFDocument in that case will not send requests for the images over HTTP and fetch the image directly from the file system. Here is a simple way to use the file url.


<cfdocument format="pdf">
<cfoutput>
Some html content
<br>
<img src=#localUrl("img1.gif")#><br>
<img src=#localUrl("images/img.jpg")#>
</cfoutput>
</cfdocument>

<cffunction name="localUrl" >
<cfargument name="file" />
<cfset var fpath = ExpandPath(file)>
<cfset var f="">
<cfset f = createObject("java", "java.io.File")>
<cfset f.init(fpath)>
<cfreturn f.toUrl().toString()>
</cffunction>




basically here I have an UDF which converts any path to local URL and then I am using that UDF in 'src' attribute of image. This can be used to fetch images, css or any other similar contents from the local machine. You should note the <cfoutput> right under cfdocumet tag that allows the evaluation of UDF before it goes to cfdocument body.. This workaround is applicable only when the these contents are present on the same machine as ColdFusion.

This workaround has another advantage too. Normally when CFDocument body has any images, it fetches those images by sending HTTP request to the local server which is served by web threads. This has its own overhead. In a way, CFDocument uses server resource for getting something which is available locally on the server. This resource can instead be used to serve actual client http requests. Converting the image path to local urls will not go through HTTP and thus should have a better performance.

18 comments:

Shaji said...

Thanks for a good tip!

Aaron Conran said...

I have been working with Sarge trying to resolve these broken red X's for images over SSL. This has been the only workaround that has worked, including importing the correct certs into the correct truststores. What is the advantage in dipping into java instead of just using file:///#ExpandPath(file)? Also please note file:// will NOT work, but file:/// does.

I am using content variables in a fusebox 4.1 environment, so I will be using a regular expression to search for all links and replace them to your function if we are printing via cfdocument. Thanks

Rupesh Kumar said...

Actually both of them will achieve the same thing. I have put the java thing to prevent any chance of mistake at user's end. I am talking about the same mistake that you mentioned which is putting file:// instead of file:///

Moreover java thing is guaranteed to work on all the OS.

Ryan Stille said...

I'm guessing this could be slow if you had a lot of calls to localUrl(). Could you check to see if the f object exists first, then if not create it and load it into the request or variables scope. Then just re-init() it again every time the localUrl() function is called.

Anonymous said...

we had an issue where in a cluster, certain cfchart images would randomly disappear when pushed to cfdocument tag.

on each machine in the cluster, add an entry into the /etc/hosts file (and then restart CF) so that all image requests from the PDF library coldfusion uses route to the local machine, and not another one on your cluster. EVen sticky sessions in the loadbalancer didnt' fix this one, but a one-liner in /etc/hosts sure did.

Anonymous said...

Thanks alot! Saved me alot of headache!

Levitra said...

Thanks

Anonymous said...

what would be the complete solution using file:///#ExpandPath(file). What lines in the function does that replace?

Anonymous said...

what would be the complete solution using file:///#ExpandPath(file). What lines in the function does that replace?

Rupesh Kumar said...

The function will become something like
<cffunction name="localUrl" >
<cfargument name="file" />
<cfreturn "file:///#ExpandPath(file)#">
</cffunction>

Anonymous said...

I'd like to use this code for a project I am working on. I'd like to modify some of the variable names also. What kind of information or credit do I need to give you along with my code if I do this? Anything? Thanks very much, this has been very helpful to me.

Rupesh Kumar said...

Good to know that it helped you. You are most welcome to use it :-). That is why I had put it in the first place.

Arquan said...

Rupesh,

Thank you very much for this solution. I've been working on this issue for a couple weeks and came across your solution which solved my issue within a few minutes!

Thanks again.

Russ

Anonymous said...

Hi all,

I am also facing the same problem of red X mark instead of images.

But my issue is that i am creating images dynamically with the help of cfcontent

Can any body help me to solve this issue. I want all dynamic images in PDF .

Thanks
Sriji Das

santosh said...

Hi Rupesh,

How about an image residing on the server to be rendered using cfdocument.

please reply.


Thanks,
Santosh

Steve Glachan said...

(Using CF 8.0.1)

I struggled with getting images to show in my PDF generated via CFDocument. The image would appear blank and I'd get the following error when I tried clicking on the image in the PDF: "There was an error processing an annotation or link. There was a problem reading the document (14).".

I used absolute urls for the images and the PDF displayed them fine when on my local testing server but not on our production server. I have come to the conclusion that it was more a http/firewall issue possibly in my case.

My fix:

Within the html that I was converting to PDF using cfdocument, I made my image urls relative (ie. <img src="images/text_image.png"/>).

I also added the localUrl="yes" attribute in the <cfdocument> tag.

Using this, CF now looks for images in the document on the local drive, instead of making a http request to retrieve them. It is now working for me on both server environs.

Cheers!

Steve

steve@twintowersweb.com said...

Thank you Rupseh, I only wish I found your post 10 hours ago. :)

It did the trick.

xanax said...

Cool stuff here!