Skip to content

Actionscript class to create QR Codes

Dear Reader,

Anyone that knows me knows that I love new toys. Sometimes it’s a new piece of hardware or a new game. Sometimes however, it is just a idea to play with. This post is all about an idea. I’ve been intrigued by QR Codes for a while. Actually, my love of bar codes goes way back to when I used to run computer systems at my parent’s company. Even back then I saw how they could change things. These days, Bar Codes are ubiquitous in retail but haven’t moved out of the business niche. QR Codes stands ready to change that. Given that most smart phones can translate them they make a great way to link the real world to the net.

There are great resources on the web to generate QR Codes and save them to your computer for later use. However, being a developers, I wanted something I could play with and incorporate into my own applications. The problem is that generating a QR Code is not a trivial thing. Lucky for us however, Google has done the hard part already and all I really need to do is wrap their API in a class and I can use this in my own applications.

What you need to get started

If you want to create QR Codes, you have to be able to do two things, create them and read them. Since this post is all about creating them, I will leave the reading them part to you. If you have a smart phone, there is probably a good application for you to use to verify your work. For the Android OS, I use BarCode Scanner.

That’s it. All you need is a way to check your work. Google does the heavy lifting of creating the graphic and the code below gives you a nice easy wrapper to allow you to include it in your Flash Builder 4 applications.

The fun part

Ok, let’s take apart the code. This is one of my first Flex components that I am confident enough to release. That having been said, I do welcome any comments or criticisms about the class.

If you haven’t yet, go review Google’s Chart API page on QR Codes. Google has boiled it down to the essentials, 3 required parameters and two optional. The heart of the class puts those parameters together and makes the call to the API. It takes the image it gets back and puts it in a form that Flex applications can re-purpose.

Planning for all EVENTualities

[Event(name="imageCreated", type="ImageCreatedEvent")]
[Event(name="imageFailed",  type="ImageFailedEvent")]

First we have to prepare for the events that can happen. In our case, either it works or it doesn’t. Either way, we need to provide the developer using this class with a way to handle both eventualities in their code so we don’t have to in our code.

The code for the events is located below, after the main class. The ImageFailedEvent is just a subclass of FaultEvent and serves only as a marker that can be trapped separately.

ImageCreatedEvent however, has an additional property of thisImage. When the even is fired, it passes a copy of the QRCode object to all listeners allowing them to easily fetch and incorporate the resulting graphic into the application.

The BINDABLEs

[Bindable] public var qualityArray:ArrayCollection = new ArrayCollection(["L","M","Q","H"]);
[Bindable] public var marginArray:ArrayCollection  =  new ArrayCollection([1,2,3,4,10,40]);

Of all the properties we have, only two are bindable, the two data arrays that specify the acceptable parameters for Error Correction and Margin.

All other parameters have reasonable defaults but in using the class, no reason to actually make them bindable presented itself.

The heart of the matter

var loader:Loader = new Loader();
loader.contentLoaderInfo.addEventListener(Event.COMPLETE, imageComplete);
loader.load(request);

The heart of the class is the createImage() method. To use the class, you instantiate the class, set the required properties, and then call createImage(). Within that method, the first thing we do is a parameter check. Make sure we have given the image a valid height and width and that we actually have something to encode. Once we can make a reasonable call to the API, we setyp for the call, make it and deal with the results.

[Side Note: QR Codes can store a variety of information. The one above is simple text. Anything that can be represented as text can be encoded. The zxing library can interpret several different types of data and the Android app will allow you to act on it appropriately. If the content starts with mailto: then the app asks me f I want to send an email to this person. If it starts with http: then the app asks me if I want to open it in a browser. Similarly, if it starts with BEGIN VCARD: then it will let me store the resulting VCARD in my contacts. Your application will most likely support similar features. In the reference application I created to test QRCode.as, I allow for encoding text, emails, URLs and VCARDS. However, this is ancillary to the QR Code specification. The specification does not dictate formats, only maximums for the different types of data (numeric, alphanumeric, binary, etc.) being stored.]

Back in February I wrote a post about Dynamically loading images from the web. In it, I talked about how calling an API that does not return XML or JSON is not as straightforward as it might be in Flex. I used the techniques (and probably the code) that I displayed in that post to make the call to Google’s chart API. I won’t rehash the previous post, you can review it as your leisure. At it’s core is a call to import flash.display.Loader to load the contents from the web.

createImage() does have one new feature, debug. If you set debug = true you will get an alert showing you the parameters being passed to Google’s API. I am a big believer in giving developers a way to see what is going on without having to hack the code. If you have Charles installed and running, you can see the call actually being made, however, I find it easier just to incorporate debugging into the code and allow developers the option. The dark side of this is that developers have the option of shooting themselves in the food this way as well. More than one developer has left debugging turned on when releasing their code.

Dealing with the aftermath

Since Flex is asynchronous, we have to have a method to deal with the image once the API call is done. imageComplete() is the handler for the COMPLETE event. In it, we set the height and width of the actual image as well as the data from the image itself. Then we dispatch the imageComplete event so that the main program can do something with the image.

Wrapping it up

That is it for the class. To use it, just instantiate, set the parameters and fire createImage() Of couse because we are talking to an API at Google, this only works with AIR applications. If you need a Flash application that runs from your own domain to be able to use QRCode.as, you will have to setup a project on your server to proxy the call to Google. (Trivial but beyond the scope of what we are discussing here.)

Until next time,
I <3 |<
=C=

Disclosure: I work with Blue Parabola, Adobe is a customer of Blue Parabola.

The Code

/*
 * Instantiate the object
 */
var qrCode:QRCode = new QRCode();
 
/*
 * Add our event listeners
 */
qrCode.addEventListener('imageCreated',this.displayImage);
qrCode.addEventListener('imageFailed',this.cannotDisplayImage);
 
 
/*
 * Setup for the call
 */
qrCode.width                = parseInt(imageWidth.text);
qrCode.height               = parseInt(imageHeight.text);
qrCode.margin               = margin.selectedIndex;
qrCode.errorCorrectionLevel = errorCorrectionLevel.selectedIndex;
qrCode.content              = contentToEncode; 			
qrCode.imageType            = grpImageType.selectedValue.toString();
 
/*
 * make the call
 */
qrCode.createImage();
/**
 * 
 * http://code.google.com/apis/chart/docs/gallery/qr_codes.html
 */
package
{
	[Event(name="imageCreated", type="ImageCreatedEvent")]
	[Event(name="imageFailed",  type="ImageFailedEvent")]
 
	public class QRCode
	{
		import flash.display.BitmapData;
		import flash.display.Loader;
		import flash.display.LoaderInfo;
		import flash.net.FileReference;
		import flash.net.URLRequest;
		import flash.net.URLVariables;
		import flash.utils.ByteArray;
 
		import mx.collections.ArrayCollection;
		import mx.controls.Alert;
		import mx.controls.Image;
		import mx.graphics.codec.JPEGEncoder;
		import mx.graphics.codec.PNGEncoder;
		import mx.rpc.events.FaultEvent;
		import mx.rpc.events.ResultEvent;
 
 
		[Bindable] public var qualityArray:ArrayCollection = new ArrayCollection(["L","M","Q","H"]);
		[Bindable] public var marginArray:ArrayCollection  =  new ArrayCollection([1,2,3,4,10,40]);		
 
		public var height:int = 150;
		public var width:int  = 150;
 
		public var content:String           = "";		
		public var debug:Boolean            = false;		
		public var errorCorrectionLevel:int = 0;
		public var imageType:String         = 'PNG';
 
		public var margin:int               = 0;
 
		protected var _image:Image = new Image();
		protected var apiUrl:String         = 'http://chart.apis.google.com/chart';
 
		public function QRCode()
		{
			/*
			* cht=qr
			* chs=<width>x<height>
			* chl=<data>
			* chd
			* choe=<output_encoding>
			* chld=<error_correction_level>|<margin>
			* 
			* Notes:
			* changing the quality needs to make sure the max number of chs hasn't been exceeded.
			* Add Types
			*/
		}
 
 
 
		public function createImage():void
		{
			// do a property check and throw appropriate error for missing properties like content to encode
			if (this.width<1) {
				dispatchEvent(new ImageFailedEvent(ImageFailedEvent.IMAGE_FAILED,false,false));
				return;
			}
			if (this.height<1) {
				dispatchEvent(new ImageFailedEvent(ImageFailedEvent.IMAGE_FAILED,false,false));
				return;
			}
			if (this.content.length<1) {
				dispatchEvent(new ImageFailedEvent(ImageFailedEvent.IMAGE_FAILED,false,false));
				return;
			}
 
			var params:URLVariables = new URLVariables();
			params.cht  = 'qr';
			params.chs  = this.height +'x'+ this.width;
			params.chl  = this.content;
			params.chld = qualityArray[errorCorrectionLevel]+"|"+marginArray[margin];
 
			var request:URLRequest = new URLRequest(apiUrl);
			request.method="POST";
			request.data = params;
 
			// DEBUGGING
			if (this.debug) {
				var a:String = apiUrl + 
					'?cht='+params.cht
					+"&chs="+params.chs
					+"&chld="+params.chld
					+"&chl="+params.chl
				Alert.show(a);			                                                       
			} // if (this.debug)
 
			var loader:Loader = new Loader();
			loader.contentLoaderInfo.addEventListener(Event.COMPLETE, imageComplete);		 	
			loader.load(request);
		} // protected function createImage():void
 
 
		protected function imageComplete(event:Event):void
		{
			var loader:Loader = (event.target as LoaderInfo).loader;
			_image.data   = loader.content;
			_image.width  = loader.width;
			_image.height = loader.height;
			dispatchEvent(new ImageCreatedEvent(ImageCreatedEvent.IMAGE_CREATED,false,false,_image)); 
			return;
		} // protected function displayImage(event:Event):void
 
		protected function cannotDisplayImage(event:FaultEvent):void
		{
			dispatchEvent(new ImageFailedEvent(ImageFailedEvent.IMAGE_FAILED,false,false)); 
		} // protected function cannotDisplayImage(event:FaultEvent):void
 
		public function getImage():Image{
			return this._image;
		}
 
	}
}

ImageCreatedEvent.as

package 
{
	import flash.events.Event;
	import mx.controls.Image;
 
	public class ImageCreatedEvent extends Event
	{
		public static const IMAGE_CREATED:String = 'imageCreated';
		protected var _image:Image;
 
		public function ImageCreatedEvent(type:String, bubbles:Boolean=false, cancelable:Boolean=false, thisImage:Image=null)
		{
			super(type, bubbles, cancelable);
			_image = thisImage;
			return;
		}
 
		override public function clone():Event 
		{
			return new ImageCreatedEvent(type, bubbles, cancelable, _image);
		}
 
	}
}

ImageFailedEvent.as

package
{
	import mx.rpc.events.FaultEvent;
 
	public class ImageFailedEvent extends FaultEvent
	{
		public static const IMAGE_FAILED:String = 'imageFailed';
 
		public function ImageFailedEvent(type:String, bubbles:Boolean=false, cancelable:Boolean=false)
		{
			super(type, bubbles, cancelable);
		}
 
	}
}
Disclosure: I work with Blue Parabola. Adobe is a customer of Blue Parabola.

9 thoughts on “Actionscript class to create QR Codes

  1. I love how most of this code is hand-waving to appease the Flex gods. Trust me, this is not a critique of your work, Cal (looks quite clean, actually). It just drives me up a wall how much code it takes (and how non-obvious the APIs are) even for the most stupid-simple things. We’re dealing with this right now at work. Seriously, try POSTing a multi-dimensional array in Flex some time.

  2. Nate!

    I agree in principal but practically, it’s no worse than Java…or Zend Framework for that matter. There is a lot of Yak Shaving involved in any serious programming these days.

  3. It’s a sad day when code has to be wastefully-written in order to be considered “serious”. In the words of Edsger Dijkstra: “If we wish to count lines of code, we should not regard them as ‘lines produced’ but as ‘lines spent’.”

    Increasing the complexity of a task by artificially inflating the number of lines of code required to complete it is *never* a good idea. Every extra, unnecessary line of code is an extra chance for something to break. More LOC means more testing and more maintenance.

    The fact that so many PHP developers have so enthusiastically adopted the idea of writing Java code with dollar signs just shows how far we have yet to go as a developer community.

    Oh, and by the way: http://bin.rad-dev.org/view/717e973f82912bcf729cc7eb3daaefd8

  4. Nah, I actually think Nate has a point: the Flash API designs are curious and unnecessarily verbose. There’s a ton of object creation that happens when there’s really no reason for it — or at very least they could simplify the developer’s life by making common tasks single-method calls. The SQLite API in AIR suffers from this, and a lot of the URLRequest stuff does too. Like this:

    > var params:URLVariables = new URLVariables();

    Really? we need a special object to set URL variables? A set of key/val pairs isn’t sufficient in almost all situations?

    You really get the impression that the Flash APIs were not designed by people who build web apps/connected apps for a living.

  5. @Nate,

    The point of the class wasn’t to see how few lines of code I could write it in. It was to create a wrapper class for the API that allowed me to re-use the code in other applications. (Besides my ass-ugly reference implementation, which I will release shortly to bring the entire series to a close) Yes, it is more verbose that your example. I’m sure we could put our head together and hammer out a one-liner in Unix that did the same job. But let’s not pretend that it, or your JavaScrit sample code, are actually executing only a few lines of code. You only had to write a few lines because someone wrote the environment you are working in. I am not arguing your point about Flex being verbose. I am saying it is unfair to single out Flex when most modern frameworks are similarly verbose. (No, I’ve not looked at Li3 to verify that you are not similarly verbose)

    @Ed,

    Ya got me. I absolutely hate the idea of using URLVariables and other classes like that. I’ve not looked at the code beneath it but I am guessing that URLVariables is nothing more than an array. It’s stupid and it is an artifact of modern frameworks that I have looked at. Zend (and I am not picking on Zend, I am sure CakePHP, symfony and Li3 do this as well) subclasses classes and implement interfaces without any additional code or functionality just to “flag” them. (Exceptions seem to be biggest place where this is done but I’ve seen it done in other places as well)

    I am not arguing that this is a good thing, my only point is that Flex is no worse than other modern frameworks and it’s unfair to pink on it just because you don’t like Flash.

    Thank you both for taking the time to comment!

    =C=

  6. “I’m sure we could put our head together and hammer out a one-liner in Unix that did the same job. But let’s not pretend that it, or your JavaScrit sample code, are actually executing only a few lines of code. You only had to write a few lines because someone wrote the environment you are working in.”

    @Cal
    For a second I thought you wrote the code to create QR codes from scratch! You are using the same API as Nate did!! I have never worked with Flex, but if it takes this much code to do the same as Nate did, I would say it’s quite fair to single out Flex as verbose.

  7. @kertz,

    No, I wrote a wrapper for the Google API. Yes, Nate’s does basically the same thing. Until you try to use it in an application that let’s the user set parameters, then you have to add properties to it. And allow the user to get the image back, handle events, etc.

    In my talk “5 Things I wish they had told me about PHP” I make the point that I love PHP because I can prototype and idea in 20 lines of code. I can concentrate on the idea, not the language or framework and just see if it works.

    Once I do that though, I go back and put the idea into an application. My 20 lines of code grow to 200 or more and my single index.php grows to 5-6 classes, not including the framework overhead. Why go through all the trouble when I had it working in 20 lines, because I write applications, not scripts. Applications have to take other things into account – security, failure, validation of inputs, etc. When you start adding in all the things it takes to properly build a reusable component, yes it bloats. It should be noted that this was a component, not an application. I’ll be publishing the reference application soon and because the component does most of the heavy lifting, it is pretty simple in operation.

    My point to Ed and Nate (and by the way, I know both of them personally, consider them friends, and respect their opinions highly) was that Flex is no better or worse than most of the other frameworks we use in the PHP world. I can’t compare it to JavaScript since i have no frame of reference there but my guess is you see similar bloat there. I do know that I saw the same bloat in early Java frameworks back when I did a little Java programming.

    I am not denying that Flex is a verbose framework. I am coming to it’s defense against those that I know have a bias against Flash and would simply attack it for the sake of attacking Flash.

    @Ed,

    I disagree.

    @both of you,

    Thanks for taking the time to join the discussion!

    =C=

Comments are closed.