Image embedded in email shows like a negative of the original image

ColdFusion 9

I am trying to embed a signature image into an email.  The image is created:

<cfoutput><cfsavecontent variable="content">#TicketSignature#</cfsavecontent></cfoutput>
<cfset Signature = ImageReadBase64(content) />
<cfimage name="thisSignature" source="#Signature#" action="read" >

When I "WritetoBrowser" the image appears fine.  All good so far.

In my Cfmail tag I use:

<cfmailparam file="signature.png" contentid="signature" content="#thisSignature#" disposition="inline" type="image/png" />
and
<img src="cid:signature"> to show the image.

My problem:  For some reason the image in the email is viewed as a negative image ... black background and light blue signature.  Somehow, the cfmailparam tag is converting the image.  Why???  I have tried everything I can think of, but nothing will change it back to white background and black signature.

Any thoughts would be very helpful.

Max
LVL 1
MaxwellTurnerAsked:
Who is Participating?
I wear a lot of hats...

"The solutions and answers provided on Experts Exchange have been extremely helpful to me over the last few years. I wear a lot of hats - Developer, Database Administrator, Help Desk, etc., so I know a lot of things but not a lot about one thing. Experts Exchange gives me answers from people who do know a lot about one thing, in a easy to use platform." -Todd S.

_agx_Commented:
IMO, the code isn't doing anything wrong - it's probably just a bug.  There are a number of image related bugs, some fixed in later versions.
https://stackoverflow.com/a/37769705/8895292

If I had to guess, cfmailparam is picking the wrong image type and/or color profile, which results in reversed colors. Hate to say it, but you may have to try the workaround in the thread above. Namely, save the image to a file and use the "file" with cfmailparam. Don't forget to use remove="yes" so it gets cleaned up.  (Yes, I realize you were probably trying to avoid more files, but .. it's the only workaround I can think of).
MaxwellTurnerAuthor Commented:
Hi agx_,  this is a problem you helped me with a while back.  I was in fact doing what you suggest ... save the image to file, then remove it using REMOVE in cfmailparam.  I used a test email address and it worked perfectly!

I encountered a problem however:  the application actually uses a list of email addresses - from 3 to 5 addresses.  I decided to CFLOOP through the addresses and use a counter to determine when to remove the address:

<cfset emailcounter = 1 />
<cfloop list="#emailto# index="e">
         <cfif emailcounter eq listlen(emailto)>
             <cfset removeimage = "yes">
       <cfelse>
             <cfset removeimage = "no">
       </cfif>
       <cfmail to="#e#" >
               ... email body ...
               <cfmailparam file="#ExpandPath('./tmpSigImage.png')#" contentid="signature" disposition="inline" remove="#removeimage#" />
       </cfmail>
       <cfset emailcounter = emailcounter + 1 />
</cfloop>

With a list of 4 email addresses, only one or two emails would send.  After studying my logs, I finally determined that it was the first one or two emails that was being moved to my Mail\Undelivr folder saying it could not find the image, making me believe that the image was not being created fast enough.  This is why I decided to try using a variable.

Using the variable, all 4 emails send, but now I have the problem of a negative image!!  Doh!  Just can't win.

I have no problem creating another file ... Do you know of a trick ... maybe a small pause/timer placed between creating the image and the cfmail code?

Thanks,

Max
MaxwellTurnerAuthor Commented:
btw, I also tried creating the image to file, then did a <cflocation> back to the same page ... hoping that would provide enough time for the image to be created, but it did not work either ... only 2 of 4 email would sent.

Strangley, if I simply change REMOVE=NO in the cfmailparam, all 4 email send fine.  I figured, akay, I'll just remove the file using directly after the cfloop:

<cfif FileExists("#expandPath('.')#\tmpSigImage.png")>
       <cffile action="delete" file="#expandPath('.')#\tmpSigImage.png" />
 </cfif>

When I tried this, none of the emails sent ... logs telling me the image was not found.  Not sure what is happening here.  Is it deleting the image too soon now?

Max
JavaScript Best Practices

Save hours in development time and avoid common mistakes by learning the best practices to use for JavaScript.

_agx_Commented:
Yes, unfortunately it sounds like it is removing the images too soon. Since the mail is spooled, it is hard to reliably know the order the messages are sent or when it is 100% safe to delete the image, since it is used by more than one email.

Unfortunately, the only workarounds I can think of are kludgey. Like creating 1 file per address. Would work, but is not ideal.

When you saved the image to a file, what file type did you use: png or jpg?

Experts Exchange Solution brought to you by

Your issues matter to us.

Facing a tech roadblock? Get the help and guidance you need from experienced professionals who care. Ask your question anytime, anywhere, with no hassle.

Start your 7-day free trial
_agx_Commented:
BTW, you tried everything else I would have tried too!

> I figured, akay, I'll just remove the file using directly after the cfloop:


You'd think that would work, but due to spooling, it doesn't. Spooling means they aren't sent right away, or maybe not even in the order you created them. So that would only work if you disable spooling - which is not ideal.
MaxwellTurnerAuthor Commented:
Hmmm, I think I was fooled into thinking it was not creating the image fast enough because it was consistently the first 2 on the LIST that did not send, however what you say makes sense, especially if I change REMOVE to NO and all 4 send.

I watched my SPOOL folder as I clicked the link and counted in my head how long the emails sat there ... turns out about 7 seconds.  I added this little bit of code, and viola, all 4 sent!  However, not a great solution as it takes 7 seconds for the next 'confirmation' page to load ... I was hoping the page would load and I could display a "please wait" gif for 7 seconds.

<cfset wait_time = 7>
<cfset Future = Second(Now()) + wait_time>
<cfloop index="Z" from="1" to="1000000000000" step="+1">
      <cfif Second(Now()) GTE Future>
              <cfbreak>
        </cfif>
</cfloop>
<cfif FileExists("#expandPath('.')#\tmpSigImage.png")>
          <cffile action="delete" file="#expandPath('.')#\tmpSigImage.png" />
 </cfif>

So still looking for a way to clean this image up.  This is actually a several step wizard, and I could do the FILEEXISTS clean-up at the beginning, but would much rather get rid of the signature after ...

I read somewhere that I could delete the image once a Session expires .... I am in fact using Session variables for this process.  How can I tell the session to delete the image when it expires ... it expires when I close the browser ... correct?

Max
_agx_Commented:
It sounded to me like they weren't sending because the file was deleted before the mails were sent ;-)

Session handling would work (...mostly, more about that in a sec). Technically the session doesn't expire when the browser is closed. It still lives on, on the server until the time elapsed since the user's last activity exceeds the server timeout.  Example, say the server session timeout is 20 minutes, and the user doesn't request any pages for 22 minutes - because they went for coffee - after 20 minutes, the session expires on the server.

Since spooled mails are sent asynchronously, it becomes a bit of a guessing game.  You could add some code the OnSessionEnd method in your Application.cfc, to delete the file. BUT .... there's still a small, outside chance the image will be in use at that time. In which case, the email wouldn't send.  Sadly the only sure-fire to ensure the file is deleted is to create 1 file for each email, and use remove=yes to let CF handle. Either that or don't use remove=yes and instead create a scheduled job, to clean up image files periodically.

None of them great options, but .. the only reliable ones if you want to be certain an email doesn't fail because of a missing image.
MaxwellTurnerAuthor Commented:
I put this outside of the cfloop:

<cfoutput><cfsavecontent variable="content">#TicketSignature#</cfsavecontent></cfoutput>
<cfset Signature = ImageReadBase64(content) />

and this inside the cfmail (and inside the cfloop):

<cfimage source="#Signature#" destination="tmpSigImage.png" action="write" overwrite="true">
<cfmailparam file="#ExpandPath('./tmpSigImage.png')#" contentid="signature" disposition="inline" remove="yes" />

and only get 1 of 4 emails successfully, so even creating a new image for each email is too fast for the spooling emails .... that is what you meant right?

This application is a multi-step wizard (a support ticket system)  ... I think my only option left is to do my fileexists & delete check on step ONE (create a new ticket) and keep REMOVE=NO, instead of deleting the image on the final step.  Maybe a scheduled job at the end of that day too.  Hopefully 2 separate users don't time it 'just right' where someone creating a new ticket deletes the image that someone just finishing a ticket has just created ... but quite unlikely as there will only be 15 or so users that will be using this application.  I'll buy a lottery ticket if that happens!  lol

Max
_agx_Commented:
Oh...Sorry, I left out an important detail about the separate files. They must have unique file names. Otherwise you still have the same problem. Threads deleting the image before the emails that use it are sent.

Try using something like #createUUID()#.png. since they're embedded, the file name doesn't matter anyway.
_agx_Commented:
Obviously with a full path too ;-)
MaxwellTurnerAuthor Commented:
Well shucks, I'm feeling a little silly again.  lol  Of course! ... unique image names!  I wasn't thinking this through properly.  I already had a counter so I just added #emailcounter# to the image name and 'ta da'! it worked.  Sad thing is this is one of the first things I tried early on even before I switched to a variable image and posted here.  I can't believe I didn't think of unique image names at the time ... would have saved much head scratching. lol  Oh well, the hardest lessons learned tend to stick best ...

Thanks again _agx_, now I can finally put this behind me and go on Christmas break without this hanging over my head!

Happy Holidays!
MaxwellTurnerAuthor Commented:
After re-reading I see you mentioned this multiple times earlier ... sorry it took me a bit to clue in ... when you mentioned unique names I paused, then gave my self a face palm (long sigh).  This is a great solution as far as I'm concerned.

Max
_agx_Commented:
>Oh well, the hardest lessons learned tend to stick best ...

Don't I know it! Glad it is all resolved. Though really, you shouldn't have to do all this if cfimage/cfmailparam didn't muck up the inages in the first place.

Ah well..  all is well, that ends well. Have a Merry Christmas!
_agx_Commented:
>  I already had a counter so I just added #emailcounter# to the image name and 'ta da'! it worked.  

One thing about that. I'd still recommend using createUUID instead. Reason being  a counter number isn't *globally* unique. If two requests hit that .cfm page at the same time, they'll be using the same file names and overwriting and deleting each other's images.

In theory, CreateUUID is globally unique. So the chances of two threads generating the same file name, at the same time, are reallly, really small. So it is much safer.
MaxwellTurnerAuthor Commented:
Thanks _agx_,

I was away for the holidays for a bit ... I can see the wisdom of what you suggest and will incorporate it.

Thanks again,

Max
It's more than this solution.Get answers and train to solve all your tech problems - anytime, anywhere.Try it for free Edge Out The Competitionfor your dream job with proven skills and certifications.Get started today Stand Outas the employee with proven skills.Start learning today for free Move Your Career Forwardwith certification training in the latest technologies.Start your trial today
ColdFusion Language

From novice to tech pro — start learning today.