Thursday, December 24, 2009

CF9: One Difference Between the New Ternary Operator and IIF()

When I first saw the ternary operator in java, I confess I did not like it. To my way of thinking, it was odd and cryptic, and I did not see the advantage over a standard if/else statement. Well, I have come a long way. For simple comparisons I now prefer its sleeker syntax over the more bulky if/else construct.


So it was quite pleasing to see the ternary introduced in ColdFusion 9. I never much cared for the IIF() function. (Too reminiscent of MS Access I suppose) But stylistic preferences aside, I just noticed the behavior of the ternary operator differs from IIF() in another sense. It uses delayed evaluation.

Take this silly example. When someCondition is true, only the variable foo is defined.

   <cfset someCondition = true />
<cfif someCondition>
<cfset foo = 123>
<cfelse>
<cfset foo = 321>
<cfset bar = 88>
</cfif>


If you are using the new ternary operator, a statement like the one below would succeed. It does not matter that the variable bar is not defined. Because the second variable is only evaluated when someCondition is false.
 
<cfset result = someCondition ? foo : bar />

However, using IIF(), both variables are evaluated immediately, whether they are used or not. So this code would result in an error: "variable BAR is undefined"

 
<cfset result = IIF(someCondition, foo, bar) />
To avoid the error you would need to use the DE() function, which just feels awkward to me.

<cfset result = IIF(someCondition, foo, DE("bar")) />

So in this sense at least, the ternary operator is a bit smarter than the old IIF() function.

...Read More

Wednesday, December 23, 2009

Merry Christmas To All

I just wanted wish everyone in the CF community a Merry Christmas. I hope you all have a wonderful holiday. See you next year!

(My favorite holiday tune)


Why NORAD Tracks Santa

...Read More

Tuesday, December 22, 2009

Wrap and Scale Text (The Ghost of Code Past)

A long time ago, I wrote a cfc for wrapping and scaling image text to fit within a given set of dimensions. I always thought it was something better done in java. Well, I finally got around to creating a java version, and it is a lot simpler to use. I also improved the algorithm used to auto-scale the text, and may write up an entry on that later. Plus I finally added support for line breaks (left-to-right text only).




Options:

Alignment: Supports left, right or center alignment
Antialiasing: Enable or disable text anti-aliasing
AutoScale: Enlarge or reduce text size to best fit the text within the given area
DrawText: Enable or disable text drawing (Could be used for measuring only)
Font: Supports custom font name, style or size
Color: Supports custom text color
Ignore Margin: (Experimental) Ignores top and bottom padding on first/last line of text for a better text fit
Increment: Supports fractional font sizes and custom incrementing

It works very well for small to moderate amounts of text. Out of curiosity, I did test it with about 62,000 characters. (Crazy, I know). While it did work, it took a while. So if you are thinking of trying to calculate the font size needed to print War and Peace on a matchbook cover, think again. It is just not designed for that kind of volume.

Anyway, I tried to make the library as flexible as possible. So for example, you could wrap text without auto-scaling. You could also get the measurements of the wrapped text without actually drawing it onto your image. (I am not sure there is a need for this. But if you wanted to do it, you could!).

Next up, I am thinking of wrapped text with mixed styles (...I do love a challenge)

Source: wrapImageText-0.1a.zip


Update 2009-12-22: Fixed wrong file version in source

...Read More

Wednesday, December 16, 2009

ColdFusion + Tomcat + Apache + Windows (Take the plunge)

For a while now I have wanted to experiment with other CF engines. But kept putting it off for various reasons. Well, this weekend I decided to take the plunge. My first attempt was using CF9's multi-server install with JRUN which met with only partial success. For whatever reason I was never able to get the Open BlueDragon WAR file to deploy correctly under JRUN.

Thanks to a suggestion from Peter J. Farrell, I decided to try using Tomcat instead. Surprisingly, it was very simple to setup. I followed some excellent posts by Todd Rafferty and Matt Woodward and now have all three engines up and running with Tomcat + Apache on windows (.. and yes Todd, it is very fast!). Now be warned, Matt's entry is long. But it is very informative, so read it all. (Besides, if you read my blog I know you are used to long entries.)

Now I am busily reading up on Tomcat and Apache, wondering why I did not do this sooner...


Update: When I installed Tomcat I selected a different JVM than the one that ships with CF9. So the first time I tried to invoke a web service CF threw an error saying the "...class file has wrong version 49.0, should be 48.0". In case you run into this issue, it was easily solved after reading a handy post by Rupesh Kumar: ColdFusion and WebService : "class file has wrong version. Note, after updating the tools.jar, I did have to stop and restart Tomcat for the change to fully take effect.

...Read More

Monday, December 7, 2009

OT: Easter Eggs

In a recent post on stackoverflow.com, member Sergii mentioned a ColdFusion easter egg. (Thanks Sergii!) Go ahead and feel smug if you already knew about it. If not, check it out. It is ... interesting.

While searching for others, I came across a few other fun ones. Not all CF-related of course. But there are definitely some strange and amusing ones out there.

Various Programs (source: www.stackoverflow.com)
http://stackoverflow.com/questions/397576/whats-your-favorite-easter-egg

JQuery *This one rocks!* (source: www.aliaspooryorik.com)
http://www.aliaspooryorik.com/blog/index.cfm/e/posts.details/post/136

Google (source: www.danvega.org)
http://www.danvega.org/blog/index.cfm?mode=cat&catid=CCF3C429-AE69-270C-3D2E62AA9CC59D70

RIA Unleashed : Boston Site (source: www.remotesynthesis.com)
http://www.remotesynthesis.com/post.cfm/announcing-ria-unleashed-boston
* See the P.S. at the bottom

...Read More

Friday, December 4, 2009

Fedex Web Services + createObject("webservice") == Migraine

A very long time ago, I wrote an entry on my novice struggles using the Fedex web services with createObject(). Though the entry shows I still had a lot to learn about web services, I still get occasional questions about it. In particular, about working with the Fedex web services and createObject(). So I will offer this advice to anyone looking to do the same - don't.

While ColdFusion definitely simplifies the process of working with web services, it is also not the best tool for every web service. Especially the more complex ones. If you have ever tried using the FedEx web services with createObject("webservice"), you know how painful it can be. They are extremely complex and deeply nested. So unless you miraculously hit on the right code the first time around, you are likely to waste a LOT of time trying to troubleshoot cryptic Axis error messages .. and the time you spend will not be fun.

So my recommendation is to try using SOAP + CFHTTP instead. The Fedex WSDL developer files include working SOAP samples. They will get you up and running much faster than trying to translate the java examples into CF code. An old entry on Russ Michaels blog has some good tips on working with SOAP. There is also a current project using the SOAP approach with the Fedex Rates Web Service, under the project name CFFedexRates. You could easily use it as a base for some of the other web services.

Take my word for it, you will be glad you did.

Update December 9, 2009:
For a version of CFFedexRates that is compatible with the Rate Service v7, see issue #2 http://code.google.com/p/cffedexrates/issues/detail?id=2


Update January 21, 2010:
The featured downloads section was updated to include a version compatible with Rate Service v7.

Update May 4, 2010:
For a version of CFFedexRates that is compatible with the Rate Service v8, see issue #4 http://code.google.com/p/cffedexrates/issues/detail?id=4

...Read More

Thursday, December 3, 2009

ColdFusion: Encryption Interoperability Issues (Beginner)

Recently, I have seen a few questions about encryption interoperability. The libraries used by ColdFusion (Sun JCE) are pretty standard. So for the most part, compatibility should not be issue. But it got me to thinking about some of the common pitfalls when trading encrypted data with external tools. While they may seem obvious to some of you, many of them were not at all obvious to me.


While I am certainly no expert on the subject, most of the issues I have encountered, or seen in various forums, tend to involve two things: cipher settings and encoding. Now I know that seems like a blatantly obvious statement. But it is the source of more problems than you might think.

Unfortunately, the ColdFusion documentation on the encrypt/decrypt functions is a bit sketchy in places. Now to a degree, the minimal documentation is understandable. Realistically it would require whole volumes to provide a comprehensive explanation of encryption. But there are a few key aspects of the encrypt/decrypt functions that I feel could really use some illumination. If only to help developers avoid some of the more common interoperability problems.

A prime example is the algorithm argument. Most examples you will see use simple names like AES or DESEDE. What is not immediately obvious is that those simple names are short-hand for several settings: the algorithm, cipher mode and padding scheme. When you use the short-hand name, ColdFusion applies the default cipher mode and padding scheme automatically. The defaults may vary depending on which algorithm you select. But in the case of AES the defaults are ECB and PKCS5Padding. (At least from what I can tell). So in other words, the algorithm values AES and AES/ECB/PKCS5Padding are equivalent. To specify a different mode or padding just change the algorithm value.

Now chances are the external tool you are working with probably does not use exactly the same defaults as ColdFusion. But once you are aware of the additional settings for the various algorithms, it is much easier to figure out how to align the results. You just need to ensure the settings on both ends match up.

Take C# for example. The defaults for the RijndaelManaged class are a bit different than ColdFusion's. For example, the default mode is CBC rather than ECB and the default key size is 256 bit. So exchanging values between the two may not work right off the bat.

                    ColdFusion            C# (RijndaelManaged)
Mode: ECB CBC
Padding: PKCS5Padding PKCS7
Key Size: 128 bit 256 bit
Block Size: 128 bit 128 bit
Allowed Key Lengths 128, *192, *256 bit 128, 192, 256 bit

* Note: Unlimited encryption is required in CF for keys larger than 128 bit

Once you have aligned the cipher settings, double check the encoding used on the various values. In ColdFusion the string to be encrypted/decrypted "is always interpreted as a UTF-8 string". From what I observed, the key value seems to be interpreted as base64. So using this collective information you can now easily adjust the settings of ColdFusion and C# to produce the same results.
<!---
Encrypt/Decrypt
---->
<h3>ColdFusion (AES/CBC/PKCS5Padding) + iv</h3>

<cfset thePlainData = "Nothing to see here folks" />

<cfset theKey = generateSecretKey("AES", 128) />
<cfset theAlgorithm = "AES/CBC/PKCS5Padding" />
<cfset theEncoding = "base64" />
<cfset theIV = BinaryDecode("7fe8585328e9ac7b7fe8585328e9ac7b", "hex") />

<cfset encryptedString = encrypt(thePlainData, theKey, theAlgorithm, theEncoding, theIV) />
<cfset decryptedString = decrypt(encryptedString, theKey, theAlgorithm, theEncoding, theIV) />


<!---
Display results
--->
<cfset keyLengthInBits = arrayLen(BinaryDecode(theKey, "base64")) * 8 />
<cfset ivLengthInBits = arrayLen(theIV) * 8 />
<cfdump var="#variables#" label="AES/CBC/PKCS5Padding Results" />



using System;
using System.Collections.Generic;
using System.Text;
using System.Security.Cryptography;

namespace AESTest
{
public class AESCBC
{
public static void Main()
{
try
{
// Just hard coded values for testing ...
// MUST change them to match the values used in the CF code
String thePlainData = "Nothing to see here folks";
String theKey = "oRJUjgbx9SGGR6v3T8JGJg==";
String theIV = "f+hYUyjprHt/6FhTKOmsew==";
String encryptedText = EncryptText(thePlainData, theKey, theIV);
String decryptedText = DecryptText(encryptedText, theKey, theIV);

Console.WriteLine("Encrypted String: {0}", encryptedText);
Console.WriteLine("Decrypted String: {0}", decryptedText);
}
catch (Exception e)
{
Console.WriteLine(e.Message);
}

Console.ReadLine();
}


public static String EncryptText(String Data, String Key, String IV)
{
// Extract the bytes of each of the values
byte[] input = Encoding.UTF8.GetBytes(Data);
byte[] key = Convert.FromBase64String(Key);
byte[] iv = Convert.FromBase64String(IV);


// Create a new instance of the algorithm with the desired settings
RijndaelManaged algorithm = new RijndaelManaged();
algorithm.Mode = CipherMode.CBC;
algorithm.Padding = PaddingMode.PKCS7;
algorithm.BlockSize = 128;
algorithm.KeySize = 128;
algorithm.Key = key;
algorithm.IV = iv;

// Create a new encryptor and encrypt the given value
ICryptoTransform cipher = algorithm.CreateEncryptor();
byte[] output = cipher.TransformFinalBlock(input, 0, input.Length);

// Finally, return the encrypted value in base64 format
String encrypted = Convert.ToBase64String(output);

return encrypted;
}

public static String DecryptText(String Data, String Key, String IV)
{
// Extract the bytes of each of the values
byte[] input = Convert.FromBase64String(Data);
byte[] key = Convert.FromBase64String(Key);
byte[] iv = Convert.FromBase64String(IV);


// Create a new instance of the algorithm with the desired settings
RijndaelManaged algorithm = new RijndaelManaged();
algorithm.Mode = CipherMode.CBC;
algorithm.Padding = PaddingMode.PKCS7;
algorithm.BlockSize = 128;
algorithm.KeySize = 128;
algorithm.Key = key;
algorithm.IV = iv;

//FromBase64String
// Create a new encryptor and encrypt the given value
ICryptoTransform cipher = algorithm.CreateDecryptor();
byte[] output = cipher.TransformFinalBlock(input, 0, input.Length);

// Finally, convert the decrypted value to UTF8 format
String decrypted = Encoding.UTF8.GetString(output);

return decrypted;
}
}
}

Hopefully these small tips will help someone else or at least keep them from pulling out their hair trying to coerce ColdFusion and some other tool into producing the same results.

...Read More

Wednesday, December 2, 2009

How to Move A Directory

The round-a-bout way to move a directory always slips through my brain like it is a sieve. So this one is a reminder to myself. Use <cfdirectory action="rename"..>, or in ColdFusion 9 use the sleeker DirectoryRename() function. Note, an error will occur if the destination directory already exists.

<cfset sourceDirec ="C:\docs\images\20091202">
<cfset destDirec ="C:\20091202">

<cfdirectory action="rename"
directory="#sourceDirec#"
newDirectory="#destDirec#" />

...Read More

Saturday, November 28, 2009

OT: This is Unicode ?

While visiting the unicode.org site, I came across this unexpected title from 1992. (Yes, it really is about unicode. Who says unicode is a dry topic ;).


Kiss Your ASCII Goodbye (John Dvorak, PC Magazine/1992)

...Read More

Friday, November 27, 2009

SOT: Heads or Tails? (Windows Resource Kit - Tail.exe Gotcha)

So I was comparing two windows implementations of tail, and came across something unexpected. While I was checking one of my tests, I noticed the results contained less lines than I requested. The base files definitely contained more than enough lines. So I knew that was not problem. After digging deeper, I discovered the lines returned were not even from the end of the file! They were from the middle.

Well, come to find out the file I was testing contained some funky characters. One of them being ascii character 26. Apparently the Windows Resource Kit version of tail.exe interprets ascii 26 as some sort of EOF (end of file) marker. Consequently tail.exe uses that marker as its starting point. Well, naturally that skews the results because that position is not necessarily the actual end of the file.

To see it for yourself, create a simple file containing 50 lines, with the ascii character 26 inserted every ten (10) lines.

<cfset specialChar  = chr(26)  />
<cfset newLine = chr(10) />
<cfsavecontent variable="data">
<cfoutput>
<cfloop from="1" to="50" index="x">Log file line #x# <cfif not x mod 10>#specialChar#</cfif>#newLine#</cfloop>
</cfoutput>
</cfsavecontent>

<cfset filePath = ExpandPath("./testLogFile.log") />
<cfset FileWrite( filePath, trim(data) ) />


Now use tail.exe to grab the last twenty (20) lines of the file. Due to the placement of the special characters, what you will end up with is the first 10 lines of the file, not the last twenty.




<cfset grabLines = 20 />
<cfexecute name='"c:\program files\Windows Resource Kits\tools\tail.exe"'
arguments='-#grabLines# #filePath#'
variable='output'
timeout='20' />

<!--- convert lines to array --->
<cfset linesFound = listToArray(output, chr(10), true) />
<cfset actualLines = listToArray( FileRead( filePath ), chr(10), true) />

<cfoutput>
<p><strong>Log File:</strong> #filePath# </p>
<p><strong>Requested Lines:</strong> #grabLines# </p>
<p><strong>Lines Found:</strong> #arrayLen(linesFound)#</p>
<p><strong>Actual Lines:</strong> #arrayLen(actualLines)#</p>
</cfoutput>


Fortunately, the GNUWin32 version does not suffer from this "problem" and returns the expected results. (Thank goodness someone knows basic subtraction)



As for the Windows Resource Kit version, well.. if we were flipping a coin, this time it was definitely "heads" - as in "head on over to sourceforge.net and get a yourself a working copy of tail.exe".

...Read More

Wednesday, November 25, 2009

OT: JAI Resources?

Does anyone know of any good resources (online, books, etcetera) on JAI? Frankly the sun docs leave a lot to be desired. If anyone can recommend a good book on the topic it would be greatly appreciated.

...Read More

Tuesday, November 24, 2009

ColdFusion 8: Blowfish Encryption (Something smells fishy .. and it is not the algorithm)

A while back I saw a thread on the adobe forums about an issue with Blowfish encryption. The issue being the results from ColdFusion's encrypt function seemed to differ from other languages. Hardly uncommon given the "delicate" nature of encryption. What grabbed my attention was that the results from ColdFusion and Java were also different. Not what I would have expected.

Take the simple example below. It sure seems like the same text and key are used for both CF and Java encryption. Yet the results are different. However, if you compare the java results with those from the online tools (mentioned in the adobe thread) they are all the same. The odd one out here is ColdFusion.

Note: These examples were chosen for simplicity. They are NOT a good example of strong encryption (usage of ECB, etcetera ...)

ColdFusion

<cfset myText        = "guess this!1 2 3" />
<cfset myKey         = generateSecretKey("Blowfish", 32) />
<cfset cfEncrypted   = Encrypt(myText, myKey, "Blowfish/ECB/NoPadding", "Hex") />

<!--- results (using 

tags because the blogger eats line breaks!) ---> <strong>ColdFusion:</strong><hr /> <cfoutput> <p><strong>MyText</strong> : #myText#</p> <p><strong>MyKey</strong> : #myKey#</p> <p><strong>CF Encrypted</strong> : #cfEncrypted#</p> </cfoutput>



Java
<!---  generate a secret key --->
<cfset SecretKeySpec     = createObject("java", "javax.crypto.spec.SecretKeySpec") />
<cfset myKeyBytes         = CharsetDecode(myKey, "utf8") />
<cfset keySpec             = SecretKeySpec.init(myKeyBytes, "Blowfish") />
<!--- get a cipher instance for encrypting --->
<cfset Cipher             = createObject("java", "javax.crypto.Cipher") />
<cfset encryptor         = Cipher.getInstance("Blowfish/ECB/NoPadding") />
<cfset encryptor.init(Cipher.ENCRYPT_MODE, keySpec) />
<!--- do the encryption --->
<cfset myTextBytes         = CharsetDecode(myText, "utf8") />
<cfset javaEncrypted     = encryptor.doFinal(myTextBytes) />

<!--- results --->
<cfoutput>
<p><strong>Java Encrypted</strong>  : #BinaryEncode(javaEncrypted, "hex")# </p>
</cfoutput>

Sample Results:


Since I am definitely not an expert on encryption, I just figured I was missing something. Eventually I did get the CF and java results to match by converting the secret key to binary and then back to base64.

<cfset myText        = "guess this!1 2 3" />
<cfset myKey         = generateSecretKey("Blowfish", 32) />
<cfset magicKey      = BinaryEncode(CharsetDecode(myKey, "utf8"), "base64") />
<cfset cfEncrypted   = Encrypt(myText, magicKey, "Blowfish/ECB/NoPadding", "Hex") />

The new results clearly showed my magicKey now matched the java keySpec value. So of course the encrypted strings also matched.


But I am not sure why that the extra step necessary. Supposedly the value returned from GenerateSecretKey() is already base64 encoded. Also, it seems to work correctly in reverse (ie when the secret key is generated from the javax.crypto.KeyGenerator).

So am I missing something here, or is this a "feature"?


Update: Yep! I was obviously staring at this for too long. I missed something obvious. I should have used BinaryDecode(myKey, "base64") to get the java key bytes and vice versa. Now it make sense! ;)

As mentioned in the comments, using CBC mode is a bit different. As it requires an IV (Initialization Vector). Here is a basic example using CBC mode. Though in practice, you probably would not use the same IV each time as I am doing here. I also included a lot of extra debugging output. So you can verify the values used by both ColdFusion and Java are the same.

<!---
    ENCRYPT WITH COLDFUSION
--->
<cfset algorithm      = "Blowfish/CBC/PKCS5Padding">
<cfset myText        = "can you guess this?" />
<cfset myKey         = generateSecretKey("Blowfish", 32) >
<cfset myIV             = CharsetDecode("12345678", "utf8")>
<cfset cfEncrypted   = Encrypt(myText, myKey, algorithm, "base64", myIV) />

<!--- results (using <p> tags because the blogger eats line breaks!) --->
<strong>ColdFusion (Encrypted):</strong><hr />
<cfoutput>
    <p><strong>MyText</strong> : #myText#</p> 
    <p><strong>MyKey</strong> : #myKey#</p>  
    <p><strong>MyIV</strong> : <cfdump var="#myIV#"></p>  
    <p><strong>CF Encrypted</strong> : #cfEncrypted#</p> 
</cfoutput>
</p>

<!---
    ENCRYPT WITH JAVA
--->
<cfset SecretKeySpec       = createObject("java", "javax.crypto.spec.SecretKeySpec") />
<cfset myKeyBytes          = BinaryDecode(myKey, "base64") />
<cfset keySpec             = SecretKeySpec.init(myKeyBytes, "Blowfish") />
<cfset ivSpec               = createObject("java", "javax.crypto.spec.IvParameterSpec").init( myIV )>
<cfset Cipher              = createObject("java", "javax.crypto.Cipher") />
<cfset encryptor           = Cipher.getInstance( algorithm ) />
<cfset encryptor.init(Cipher.ENCRYPT_MODE, keySpec, ivSpec) />
<cfset myTextBytes         = CharsetDecode(myText, "utf8") />
<!--- convert encrypted byte array to base64 string --->
<cfset javaEncrypted       = BinaryEncode( encryptor.doFinal(myTextBytes), "base64") />

<!--- results (using <p> tags because the blogger eats line breaks!) --->
<strong>Java Encrypted:</strong><hr />
<cfoutput>
    <p><strong>myTextBytes (utf8)</strong> : #CharsetEncode(myTextBytes, "utf8")#</p>  
    <p><strong>keySpec</strong> : #BinaryEncode(keySpec.getEncoded(), "base64")#</p> 
    <p><strong>ivSpec (raw)</strong> : <cfdump var="#ivSpec.getIV()#"></p>  
    <p><strong>ivSpec (utf8)</strong> : #CharsetEncode(ivSpec.getIV(), "utf8")#</p>  
    <p><strong>Java Encrypted</strong>  : #javaEncrypted# </p>
</cfoutput>

Now just to prove the results are compatible, you can decrypt the ColdFusion results with Java (and vice versa).

<!---
    DECRYPT *COLDFUSION* RESULT WITH JAVA
--->
<cfset SecretKeySpec     = createObject("java", "javax.crypto.spec.SecretKeySpec") />
<cfset myKeyBytes        = BinaryDecode(myKey, "base64") />
<cfset keySpec           = SecretKeySpec.init(myKeyBytes, "Blowfish") />
<cfset ivSpec             = createObject("java", "javax.crypto.spec.IvParameterSpec").init( myIV )>
<cfset Cipher            = createObject("java", "javax.crypto.Cipher") />
<cfset decryptor         = Cipher.getInstance( algorithm ) />
<cfset decryptor.init(Cipher.DECRYPT_MODE, keySpec, ivSpec) />
<!--- convert our encrypted (base64 encoded) string to a byte array --->
<cfset encryptedBytes     = BinaryDecode( cfEncrypted , "base64") />
<!--- the decrypted bytes will be in UTF8 --->
<cfset javaDecrypted      = CharsetEncode( decryptor.doFinal( encryptedBytes ), "utf8") />

<cfoutput>
    <p><strong>Java Decrypted</strong>  : #javaDecrypted# </p>
</cfoutput>

<!---
    DECRYPT *JAVA* RESULT WITH COLDFUSION
--->
<!--- decrypt (base64 encoded) encrypted string --->
<cfset cfDecrypted   = Decrypt(javaEncrypted, myKey, algorithm, "base64", myIV) />

<cfoutput>
    <p><strong>ColdFusion Decrypted</strong>  : #cfDecrypted# </p>
</cfoutput>

...Read More

Thursday, November 19, 2009

Seeing Stripes: Experiment with ZXing

I recently took a look at the ZXing Project which is described as "an open-source, multi-format 1D/2D barcode image processing library implemented in Java.". While its primary focus is mobile phones, I decided to look at the javase component of the project. ZXing also comes with a servlet. But I first wanted to see how the lower level components worked.


Setup
Not seeing any pre-compiled jars, I downloaded the latest sources (ZXing-1.4.zip). After loading it into Eclipse, and poked around a bit to figure out how to decode barcode images of different types. Finally, I used the pre-made ant files to create the two jars I needed: core.jar and javase.jar.

Decoding barcode images
While the project has some decent documentation, the developer notes seem to be outdated in a few places. But examining some of the javase classes turned up a convenience class called MultiFormatReader. From what I could tell it provides a simple way to decode barcodes when you may not know the type ahead of time. Obviously if you know the barcode type ahead of time, it is more efficient to use the specific reader for that type. But since I wanted to test all supported types the MultiFormatReader was perfect for my needs.

Now before I could test ZXing, I needed some barcode images. So I used CFBarbecue.cfc to generate a sample of all of the barcodes supported by the Barbecue library. Now ZXing does not support all of them. But there is a good intersection between the two. Once I had my images, I created an instance of MultiFormatReader, using the ever-handy JavaLoader.cfc. I also passed the reader some hints to increase accuracy.

Note: This code is for demonstration purposes only. Normally, the javaLoader should be stored in the server scope to avoid memory leaks.

<cfset paths = [] />
<cfset arrayAppend(paths, expandPath('zxing-1.4/core/core.jar')) />
<cfset arrayAppend(paths, expandPath('zxing-1.4/javase/javase.jar')) />
<cfset loader = createObject("component", "javaloader.JavaLoader").init( paths ) />

<!--- Create hints for the reader. TRY_HARDER increases accuracy --->
<cfset DecodeHintType = loader.create("com.google.zxing.DecodeHintType") />
<cfset hints = loader.create("java.util.Hashtable").init() />
<cfset hints.put(DecodeHintType.TRY_HARDER, javacast("boolean", true)) />

<!--- Since we will be reading multiple images, create one reader and reuse it  --->
<cfset Reader = loader.create("com.google.zxing.MultiFormatReader").init() />
<cfset Reader.setHints(hints) />

Next, I used cfdirectory to grab a list of all of my barcode images. Then looped through the images and attempted to decode each one. Now in order to use the reader's decode method, I needed to convert my image to a compatible format: BinaryBitmap. So I read in each image, extracted the underlying BufferedImage and passed it through a few of the ZXing classes designed to prepare images for decoding.

Finally, I called the reader's decode method to decode the image. If the image was successfully decoded it returns a Result object containing some useful properties like the raw text and barcode type.

One thing to be aware of is that error handling is unlikely to yield any useful information, beyond the fact that an error occurred. From the notes in the current source code, the way in which decoding errors are handled is undergoing a transformation, to increase performance. So for now do not expect much exception information beyond name, rank and serial number.


<!--- read all barcode images in my subdirectory --->
<cfdirectory action="list" directory="#ExpandPath('images')#"
            filter="*.gif"
            name="barcodeImages" />

<!---
    Try and decode each barcode with z-xing.
    Note: Not all codes are supported by z-xing!!!
--->
<cfoutput query="barcodeImages">
    <cfset imagePath = Directory &"/"& name />
    <cftry>

        <!--- display the original image --->
        <strong>Barcode Image</strong> #name# <br />
        <cfimage action="writeToBrowser" source="#imagePath#"><br />

        <!--- extract the BufferedImage of the current barcode --->
        <cfset img = ImageGetBufferedImage(ImageRead(imagePath)) />

        <!--- prepare the image for decoding --->
        <cfset source = loader.create("com.google.zxing.client.j2se.BufferedImageLuminanceSource").init(img) />
        <cfset binarizer = loader.create("com.google.zxing.common.GlobalHistogramBinarizer").init(source) />
        <cfset bitmap = loader.create("com.google.zxing.BinaryBitmap").init(binarizer) />

        <!--- This method reuses the existing settings for decoding multiple images --->
        <cfset decodedResult = Reader.decodeWithState(bitmap) />

        <!--- display the results --->
        <strong>Decoded Text</strong> #decodedResult.getText()# <br />
        <strong>Decoded Format</strong> #decodedResult.getBarcodeFormat()# <br />

        <cfcatch>
            Not supported or unable to decode: #cfcatch.type# #cfcatch.message#
        </cfcatch>
    </cftry>
    <br /><br /><hr />
</cfoutput>




All of my sample images were decoded except:

  • 2of7
  • Codabar
  • Int2of5
  • Monarch
  • NW7
  • PDF417
  • PostNet
  • Std2of5
  • USD4
While I knew some of them are not supported, but I was surprised that the PD417 and Interleaved 2 of 5 samples failed. I am not yet sure why. For now I will assume it was an error on my part. I will have to investigate the individual readers further.

However, I was able to decode a DataMatrix barcode using the DataMatrixReader. (Created using the free servlet at IDAutomation.com). Just be aware the current project documentation lists DataMatrix decoding as "alpha quality". So do not expect miracles.




<cfset imagePath = "c:\barCode_loremIpsum_idAutomation.gif" />

<cfset DecodeHintType = loader.create("com.google.zxing.DecodeHintType") />
<cfset hints = loader.create("java.util.Hashtable").init() />
<cfset hints.put(DecodeHintType.PURE_BARCODE, javacast("boolean", true)) />

<!--- prepare the image for decoding --->
<cfset img = ImageGetBufferedImage(ImageNew(imagePath)) />
<cfset source = loader.create("com.google.zxing.client.j2se.BufferedImageLuminanceSource").init(img) />
<cfset binarizer = loader.create("com.google.zxing.common.GlobalHistogramBinarizer").init(source) />
<cfset bitmap = loader.create("com.google.zxing.BinaryBitmap").init(binarizer) />

<!--- attempt to decode the image --->
<cfset Reader = loader.create("com.google.zxing.datamatrix.DataMatrixReader") />
<cfset decodedResult = Reader.decode( bitmap, hints ) />

<cfoutput>
<!--- display the original image --->
<strong>Barcode Image</strong> #imagePath# <br />
<cfimage action="writeToBrowser" source="#imagePath#"><br />

<!--- display the results --->
<strong>Decoded Result</strong><br />
#decodedResult.getText()# <hr />
</cfoutput>

Other Classes
The j2se component also has a few classes you might find useful for quick tests. The first is GUIRunner. It is a very rudimentary GUI you can use to test a single bar code image. To use it, simply open a command prompt and enter the following. Just modify the "-cp" value (classpath) to point to the location of your jars.

Example:


C:\test\zxing> java -cp c:\test\zxing\core.jar;c:\test\zxing\javase.jar com.google.zxing.client.j2se.GUIRunner


The other class is CommandLineRunner. It can be used to decode a single image, a directory of images or a url. Just supply the locations of your jars and the file/directory/url you wish to decode. The "--try_harder" hint can be used to increase accuracy, and the "--dump_results" option to save the results to a file. Note: The results of each barcode are saved to individual files. So keep that in mind if you are decoding an entire directory.

Example:

C:\test\zxing> java -cp c:\test\zxing\core.jar;c:\test\zxing\javase.jar com.google.zxing.client.j2se.CommandLineRunner c:/test/zxing/barcodeimages --try_harder


Well, that is it for now. I will have to experiment with some lower quality images, to see how the results stack up in a real test. (Once I find a supported phone ...)

...Read More

Wednesday, November 18, 2009

SOT: Who is the fairest browser of them all?

I have long been a fan of Firefox. But somewhere in between versions 2 and 3.5 it started behaving like a black-hole of resources, sucking up more and more memory, growing increasing sluggish. Not to mention taking forever to start up. So fickle user that I am, I have been trying out other browsers while I give Firefox one last chance before giving it the boot.

Of course one of the browsers I am (re-)trying is Chrome. I did use it briefly when it first came out. But quickly gravitated back to my beloved Firefox, with all of its wonderful extensions and flexible configuration. But I will admit Chrome is extremely fast. No news there. What was a nice surprise was its mini-Task-Manager (Shft-Esc) and the "Stats for Geeks" breakdown.

Meanwhile, I did some file shuffling and installed a completely fresh copy of FireFox 3.5.5. Just to see if I could get a sense of whether the true culprit was FireFox itself, a specific setting or one of my plugins. I tried to start with as little functionality and minimal settings as possible. Not all of them are relevant. But in the interest of full disclosure, here is a run down of the changes I made to the default settings:

Plugins:
- *Disabled* all of the plugins detected by default (EXCEPT Shockwave Flash 10.0.32.18)

I always wondered why my plugin list displayed certain entries I never added explicitly. It turns out it is due to Firefox's automatic scan for common plugins: http://kb.mozillazine.org/Plugin_scanning#Related_preferences

Extensions:
- *Added* Echofon (aka TwitterFox)

Privacy:
- *Changed* Remember browsing history to (at least 30 days)
- *Disabled* Remember search and form history
- *Enabled* Clear history when Firefox closes: everything

Cache:
- *Changed* Cache settings to use up to 30MB
Reason: Sometime after upgrading to FF3+, I noticed the autosuggest feature in the location bar had changed. It got progressively slower over time. So I am experimenting with a lower history threshold to see how it performs (before taking the extreme step of disabling it entirely).

Content:
- *Disabled* Java (No point enabling it since I disabled the plugin)

Startup:
- *Changed* Show a blank page

Update:
- *Changed* When updates to Firefox are found: (Ask me what I want to do)

So far Firefox is doing a lot better. Definitely faster, though maybe not quite as fast as Chrome. At one point I had 15 tabs open in Firefox (only one with Flash Content). Firefox peaked at around 160-180M memory usage in the windows Task Manager and still remained very responsive.

Now to start adding back a plugin at a time, and wait for ugly things to happen. Though I have to say I am enjoying reading some of the more creative ways people express their displeasure with the latest versions of Firefox.


Update November 19, 2009: I am now experimenting with the Firefox 3.6b3 beta and also a plugin called AFOM 2.0 for memory management. (Note: I do not know if this plugin officially supports 3.6b3 yet). But the results from my limited testing seem very promising. (Windows only)


Update November 27, 2009: While the plugin helps, there are still issues. I think it is time to declare defeat. The memory problem is definitely Firefox!

...Read More

Monday, November 16, 2009

CFBarbecue.cfc (...Just because)

While experimenting with the Barbecue library last week, I put together a cfc for my testing. (You knew that was coming. It is the old developer story: read, test, build something, re-test). Anyway, I decided to post it for anyone else out there experimenting with Barbecue. The cfc can generate any of the 25+ barcodes Barbecue handles, with a full range of settings. (Rather than repeat the same code 25+ times, the cfc uses reflection)




Keep in mind the Barbecue library also includes servlet, which is extremely easy to use. Since I was using the built-in webserver at the time, the greatest challenge for me was figuring out how to add a servlet. Not surprisingly I did not find much in the way of documentation. But once I finally got it working, the servlet was a snap to use. Just a simple call to a url:

http://127.0.0.1:8500/barbecue/?data=12345&type=PDF417

Download CFBarbecue.cfc (See the download widget on the right menu)



November 19, 2009: Updated source to include missing lib\jdom.jar. Note: This jar is only needed if you are using the JavaLoader and want SVG functionality.

...Read More

Sunday, November 15, 2009

The Wrong Way to Retrieve the Last Record ID Inserted

Hardly a week goes by without seeing some forum post that either uses or recommends my pet peeve: the erroneous SELECT MAX(id) query. Mind you this is across the board, not just in CF. For some bizarre reason, this faulty technique never seems to die. And it REALLY should. There are so many correct alternatives, I cannot understand why anyone would still use it for new code. If you are even considering it, might I inject a note of sanity and recommend some excellent reading material on Adrian J. Moreno's blog:


(His comments on the topic are much more educational and polite than mine ;-)

...Read More

Friday, November 13, 2009

Everything CFDBINFO Never Told You About Your Database (Connection Metadata)

I was doing some debugging and came across the nifty java.sql.DatbaseMetaData class. It is an interface implemented by the various database drivers and it provides you with a ton of information about both your database and driver capabilities. While you can glean some of the information from <cfdbinfo>, DatbaseMetaData tells you so much more.


To use it, you need to open a database connection first. If you have full server access, the simplest method is using the internal ServiceFactory. But you could just as easily open your own jdbc connection. Then call one of the 100+ methods to discover things you may have always wondered about. For example:


  • What database or driver version you are using?
  • What is the maximum number of concurrent connections?
  • What is the maximum number of tables in an ORDER BY?
  • What happens when you concatenate a NULL value with a non-NULL value?


Apparently, the DatabaseMetaData class can tell you that. While I would not take all of these values "as gospel", they do provide some solid guidelines.
<cfscript>
myDSN = "MSSQL_2005";

// open a connection to the datasource
factory = createObject("java", "coldfusion.server.ServiceFactory");
datasource = factory.getDataSourceService().getDataSource( myDSN );
connection = datasource.getConnection();
// extract the database metadata
metadata = connection.getMetaData();

results = {};

// Display version information
results["getJDBCMajorVersion()"] = metadata.getJDBCMajorVersion();
results["getJDBCMinorVersion()"] = metadata.getJDBCMinorVersion();
results["getDriverMinorVersion()"] = metadata.getDriverMinorVersion();
results["getDriverName()"] = metadata.getDriverName();
results["getDriverVersion()"] = metadata.getDriverVersion();
results["getDatabaseMajorVersion()"] = metadata.getDatabaseMajorVersion();
results["getDatabaseMinorVersion()"] = metadata.getDatabaseMinorVersion();
results["getDatabaseProductName()"] = metadata.getDatabaseProductName();
results["getDatabaseProductVersion()"] = metadata.getDatabaseProductVersion();

// cleanup
connection.close();
</cfscript>

<cfdump var="#results#" label="[#myDSN#] java.sql.DatbaseMetaData" />
As there are so many functions, I used a bit of reflection to quickly dump all of the simple properties into a structure. (See the full code at the end of the entry). It was interesting to see how the metadata differed across drivers. Both in what information was exposed and the results of the core methods. Notice the sql keywords differ when using the JTDS driver and the built-in CF8 driver.

Note: Unfortunately, getSQLKeywords() only returns potential keywords that are not also ansi sql:2003 reserved words. But with a little effort you could easily create a function to list of all potential keywords.

CF8 Driver

JTDS Driver


The metadata contains all kinds of interesting information. So if you ever wondered about your database or driver capabilities, read up on java.sql.DatbaseMetaData. What it reveals could surprise you.

Full Code

<cfscript>
myDSN = "MSSQL_2005";

// open a connection to the datasource
factory = createObject("java", "coldfusion.server.ServiceFactory");
datasource = factory.getDataSourceService().getDataSource( myDSN );
connection = datasource.getConnection();
// extract the database metadata
connMetadata = connection.getMetaData();

// get all methods in the metadata class
allMethods = connMetadata.getClass().getDeclaredMethods();
Modifier = createObject("java", "java.lang.reflect.Modifier");

results = {};

// Execute all simple methods and store the results
for(x=1; x <= arrayLen(allMethods); x++)
{
method = allMethods[x];

// Get the parameters for the current method
parameters = method.getParameterTypes();

// If this is a simple method, requiring no extra information ..
if (Modifier.isPublic(method.getModifiers()))
{
if (not arrayLen(parameters) AND method.getName() neq "getConnection")
{
try
{
// run the current method
data = method.invoke( connMetadata, parameters );
// convert resultsets to a query object first
if ( isInstanceOf(data, "java.sql.ResultSet")) {
data = createObject("java", "coldfusion.sql.QueryTable").init(data);
}
results[method.getName()] = data;
}
catch(java.lang.Exception e)
{
results[method.getName()] = e.message;
}
}
}
}

connection.close() ;
</cfscript>

<cfdump var="#results#" label="connection.getMetaData() " />

...Read More

Thursday, November 12, 2009

MS SQL: Useless Function Trivia

While reviewing some MS SQL 2005 metadata, I stumbled across something I never noticed before. It still supports the old ODBC "{fn}" functions. With the exception of CurDate and CurTime, there is probably not much need for them. (Nor do I find the syntax particularly appealing ...). But still, it was surprising to find they do still work. So there is my MS SQL oddity for the day.

 
<cfquery name="q" datasource="MSSQL_2005">
SELECT
{fn CurDate()} AS [CurrentDate],
{fn CurTime()} AS [CurrentTime],
{fn DayName( getDate() )} AS [DayName],
{fn DayOfMonth( getDate() )} AS [DayOfMonth],
{fn DayOfWeek (getDate() )} AS [DayOfWeek],
{fn DayOfYear(getDate() )} AS [DayOfYear],
{fn Extract(Day From getDate())} AS [ExtractDay],
{fn Hour(getDate() )} AS [Hour],
{fn Minute(getDate() )} AS [Minute],
{fn Month(getDate() )} AS [Month],
{fn MonthName(getDate() )} AS [MonthName],
{fn Now()} AS [Now],
{fn Quarter(getDate() )} AS [Quarter],
{fn Second(getDate() )} AS [Second],
{fn Week(getDate() )} AS [Week],
{fn Year(getDate() )} AS [Year],
{fn TimestampAdd(SQL_TSI_DAY, 14, getDate())} AS [TimestampAdd],
{fn TimestampDiff(SQL_TSI_DAY, '2009-10-31', getDate())} AS [TimestampDiff]
</cfquery>
<cfdump var="#q#" label="Old MS SQL Functions fn{}" />

...Read More

OT: Lame error messages

So I was having fun, trying to break things, and came across a lame excuse only a computer could get away with:

com.mysql.jdbc.exceptions.MySQLSyntaxErrorException: memory exhausted.

I can see it now: "Sorry Boss-man. No can do. Geez, my memory is exhausted. I need time to relax, recharge, reboot. Check back with me tomorrow."

...Read More

Sunday, November 8, 2009

ColdFusion: Introduction to the Java Barbecue Library (For Beginners)

Just to get this out of the way: if you are looking for a CF wrapper for the Barbecue library, this is not the entry for you. If you are interested in the basics of using Barbecue, feel free to read on.

Why write yet another entry on Barbecue? Well, I was recently reading about barcodes, for my own edification, and did not find many introductory resources (you know ... ones that ask and answer those silly novice questions?). So decided to write up my findings and first impressions, in the hopes of providing someone else with a starting point in their research. Even if they are not using CF.

Background on Barcodes
Obviously using the library requires you know at least a little bit about the different bar code types. For example, some encode numeric digits only, others have different limits as to how many characters they can encode, etcetera. Two of the more helpful references I came across were: Different Types of Barcodes and a high level Barcode Comparison Chart.

Installing the Barbecue Library
Using the Barbecue library is very simple. Just download the library from sourceforge.net and extract the main jar from the .zip file. The current jar version is: barbecue-1.5-beta1.jar. Then place the main jar somewhere in your CF classpath, such as: C:\ColdFusion8\wwwroot\WEB-INF\lib. Finally restart the ColdFusion server so it will detect the jar.

Another option is to use Mark Mandel's JavaLoader.cfc, which is the option I chose. Just load the Barbecue jar in your array of paths, and create a new instance of the JavaLoader. (Obviously, this is just for demonstration. Usually you store the javaLoader instance in the server scope to avoid memory leaks).

Note: The jdom.jar must also be added to the JavaLoader paths if you intend to use Barbecue's SVG functionality.

<cfset paths = [] />
<cfset arrayAppend(paths, expandPath('barbecue-1.5-beta1/barbecue-1.5-beta1.jar')) />
<cfset arrayAppend(paths, expandPath('barbecue-1.5-beta1/lib/jdom.jar')) />
<cfset loader = createObject("component", "javaloader.JavaLoader").init( paths ) />

Factory Workers: (So much for progress..)
I found the easiest way to work with bar codes was using the factory classes. The factories provide easy to use static methods that make it a breeze to create, format and save barcodes.


Creating a Barcode
To a create a barcode, you use the BarcodeFactory. This class contains about 25+ methods for creating several different types of barcodes, such as Interleaved 2 or 5, EAN128, etcetera. Currently, only one 2d bacode is supported: PDF417. The methods are intuitively named, and most require only the string to be encoded. For example, to create a Bookland barcode, just grab a reference to the Factory and call the createBookland() method with the value you wish to encode:

<cfset factory = loader.create("net.sourceforge.barbecue.BarcodeFactory") />
<cfset barcode = factory.createBookland( "0123456789" ) />

Creating a Barcode Image (Simple)

Now the object returned from the BarCodeFactory is not image. It is an instance of one of the Barbecue classes: net.sourceforge.barbecue.Barcode. But there are several easy ways for converting the barcode object into an image. I found at least five (5) methods. (There is a catch to a few of them, but more about that in a minute) .

The first three (3) options use the BarcodeImageHandler factory. It has a few handy methods for converting a barcode object to either an in memory image or to a physical file.

(1) Generating a BufferedImage
To convert a barcode object to a BufferedImage, simply grab a reference to the factory, then call the getImage() method. To display or manipulate the BufferedImage in CF, just wrap it in a CF image object.
<cfset ImageHandler = loader.create("net.sourceforge.barbecue.BarcodeImageHandler") />
<cfset buffImage = ImageHandler.getImage( barcode ) />
<cfset barcodeImage = ImageNew(buffImage) />
<cfimage action="writeToBrowser" source="#barcodeImage#" />
(2) Writing Images to an OutputStream
You can also write the image directly to an OutputStream. There are various uses of OuputStream's, such as streaming images to the browser. There is probably less need for that particular technique in later versions of CF. But as there are several different types of streams, it is good to be aware of this option.

The image handler has three (3) methods for writing images to a stream: writePNG(), writeGIF() and writeJPG(). As their names imply, they generate an image in the desired format and write the image bytes to the supplied stream. You can then use the stream however you wish:

<cfset ImageHandler = loader.create("net.sourceforge.barbecue.BarcodeImageHandler") />
<cfset outStream = loader.create("java.io.ByteArrayOutputStream").init() />
<cfset ImageHandler.writePNG( barcode, outStream ) />
<!--- 1) Display the image using cfimage ... --->
<cfset barcodeImage = ImageNew(outStream.toByteArray()) />
<cfimage action="writeToBrowser" source="#barcodeImage#" />

<!--- 2) OR .. using cfcontent ... --->
<cfcontent type="image/png" variable="#outStream.toByteArray()#" />

(3) Saving Images to a File
Lastly, the handler has three methods for saving barcode images to a physical file: saveGIF(), saveJPG() and savePNG(). It is much like the previous option, only you pass in a java.io.File object representing the output file.

<cfset ImageHandler = loader.create("net.sourceforge.barbecue.BarcodeImageHandler") />
<cfset IOFile = loader.create("java.io.File") />

<!--- save the barcode in three (3) different formats  --->
<cfset imageFile = IOFile.init( ExpandPath("myBarcode.gif") ) />
<cfset ImageHandler.saveGIF( barcode, imageFile ) />
<img src="myBarcode.gif" alt="GIF  Format" />

Now earlier I mentioned a catch. In my tests, the generated images all had a black border along the bottom of the barcode. I am guessing it has something to do with the canvas color of the image object. One way I was able to remove it, was by creating a blank image first. So I could control the canvas color. Then use some lower level methods to draw the barcode onto my image. That brings me to the other two (2) options for creating images.




Creating a Barcode Image (Advanced)

While not as easy as using the factory methods, you can also use two methods within the Barcode class itself to generate an image. But unlike using the image factory, it is strictly B.Y.O. (bring your own) .. image. You must already have a blank image in order to use these methods. They are probably what the BarcodeImageFactory uses behind the scenes. However, I thought I would point them out to explain how they work.

1) Using Barcode.draw(graphics, x, y)
The first, and simpler, method is: draw(). To use it, I just created a blank CF image. Since the default canvas color for "rgb" images is black, I changed it to white. Then extracted the graphics object from my image and passed it into the draw function. The result was a barcode without that bottom border.




<!--- Option 1: RBG image, white canvas --->
<cfset img = ImageNew("", barcode.getWidth(), barcode.getHeight(), "rgb", "ffffff") />
<cfset graphics = ImageGetBufferedImage(img).getGraphics() />
<cfset barcode.draw( graphics, 0, 0) />

<cfimage action="writeToBrowser" source="#img#">
<br /><br />

<!--- Option 2: Transparent image --->
<cfset img = ImageNew("", barcode.getWidth(), barcode.getHeight(), "argb") />
<cfset graphics = ImageGetBufferedImage(img).getGraphics() />
<cfset barcode.draw( graphics, 0, 0) />

<div style="background-color: blue;">
<cfimage action="writeToBrowser" source="#img#"> <br />
</div>

2) Using Barcode.output(output)
The second method is a little more complex. You probably will not need it. But still, it is good to know. The output() method works with an instance of what Barbecue calls an Output object. The are several types of Output classes, and they are all just used to convert Barcode objects from one format to another: like to image, svg or eps.

Since I wanted to create an image, I used the GraphicsOutput class. It is not as complicated as it looks, but it does involve more moving pieces than other options. Constructing a GraphicsOutput object requires several pieces of information: the image graphics, barcode font and fore/background colors. You simply pass these objects into the constructor to create a new Output object. Then call Barcode.output() to paint the barcode onto your image graphics using the supplied settings.

Example

Code

<!--- create a new image and extract the graphics --->
<cfset img = ImageNew("", barcode.getWidth(), barcode.getHeight(), "rgb", "ffffff") />
<cfset graphics = ImageGetBufferedImage(img).getGraphics() />

<!--- create the desired font and colors --->
<cfset Font = createObject("java", "java.awt.Font") />
<cfset Color = createObject("java", "java.awt.Color") />

<cfset barcodeFont = Font.init("Arial", Font.PLAIN, 12 ) />
<cfset backgroundColor = Color.BLUE />
<cfset foregroundColor = Color.decode("##008040") />

<!--- grab a Color object that will be used to set the barcode fore/background --->
<cfset GraphicsOutput = loader.create("net.sourceforge.barbecue.output.GraphicsOutput")>
<cfset gOut = GraphicsOutput.init( graphics, barcodeFont, foregroundColor, backgroundColor ) />
<cfset barcode.output( gOut ) />

<cfset ImageWrite( img, ExpandPath("ColdFusion_Barbecue_GraphicsOutput.png")) />
<!--- display the barcode image --->
<cfimage action="writeToBrowser" source="#img#"> <br />


Working with Barcode Properties

Working with the various ways of creating images eventually brought me back to the Barcode class itself, and using its methods to alter some of the default settings. One of the things that struck me about the Barcode class is that it extends JComponent. At first that seemed a bit strange, as JComponent is one of the base classes for swing gui components. But further reading revealed Barbecue can be used in servlets or swing applications. So extending JComponent made more sense.

Now I mentioned swing for a reason. After testing various Barcode properties, I quickly realized many of the properties only apply if you are using swing. For example, there are several methods for adjusting width and height. But they had no effect when using images. Considering these methods are inherited from JComponent, that makes sense. (Not that you can really force the barcode dimensions anyway). An entry on Adam Presley's blog confirmed my suspicion. You cannot set the dimensions explicitly. But instead should use the barWidth and barHeight properties to adjust the overall barcode size.

Here are some of the more useful Barcode properties.

Barcode.setBarWidth()/setBarHeight()
These methods can be used to set the width of the thinnest bar or the height of a single bar, within the overall barcode. You may need to experiment to get the correct settings. So you do not end up with an invalid barcode or one that does not render properly ;)

setBarWidth( 6 )


Code

<cfset BarcodeFactory = loader.create("net.sourceforge.barbecue.BarcodeFactory") />
<cfset barcode = BarcodeFactory.createStd2of5( "0123456789" ) />
<cfset barcode.setBarWidth( 6 ) />
...

Barcode.setDrawingQuietSection(boolean)
This setting controls whether extra whitespace (ie quiet zone) should be rendered.

Code

	<cfset barcode.setDrawingQuietSection( false ) />

setDrawingQuietSection(true)

setDrawingQuietSection(false)


Barcode.setDrawingText(boolean)
This setting controls whether the barcode data (ie un-encoded) is displayed beneath the barcode.

Code

	<cfset barcode.setDrawingText( false ) />
setDrawingText(false)


Barcode.setLabel(string)
This method should allow you to override the text displayed beneath the barcode. But I was not able to get this setting to work.

Barcode.setFont(java.awt.Font)
This method allows you to use a different font for any text displayed beneath the barcode.

Code:

<cfset Font = createObject("java", "java.awt.Font") />
<cfset barcodeFont = Font.init("Arial", Font.ITALIC, 12) />
<cfset barcode.setFont( barcodeFont ) />
setFont( [Arial, Italic, size 12] )



Barcode.setBackground(java.awt.Color)/setForeground(java.awt.Color)
These methods can be used to change the foreground or background colors of the barcode. They are inherited from JComponent, but are used by the Barcode class.

Code:

<!--- change foreground --->
<cfset Color = createObject("java", "java.awt.Color") />
<cfset barcode.setForeground( Color.RED ) />

<!--- change background (hex color) --->
<cfset Color = createObject("java", "java.awt.Color") />
<cfset barcode.setBackground( Color.decode("##0080c0") ) />

setForeground()


setBackground()


Barcode.setResolution(int)
Used to adjust the resolution. Default is 72dpi.

Code:
	<cfset barcode.setResolution( 300 ) />
SVG
You can also convert barcode objects to svg. Simply grab a reference to the SVGFormatter class, then call the formatAsSVG() method. The result is an svg string.

Code:
<cfset BarcodeFactory = loader.create("net.sourceforge.barbecue.BarcodeFactory") />
<cfset barcode = BarcodeFactory.createBookland( "0123456789" ) />
<cfset SVGFormatter = loader.create("net.sourceforge.barbecue.formatter.SVGFormatter") />
<cfset svgString = SVGFormatter.formatAsSVG( barcode )>
Related Entries:
CFBarbecue.cfc (...Just because)
Seeing Stripes: Experiment with ZXing


...Read More

Saturday, November 7, 2009

OT: ColdFusion Trials (Let's Do The Time Warp Again)

I was checking my "trials" email folder the other day and these messages gave me a chuckle. That and the fact that I received a "Your ColdFusion 9 trial starts now" message, before my download had even finished! Can you spot what is wrong here?


Yes... I did download the ColdFusion 9 Trial previously. But it still made me laugh ;)

...Read More

ColdFusion: Small Try/Catch Gotcha With CreateObject() and Exceptions

Exceptions can be deceptive and loathsome creatures at times. Case in point, I was happily using createObject() to load an object from a java jar in my classpath. I then added a try/catch. So I could detect when the jar probably was not added to the CF classpath properly, and display a more meaningful error message.


<cfscript>
try
{
handler = createObject("java", "net.sourceforge.barbecue.BarcodeImageHandler");
}
catch(java.lang.ClassNotFoundException e)
{
throwError(message="Verify the Barbecue jar is in your CF classpath", type="SupportingClassNotFound");
}
</cfscript>

Great in theory, the only problem was my try/catch did not work. I double checked the stack trace and it sure seemed like I was catching the correct exception type: java.lang.ClassNotFoundException. So I tried the broader type java.lang.Exception. Then cfdump'd the details, to see if I could spot the problem. Sure enough, the actual exception type was not what CF was reporting on the error screen. The actual exception type being:
coldfusion.runtime.java.JavaObjectClassNotFoundException.


(Boy, what a mouthful. As if the base exception name is not long enough already...?)


Apparently the stack trace in the error page was displaying the RootCause, not the actual exception thrown. While that makes some sense, unfortunately the thrown exception type is what is needed to catch the darned thing. Anyway, once I changed my try/catch to use the actual exception type being thrown, everything was just dandy.
<cfscript>
try
{
handler = createObject("java", "net.sourceforge.barbecue.BarcodeImageHandler");
}
catch(coldfusion.runtime.java.JavaObjectClassNotFoundException e)
{
throwError(message="Verify the Barbecue jar is in your CF classpath", type="SupportingClassNotFound");
}
</cfscript>
What is the world coming to when you cannot even trust error messages Oh, wait. Scratch that. Error messages lie all the time. I am sure they do not mean to lie, they just hold back critical information when you most need it. The secretive little trolls ;)

...Read More

No more use for StructFind() ?

For some reason I always forget about the one good use for StructFind(). (Thank goodness for old blog entries). Though it seems even that use is gone in ColdFusion 9. Can anyone think of another one?


<!--- Works in CF9, not CF8 --->
<cfoutput>
#getSomeStructure()["dog"]#
</cfoutput>

<cffunction name="getSomeStructure" returntype="struct">
<cfset var myStruct = {} />
<cfset myStruct.cat = "felix" />
<cfset myStruct.dog = "clifford" />
<cfreturn myStruct />
</cffunction>

...Read More

Tuesday, November 3, 2009

A Better PdfPageEventHandler with JavaLoader/CFCDynamicProxy

Yesterday , I wrote about discovering a true gem in latest version of Mark Mandel's JavaLoader: the CFCDynamicProxy. This tiny, but powerful, class acts as a wrapper around a ColdFusion cfc. Essentially allowing it to mimic a native java object and communicate directly with other java objects in ways it never could before! We all know just about everything in ColdFusion boils down to a java object internally. But until now there were certain things you just could not do from a cfc without taking the r-e-a-l-l-y long way around. Well, the CFCDynamicProxy changes all that.


A perfect example is page events in iText. In order to add custom headers, footers, etcetera with iText you need a custom java class that implements the PdfPageEvent interface. Well as easy and natural as that is in a java environment, it is a bit awkward to have to create and load a new java class in CF every time you wish to create a different set of headers or footers.

Eventually, I came up with a more dynamic method of calling a cffunction to add headers/footers from a java, but it still required that custom java class. But using the CFCDynamicProxy, I was able to simplify and rewrite the code entirely in CF, and let me say... man what a difference! It is now totally dynamic and written in native CF, no extra java classes needed. In comparison to the elegance of using the new Proxy, my previous attempt looks like a decrepit, wart-nosed-hag. To be fair, a lot of what the previous entry was doing manually is still going on in the background. But
the CFCDynamicProxy makes it much, much simpler.

The first step in my code beautification effort was creating a CFC that implemented all of the methods in the iText PdfPageEvent interface. Just as you would if you were implementing a CF interface. Using the PdfPageEvent API, I created a cfc with a function for each of the methods in that interface, taking care to properly align the arguments and data types so they matched their java counterparts.

Mapping the java to ColdFusion data types is pretty simple. Any arguments that are instances of a java class (like PdfWriter, Document, Rectangle) are all mapped to type="any". The rest are usually primitive types like "string" and "float" and the conversions are intuitive, for the most part.






Next I filled in a few functions needed for generating basic page footers. The key function was onPageEnd, which is where all the action takes place. OnPageEnd will be called by iText just before new pages are written to my new pdf document. So inside that function, I grab the writer object and use it to set the desired properties and finally write the footer text onto the pdf.


<cffunction name="onEndPage" access="public" returntype="void" output="true"
hint="Called when a page is finished, just before being written to the document.">
<cfargument name="writer" type="any" required="true" hint="Writer for the target pdf. Instance of com.lowagie.text.pdf.PdfWriter" />
<cfargument name="document" type="any" required="true" hint="Document for target pdf. Instance of com.lowagie.text.Document" />
<cfset var Local = {} />

<cfscript>
if (len(variables.instance.footerText))
{
Local.cb = arguments.writer.getDirectContent();
Local.cb.saveState();

Local.cb.beginText();
Local.cb.setColorFill( variables.instance.textColor );
Local.cb.setFontAndSize( variables.instance.font, javacast("float", variables.instance.fontSize) );
Local.cb.setTextMatrix( arguments.document.left(), arguments.document.bottom() - 10);
Local.text = variables.instance.footerText &" page ["& arguments.writer.getPageNumber() &"]";
Local.cb.showText( Local.text );
Local.cb.endText();

Local.cb.restoreState();
}
</cfscript>

</cffunction>


Having completed my faux-java-class/cfc, the next step was using the CFCDynamicProxy to see if this stuff actually worked. Using the Dynamic proxy is like using any other jar with the JavaLoader. You just add the new cfcdynamicproxy.jar to the array of paths. Then create an instance of the JavaLoader, except this time you set the loadColdFusionClassPath parameter equal to true. This allows the JavaLoader to access ColdFusion's classes to create the proxy object.

Note: The code example included with JavaLoader 1.0 was very easy to follow. So my base code is ripped straight out of Mark's example.



<cfscript>
//add the javaloader dynamic proxy library (and the iText jar) to the javaloader
libpaths = [];
arrayAppend(libpaths, expandPath("/javaLoader/support/cfcdynamicproxy/lib/cfcdynamicproxy.jar"));
arrayAppend(libpaths, expandPath("/dev/itext/iText-2.1.7.jar") );

//we HAVE to load the ColdFusion class path to use the dynamic proxy, as it uses ColdFusion's classes
loader = createObject("component", "javaLoader.JavaLoader").init(loadPaths=libpaths, loadColdFusionClassPath=true);
</cfscript>

Once you have an instance of the JavaLoader, instantiate your cfc as usual. (In my case, I am instantiating my PdfPageEventHander.cfc) The last step is to wrap the cfc in a proxy, which was incredibly simple. First you create an array of all of the interfaces, your cfc implements. Then grab a reference to the new Dynamic Proxy class and use its createInstance method to create your proxy object. That is all there is to it.


<cfscript>
//....
//intialize the page event handler component
eventHandler = createObject("component", "PdfPageEventHandler").init( font=textFont, fontSize=10, textColor=textColor);
//add a custom footer
eventHandler.setFooterText( "www.clueless.corp * 85 anywhere blvd * lost city" );

//we can pass in an array of strings which name all the interfaces we want out dynamic proxy to implement
interfaces = ["com.lowagie.text.pdf.PdfPageEvent"];

//get a reference to the dynamic proxy class
CFCDynamicProxy = loader.create("com.compoundtheory.coldfusion.cfc.CFCDynamicProxy");

// create a proxy that we will pass to the iText writer
eventHandlerProxy = CFCDynamicProxy.createInstance(eventHandler, interfaces);
</cfscript>

Now for the real test. I passed my proxy object into my iText writer object, just as I would if it were a native java class:
// ...
<cfscript>
fullPathToOutputFile = ExpandPath("./ABetterPdfPageEventHandler.pdf");
document = loader.create("com.lowagie.text.Document").init();
outStream = createObject("java", "java.io.FileOutputStream").init(fullPathToOutputFile);
writer = loader.create("com.lowagie.text.pdf.PdfWriter").getInstance(document, outStream);

// ** register the PROXY as the page event handler **
writer.setPageEvent( eventHandlerProxy );

document.open();
Paragraph = loader.create("com.lowagie.text.Paragraph");
document.add( Paragraph.init("Paragraph Text....") );
document.newPage();
document.add( Paragraph.init("Paragraph Text....") );
document.newPage();
document.close();
outStream.close();

WriteOutput("Done!");
</cfscript>


.. and iText was none the wiser! Voila, instant footers.



A few simple changes to my cfc and I had instant headers instead. It feels like writing java code ... except in CF ;)



You know when I woke up this morning, I thought all of this might have been just a dream. But CFCDynamicProxy is real .. and this class seriously rocks!


...Read More

  © Blogger templates The Professional Template by Ourblogtemplates.com 2008

Header image adapted from atomicjeep