<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Lon Hosford&#039;s Bitbox &#187; AIR (Adobe Integrated Runtime)</title>
	<atom:link href="http://www.lonhosford.com/lonblog/category/technologies/adobe/air-adobe-integrated-runtime/feed/" rel="self" type="application/rss+xml" />
	<link>http://www.lonhosford.com/lonblog</link>
	<description>Lon (Alonzo) Hosford&#039;s Professional Consulting Blog</description>
	<lastBuildDate>Fri, 03 Feb 2012 00:55:07 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.0.1</generator>
		<item>
		<title>Read Flash SWF Header in AIR with Parsley Framework</title>
		<link>http://www.lonhosford.com/lonblog/2011/01/17/read-flash-swf-header-in-air-with-parsley-framework/</link>
		<comments>http://www.lonhosford.com/lonblog/2011/01/17/read-flash-swf-header-in-air-with-parsley-framework/#comments</comments>
		<pubDate>Mon, 17 Jan 2011 18:34:47 +0000</pubDate>
		<dc:creator>Lon Hosford</dc:creator>
				<category><![CDATA[AIR (Adobe Integrated Runtime)]]></category>
		<category><![CDATA[Actionscript 3]]></category>
		<category><![CDATA[Flex]]></category>
		<category><![CDATA[Parsley]]></category>
		<category><![CDATA[Actionscript]]></category>
		<category><![CDATA[Air]]></category>
		<category><![CDATA[BorderContainer]]></category>
		<category><![CDATA[Flex 4]]></category>

		<guid isPermaLink="false">http://www.lonhosford.com/lonblog/?p=1204</guid>
		<description><![CDATA[I was asked to look for open source tools to read the basic information contained in a Flash swf file. Most of the items you find are for reverse engineering swf files. In the search process I found two sources to parse the swf file header. The first was a php script written by Carlos [...]]]></description>
			<content:encoded><![CDATA[<p>I was asked to look for open source tools to read the basic information contained in a Flash swf file. Most of the items you find are for reverse engineering swf files. In the search process I found two sources to parse the swf file header. <img alt="" src="http://lh6.ggpht.com/_e5pwU0LJbN8/TTSEk55KhzI/AAAAAAAAFxY/LhkeY0x2TOA/s800/adobe-swf_header_published.png" class="alignleft" width="400" height="235" /></p>
<p>The first was a <a href="http://www.fecj.org/extra/SWF-info-and-Frame-Rate-extraction.html"  target = "_blank">php script</a> written by Carlos Falo Hervá. You need to scroll down to the end of the post to find his work. This works just fine if you want to run off a server. </p>
<p>The second is <a href="http://simplistika.com/parsing-reading-swf-header/"  target = "_blank">written in Actionscript</a> for Flash CS5. Only the author&#8217;s handle, jared, and not the author&#8217;s name is not available at the site. The download works nice and you can run from the author&#8217;s web page. You need type or paste in a url for the swf file name. The code is all dropped in on the first frame.</p>
<p>This site also provided a nice link to the <a href="http://www.adobe.com/content/dam/Adobe/en/devnet/swf/pdf/swf_file_format_spec_v10.pdf"  target = "_blank">Adobe SWF file version 10 specifications</a> if you care to know. Page 25 has the key information on the SWF header. This is also repeated in the SWFHeaderParser parser class presented in this post.</p>
<p>I took the code for the second example and created a drag and drop version Adobe Air. First I separated the code into two Actionscript classes. One for parsing and one for loading swf file bytes. These classes are named respectively SWFHeaderParser and SWFHeaderLoader. I added a value object, SWFHeader_vo, to pass around the values. </p>
<p>I glued those items together in an Air project using the Parsley framework. This gave me a chance to use Parsley DynamicObject for the SWFHeaderLoader class that loads the SWF file for the parser.</p>
<p>The result is a fully decoupled version that serves as a super example of model-view-controller using Parsley for an Air app.</p>
<p>Download files:<br />
Here is the Flex project if you want to explore the code and the Air installer in case you just want the tool.</p>
<ul style = "padding-top:10px">
<li><a href="http://www.lonhosford.com/content/flex/swf/SWFHeader_Air.fxp"  onClick="javascript: pageTracker._trackPageview('/downloads/SWFHeader_Air.fxp'); ">Flex Builder 4 Flex Project</a></li>
<li><a href="http://www.lonhosford.com/content/flex/swf/SWFHeader.air"  onClick="javascript: pageTracker._trackPageview('/downloads/SWFHeader.air'); ">Adobe Air Installer</a></li>
</ul>
<p><div style = "text-align:center"><script type="text/javascript"><!--
google_ad_client = "pub-8926707286265620";
/* 300x250, created 7/29/10 */
google_ad_slot = "4548376258";
google_ad_width = 300;
google_ad_height = 250;
//-->
</script>
<script type="text/javascript"
src="http://pagead2.googlesyndication.com/pagead/show_ads.js">
</script></div></p>
<p><strong>SWF Parser &#8211; SWFHeaderParser.as</strong><br />
The parser class is first as that is what you might want to integrate into your code. Note it returns the SWFHeader_vo class. The comments are scant in this class since I did write the code and only converted it into a class, returned a value object and removed code not specific to parsing the bytes. The value object is created and populated on lines 85 &#8211; 95.</p>
<pre class="brush: as3; wrap-lines: false;">
package swf
{
	import flash.geom.Rectangle;
	import flash.utils.ByteArray;
	import flash.utils.Endian;
	import vo.SWFHeader_vo;
	/**
	 * SWF Header parsing. Code adapted from http://simplistika.com/parsing-reading-swf-header/.
	 * */
	public class SWFHeaderParser
	{
		private var xByte : uint;
		private var xNBits : int;
		private var xOffset : int;
		public function SWFHeaderParser()
		{
		}
		/**
		 * Get a SWFHeader_vo object from a ByteArray
		 * @param swfByteArray Any byte array, but expecting a SWF binary file or header from SWF file.
		 * @return Parsed values in SWFHeader_vo object.
		 * */
		public function parseBytes(swfByteArray : ByteArray) : SWFHeader_vo
		{
			return fParse(swfByteArray);
		}
		/**
		 * Original code from http://simplistika.com/parsing-reading-swf-header/ changed to
		 * return SWFHeader_vo object.
		 * */
		private function fParse(v : ByteArray) : SWFHeader_vo
		{
			/*
			Field		Type 	Comment
								Signature byte
			Signature	UI8		&quot;F&quot; indicates uncompressed
								&quot;C&quot; indicates compressed (SWF 6 and later only)
			Signature	UI8		Signature byte always &quot;W&quot;
			Signature	UI8		Signature byte always &quot;S&quot;

			Version		UI8		Single byte file version
			FileLength	UI32	Length of entire file in bytes
			FrameSize	RECT	Frame size in twips
			FrameRate	UI16	Frame delay in 8.8 fixed number of frames per second
			FrameCount	UI16	Total number of frames in file
			The header begins with a three-byte signature of either 0×46, 0×57, 0×53 (&quot;FWS&quot;); or 0×43, 0×57, 0×53 (“CWS”). An FWS signature indicates an uncompressed SWF file; CWS indicates that the entire file after the first 8 bytes (that is, after the FileLength field) was compressed by using the ZLIB open standard. The data format that the ZLIB library uses is described by Request for Comments (RFCs) documents 1950 to 1952. CWS file compression is permitted in SWF 6 or later only.

			A one-byte version number follows the signature.
			The version number is not an ASCII character,
			but an 8-bit number. For example, for SWF 4, the version byte is 0×04, not the
			ASCII character “4? (0×34). The FileLength field is the total length of the
			SWF file, including the header. If this is an uncompressed SWF file (FWS signature),
			the FileLength field should exactly match the file size. If this is a compressed SWF file
			(CWS signature), the FileLength field indicates the total length of the file after decompression,
			and thus generally does not match the file size. Having the uncompressed size available can make
			the decompression process more efficient. The FrameSize field defines the width and height
			of the on-screen display. This field is stored as a RECT structure, meaning that its size
			may vary according to the number of bits needed to encode the coordinates. The FrameSize
			RECT always has Xmin and Ymin value of 0; the Xmax and Ymax members define the width and
			height. The FrameRate is the desired playback rate in frames per second.

			Source: http://simplistika.com/parsing-reading-swf-header/

			*/
			var vFormat : String;
			var vSwfVersion : int;
			var vFileLength : int;
			var vFrameRate : int;
			var vTotalFrames : int;
			var vFrameSize : Rectangle;
			v.endian = Endian.LITTLE_ENDIAN;
			vFormat = v.readUTFBytes(3);
			vSwfVersion = v.readByte();
			vFileLength = v.readUnsignedInt();
			v.readBytes(v);
			v.length -= 8;
			if (vFormat == &quot;CWS&quot;)
				v.uncompress();
			v.position = 0;
			vFrameSize = new Rectangle();
			vFrameSize.left = xfReadNBits(v, true) / 20;
			vFrameSize.right = xfReadNBits(v) / 20;
			vFrameSize.top = xfReadNBits(v) / 20;
			vFrameSize.bottom = xfReadNBits(v) / 20;
			vFrameRate = v.readUnsignedByte() / 256 + v.readUnsignedByte();
			vTotalFrames = v.readUnsignedShort();
			var swfHeader_vo:SWFHeader_vo = new SWFHeader_vo();
			swfHeader_vo.format = vFormat;
			swfHeader_vo.swfVersion = vSwfVersion;
			swfHeader_vo.sizeUncompressed = vFileLength;
			swfHeader_vo.width = vFrameSize.width;
			swfHeader_vo.height =vFrameSize.height;
			swfHeader_vo.frameRate = vFrameRate;
			swfHeader_vo.totalFrames = vTotalFrames;
			return swfHeader_vo;
		}
		/**
		 * Original code from http://simplistika.com/parsing-reading-swf-header/.
		 * */
		private function xfReadNBits(v : ByteArray, vStart : Boolean = false) : uint
		{
			var n : uint;
			if (vStart)
			{
				xByte = v.readUnsignedByte();
				xNBits = xByte &gt;&gt; 3;
				xOffset = 3;
			}
			n = xByte &lt;&lt; (32 - xOffset) &gt;&gt; (32 - xNBits);
			xOffset -= xNBits;
			while (xOffset &lt; 0)
			{
				xByte = v.readUnsignedByte();
				n |= (xOffset &lt; -8) ? (xByte &lt;&lt; (-xOffset - 8)) : (xByte &gt;&gt; (-xOffset - 8));
				xOffset += 8;
			}
			return n;
		}
	}
}
</pre>
<p><strong>SWF Loader &#8211; SWFHeaderLoader.as</strong><br />
This is my class to load the SWF and it uses Parsley messaging. If you do not intend to use Parsley, you need to add your own messaging. </p>
<p>Most of this is standard URLLoader coding. </p>
<p>The SWFHeaderParser is called on line 62. On line 63, the actual bytes are not gotten from the SWFHeaderParser. So it can be a bit misleading that that is information found in the SWF header. The SWF header only contains the uncompressed file size.</p>
<pre class="brush: as3; wrap-lines: false;">
package swf
{
	import events.SWFHeaderLoaderCompleteEvent;
	import events.SWFHeaderLoaderErrorEvent;
	import events.SWFHeaderLoaderProgressEvent;
	import flash.events.Event;
	import flash.events.HTTPStatusEvent;
	import flash.events.IOErrorEvent;
	import flash.events.ProgressEvent;
	import flash.events.SecurityErrorEvent;
	import flash.net.URLLoader;
	import flash.net.URLLoaderDataFormat;
	import flash.net.URLRequest;
	import models.SWFHeaderModel;
	import vo.SWFHeader_vo;
	/**
	 * Byte loader for a SWF file.
	 * */
	public class SWFHeaderLoader
	{
		/**
		 * Has a URLLoader
		 * */
		private var urlLoader:URLLoader;
		/**
		 * Model
		 * */
		[Inject]
		public var model:SWFHeaderModel;
		/**
		 * Receives Parsley messages.
		 * */
		[MessageDispatcher]
		public var dispatcher:Function;
		/**
		 * Load the bytes from a file.
		 * */
		public function load(url:String):void
		{
			urlLoader = new URLLoader();
			urlLoader.addEventListener(Event.COMPLETE, urlLoader_completeEventHandler);
			urlLoader.addEventListener(ProgressEvent.PROGRESS, urlLoader_progressHandler);
			urlLoader.addEventListener(SecurityErrorEvent.SECURITY_ERROR, urlLoader_securityErrorHandler);
			urlLoader.addEventListener(HTTPStatusEvent.HTTP_STATUS, urlLoader_httpStatusHandler);
			urlLoader.addEventListener(IOErrorEvent.IO_ERROR, urlLoader_ioErrorHandler);
			urlLoader.dataFormat = URLLoaderDataFormat.BINARY;
			urlLoader.load( new URLRequest(url) );
		}
		/**
		 * Handler for the urlLoader ProgressEvent.PROGRESS event.
		 * */
		private function urlLoader_progressHandler(event:ProgressEvent):void
		{
			dispatcher(new SWFHeaderLoaderProgressEvent(SWFHeaderLoaderProgressEvent.PROGRESS, event.bytesLoaded, event.bytesTotal));
		}
		/**
		 * Handler for the urlLoader Event.COMPLETE event.
		 * */
		private function urlLoader_completeEventHandler(event:Event):void
		{
			var swfHeader_vo:SWFHeader_vo = new SWFHeader_vo();
			swfHeader_vo  = new SWFHeaderParser().parseBytes(urlLoader.data);
			swfHeader_vo.sizeCompressed = urlLoader.bytesLoaded;
			dispatcher(new SWFHeaderLoaderCompleteEvent(SWFHeaderLoaderCompleteEvent.COMPLETE, swfHeader_vo));
		}
		/**
		 * Handler for the urlLoader SecurityErrorEvent.SECURITY_ERROR event.
		 * */
		private function urlLoader_securityErrorHandler(event:SecurityErrorEvent):void
		{
			dispatcher(new SWFHeaderLoaderErrorEvent(SWFHeaderLoaderErrorEvent.SECURITY_ERROR));
		}
		/**
		 * Handler for the urlLoader HTTPStatusEvent.HTTP_STATUS event.
		 * */
		private function urlLoader_httpStatusHandler(event:HTTPStatusEvent):void
		{
			if (event.status != 200 &amp;&amp; event.status != 0)
			{
				dispatcher(new SWFHeaderLoaderErrorEvent(SWFHeaderLoaderErrorEvent.HTTP_ERROR));
			}
		}
		/**
		 * Handler for the urlLoader IOErrorEvent.IO_ERROR event.
		 * */
		private function urlLoader_ioErrorHandler(event:IOErrorEvent):void
		{
			dispatcher(new SWFHeaderLoaderErrorEvent(SWFHeaderLoaderErrorEvent.IO_ERROR));
		}
	}
}
</pre>
<p><strong>SWF Header Value Object &#8211; SWFHeader_vo.as</strong><br />
The value object I use in the SWFHeaderLoader and SWFHeaderParser.</p>
<pre class="brush: as3; wrap-lines: false;">
package vo
{
	/**
	 * Defines the values in the SWF file header record needed for app
	 * */
	[Bindable]
	public class SWFHeader_vo
	{
		public var format:String;			//	3 characters.
											//  First character:
											//  	&quot;F&quot; indicates uncompressed.
											// 		&quot;C&quot; indicates compressed (SWF 6 and later only).
											//  Second character always &quot;W&quot;.
											//  Third character always &quot;S&quot;.
		public var swfVersion:Number;		//	The swf version.
		public var frameRate:Number;		//	Frame rate.
		public var totalFrames:Number;		//	Number of frames on main timeline.
		public var width:Number;			//	Stage width.
		public var height:Number;			//	Stage height.
		public var sizeUncompressed:Number;	//	File size before compression.
		public var sizeCompressed:Number;	//	Actual file size.
	}
}
</pre>
<p><div style = "text-align:center"><script type="text/javascript"><!--
google_ad_client = "pub-8926707286265620";
/* 300x250, created 7/29/10 */
google_ad_slot = "4548376258";
google_ad_width = 300;
google_ad_height = 250;
//-->
</script>
<script type="text/javascript"
src="http://pagead2.googlesyndication.com/pagead/show_ads.js">
</script></div><br />
The remainder code listings are the wiring into a Parsley Framework and published as an Adobe Air project.</p>
<p><strong>Application Class &#8211; SWFHeader.mxml</strong></p>
<p>This is the application mxml file. Gotta love how Parsley helps make these application  mxml files minimal.</p>
<pre class="brush: as3; wrap-lines: false;">
&lt;?xml version=&quot;1.0&quot; encoding=&quot;utf-8&quot;?&gt;
&lt;!---
Application container
--&gt;
&lt;s:WindowedApplication xmlns:fx=&quot;http://ns.adobe.com/mxml/2009&quot;
					   xmlns:s=&quot;library://ns.adobe.com/flex/spark&quot;
					   xmlns:mx=&quot;library://ns.adobe.com/flex/mx&quot;
					   xmlns:parsley=&quot;http://www.spicefactory.org/parsley&quot;
					   xmlns:views=&quot;views.*&quot;
					   height = &quot;450&quot;
					   backgroundAlpha=&quot;0&quot;
					   showStatusBar=&quot;false&quot; 					&gt;
	&lt;fx:Declarations&gt;
		&lt;!-- Place non-visual elements (e.g., services, value objects) here --&gt;
		&lt;parsley:ContextBuilder config=&quot;ParsleyConfiguration&quot;  /&gt;
		&lt;parsley:Configure/&gt;
	&lt;/fx:Declarations&gt;
	&lt;views:Main  /&gt;
&lt;/s:WindowedApplication&gt;
</pre>
<p><strong>Parsley Configuration File &#8211; ParsleyConfiguration.mxml</strong><br />
My first example that uses a Parsley DynamicObject. It is the SWFHeaderLoader class. I had a lot of silent failures before I could get this to stick. The result is that Parsley manages objects made from SWFHeaderLoader so we can have Parsley messaging and insertions. Only tried it with one object however.</p>
<pre class="brush: as3; wrap-lines: false;">
&lt;?xml version=&quot;1.0&quot; encoding=&quot;utf-8&quot;?&gt;
&lt;!---
Parsley framework configuration file
--&gt;
&lt;Objects
	xmlns:fx=&quot;http://ns.adobe.com/mxml/2009&quot;
	xmlns=&quot;http://www.spicefactory.org/parsley&quot;
	xmlns:spicefactory=&quot;http://www.spicefactory.org/parsley&quot;
	xmlns:models=&quot;models.*&quot;
	xmlns:controllers=&quot;controllers.*&quot;
	xmlns:s=&quot;library://ns.adobe.com/flex/spark&quot;
	&gt;
	&lt;fx:Script&gt;
		&lt;![CDATA[
			// Required for DynamicObject SWFHeaderLoader.
			import swf.*;
		]]&gt;
	&lt;/fx:Script&gt;
	&lt;fx:Declarations&gt;
		&lt;!--
		Parsley defined objects.
		--&gt;
		&lt;spicefactory:DynamicObject type = &quot;{SWFHeaderLoader}&quot; /&gt;
		&lt;models:ApplicationModel/&gt;
		&lt;models:SWFHeaderModel/&gt;
		&lt;controllers:ApplicationController/&gt;
	&lt;/fx:Declarations&gt;
&lt;/Objects&gt;
</pre>
<p><strong>Air Application Descriptor File &#8211; SWFHeader-app.xml</strong><br />
Key here is that this an Air 2.0 project indicated on line 2 and line 8.</p>
<pre class="brush: xml; wrap-lines: false;">
&lt;?xml version=&quot;1.0&quot; encoding=&quot;utf-8&quot; standalone=&quot;no&quot;?&gt;
&lt;application xmlns=&quot;http://ns.adobe.com/air/application/2.0&quot;&gt;

&lt;!-- Adobe AIR Application Descriptor File Template.

	Specifies parameters for identifying, installing, and launching AIR applications.

	xmlns - The Adobe AIR namespace: http://ns.adobe.com/air/application/2.0
			The last segment of the namespace specifies the version
			of the AIR runtime required for this application to run.

	minimumPatchLevel - The minimum patch level of the AIR runtime required to run
			the application. Optional.
--&gt;

	&lt;!-- The application identifier string, unique to this application. Required. --&gt;
	&lt;id&gt;SwfHeader&lt;/id&gt;

	&lt;!-- Used as the filename for the application. Required. --&gt;
	&lt;filename&gt;SWFHeaderInspector&lt;/filename&gt;

	&lt;!-- The name that is displayed in the AIR application installer.
	     May have multiple values for each language. See samples or xsd schema file. Optional. --&gt;
	&lt;name&gt;SWF Header Inspector&lt;/name&gt;

	&lt;!-- An application version designator (such as &quot;v1&quot;, &quot;2.5&quot;, or &quot;Alpha 1&quot;). Required. --&gt;
	&lt;version&gt;v0.01&lt;/version&gt;

	&lt;!-- Description, displayed in the AIR application installer.
	     May have multiple values for each language. See samples or xsd schema file. Optional. --&gt;
	&lt;!-- &lt;description&gt;&lt;/description&gt; --&gt;

	&lt;!-- Copyright information. Optional --&gt;
	&lt;!-- &lt;copyright&gt;&lt;/copyright&gt; --&gt;

	&lt;!-- Publisher ID. Used if you're updating an application created prior to 1.5.3 --&gt;
	&lt;!-- &lt;publisherID&gt;&lt;/publisherID&gt; --&gt;

	&lt;!-- Settings for the application's initial window. Required. --&gt;
	&lt;initialWindow&gt;
		&lt;!-- The main SWF or HTML file of the application. Required. --&gt;
		&lt;!-- Note: In Flash Builder, the SWF reference is set automatically. --&gt;
		&lt;content&gt;[This value will be overwritten by Flash Builder in the output app.xml]&lt;/content&gt;

		&lt;!-- The title of the main window. Optional. --&gt;
		&lt;!-- &lt;title&gt;&lt;/title&gt; --&gt;

		&lt;!-- The type of system chrome to use (either &quot;standard&quot; or &quot;none&quot;). Optional. Default standard. --&gt;
		&lt;systemChrome&gt;none&lt;/systemChrome&gt;

		&lt;!-- Whether the window is transparent. Only applicable when systemChrome is none. Optional. Default false. --&gt;
		&lt;transparent&gt;true&lt;/transparent&gt;

		&lt;!-- Whether the window is initially visible. Optional. Default false. --&gt;
		&lt;!-- &lt;visible&gt;&lt;/visible&gt; --&gt;

		&lt;!-- Whether the user can minimize the window. Optional. Default true. --&gt;
		&lt;!-- &lt;minimizable&gt;&lt;/minimizable&gt; --&gt;

		&lt;!-- Whether the user can maximize the window. Optional. Default true. --&gt;
		&lt;!-- &lt;maximizable&gt;&lt;/maximizable&gt; --&gt;

		&lt;!-- Whether the user can resize the window. Optional. Default true. --&gt;
		&lt;!-- &lt;resizable&gt;&lt;/resizable&gt; --&gt;

		&lt;!-- The window's initial width. Optional. --&gt;
		&lt;!-- &lt;width&gt;&lt;/width&gt; --&gt;

		&lt;!-- The window's initial height. Optional. --&gt;
		&lt;!-- &lt;height&gt;&lt;/height&gt; --&gt;

		&lt;!-- The window's initial x position. Optional. --&gt;
		&lt;!-- &lt;x&gt;&lt;/x&gt; --&gt;

		&lt;!-- The window's initial y position. Optional. --&gt;
		&lt;!-- &lt;y&gt;&lt;/y&gt; --&gt;

		&lt;!-- The window's minimum size, specified as a width/height pair, such as &quot;400 200&quot;. Optional. --&gt;
		&lt;!-- &lt;minSize&gt;&lt;/minSize&gt; --&gt;

		&lt;!-- The window's initial maximum size, specified as a width/height pair, such as &quot;1600 1200&quot;. Optional. --&gt;
		&lt;!-- &lt;maxSize&gt;&lt;/maxSize&gt; --&gt;
	&lt;/initialWindow&gt;

	&lt;!-- The subpath of the standard default installation location to use. Optional. --&gt;
	&lt;!-- &lt;installFolder&gt;&lt;/installFolder&gt; --&gt;

	&lt;!-- The subpath of the Programs menu to use. (Ignored on operating systems without a Programs menu.) Optional. --&gt;
	&lt;!-- &lt;programMenuFolder&gt;&lt;/programMenuFolder&gt; --&gt;

	&lt;!-- The icon the system uses for the application. For at least one resolution,
		 specify the path to a PNG file included in the AIR package. Optional. --&gt;
	&lt;!-- &lt;icon&gt;
		&lt;image16x16&gt;&lt;/image16x16&gt;
		&lt;image32x32&gt;&lt;/image32x32&gt;
		&lt;image48x48&gt;&lt;/image48x48&gt;
		&lt;image128x128&gt;&lt;/image128x128&gt;
	&lt;/icon&gt; --&gt;

	&lt;!-- Whether the application handles the update when a user double-clicks an update version
	of the AIR file (true), or the default AIR application installer handles the update (false).
	Optional. Default false. --&gt;
	&lt;!-- &lt;customUpdateUI&gt;&lt;/customUpdateUI&gt; --&gt;

	&lt;!-- Whether the application can be launched when the user clicks a link in a web browser.
	Optional. Default false. --&gt;
	&lt;!-- &lt;allowBrowserInvocation&gt;&lt;/allowBrowserInvocation&gt; --&gt;

	&lt;!-- Listing of file types for which the application can register. Optional. --&gt;
	&lt;!-- &lt;fileTypes&gt; --&gt;

		&lt;!-- Defines one file type. Optional. --&gt;
		&lt;!-- &lt;fileType&gt; --&gt;

			&lt;!-- The name that the system displays for the registered file type. Required. --&gt;
			&lt;!-- &lt;name&gt;&lt;/name&gt; --&gt;

			&lt;!-- The extension to register. Required. --&gt;
			&lt;!-- &lt;extension&gt;&lt;/extension&gt; --&gt;

			&lt;!-- The description of the file type. Optional. --&gt;
			&lt;!-- &lt;description&gt;&lt;/description&gt; --&gt;

			&lt;!-- The MIME content type. --&gt;
			&lt;!-- &lt;contentType&gt;&lt;/contentType&gt; --&gt;

			&lt;!-- The icon to display for the file type. Optional. --&gt;
			&lt;!-- &lt;icon&gt;
				&lt;image16x16&gt;&lt;/image16x16&gt;
				&lt;image32x32&gt;&lt;/image32x32&gt;
				&lt;image48x48&gt;&lt;/image48x48&gt;
				&lt;image128x128&gt;&lt;/image128x128&gt;
			&lt;/icon&gt; --&gt;

		&lt;!-- &lt;/fileType&gt; --&gt;
	&lt;!-- &lt;/fileTypes&gt; --&gt;

&lt;/xml&gt;
</pre>
<p><strong>Main.mxml</strong><br />
The layout is a header and a body. The header is set up to be the move area for the window. he header is the top_hg HGroup on line 60. The body is the SWFHeaderBasic component on line 62. Here you could swap another body view easily.</p>
<pre class="brush: as3; wrap-lines: false;">
&lt;?xml version=&quot;1.0&quot; encoding=&quot;utf-8&quot;?&gt;
&lt;!---
Main view
--&gt;
&lt;s:Group xmlns:fx=&quot;http://ns.adobe.com/mxml/2009&quot;
		 xmlns:s=&quot;library://ns.adobe.com/flex/spark&quot;
		 xmlns:mx=&quot;library://ns.adobe.com/flex/mx&quot;
		 xmlns:sf=&quot;http://www.spicefactory.org/parsley&quot;

		 width=&quot;100%&quot; height=&quot;100%&quot;
		 xmlns:views=&quot;views.*&quot;
		 xmlns:ui=&quot;ui.*&quot;
		 &gt;
	&lt;fx:Script&gt;
		&lt;![CDATA[
			import ui.ApplicationCloseButtonSkin;
			/**
			 * Called once Parsley framework has reflected.
			 * */
			[Init]
			public function parsleyInit():void
			{
				top_hg.addEventListener(MouseEvent.MOUSE_DOWN, top_hg_onMouseDown);
			}
			/**
			 * Handler for top_hg MouseEvent.MOUSE_DOWN. Handles OS window move.
			 * */
			private function top_hg_onMouseDown( evt:MouseEvent):void
			{
				stage.nativeWindow.startMove();
			}
		]]&gt;
	&lt;/fx:Script&gt;
	&lt;fx:Declarations&gt;
		&lt;sf:Configure/&gt;
	&lt;/fx:Declarations&gt;
	&lt;fx:Style&gt;
		#appName_lbl {
			font-weight:bold;
			color: #291F65;
			font-size:18px;
		}
		#version_lbl {
			font-weight:bold;
			color: #291F65;
			font-size:10px;
		}
	&lt;/fx:Style&gt;
	&lt;s:VGroup width=&quot;100%&quot; height=&quot;100%&quot;&gt;
	&lt;s:BorderContainer width=&quot;100%&quot; height=&quot;100%&quot;
					   cornerRadius=&quot;20&quot; borderWeight=&quot;3&quot;
					   borderColor=&quot;0x000000&quot; dropShadowVisible=&quot;true&quot;
					   backgroundColor=&quot;#858282&quot;&gt;
		&lt;s:VGroup  verticalAlign=&quot;middle&quot; width=&quot;100%&quot; height=&quot;100%&quot;
			paddingLeft=&quot;6&quot; paddingRight=&quot;6&quot; paddingBottom=&quot;12&quot;
				&gt;
			&lt;!---
			Appplication header and drag area.
			--&gt;
			&lt;s:HGroup id = &quot;top_hg&quot; width=&quot;100%&quot;  verticalAlign=&quot;middle&quot; paddingTop=&quot;12&quot;  &gt;
				&lt;s:HGroup  paddingLeft=&quot;5&quot; verticalAlign=&quot;middle&quot; horizontalAlign=&quot;left&quot; width=&quot;100%&quot;  gap=&quot;30&quot;&gt;
					&lt;mx:Image source=&quot;@Embed(source='../assets/Adobe-swf_icon_40x40_published.png')&quot; /&gt;
					&lt;s:Label id = &quot;appName_lbl&quot; text = &quot;SWF Header Inspection Tool&quot;/&gt;
				&lt;/s:HGroup&gt;
				&lt;s:HGroup  paddingRight=&quot;12&quot; verticalAlign=&quot;middle&quot; horizontalAlign=&quot;right&quot; width=&quot;100%&quot;  &gt;
					&lt;ui:ApplicationCloseButton
						click=&quot;NativeApplication.nativeApplication.exit()&quot; skinClass=&quot;ui.ApplicationCloseButtonSkin&quot;/&gt;

				&lt;/s:HGroup&gt;			

			&lt;/s:HGroup&gt;
			&lt;views:SWFHeaderBasic    width=&quot;100%&quot; height=&quot;100%&quot; /&gt;
			&lt;s:Label id = &quot;version_lbl&quot; text = &quot;Version 1.00&quot;/&gt;
		&lt;/s:VGroup&gt;

	&lt;/s:BorderContainer&gt;
	&lt;/s:VGroup&gt;
&lt;/s:Group&gt;
</pre>
<p><strong>SWFHeaderBasic.mxml</strong><br />
Body section for the application. You can easily plug in your own view to replace this one.</p>
<pre class="brush: as3; wrap-lines: false;">
&lt;?xml version=&quot;1.0&quot; encoding=&quot;utf-8&quot;?&gt;
&lt;!---
Basic view.
--&gt;
&lt;s:Group xmlns:fx=&quot;http://ns.adobe.com/mxml/2009&quot;
		 xmlns:s=&quot;library://ns.adobe.com/flex/spark&quot;
		 xmlns:sf=&quot;http://www.spicefactory.org/parsley&quot;
		 xmlns:mx=&quot;library://ns.adobe.com/flex/mx&quot;
		 &gt;
	&lt;fx:Script&gt;
		&lt;![CDATA[
			import events.LoadSwfUrlHeaderRequestEvent;
			import models.SWFHeaderModel;
			import mx.binding.utils.BindingUtils;
			import mx.binding.utils.ChangeWatcher;
			import mx.events.FlexEvent;
			/**
			 * File name extracted from url or path.
			 * */
			[Bindable]
			private var fileName:String = '';
			[Inject]
			[Bindable]
			public var model:SWFHeaderModel;

			[MessageDispatcher]
			public var dispatcher:Function;
			/**
			 * Called once Parsley framework has reflected.
			 * */
			[Init]
			public function parsleyInit():void
			{
				// Bind the model changes from bytesLoaded in order to call a method.
				ChangeWatcher.watch(model, &quot;bytesLoaded&quot;, updateProgressBar);
				addEventListener(NativeDragEvent.NATIVE_DRAG_ENTER, onDragEnterHandler);
				addEventListener(NativeDragEvent.NATIVE_DRAG_DROP, onDragDropHandler);
			}
			/**
			 * Handler for the load_btn Mouse.Click event.
			 * */
			protected function load_btn_clickHandler(event:MouseEvent):void
			{
				fileName = url_ti.text.substr(   url_ti.text.lastIndexOf( '/' ) + 1);
				dispatcher(new LoadSwfUrlHeaderRequestEvent (LoadSwfUrlHeaderRequestEvent.LOAD, url_ti.text));
			}
			/**
			 * Handler for the load_btn Mouse.Click event.
			 * */
			private function updateProgressBar(bytesLoaded:Number):void
			{
				//trace (className + &quot;.updateProgressBar(...) - model.bytesLoaded: &quot; + model.bytesLoaded);
				//trace (className + &quot;.updateProgressBar(...) - model.bytesTotal: &quot; + model.bytesTotal);
				bar.setProgress(model.bytesLoaded,model.bytesTotal);
				var pct:Number = Math.round((model.bytesLoaded/model.bytesTotal) * 100);
				bar.label= &quot;Current Progress&quot; + &quot; &quot; + pct + &quot;%&quot;;
			}
			/**
			 * Handler for the NativeDragEvent.NATIVE_DRAG_ENTER event.
			 * */
			private function onDragEnterHandler(e:NativeDragEvent):void
			{
				//trace (className + &quot;.onDragEnterHandler(...)&quot;);
				if(e.clipboard.hasFormat(ClipboardFormats.FILE_LIST_FORMAT))
				{
					//Get the array of File objects
					var files:Array = e.clipboard.getData(ClipboardFormats.FILE_LIST_FORMAT) as Array;
					var file:File = File(files[0]);
					//Allow only one file as only supporting one in this application and soft check it is a swf file.
					if( files.length == 1 &amp;&amp; file.url.substr(file.url.length-4).toLowerCase() == &quot;.swf&quot; )
					{
						//Triggers NativeDragEvent.NATIVE_DRAG_DROP event.
						//This is when we see the drop icon.
						NativeDragManager.acceptDragDrop(this);
					}
				}
			}
			/**
			 * Handler for the NativeDragEvent.NATIVE_DRAG_DROP event.
			 * */
			private function onDragDropHandler(e:NativeDragEvent):void
			{
				//trace (className + &quot;.onDragDropHandler(...)&quot;);
				var files:Array = e.clipboard.getData(ClipboardFormats.FILE_LIST_FORMAT) as Array;
				var file:File = File(files[0]);
				//trace (className + &quot;.onDragDropHandler(...) - file.url:&quot; + file.url);
				url_ti.text = file.url;
				fileName = file.name;
				// Optional can initiate the load automatically.
				dispatcher(new LoadSwfUrlHeaderRequestEvent (LoadSwfUrlHeaderRequestEvent.LOAD, url_ti.text));
			}
		]]&gt;
	&lt;/fx:Script&gt;
	&lt;fx:Declarations&gt;
		&lt;sf:Configure/&gt;
		&lt;mx:NumberFormatter id=&quot;numberFormatter&quot; precision=&quot;0&quot;  useThousandsSeparator=&quot;true&quot; /&gt;
	&lt;/fx:Declarations&gt;
	&lt;!---
	Create NativeDrag target with BorderContainer
	--&gt;
	&lt;s:BorderContainer
				width=&quot;100%&quot; height=&quot;100%&quot;
					   borderAlpha=&quot;0&quot;
					   backgroundColor=&quot;0xffffff&quot;
					   &gt;
	&lt;s:VGroup verticalAlign=&quot;top&quot;  horizontalAlign=&quot;center&quot; width=&quot;100%&quot; height=&quot;100%&quot;
			   paddingTop=&quot;10&quot;
			   &gt;
		&lt;s:HGroup &gt;
			&lt;s:Label  text=&quot;SWF URL:&quot; paddingTop=&quot;6&quot; /&gt;
			&lt;s:VGroup &gt;
				&lt;s:HGroup  verticalAlign=&quot;middle&quot;&gt;
				&lt;s:TextInput id=&quot;url_ti&quot; text=&quot;Enter swf http url or drag from desktop&quot; width=&quot;294&quot; /&gt;
				&lt;s:Button  label=&quot;Load SWF&quot; id=&quot;load_btn&quot; click=&quot;load_btn_clickHandler(event)&quot;/&gt;
				&lt;/s:HGroup&gt;
				&lt;!---
				Progress bar for slow loads such as the internet.
				--&gt;
				&lt;mx:ProgressBar id=&quot;bar&quot;   labelPlacement=&quot;bottom&quot; minimum=&quot;0&quot; visible=&quot;true&quot; maximum=&quot;100&quot;
								color=&quot;0x323232&quot;
								label=&quot;Current Progress 0%&quot; direction=&quot;right&quot;
								mode=&quot;manual&quot; width=&quot;100%&quot;/&gt;
			&lt;/s:VGroup&gt;
		&lt;/s:HGroup&gt;
		&lt;!---
		The result values
		--&gt;
		&lt;mx:Form width=&quot;100%&quot; height=&quot;100%&quot;       &gt;
			&lt;mx:FormItem label=&quot;SWF File name: &quot;&gt;
				&lt;s:Label color=&quot;0x000000&quot; text=&quot;{fileName}&quot;/&gt;
			&lt;/mx:FormItem&gt;
			&lt;mx:FormItem label=&quot;Flash player version: &quot;&gt;
				&lt;s:Label color=&quot;0x000000&quot; text=&quot;{numberFormatter.format(model.swfHeader_vo.swfVersion)}&quot;/&gt;
			&lt;/mx:FormItem&gt;
			&lt;s:HGroup&gt;
				&lt;mx:FormItem label=&quot;Size uncompressed: &quot;&gt;
					&lt;s:Label color=&quot;0x000000&quot; text=&quot;{numberFormatter.format(model.swfHeader_vo.sizeUncompressed)}&quot;/&gt;
				&lt;/mx:FormItem&gt;
				&lt;mx:FormItem label=&quot;Compressed: &quot;&gt;
					&lt;s:Label color=&quot;0x000000&quot; text=&quot;{numberFormatter.format(model.swfHeader_vo.sizeCompressed)}&quot;/&gt;
				&lt;/mx:FormItem&gt;
			&lt;/s:HGroup&gt;
			&lt;mx:FormItem label=&quot;Frame rate: &quot;&gt;
				&lt;s:Label color=&quot;0x000000&quot; text=&quot;{numberFormatter.format(model.swfHeader_vo.frameRate)}&quot;/&gt;
			&lt;/mx:FormItem&gt;
			&lt;mx:FormItem label=&quot;Total frames: &quot;&gt;
				&lt;s:Label color=&quot;0x000000&quot; text=&quot;{numberFormatter.format(model.swfHeader_vo.totalFrames)}&quot;/&gt;
			&lt;/mx:FormItem&gt;
			&lt;mx:FormItem label=&quot;Width: &quot;&gt;
				&lt;s:Label  color=&quot;0x000000&quot; text=&quot;{numberFormatter.format(model.swfHeader_vo.width)}&quot;/&gt;
			&lt;/mx:FormItem&gt;
			&lt;mx:FormItem label=&quot;Height: &quot;&gt;
				&lt;s:Label color=&quot;0x000000&quot; text=&quot;{numberFormatter.format(model.swfHeader_vo.height)}&quot;/&gt;
			&lt;/mx:FormItem&gt;
		&lt;/mx:Form&gt;
	&lt;/s:VGroup&gt;
	&lt;/s:BorderContainer&gt;
&lt;/s:Group&gt;
</pre>
<p><strong>ApplicationCloseButton.as</strong><br />
Own version of the spark Button class.</p>
<pre class="brush: as3; wrap-lines: false;">
package ui
{
	import spark.components.Button;
	/**
	 * Button to close the application
	 * */
	public class ApplicationCloseButton extends Button
	{
		public function ApplicationCloseButton()
		{
			super();
		}
	}
}
</pre>
<p><strong>ApplicationCloseButtonSkin.mxml</strong><br />
Skin for the application close button class, ApplicationCloseButton. I had some trouble with the edges of the images causing a repeating state change on rollover. I solved this by making a transparent background on line 29 a few pixels larger than the bitmaps. The bitmaps then had their verticalCenter and  horizontalCenter properities set to zero to keep them in the middle. See lines 34 and 36.</p>
<pre class="brush: as3; wrap-lines: false;">
&lt;?xml version=&quot;1.0&quot; encoding=&quot;utf-8&quot;?&gt;
&lt;!---
Skin for ui.ApplicationCloseButton
--&gt;
&lt;s:SparkSkin xmlns:fx=&quot;http://ns.adobe.com/mxml/2009&quot;
			 xmlns:s=&quot;library://ns.adobe.com/flex/spark&quot;
			 xmlns:fb=&quot;http://ns.adobe.com/flashbuilder/2009&quot;

			 alpha.disabled=&quot;0.5&quot;&gt;

	&lt;!-- host component --&gt;
	&lt;fx:Metadata&gt;
		&lt;![CDATA[
		/* @copy spark.skins.spark.ApplicationSkin#hostComponent 	*/
		[HostComponent(&quot;ui.ApplicationCloseButton&quot;)]
		]]&gt;
	&lt;/fx:Metadata&gt;

	&lt;!-- states --&gt;
	&lt;s:states&gt;
		&lt;s:State name=&quot;up&quot; /&gt;
		&lt;s:State name=&quot;over&quot; /&gt;
		&lt;s:State name=&quot;down&quot; /&gt;
		&lt;s:State name=&quot;disabled&quot; /&gt;
	&lt;/s:states&gt;
	&lt;!---
	Hit area.
	--&gt;
	&lt;s:Rect  left=&quot;0&quot; right=&quot;0&quot; top=&quot;0&quot; bottom=&quot;0&quot;  width=&quot;34&quot; height=&quot;34&quot;&gt;
		&lt;s:fill&gt;
			&lt;s:SolidColor color=&quot;0xffffff&quot; alpha=&quot;0&quot; /&gt;
		&lt;/s:fill&gt;
	&lt;/s:Rect&gt;
	&lt;s:BitmapImage  verticalCenter=&quot;0&quot;  horizontalCenter=&quot;0&quot; source=&quot;@Embed('../assets/red_glossy_close_up_button_published.png')&quot;
				   includeIn=&quot;up, disabled, down &quot;/&gt;
	&lt;s:BitmapImage verticalCenter=&quot;0&quot;  horizontalCenter=&quot;0&quot;  source=&quot;@Embed('../assets/green_glossy_close_up_button_published.png')&quot;
				   includeIn=&quot;over&quot;/&gt;

&lt;/s:SparkSkin&gt;
</pre>
<p><strong>ApplicationModel.as</strong><br />
I could have merged the two models, but as a practice in Parsley frameworks, I create an ApplicationModel by default.</p>
<pre class="brush: as3; wrap-lines: false;">
package models
{
	/**
	 * Application model.
	 * */
	public class ApplicationModel
	{

	}
}
</pre>
<p><div style = "text-align:center"><script type="text/javascript"><!--
google_ad_client = "pub-8926707286265620";
/* 300x250, created 7/29/10 */
google_ad_slot = "4548376258";
google_ad_width = 300;
google_ad_height = 250;
//-->
</script>
<script type="text/javascript"
src="http://pagead2.googlesyndication.com/pagead/show_ads.js">
</script></div><br />
<strong>SWFHeaderModel.as</strong><br />
The model containing all the bound data. Flex binding is magnificent for creating model-view-controller solutions. </p>
<pre class="brush: as3; wrap-lines: false;">
package models
{
	import vo.SWFHeader_vo;
	/**
	 * Model for the SWF header information.
	 * */
	public class SWFHeaderModel
	{
		/**
		 * Swf header field values.
		 * */
		[Bindable]
		public var swfHeader_vo:SWFHeader_vo;
		/**
		 * Nest the ApplicationModel.
		 * */
		[Inject]
		[Bindable]
		public var applicationModel:ApplicationModel;
		/**
		 * Bytes loaded.
		 * */
		[Bindable]
		public var bytesLoaded:Number;
		/**
		 * Total bytes to load.
		 * */
		[Bindable]
		public var bytesTotal:Number;
		/**
		 * Initialize model values.
		 * */
		public function SWFHeaderModel()
		{
			swfHeader_vo = new SWFHeader_vo();
		}
	}
}
</pre>
<p><strong>ApplicationController.as</strong><br />
I could have created a controller just for the SWF header if I wanted a separate model-view-controller  for SWF header decoupled from the application. </p>
<pre class="brush: as3; wrap-lines: false;">
package controllers
{
	import events.LoadSwfUrlHeaderRequestEvent;
	import events.SWFHeaderLoaderCompleteEvent;
	import events.SWFHeaderLoaderErrorEvent;
	import events.SWFHeaderLoaderProgressEvent;
	import flash.net.URLRequest;
	import models.SWFHeaderModel;
	import mx.controls.Alert;
	import swf.SWFHeaderLoader;
	import vo.SWFHeader_vo;
	/**
	 * Application controller
	 * */
	public class ApplicationController
	{
		/**
		 * Toggle to indicate if an Alert error is being shown. All loading errors are
		 * routed to one method and so if there are more than one per SWFHeaderLoader.load
		 * request, this prevents multiple Alert popups.
		 * @see swfHeaderErroMessageHandler
		 * */
		private var showingSwfLoadError:Boolean = false;
		/**
		 * The SWFHeaderLoader
		 * */
		[Inject]
		public var swfHeaderLoader:SWFHeaderLoader;
		/**
		 * The model for MVC.
		 * */
		[Inject]
		[Bindable]
		public var model:SWFHeaderModel;
		/**
		 * Information Alert dialog title.
		 * */
		private var lang_info_alert_title:String = &quot;Attention&quot;;
		/**
		 * Message when file name does not contain a .swf extension.
		 * */
		private var lang_tool_requires_swf:String = &quot;Tool is designed for Flash Movie \&quot;.swf\&quot; files&quot;;
		/**
		 * Message when swf file could not be loaded for any reason.
		 * */
		private var lang_unable_to_load_swf:String = &quot;Sorry, unable to load the file.&quot;;
		/**
		 * Handler for LoadSwfUrlHeaderRequestEvent. Validate file extension. Load swf.
		 * */
		[MessageHandler]
		public function swfUrlLoadRequestMessageHandler( message : LoadSwfUrlHeaderRequestEvent ):void
		{
			model.swfHeader_vo = new SWFHeader_vo();
			// File url does not have a .swf at end.
			if ( message.swf_url.substr(message.swf_url.length-4).toLowerCase() != &quot;.swf&quot;  )
			{
				Alert.show(lang_tool_requires_swf ,lang_info_alert_title);
			}
			// File url has a .swf at end.
			else
			{
				showingSwfLoadError = false;
				swfHeaderLoader.load(message.swf_url);
			}
		}
		/**
		 * Handler for SWFHeaderLoaderProgressEvent.
		 * */
		[MessageHandler]
		public function swfHeaderProgressMessageHandler( message : SWFHeaderLoaderProgressEvent ):void
		{
			model.bytesTotal = message.bytesTotal;
			model.bytesLoaded = message.bytesLoaded;
		}
		/**
		 * Handler for SWFHeaderLoaderCompleteEvent.
		 * */
		[MessageHandler]
		public function swfHeaderLoadedMessageHandler( message : SWFHeaderLoaderCompleteEvent ):void
		{
			model.swfHeader_vo = message.swfHeader_vo;
		}
		/**
		 * Handler for SWFHeaderLoaderErrorEvent.
		 * */
		[MessageHandler]
		public function swfHeaderErroMessageHandler( message : SWFHeaderLoaderErrorEvent ):void
		{
			if (!showingSwfLoadError)
			{
				showingSwfLoadError = true;
				Alert.show(lang_unable_to_load_swf,lang_info_alert_title);
			}
		}
	}
}
</pre>
<p><strong>LoadSwfUrlHeaderRequestEvent.as</strong><br />
Event handler for requests to load a swf. Note how Parsley simplifies the event code.</p>
<pre class="brush: as3; wrap-lines: false;">
package events
{
	import flash.events.Event;
	/**
	 * Request the loading of a swf file.
	 * */
	public class LoadSwfUrlHeaderRequestEvent extends Event
	{
		public static const LOAD:String = &quot;event.load&quot;;
		public var swf_url:String;
		public function LoadSwfUrlHeaderRequestEvent(type:String, swf_url:String, bubbles:Boolean=false, cancelable:Boolean=false)
		{
			super(type, bubbles, cancelable);
			this.swf_url = swf_url;
		}
	}
}
</pre>
<p><strong>SWFHeaderLoaderProgressEvent.as</strong><br />
For monitoring the loading particularly when over the internet.</p>
<pre class="brush: as3; wrap-lines: false;">
package events
{
	import flash.events.Event;
	/**
	 * Progress of loading a swf file.
	 * */
	public class SWFHeaderLoaderProgressEvent extends Event
	{
		public static const PROGRESS:String = &quot;event_SWFHeaderLoaderEvent_progress&quot;;
		/**
		 * Bytes loaded.
		 * */
		public var bytesLoaded:Number;
		/**
		 * Total bytes to load.
		 * */
		public var bytesTotal:Number;
		public function SWFHeaderLoaderProgressEvent(type:String, bytesLoaded:Number, bytesTotal:Number, bubbles:Boolean=false, cancelable:Boolean=false)
		{
			super(type, bubbles, cancelable);
			this.bytesLoaded = bytesLoaded;
			this.bytesTotal = bytesTotal;

		}
	}
}
</pre>
<p><strong>SWFHeaderLoaderCompleteEvent.as</strong><br />
When an SWF is completely loaded. You may want to redesign to stop once the header is loaded. However I had thought it might be nice to show the SWF at one point or proceed to extract other information.</p>
<pre class="brush: as3; wrap-lines: false;">
package events
{
	import flash.events.Event;
	import vo.SWFHeader_vo;
	/**
	 * Completion of loading a swf file.
	 * */
	public class SWFHeaderLoaderCompleteEvent extends Event
	{
		public static const COMPLETE:String = &quot;event_SWFHeaderLoaderEvent_complete&quot;;
		/**
		 * Swf file header data.
		 * */
		public var swfHeader_vo:SWFHeader_vo;
		public function SWFHeaderLoaderCompleteEvent(type:String, swfHeader_vo:SWFHeader_vo, bubbles:Boolean=false, cancelable:Boolean=false)
		{
			super(type, bubbles, cancelable);
			this.swfHeader_vo = swfHeader_vo;
		}
	}
}
</pre>
<p><strong>SWFHeaderLoaderErrorEvent.as</strong><br />
Errors in loading the Swf file.</p>
<pre class="brush: as3; wrap-lines: false;">
package events
{
	import flash.events.Event;
	/**
	 * Errors from loading a swf file.
	 * */
	public class SWFHeaderLoaderErrorEvent extends Event
	{
		public static const SECURITY_ERROR:String = &quot;event_SWFHeaderLoaderEvent_security&quot;;
		public static const HTTP_ERROR:String = &quot;event_SWFHeaderLoaderEvent_HTTP&quot;;
		public static const IO_ERROR:String = &quot;event_SWFHeaderLoaderEvent_IO&quot;;
		public function SWFHeaderLoaderErrorEvent(type:String, bubbles:Boolean=false, cancelable:Boolean=false)
		{
			super(type, bubbles, cancelable);
		}
	}
}
</pre>
]]></content:encoded>
			<wfw:commentRss>http://www.lonhosford.com/lonblog/2011/01/17/read-flash-swf-header-in-air-with-parsley-framework/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>ZEND AMF (Action Message Format) Minimalist Example Using RemoteObject and MySQL</title>
		<link>http://www.lonhosford.com/lonblog/2010/08/26/zend-amf-action-message-format-minimalist-example-using-remoteobject-and-mysql/</link>
		<comments>http://www.lonhosford.com/lonblog/2010/08/26/zend-amf-action-message-format-minimalist-example-using-remoteobject-and-mysql/#comments</comments>
		<pubDate>Thu, 26 Aug 2010 18:10:46 +0000</pubDate>
		<dc:creator>Lon Hosford</dc:creator>
				<category><![CDATA[AIR (Adobe Integrated Runtime)]]></category>
		<category><![CDATA[Actionscript 3]]></category>
		<category><![CDATA[Flex]]></category>
		<category><![CDATA[Php 5]]></category>
		<category><![CDATA[Actionscript]]></category>
		<category><![CDATA[Air]]></category>
		<category><![CDATA[AMF]]></category>
		<category><![CDATA[Flex 4]]></category>
		<category><![CDATA[RemoteObject]]></category>

		<guid isPermaLink="false">http://www.lonhosford.com/lonblog/?p=787</guid>
		<description><![CDATA[I always liked and appreciated the AMFPHP implementation of Adobe Action Message Format. I have seen it implemented in some robust applications and it held its own. Among its best features is the browser which is very handy for testing. However Adobe has now gotten involved with Zend AMF and this is a quick shot [...]]]></description>
			<content:encoded><![CDATA[<p>I always liked and appreciated the <a href="http://www.amfphp.org/" target="_blank">AMFPHP implementation</a> of Adobe Action Message Format. I have seen it implemented in some robust applications and it held its own. <img class="alignleft" style="margin-right: 5px; border: 1px solid black;" src="http://lh5.ggpht.com/_e5pwU0LJbN8/THaPGSfNSzI/AAAAAAAAFsA/cPvKMfUdzR4/s800/Zend_AMF_Minimalist_8-26-2010%2011-56-17%20AM.png" alt="" width="232" height="171" /> Among its best features is the browser which is very handy for testing. However Adobe has now gotten involved with <a href="http://framework.zend.com/download/amf" target = "_blank">Zend AMF</a> and this is a quick shot at putting it together using the RemoteObject class.</p>
<p>The first example I looked at is from Lee Brimelow&#8217;s <a href="http://gotoandlearn.com/play.php?id=90" target="_blank">Introduction to ZendAMF</a> at gotoAndLearn.com. This is a great example and worth the watch to get a rounded view of using Zend AMF. He uses NetConnection and Responder classes in Flash CS4.  You can use his example in Flex and Air.</p>
<div class="wp-caption alignright" style="width: 121px"><a href="http://www.amazon.com/gp/product/B003KG2HLG?ie=UTF8&amp;tag=hosfordusa&amp;linkCode=as2&amp;camp=1789&amp;creative=390957&amp;creativeASIN=B003KG2HLG5" target="_blank"><img class="  " title="Adobe Flash Builder 4 and Flex 4 - Essential Training" src="http://lh5.ggpht.com/_e5pwU0LJbN8/THaJ7e_VZDI/AAAAAAAAFr4/6gLBuTgzkq0/s800/FlashBuilder4AndFlex441KcCjdUF%2BL._SL160_.jpg" alt="Adobe Flash Builder 4 and Flex 4 - Essential Training" " width="111"  height="160" /></a><p class="wp-caption-text">Learn More About Flex&nbsp;4</p></div>
<p>Eventually I put Zend AMF to practical use by revamping <a href="http://www.defineit.com" target = "_blank">DefineIt.com</a> to use it along with the <a href="http://www.lonhosford.com/lonblog/2010/09/27/parsley-mvc-remoteobject-zend-amf-and-mysql-basic-flex-example/">Parsley framework</a>.</p>
<p>Flash CS4 does not have a native RemoteObject class, so that leads us to using Flex.</p>
<p><strong>Download files</strong><br />
You can build this with the free Flex SDK by using the code in the src folder. This example was built with Flex 4.</p>
<ul style="padding-top: 5px;">
<li><a href="http://www.lonhosford.com/content/flex/amf/zend/ZendAMFRemoteObjectGetTable_Air.fxp">Flex Builder 4 Air Project</a></li>
<li><a href="http://www.lonhosford.com/content/flex/amf/zend/ZendAMFRemoteObjectGetTable_Flex.fxp">Flex Builder 4 Flex Project</a></li>
<li><a href="http://framework.zend.com/download/amf" target = "_blank">Zend AMF</a> &#8211; Download and decompress into a folder on your web server. You need to adjust line 26 of the Zend AMF gateway script shown below to account for the path the Zend folder&#8217;s parent folder.</li>
</ul>
<p>The following uses the code from the Flex example, but other than the Application and WindowedApplication tags the code is the same.</p>
<p><strong>Application Class &#8211; ZendAMFRemoteObjectGetTable_Flex</strong><br />
Rather than use a services-config.xml file linked to the compiler line, I choose to include the channel information in the MXML. More on this method is detailed by Chris Callendar&#8217;s post <a href="http://flexdevtips.blogspot.com/2009/05/using-flex-and-amfphp-without-services.html" target = "_blank">Using Flex and AMFPHP without a services-config.xml file</a>.</p>
<pre class="brush: as3; highlight: [32]; wrap-lines: false;">
&lt;?xml version=&quot;1.0&quot; encoding=&quot;utf-8&quot;?&gt;
&lt;!--
Demonstrates use of the RemoteObject to communicate with Zend AMF.
&lt;p&gt;Author: Lon Hosford http://www.lonhosford.com 908 996 3773&lt;/p&gt;
--&gt;
&lt;s:Application xmlns:fx=&quot;http://ns.adobe.com/mxml/2009&quot;
			   xmlns:s=&quot;library://ns.adobe.com/flex/spark&quot;
			   xmlns:mx=&quot;library://ns.adobe.com/flex/mx&quot; minWidth=&quot;955&quot; minHeight=&quot;600&quot;&gt;
	&lt;fx:Declarations&gt;
		&lt;!-- Alternative to services-config.xml added to the compiler option services --&gt;
		&lt;s:ChannelSet id = &quot;zend_amf_channel_set&quot;&gt;
			&lt;s:channels&gt;
				&lt;s:AMFChannel uri=&quot;{GATEWAY_URL}&quot;/&gt;
			&lt;/s:channels&gt;
		&lt;/s:ChannelSet&gt;
		&lt;!-- MembershipService RemoteObject --&gt;
		&lt;s:RemoteObject	id=&quot;members_ro&quot;
						destination=&quot;zend-amf&quot;
						source=&quot;MembershipService&quot;
						channelSet=&quot;{zend_amf_channel_set}&quot;
						showBusyCursor=&quot;true&quot;
						fault=&quot;membersError(event)&quot;&gt;
			&lt;s:method name=&quot;getAllMembers&quot; result=&quot;getAllMembersResult(event)&quot;/&gt;
		&lt;/s:RemoteObject&gt;
	&lt;/fx:Declarations&gt;
</pre>
<p><div style = "text-align:center"><script type="text/javascript"><!--
google_ad_client = "pub-8926707286265620";
/* 300x250, created 7/29/10 */
google_ad_slot = "4548376258";
google_ad_width = 300;
google_ad_height = 250;
//-->
</script>
<script type="text/javascript"
src="http://pagead2.googlesyndication.com/pagead/show_ads.js">
</script></div><br />
On line 33 you need to set the GATEWAY_URL to match your configuration. You might want to use a default file name such as index.php for the gateway script so you only need a path to the folder.</p>
<pre class="brush: as3; first-line: 26; wrap-lines: false;">
	&lt;fx:Script&gt;
		&lt;![CDATA[
			import mx.controls.Alert;
			import mx.events.FlexEvent;
			import mx.rpc.events.FaultEvent;
			import mx.rpc.events.ResultEvent;
			// [ADD YOUR HTTP URL TO THE GATEWAY PHP SCRIPT]
			private const GATEWAY_URL:String = &quot;http://YOUR_DOMAIN/PATH_TO_GATEWAY SCRIPT/&quot;;
			/**
			 * Member value object. Not used in this example. Included for information only.
			 * Use this to convert the data received. In this example the DataGrid dataprovider
			 * property converted the incoming amf array to an ArrayCollection and we mapped
			 * using the DataGridColumn dataField property.
			 * */
			private var memberData:MemberData;
			[Bindable]
			private var lang_title:String = &quot;Minimalist Zend AMF Example Using RemoteObject&quot;;
			/**
			 * Invoke RemoteObject members_ro getAllMembers method.
			 * */
			protected function getAllMembers():void
			{
				members_ro.getAllMembers();
			}
			/**
			 * Empties the member_dg DataGrid.
			 * */
			protected function clearDataGrid():void
			{
				member_dg.dataProvider = {};
			}
			/**
			 * RemoteObject members_ro ResultEvent handler for the remote getAllMembers method.
			 * &lt;p&gt;Data arrives as an array and dataProvider property converts to ArrayCollection.
			 * The member_dg DataGrid contains DataGridColumn to match the expected field using
			 * the dataField property.&lt;/p&gt;
			 * */
			protected function getAllMembersResult(e:ResultEvent):void
			{
				member_dg.dataProvider = e.result; // ResultEvent result property is an array
			}
			/**
			 * RemoteObject members_ro default FaultEvent handler.
			 * */
			protected function membersError(e:FaultEvent):void
			{
				Alert.show(e.toString());
			}
		]]&gt;
	&lt;/fx:Script&gt;
</pre>
<p><div style = "text-align:center"><script type="text/javascript"><!--
google_ad_client = "pub-8926707286265620";
/* 300x250, created 7/29/10 */
google_ad_slot = "4548376258";
google_ad_width = 300;
google_ad_height = 250;
//-->
</script>
<script type="text/javascript"
src="http://pagead2.googlesyndication.com/pagead/show_ads.js">
</script></div><br />
This is the UI. The member_dg DataGrid contains DataGridColumn to match the expected fields in the MemberData object using the dataField property. This is a one to one relationship, however you may find times where you need to construct your own fields such as a concatenation of first and last name and thus populate the DataGrid with your own collection object.</p>
<pre class="brush: as3; first-line: 76; wrap-lines: false;">
	&lt;s:VGroup horizontalAlign=&quot;center&quot; width=&quot;100%&quot; paddingTop=&quot;25&quot;&gt;
		&lt;s:Label text=&quot;{lang_title}&quot; fontSize=&quot;20&quot;/&gt;

		&lt;mx:DataGrid  id=&quot;member_dg&quot;  height=&quot;100&quot;&gt;
			&lt;mx:columns&gt;
				&lt;mx:DataGridColumn headerText=&quot;Member Key&quot; dataField=&quot;memberKey&quot; width = &quot;100&quot;/&gt;
				&lt;mx:DataGridColumn headerText=&quot;First Name&quot; dataField=&quot;firstName&quot;/&gt;
				&lt;mx:DataGridColumn headerText=&quot;Last Name&quot; dataField=&quot;lastName&quot;/&gt;
				&lt;mx:DataGridColumn headerText=&quot;Email Address&quot; dataField=&quot;emailAddress&quot; width=&quot;200&quot;/&gt;
			&lt;/mx:columns&gt;
		&lt;/mx:DataGrid&gt;
		&lt;s:HGroup&gt;
			&lt;s:Button label=&quot;Get Members&quot; click=&quot;{getAllMembers();}&quot;/&gt;
			&lt;s:Button label=&quot;Clear&quot; click=&quot;{clearDataGrid();}&quot;/&gt;
		&lt;/s:HGroup&gt;
	&lt;/s:VGroup&gt;

&lt;/s:Application&gt;
</pre>
<p><strong>MemberData Value Object Class for Actionscript</strong><br />
This is the value object on the client side to define the field names for a member object. This is mapped to the server side on line 37 of the Zend AMF gateway script.</p>
<pre class="brush: as3; wrap-lines: false;">
package
{
	/**
	 * Value object defining the member data
	 * */
	[RemoteClass(alias=&quot;MemberData&quot;)]
	[Bindable]
	public class MemberData
	{
		public var memberKey:uint;
		public var firstName:String;
		public var lastName:String;
		public var emailAddress:String;
	}
}
</pre>
<p><strong>Zend AMF Gateway PHP Script</strong><br />
This is the gateway program for the Zend Amf. This is the file that is referenced on line 13 of the MXML file. In this example it was named index.php.</p>
<pre class="brush: php;">
&lt;?php
/**
*  Sample Zend AMF gateway
*  @return Endpoint [Zend Amf Endpoint]
* */

// Configurable values
// Debugging values
$debug = true;                             // Debugging status
if ($debug)
{
	// Report all errors, warnings, interoperability and compatibility
	error_reporting(E_ALL|E_STRICT);
	// Show errors with output
	ini_set(&quot;display_errors&quot;, &quot;on&quot;);
}
else
{
	error_reporting(0);
	ini_set(&quot;display_errors&quot;, &quot;off&quot;);
}
// Add the Zend AMF installation folder to the include path.
// In this example the frameworks folder is a sibling folder to
// this application folder. The frameworks folder contains the Zend
// folder that is extracted from http://framework.zend.com/download/amf
ini_set(&quot;include_path&quot;, ini_get(&quot;include_path&quot;) . PATH_SEPARATOR . &quot;..\\frameworks&quot; );

// Instantiate the Zend Amf server
require_once 'Zend/Amf/Server.php';
$server = new Zend_Amf_Server();

// Register your service classes
require_once 'MembershipService.php';
$server-&gt;setClass(&quot;MembershipService&quot;);

//Map ActionScript value objects to the PHP value objects.
$server-&gt;setClassMap(&quot;MemberData&quot;, &quot;MemberData&quot;);

// Return the handle.
echo ($server-&gt;handle());

?&gt;
</pre>
<p><div style = "text-align:center"><script type="text/javascript"><!--
google_ad_client = "pub-8926707286265620";
/* 300x250, created 7/29/10 */
google_ad_slot = "4548376258";
google_ad_width = 300;
google_ad_height = 250;
//-->
</script>
<script type="text/javascript"
src="http://pagead2.googlesyndication.com/pagead/show_ads.js">
</script></div><br />
<strong>MembershipService Class</strong><br />
This is the service class that contains remote methods. Generally a the database and business logic is delegated to another API you write. In this case they are all together for simplicity.</p>
<pre class="brush: php;">
&lt;?php
/**
*	Service class exposing the methods to deal with membership.
*   This example includes business logic for simplicity of study.
*/
require_once 'MemberData.php';
class MembershipService
{
	public function MembershipService()
	{
		// Connect to MySql database.
		// Supply your own MySQL access values.
		// These are defaults when running on your own private computer.
		mysql_connect(&quot;localhost&quot;, &quot;root&quot;, &quot;&quot;);
		// Select the database.
		// Supply your own database name.
		mysql_select_db(&quot;test&quot;);
	}
	/**
	*	Get all members and all fields.
	*/
	public function getAllMembers()
	{
		// Array of MemberData objects.
		$members = array();
		// Selecting all fields and all records from table.
		// Supply your own table name and optionally your own SQL statement.
		$result = mysql_query(&quot;SELECT * FROM zend_amf_members&quot;);
		// Assuming mysql_query success. Slog through records.
		while ($row = mysql_fetch_assoc($result))
		{
			// Create a MemberData value object and populate.
			$member = new MemberData();
			$member-&gt;memberKey = $row[&quot;memberKey&quot;];
			$member-&gt;firstName = $row[&quot;firstName&quot;];
			$member-&gt;lastName = $row[&quot;lastName&quot;];
			$member-&gt;emailAddress = $row[&quot;emailAddress&quot;];
			array_push($members, $member);
		}
		// Return the members array to client.
		return $members;
	}
}
?&gt;
</pre>
<p><strong>MemberData Value Object Class for PHP</strong><br />
This is the value object on the server side to define the field names for a member object. This is mapped to the client side on line 37 of the Zend AMF gateway script.</p>
<pre class="brush: php;">
&lt;?php
/**
 * Value object defining the member data
 * */
class MemberData
{
  public $memberKey;	// uint
  public $firstName;	// String
  public $lastName;		// String
  public $emailAddress;	// String
}
?&gt;
</pre>
<p><strong>SQL To Create Testing Table</strong><br />
The PHP script uses zend_amf_members for the table and this is the SQL to create that table. In this example the database was called test.</p>
<pre class="brush: sql;">
SET SQL_MODE=&quot;NO_AUTO_VALUE_ON_ZERO&quot;;

--
-- Database: `test`
--

-- --------------------------------------------------------

--
-- Table structure for table `zend_amf_members`
--

CREATE TABLE IF NOT EXISTS `zend_amf_members` (
  `memberKey` int(10) unsigned NOT NULL auto_increment,
  `firstName` varchar(30) NOT NULL default '',
  `lastName` varchar(30) NOT NULL default '',
  `emailAddress` varchar(50) NOT NULL default '',
  PRIMARY KEY  (`memberKey`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 COMMENT='Zend Amf Examples' AUTO_INCREMENT=7 ;

--
-- Dumping data for table `zend_amf_members`
--

INSERT INTO `zend_amf_members` (`memberKey`, `firstName`, `lastName`, `emailAddress`) VALUES
(1, 'Donald', 'Duck', 'quacker@pond.com'),
(2, 'Daffy', 'Duck', 'daft_2x@farm.org'),
(3, 'Elmer', 'Fudd', 'elmer.fudd@hunters.net'),
(4, 'Bugs', 'Bunny', 'whats_up_doc@underground.org'),
(5, 'Yosemite', 'Sam', 'varmint_chaser@forest.com'),
(6, 'Wile', 'Coyote', 'ceo@acme.com');
</pre>
]]></content:encoded>
			<wfw:commentRss>http://www.lonhosford.com/lonblog/2010/08/26/zend-amf-action-message-format-minimalist-example-using-remoteobject-and-mysql/feed/</wfw:commentRss>
		<slash:comments>5</slash:comments>
		</item>
		<item>
		<title>Adobe AIR NativeProcess Silent Print PDF with BioPDF&#8217;s Acrobat Wrapper</title>
		<link>http://www.lonhosford.com/lonblog/2010/08/12/adobe-air-nativeprocess-silent-print-pdf-with-biopdfs-acrobat-wrapper/</link>
		<comments>http://www.lonhosford.com/lonblog/2010/08/12/adobe-air-nativeprocess-silent-print-pdf-with-biopdfs-acrobat-wrapper/#comments</comments>
		<pubDate>Fri, 13 Aug 2010 03:08:31 +0000</pubDate>
		<dc:creator>Lon Hosford</dc:creator>
				<category><![CDATA[AIR (Adobe Integrated Runtime)]]></category>
		<category><![CDATA[Actionscript 3]]></category>
		<category><![CDATA[Adobe]]></category>
		<category><![CDATA[Actionscript]]></category>
		<category><![CDATA[Air]]></category>
		<category><![CDATA[CursorManager]]></category>
		<category><![CDATA[Flex 4]]></category>
		<category><![CDATA[NativeProcess]]></category>

		<guid isPermaLink="false">http://www.lonhosford.com/lonblog/?p=513</guid>
		<description><![CDATA[By Lon (Alonzo) Hosford A current Adobe Air Windows OS project I am developing requires printing a Adobe PDF file without user interaction or at the minimum pressing a simple print button without seeing a print dialog window. This is often called silent printing. Adobe Reader prior to version 8 allowed silent printing from the [...]]]></description>
			<content:encoded><![CDATA[<p><strong>By Lon (Alonzo) Hosford</strong><br />
A current Adobe Air Windows OS project I am developing requires printing a Adobe PDF file without user interaction or <img alt="" src="http://lh6.ggpht.com/_e5pwU0LJbN8/TGrfPvRpyxI/AAAAAAAAFq0/DCd2HneCENw/s800/silentprinter_published.png" class="alignleft" width="166" height="160" />at the minimum pressing a simple print button without seeing a print dialog window. This is often called silent printing.</p>
<p>Adobe Reader prior to version 8 allowed silent printing from the command line. You find references throughout the internet <code> AcroRd32.exe /N /T PdfFile PrinterName [PrinterDriver[PrinterPort]]</code> However for security this was changed to require the user to finish the printing. If you are developing a kiosk as I am, we want to avoid users having to know how to use printer dialogs or Adobe Reader menus.</p>
<div style = "float:right;margin-left:5px;"><div style = "text-align:center"><script type="text/javascript"><!--
google_ad_client = "pub-8926707286265620";
/* 300x250, created 7/29/10 */
google_ad_slot = "4548376258";
google_ad_width = 300;
google_ad_height = 250;
//-->
</script>
<script type="text/javascript"
src="http://pagead2.googlesyndication.com/pagead/show_ads.js">
</script></div></div>
<p><strong>Javascript Injection</strong><br />
In researching the alternatives I found the ability to insert Javascript into the PDF file you want to print. This requires Adobe Acrobat Pro and a manual effort of a highly technical nature. I tried this and for a once in while PDF file it is acceptable, but not suited for dynamically third party software generated PDF files or PDF files from multiple sources. Plus it still would require AIR showing the HTMLLoader wrapping the Adobe Reader. You can review this alternative in <a href="http://cookbooks.adobe.com/post_Printing_a_PDF_document_from_AIR_without_displayin-16288.html" target ="_blank">Adobe Cookbooks</a>.</p>
<p>Other alternatives were command line implementations offered by PDF software companies. These would allow using the NativeProcess in Adobe AIR to execute the printing without user intervention or allow designing a simple user interface like a big print button.</p>
<p><strong>PDFPrint</strong><br />
A decent command line solution was <a href="http://www.verypdf.com/pdfprint/index.html" target ="_blank">PDFPrint</a> from VeryPDF. Price is $199 (US) per installation. The trial version seemed to work well cobbled by a watermark and page count limitation, but the price forced me to press on. </p>
<p><strong>printto.exe</strong><br />
I came across <a href="http://www.biopdf.com/guide/examples/command_line/"  target ="_blank">printto.exe</a> from BioPDF. The printto.exe program uses the default application set by the Windows OS which is Acrobat Reader for PDF. This actually works but it does leaves Acrobat Reader open. I did not try it with other Acrobat Reader clones. Still you can use it, works with any file and it is free free. </p>
<p><strong>GhostScript</strong><br />
Another choice is <a href="http://www.ghostscript.com/" target ="_blank">GhostScript</a>. This works well once you select a &#8220;device&#8221;. You need to install it and run from the installation directory. I put it on a Windows XP machine and the command line was <code>"C:\Program Files\gs\gs8.71\bin\gswin32c.exe" "@printtestgscmdline.txt"</code> The argument <code>@printtestgscmdline.txt</code> is a way to feed the arguments from a file. The arguments I used are <code>-sDEVICE="printer-device-id" -dNOPAUSE "full-path-to-pdf/your.pdf" -c quit</code>. To get the valid printer device id you can get a list by starting ghostscript interactive mode and typing <code>devicenames ==</code>. I passed on this as I could not get the silencing arguments to work if you copied the installation directory executable files to another directory or machine. I needed something more portable. Also I am not sure of the PDF currency of the software.</p>
<p><strong>Acrobat Wrapper version 2.0.0.23</strong><br />
The one I settled on that is free for non-commercial use. The program file name is <a href="http://www.biopdf.com/acrowrap/close_adobe_reader.php" target ="_blank">acrowrap.exe</a> and called Acrobat Wrapper version 2.0.0.23.  You can download and use it without any watermarks or page limits. For commercial use you need to buy PDF Writer at $29 (US) per user with price cuts starting with 2 users; but you do not have to install that. Acrobat Wrapper downloads and installs on its own. Once installed you can take the acrowrap.exe from the installation directory and place it in any folder or computer and it happily runs from the command line. Thus for our commercial use we will buy licenses of PDF Writer for each copy of acrowrap.exe we install on our kiosks. The one drawback is when the printing is delayed the Adobe Reader window appears in a ghostly fashion but not accessible by the user.</p>
<p>You folks at BioPDF should make Acrobat Wrapper a product and simplify so that you do not have to install to get the executable. Also if you can keep Acrobat Reader minimized, that would be sweet.</p>
<div style = "float:left;margin-right:5px;"><div style = "text-align:center"><script type="text/javascript"><!--
google_ad_client = "pub-8926707286265620";
/* 300x250, created 7/29/10 */
google_ad_slot = "4548376258";
google_ad_width = 300;
google_ad_height = 250;
//-->
</script>
<script type="text/javascript"
src="http://pagead2.googlesyndication.com/pagead/show_ads.js">
</script></div></div>
<p><strong>Using Acrobat Wrapper</strong><br />
The following is a demonstration of how to use Acrobat Wrapper with a NativeProcess Adobe Air application. In this example the user will hit the print button. If your file is generated or downloaded automatically for the user, you can go directly to printing without further user interaction. </p>
<p>I added some options like full screen and always in front for a kiosk type of installation. See code lines 29 and 30 respectively for settings and 46 to 54 for the implementation. </p>
<p>Create an Air project in Flex Builder and paste the code below into the main application mxml.</p>
<p>You need to download and install Acrobat Wrapper on any Windows computer. Then create a folder in your Flex application under the &#8220;src&#8221; folder and name it the name &#8220;native_apps&#8221; shown on code line 19 or provide your own folder name. Copy to this folder the acrowrap.exe file from the installation directory that Acrobat Wrapper placed it. For example on a Windows XP computer it was <code>C:\Program Files\bioPDF\Acrobat Wrapper</code>.</p>
<p>Add a sub folder named &#8220;in&#8221; or a name of your choice on code line 24. The &#8220;in&#8221; folder will hold your PDF file named on code line 23. </p>
<p><strong>Testing Hints</strong><br />
In testing you will find that if it fails there is no feedback from acrowrap.exe we can pickup from the exit event handler. The exit code is 0 when it prints or fails to print. Thus the best debugging is to create a batch file and mimic what is happening in the AIR application until you find the solution. For example double back slashing and having the &#8220;/t&#8221; typed as &#8220;\t&#8221; were problems I encountered. </p>
<p>Also you need Acrobat Reader installed.</p>
<p><a href="http://www.lonhosford.com/content/flex/NativeProcessPDFPrintAcrowrap/NativeProcessPDFPrintAcrowrap.zip">Download Flex Builder 4 Project File</a></p>
<pre class="brush: as3; wrap-lines: false;">
&lt;?xml version=&quot;1.0&quot; encoding=&quot;utf-8&quot;?&gt;
&lt;!--
    Purpose: Demonstrate NativeProcess printing PDF with arcowrap from BioPDF
	Author: Lon Hosford www.lonhosford.com 908 996 3773
	Date: August 12, 2010

--&gt;
&lt;s:WindowedApplication xmlns:fx=&quot;http://ns.adobe.com/mxml/2009&quot;
					   xmlns:s=&quot;library://ns.adobe.com/flex/spark&quot;
					   xmlns:mx=&quot;library://ns.adobe.com/flex/mx&quot;
					   height = &quot;500&quot;
					   applicationComplete=&quot;applicationCompleteHandler(event)&quot;
					   &gt;
	&lt;fx:Script&gt;
		&lt;![CDATA[
			import mx.controls.Alert;
			import mx.events.FlexEvent;
			import mx.managers.CursorManager;
			private const NATIVE_APP_REL_PATH:String = &quot;native_apps&quot;; 	// Relative to applicationDirectory
			private const PDF_PRINT_APP_FILE_NAME:String = &quot;acrowrap.exe&quot;;
			private const PDF_PRINT_APP_SWITCH:String = &quot;/t&quot;; 			// Required switch for acrowrap.exe
																		// to close Adobe Reader.
			private const PDF_FILE_NAME:String = &quot;readme.pdf&quot;;			// Const for demo only. Var is ok.
			private const PDF_FILE_REL_PATH:String = &quot;in&quot;;				// Relative to applicationDirectory
			private const PRINTER_NAME:String = &quot;&quot;;						// Blank will use default printer
																		// Network printer double backslash
																		// Ex:\\\\ServerName\\PrinterName

			private var displayFullScreen:Boolean = true;				// Full screen state
			private var displayAlwaysInFront:Boolean = true;			// Always in front state

			// Language localization
			private var lang_Attention:String = &quot;Attention&quot;;
			private var lang_NativeNotSupported:String = &quot;NativeProcess not supported. Flash Version: &quot;;
			private var lang_IsWindowsOSOnly:String = &quot; is a Windows OS only program.&quot;;
			[Bindable]
			private var lang_PrintPDFBtnLabel:String = &quot;Print PDF&quot;;
			[Bindable]
			private var lang_ClearBtnLabel:String = &quot;Clear&quot;;
			[Bindable]
			private var lang_ConsoleLabel:String = &quot;Console:&quot;;
			protected function applicationCompleteHandler(event:FlexEvent):void
			{
				console(className + &quot;.applicationCompleteHandler(...)&quot;);
				// Display full screen
				if (displayFullScreen)
				{
					stage.displayState = StageDisplayState.FULL_SCREEN;
				}
				// Make this application always in front
				if (displayAlwaysInFront)
				{
					this.alwaysInFront = true;
				}
				// NativeProcess not supported
				if (!NativeProcess.isSupported)
				{
					showNativeProcessUnsupported();
				}
			}
			/* ========================================================================
				NativeProcess
			======================================================================== */
			/**
			 * Print the pdf
			 * */
			public function printPDF():void
			{
				var process:NativeProcess;
				var backSlashPattern:RegExp = /\\/g;
				var exeFileName:File;
				var printFileName:String;
				var processArgs:Vector.&lt;String&gt;;

				setUIStateToPrinting();

				// Windows OS
				if (Capabilities.os.toLowerCase().indexOf(&quot;win&quot;) &gt; -1)
				{
					// Create File object of the application directory
					exeFileName = File.applicationDirectory;
					// Refine the file object to the NativeApps subdirectory of application directory
					exeFileName = exeFileName.resolvePath(NATIVE_APP_REL_PATH);
					// Refine the file object the application file name
					exeFileName = exeFileName.resolvePath(PDF_PRINT_APP_FILE_NAME);
					printFileName = exeFileName.nativePath.substr(0, exeFileName.nativePath.indexOf(exeFileName.name))  +  PDF_FILE_REL_PATH + &quot;\\&quot; + PDF_FILE_NAME;
					printFileName = printFileName.replace(backSlashPattern, &quot;\\\\&quot;)	;
					console(&quot;Printing &quot; + printFileName);
					processArgs = new Vector.&lt;String&gt;();
					processArgs.push(PDF_PRINT_APP_SWITCH);
					processArgs.push(printFileName);
					processArgs.push(PRINTER_NAME); 

					var nativeProcessStartupInfo:NativeProcessStartupInfo;
					nativeProcessStartupInfo = new NativeProcessStartupInfo();
					nativeProcessStartupInfo.arguments = processArgs ;
					nativeProcessStartupInfo.executable = exeFileName  ;

					console(&quot;Executing &quot; + nativeProcessStartupInfo.executable.nativePath);
					console(&quot;Arguments &quot; + nativeProcessStartupInfo.arguments.toString());

					// Create NativeProcess, create listeners and start.
					process = new NativeProcess();
					process.addEventListener(NativeProcessExitEvent.EXIT, nativeProcessExitEventHandler);
					process.addEventListener(IOErrorEvent.STANDARD_ERROR_IO_ERROR, ioEventHandler);
					process.start(nativeProcessStartupInfo);
					this.focusManager.showFocus();
				}
				else
				{
					showUnsupportedOS();
				}
			}
			/**
			 * Signals the native process has exited. The exitCode property contains the
			 * value the process returns to the host operating system on exit.
			 * If the AIR application terminates the process by calling the exit()
			 * method of the NativeProcess object, the exitCode property is set to NaN.
			 * */
			public function nativeProcessExitEventHandler(event:NativeProcessExitEvent):void
			{
				console(className + &quot;.nativeProcessExitEventHandler(...) - Process exited with &quot; + event.exitCode);
				setUIStateToReady();
			}
			/**
			 * Signals that reading from the standard error (stderror) stream has failed.
			 * */
			public function ioEventHandler(event:IOErrorEvent):void
			{
				console(className + &quot;.ioEventHandler(...) - IOError - &quot; + event.toString());
				setUIStateToReady();
			}
			/* ========================================================================
				UI
			======================================================================== */
			/**
			 * Set the UI ready state. Not printing.
			 * */
			public function setUIStateToReady():void
			{
				print_btn.enabled = true;
				CursorManager.removeBusyCursor();
			}
			/**
			 * Set the UI printing state.
			 * */
			public function setUIStateToPrinting():void
			{
				print_btn.enabled = false;
				CursorManager.setBusyCursor();
			}
			/**
			 * Show unsupported OS.
			 * */
			public function showUnsupportedOS():void
			{
				Alert.show( PDF_PRINT_APP_FILE_NAME + lang_IsWindowsOSOnly, lang_Attention);
				setUIStateToReady()
			}
			/**
			 * Show native process unsupported.
			 * */
			public function showNativeProcessUnsupported():void
			{
				Alert.show( lang_NativeNotSupported + Capabilities.version, lang_Attention);
			}
			/**
			 * Clear the console
			 * */
			public function clearConsole():void
			{
				console_ta.text = &quot;&quot;;
			}
			/**
			 * Append to the console
			 * */
			public function console(msg:String):void
			{
				trace(msg);
				console_ta.text += msg + &quot;\n&quot;;
			}
		]]&gt;
	&lt;/fx:Script&gt;
	&lt;s:VGroup width=&quot;100%&quot; height = &quot;100%&quot; &gt;
		&lt;s:Button id = &quot;print_btn&quot; label=&quot;{lang_PrintPDFBtnLabel}&quot; click=&quot;printPDF()&quot;/&gt;
		&lt;s:Label text=&quot;{lang_ConsoleLabel}&quot; textAlign=&quot;left&quot;/&gt;
		&lt;s:TextArea id=&quot;console_ta&quot; height = &quot;100%&quot; width=&quot;100%&quot;/&gt;
		&lt;s:Button label=&quot;{lang_ClearBtnLabel}&quot; click=&quot;clearConsole()&quot;/&gt;
	&lt;/s:VGroup&gt;
&lt;/s:WindowedApplication&gt;
</pre>
]]></content:encoded>
			<wfw:commentRss>http://www.lonhosford.com/lonblog/2010/08/12/adobe-air-nativeprocess-silent-print-pdf-with-biopdfs-acrobat-wrapper/feed/</wfw:commentRss>
		<slash:comments>11</slash:comments>
		</item>
		<item>
		<title>Adobe AIR (P2P) Peer To Peer Text Chat with NetGroup</title>
		<link>http://www.lonhosford.com/lonblog/2010/08/02/adobe-air-peer-to-peer-text-chat-with-netgroup/</link>
		<comments>http://www.lonhosford.com/lonblog/2010/08/02/adobe-air-peer-to-peer-text-chat-with-netgroup/#comments</comments>
		<pubDate>Mon, 02 Aug 2010 12:34:01 +0000</pubDate>
		<dc:creator>Lon Hosford</dc:creator>
				<category><![CDATA[AIR (Adobe Integrated Runtime)]]></category>
		<category><![CDATA[Flex]]></category>
		<category><![CDATA[P2P]]></category>
		<category><![CDATA[Technologies]]></category>
		<category><![CDATA[Actionscript]]></category>
		<category><![CDATA[Air]]></category>
		<category><![CDATA[Flex 4]]></category>
		<category><![CDATA[Peer to Peer]]></category>

		<guid isPermaLink="false">http://www.lonhosford.com/lonblog/?p=272</guid>
		<description><![CDATA[By Lon (Alonzo) Hosford This is a minimalist example of an Adobe AIR peer to peer text chat application using the NetGroup class for peer communication. Create an new AIR application and replace all the code with the code below. You need to change line 15 to include your own Adobe Stratus developer key. You [...]]]></description>
			<content:encoded><![CDATA[<p><strong>By Lon (Alonzo) Hosford</strong></p>
<p>This is a minimalist example of an Adobe AIR peer to peer text chat application using the NetGroup class for peer communication. Create an new AIR application and replace all the code with the code below.</p>
<div style="float: right;"><div style = "text-align:center"><script type="text/javascript"><!--
google_ad_client = "pub-8926707286265620";
/* 300x250, created 7/29/10 */
google_ad_slot = "4548376258";
google_ad_width = 300;
google_ad_height = 250;
//-->
</script>
<script type="text/javascript"
src="http://pagead2.googlesyndication.com/pagead/show_ads.js">
</script></div></div>
<p>You need to change line 15 to include your own Adobe Stratus developer key. You can use the Adobe Stratus server by obtaining a developer key at <a href="http://labs.adobe.com/technologies/stratus/">Adobe Labs</a>.</p>
<p>On line 16 you need to create a name for your group. You want something unique so consider a reverse domain prefix such as com.yourdomain plus an application name and something unique. For example com.bugsbunny.carrot-chat.b7-4d;8k9.</p>
<p>You can also make this a Flex application. Create a flex application and paste all but the WindowedApplication MXML tags between your Application MXML tags.</p>
<p>The application starts once the user clicks the connect button. This establishes a connection with the Adobe Stratus server on line 51. Once the connection is successful, ncNetStatus(&#8230;) handler receives a NetConnection.Connect.Success event code and calls the setupGroup() method on line 100. The setupGroup() method creates the requisite specification to join the group you identified in line 16.</p>
<p>Once the user selects the user name and clicks connect, the user name cannot be changed. That is an application design choice on line 190 with the enabled property for the  user_ti TextInput component. However you might notice the new Flex 4 two way binding used for the userName variable and the user_ti TextInput component text property on line 190. If you want to change the user name after the connection just remove the enabled property on line 190.</p>
<pre class="brush: as3; wrap-lines: false;">
.
.
.
&lt;?xml version=&quot;1.0&quot; encoding=&quot;utf-8&quot;?&gt;
&lt;s:WindowedApplication xmlns:fx=&quot;http://ns.adobe.com/mxml/2009&quot;
					   xmlns:s=&quot;library://ns.adobe.com/flex/spark&quot;
					   xmlns:mx=&quot;library://ns.adobe.com/flex/mx&quot;
					   creationComplete=&quot;creationCompleteHandler(event)&quot;
					   height=&quot;600&quot; width=&quot;800&quot;
					   &gt;
	&lt;fx:Script&gt;
		&lt;![CDATA[
			import mx.events.FlexEvent;
			private const SERVER:String = &quot;rtmfp://stratus.adobe.com/&quot;;
			private const DEVKEY:String = &quot;{YOUR ADOBE STRATUS DEVELOPER KEY}&quot;;
			private const NETGROUP:String = &quot;{YOUR NETGROUP NAME}&quot;;
			private var _nc:NetConnection;
			private var _netGroup:NetGroup;

			// NetGroup specifier for NETGROUP
			[Bindable]
			private var groupSpecWithAuthorizations:String;

			// Connected to Stratus server and to NETGROUP
			[Bindable]
			private var _connected:Boolean = false;

			// _userName name in chat.
			[Bindable]
			private var _userName:String;

			// Used to store our P2P Peer ID for binding to UI.
			[Bindable]
			private var _nearID:String;

			// Counter to make every NetGroup post message unique
			private var _msgOutCount:int = 0;

			/**
			 * CreationComplete event handler for WindowedApplication
			 * */
			protected function creationCompleteHandler(event:FlexEvent):void
			{
				console (className + &quot;.creationCompleteHandler(...) - Capabilities.version:&quot; + Capabilities.version);
				// Generate a default user name;
				_userName = &quot;user&quot; +  Math.round(Math.random()*10000);
			}
			/**
			 * Connect with Stratus server
			 * */
			private function connect():void
			{
				_nc = new NetConnection();
				_nc.addEventListener(NetStatusEvent.NET_STATUS,ncNetStatus);
				_nc.connect(SERVER+DEVKEY);
			}
			/**
			 * NetStatusEvent.NET_STATUS event handler for _nc and _netGroup
			 * */
			private function ncNetStatus(event:NetStatusEvent):void
			{
				console( className + &quot;.ncNetStatus(...) - event.info.code:&quot; + event.info.code);

				switch(event.info.code)
				{
					case &quot;NetConnection.Connect.Success&quot;:
						_nearID = _nc.nearID;// or you can use event.target.nearID;
						setupGroup();
						break;
					case &quot;NetGroup.Connect.Success&quot;:
						_connected = true;
						break;
					case &quot;NetGroup.Posting.Notify&quot;:
						receivePostNotifyMessage(event.info.message);
						break;
					// FYI: More NetGroup event info codes
					case &quot;NetGroup.Neighbor.Connect&quot;:
					case &quot;NetGroup.LocalCoverage.Notify&quot;:
					case &quot;NetGroup.SendTo.Notify&quot;: // event.info.message, event.info.from, event.info.fromLocal
					case &quot;NetGroup.MulticastStream.PublishNotify&quot;: // event.info.name
					case &quot;NetGroup.MulticastStream.UnpublishNotify&quot;: // event.info.name
					case &quot;NetGroup.Replication.Fetch.SendNotify&quot;: // event.info.index
					case &quot;NetGroup.Replication.Fetch.Failed&quot;: // event.info.index
					case &quot;NetGroup.Replication.Fetch.Result&quot;: // event.info.index, event.info.object
					case &quot;NetGroup.Replication.Request&quot;: // event.info.index, event.info.requestID
					default:
					{
						break;
					}
				}
			}

			// ========================================
			//  NETGROUP Methods
			// ========================================

			/**
			 * Connect with the NETGROUP
			 * */
			private function setupGroup():void
			{
				console( className + &quot;.setupGroup()&quot;);
				var groupspec:GroupSpecifier = new GroupSpecifier(NETGROUP);
				// Allow group members to open channels to server
				groupspec.serverChannelEnabled = true;
				// Allow group members to post
				groupspec.postingEnabled = true;

				// Create the group specifi
				groupSpecWithAuthorizations = groupspec.groupspecWithAuthorizations();

				// Join the group specified by groupspec
				_netGroup = new NetGroup(_nc, groupSpecWithAuthorizations);

				// Register listener for NetGroup NetStatus events
				_netGroup.addEventListener(NetStatusEvent.NET_STATUS, ncNetStatus);
			}
			/**
			 * Post a message to NETGROUP;
			 * @param messageText String. Text message to send.
			 * */
			private function sendMessageToGroup(messageText:String):void
			{
				console( className + &quot;.sendMessageToGroup(...) - messageText:&quot; + messageText);
				// Construct message object
				var netGroupMessage:Object = new Object();
				netGroupMessage.sender = _netGroup.convertPeerIDToGroupAddress(_nc.nearID);
				netGroupMessage.user = _userName;
				netGroupMessage.text = messageText;
				netGroupMessage.sequence = ++ _msgOutCount;// Only unique message objects are sent.
				// Send netGroupMessage object to all members of the NETGROUP
				_netGroup.post(netGroupMessage);
			}
			/**
			 * Receive a NetGroup.Posting.Notify message from NETGROUP
			 * @param netGroupMessage Object. NetGroup post message object.
			 * */
			private function receivePostNotifyMessage(netGroupMessage:Object):void
			{
				updateUI(netGroupMessage.user, netGroupMessage.text)
 			}

			// ========================================
			//  UI Methods
			// ========================================

			/**
			 * Join chat group
			 * */
			private function joinChat():void
			{
				// Connect to server
				connect();
			}
			/**
			 * Post a message to NETGROUP;
			 * */
			private function sendMessage():void
			{
				// Send message to NetGroup
				sendMessageToGroup(message_ti.text);
				// Update local view of message sent
				updateUI(_userName, message_ti.text);
			}
			/**
			 * Update UI with message object received from NETGROUP
			 * @param userName String. Name of user sending message.
			 * @param messageText String. Text of message to sent.
			 * */
			private function updateUI(userName:String, messageText:String):void
			{
				chat_ta.appendText( userName + &quot;: &quot; + messageText + &quot;\n&quot;);
			}

			private function console(msg:String):void
			{
				trace(msg);
				out_ta.appendText(msg + &quot;\n&quot;);
			}
		]]&gt;
	&lt;/fx:Script&gt;
	&lt;s:layout &gt;
		&lt;s:VerticalLayout horizontalAlign=&quot;left&quot;
						  paddingLeft=&quot;10&quot; paddingTop=&quot;10&quot; paddingRight=&quot;10&quot; paddingBottom=&quot;10&quot;/&gt;
	&lt;/s:layout&gt;
	&lt;!--UI For Chat--&gt;
	&lt;s:Label text = &quot;Chat log:&quot; /&gt;
	&lt;s:TextArea id=&quot;chat_ta&quot; height = &quot;120&quot; width=&quot;100%&quot;/&gt;
	&lt;s:HGroup width=&quot;100%&quot;&gt;
		&lt;s:TextInput  id=&quot;user_ti&quot; text=&quot;@{_userName}&quot; enabled=&quot;{!_connected}&quot;/&gt;
		&lt;s:Button label=&quot;Connect&quot; click=&quot;joinChat()&quot; enabled=&quot;{!_connected}&quot; /&gt;
		&lt;s:TextInput  id=&quot;message_ti&quot;  width=&quot;70%&quot; enter=&quot;sendMessage()&quot; enabled=&quot;{_connected}&quot;/&gt;
		&lt;s:Button label=&quot;Send&quot; click=&quot;sendMessage()&quot; enabled=&quot;{_connected}&quot; /&gt;
		&lt;s:Button label=&quot;Clear&quot; click=&quot;chat_ta.text = '';&quot;   /&gt;

	&lt;/s:HGroup&gt;

	&lt;!--UI For Tracing P2P--&gt;
	&lt;s:Label text = &quot;P2P Information&quot; paddingTop=&quot;20&quot;/&gt;

	&lt;s:HGroup horizontalAlign=&quot;left&quot; width=&quot;100%&quot; &gt;
		&lt;s:Label text = &quot;Peer Id: &quot; /&gt;
		&lt;s:Label id = &quot;peerId_lbl&quot; text = &quot;{_nearID}&quot;/&gt;
	&lt;/s:HGroup&gt;

	&lt;s:HGroup horizontalAlign=&quot;left&quot; width=&quot;100%&quot; &gt;
		&lt;s:Label text = &quot;Group Id: &quot; /&gt;
		&lt;s:Label id = &quot;groupId_lbl&quot; text = &quot;{groupSpecWithAuthorizations}&quot; /&gt;
	&lt;/s:HGroup&gt;

	&lt;s:Label text = &quot;Trace log:&quot; paddingTop=&quot;20&quot;/&gt;

	&lt;s:TextArea id=&quot;out_ta&quot;  height = &quot;250&quot; width=&quot;100%&quot; lineBreak=&quot;explicit&quot; fontFamily=&quot;_typewriter&quot;/&gt;
	&lt;s:HGroup horizontalAlign=&quot;center&quot; width=&quot;100%&quot;&gt;
		&lt;s:Button label=&quot;Clear&quot; click=&quot;out_ta.text = '';&quot;   /&gt;
	&lt;/s:HGroup&gt;
&lt;/s:WindowedApplication&gt;
</pre>
<p>I had experienced some problems with launching the application in Eclipse Flex Builder 4. I had published the application and launched on other computers. The Flex Builder 4 launch would not post to the NetGroup or receive from it if launched before other application instances on other machines. However if it was launched after another application instance was launched, it seemed to post and receive posts.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.lonhosford.com/lonblog/2010/08/02/adobe-air-peer-to-peer-text-chat-with-netgroup/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Adobe AIR Native Drag and Drop File Upload With ProgressBar and PHP</title>
		<link>http://www.lonhosford.com/lonblog/2010/07/29/adobe-air-native-drag-and-drop-file-upload-with-progressbar-and-php/</link>
		<comments>http://www.lonhosford.com/lonblog/2010/07/29/adobe-air-native-drag-and-drop-file-upload-with-progressbar-and-php/#comments</comments>
		<pubDate>Thu, 29 Jul 2010 14:31:29 +0000</pubDate>
		<dc:creator>Lon Hosford</dc:creator>
				<category><![CDATA[AIR (Adobe Integrated Runtime)]]></category>
		<category><![CDATA[Php]]></category>
		<category><![CDATA[Php 5]]></category>

		<guid isPermaLink="false">http://www.lonhosford.com/lonblog/?p=157</guid>
		<description><![CDATA[This is an example of using Adobe AIR native drag and drop to upload a file to a web server using PHP5. The application only uploads one file at a time and assumes the drag operation is anywhere over the application. This is the PHP code that will place the file into a folder named [...]]]></description>
			<content:encoded><![CDATA[<div id="fb-root"></div>
<p><script src="http://connect.facebook.net/en_US/all.js#appId=105467682877384&amp;xfbml=1"></script><fb:like href="http://www.lonhosford.com/lonblog/2010/07/29/adobe-air-native-drag-and-drop-file-upload-with-progressbar-and-php/" send="true" width="450" show_faces="true" font=""></fb:like></p>
<p><img class="alignleft" src="http://lh5.ggpht.com/_e5pwU0LJbN8/TFeF0SVgjJI/AAAAAAAAFp0/NDQo9acVBEw/s800/computer_upload_arrow.jpg" alt="" width="50" height="50" />This is an example of using Adobe AIR native drag and drop to upload a file to a web server using PHP5. The application only uploads one file at a time and assumes the drag operation is anywhere over the application.</p>
<div style="float: right;"><div style = "text-align:center"><script type="text/javascript"><!--
google_ad_client = "pub-8926707286265620";
/* 300x250, created 7/29/10 */
google_ad_slot = "4548376258";
google_ad_width = 300;
google_ad_height = 250;
//-->
</script>
<script type="text/javascript"
src="http://pagead2.googlesyndication.com/pagead/show_ads.js">
</script></div></div>
<p>This is the PHP code that will place the file into a folder named upload. The upload folder is a child folder under the folder for the PHP script. On line 13 you need to replace YOUR APPFOLDER with the folder you plan to install the PHP script. This also must be matched in the AIR MXML application.</p>
<p>You also need to set permissions on the upload folder to allow uploading.</p>
<p>The PHP script returns XML.The result.status node provide a true or false value for the success feedback.<br />
<strong>file_upload.php</strong></p>
<pre class="brush: php; wrap-lines: false;">
.
.
.
&lt;?php
	$returnXML = &quot;&quot;;
	$returnLog = &quot;Receiving upload...\n&quot;;
    // Filedata is the default name used in uploading
	$returnLog .= &quot;temporary file name = &quot; . $_FILES['Filedata']['tmp_name'].&quot;\n&quot;;
	$returnLog .= &quot;file name = &quot; . $_FILES['Filedata']['name'].&quot;\n&quot;;
	$returnLog .= &quot;file size = &quot; . $_FILES['Filedata']['size'].&quot;\n&quot;;
	$file_temp = $_FILES['Filedata']['tmp_name'];
	$file_name = $_FILES['Filedata']['name'];
	$file_path = $_SERVER['DOCUMENT_ROOT'].&quot;/YOUR APPFOLDER/upload&quot;;
	$returnStatus = &quot;false&quot;;
	$returnLog .= &quot;attempting to move file...\n&quot;;
	if(  move_uploaded_file( $file_temp, $file_path . &quot;/&quot; . $file_name) )
	{
		$returnStatus = &quot;true&quot;;
	}

	$returnLog .= &quot;file move results = &quot; . $returnStatus . &quot;\n&quot;;
	$returnXML .= &quot;&lt;return&gt;&quot;;
	$returnXML .= &quot;&lt;log&gt;&quot;;
	$returnXML .= $returnLog;
	$returnXML .= &quot;&lt;/log&gt;&quot;;
	$returnXML .= &quot;&lt;status&gt;&quot;;
	$returnXML .= $returnStatus;
	$returnXML .= &quot;&lt;/status&gt;&quot;;

	$returnXML .= &quot;&lt;/return&gt;&quot;;
	echo $returnXML ;

?&gt;
</pre>
<p>This is the AIR main MXML code. You need to modify line 21 to include the URL to where you want to place the PHP script to handle the file upload.</p>
<pre class="brush: as3; wrap-lines: false;">
.
.
.
&lt;?xml version=&quot;1.0&quot; encoding=&quot;utf-8&quot;?&gt;
&lt;s:WindowedApplication xmlns:fx=&quot;http://ns.adobe.com/mxml/2009&quot;
					   xmlns:s=&quot;library://ns.adobe.com/flex/spark&quot;
					   xmlns:mx=&quot;library://ns.adobe.com/flex/mx&quot;
					   creationComplete=&quot;creationCompleteHandler(event)&quot;
					   &gt;
	&lt;fx:Script&gt;
		&lt;![CDATA[
			import flash.desktop.ClipboardFormats;
			import flash.desktop.NativeDragManager;
			import flash.events.DataEvent;
			import flash.events.NativeDragEvent;
			import flash.filesystem.File;

			import mx.controls.Alert;
			import mx.events.FlexEvent;

			private const UPLOAD_SCRIPT_URL:String = &quot;http://www.YOURDOMAIN.com/YOUR APPFOLDER/file_upload.php&quot;

			protected function creationCompleteHandler(event:FlexEvent):void
			{
				console(className + &quot;.creationCompleteHandler(...) - url:&quot; + url );
				addEventListener(NativeDragEvent.NATIVE_DRAG_ENTER, onDragEnterHandler);
				addEventListener(NativeDragEvent.NATIVE_DRAG_DROP, onDragDropHandler);
			}
			/**
			 * Event handler for NativeDragEvent.NATIVE_DRAG_ENTER.
			 * May be called multiple times as file object is dragged over the application.
			 * &lt;p&gt;
			 * Check that there are file objects being dragged and only one file is being dragged.
			 * &lt;/p&gt;
			 * */
			private function onDragEnterHandler(e:NativeDragEvent):void
			{
				console(className + &quot;.onDragIn(...)&quot;);
				//Does the clipboard have an array of File objects (AIR only)
				if(e.clipboard.hasFormat(ClipboardFormats.FILE_LIST_FORMAT))
				{
					//Get the array of File objects
					var files:Array = e.clipboard.getData(ClipboardFormats.FILE_LIST_FORMAT) as Array;

					//Allow only one file as only supporting one in this application
					if(files.length == 1)
					{
						//Triggers NativeDragEvent.NATIVE_DRAG_DROP event.
						NativeDragManager.acceptDragDrop(this);
					}
				}
			}
			/**
			 * Event handler for NativeDragEvent.NATIVE_DRAG_DROP
			 * Occurs when the file object is dropped over the application
			 * */
			private function onDragDropHandler(e:NativeDragEvent):void
			{
				var urlRequest:URLRequest = new URLRequest();
				urlRequest.url = UPLOAD_SCRIPT_URL + &quot;?cache=&quot; + new Date().getTime();
				console(className + &quot;.onDragDropHandler(...) - urlRequest.url:&quot; + urlRequest.url);
				//Get the array of File objects. The array should only have one entry per onDragEnterHandler(...)
				var files:Array = e.clipboard.getData(ClipboardFormats.FILE_LIST_FORMAT) as Array;

				//Create a File object and register events.
				var file:File = File(files[0]);
				file.addEventListener(Event.COMPLETE, file_CompleteHandler);
				file.addEventListener(Event.OPEN, file_OpenHandler);
				file.addEventListener(DataEvent.UPLOAD_COMPLETE_DATA, file_UploadCompleteDataHandler);
				file.addEventListener(IOErrorEvent.IO_ERROR, file_IOErrorHandler);
				file.addEventListener(HTTPStatusEvent.HTTP_STATUS , file_HTTPStatusHandler);
				file.addEventListener(SecurityErrorEvent.SECURITY_ERROR, file_SecurityErrorHandler);
				file.addEventListener(ProgressEvent.PROGRESS, file_progressHandler);

				console(className + &quot;.onDragDropHandler(...) - file.url:&quot; + file.url);
				console(className + &quot;.onDragDropHandler(...) - file.name:&quot; + file.name);
				console(className + &quot;.onDragDropHandler(...) - file.nativePath:&quot; + file.nativePath);

				try {
					// Start the file upload
					console(className + &quot;.onDragDropHandler(...) - uploading &quot; + file.name + &quot; ...\n&quot; );
					file.upload(urlRequest, &quot;Filedata&quot;, false);
				}
				catch (error:Error) {
					console(className + &quot;.onDragDropHandler(...) - Unable to upload file.&quot; + file.name);
					console(className + &quot;.onDragDropHandler(...) - error.&quot; + error.toString());
				}
			}
			private function file_progressHandler(event:ProgressEvent):void {
				var file:FileReference = FileReference(event.target);
				console(className + &quot;.progressHandler(...) - name=&quot; + file.name + &quot; bytesLoaded=&quot; + event.bytesLoaded + &quot; bytesTotal=&quot; + event.bytesTotal);
				progressBar.setProgress(event.bytesLoaded, event.bytesTotal);
				progressBar.label = &quot;CurrentProgress: &quot; + Math.round( (event.bytesLoaded /event.bytesTotal * 100)) + &quot;%&quot;;
			}

			/**
			 * Dispatched when an upload or download operation starts.
			 */
			private function file_OpenHandler(event:Event):void
			{
				console(className + &quot;.file_OpenHandler(...) - event:&quot; + event );
			}
			/**
			 * Dispatched when an upload fails and an HTTP status code is available to describe the failure.
			 */
			private function file_HTTPStatusHandler(event:HTTPStatusEvent):void
			{
				console(className + &quot;.file_HTTPStatusHandler(...) - event:&quot; + event );
			}
			/**
			 * Dispatched when the upload fails for various I/O reasons.
			 */

			private function file_IOErrorHandler(event:IOErrorEvent):void
			{
				console(className + &quot;.file_IOErrorHandler(...) - event:&quot; + event );
			}
			/**
			 * Dispatched when an operation violates a security constraint.
			 */

			private function file_SecurityErrorHandler(event:SecurityErrorEvent):void
			{
				console(className + &quot;.file_SecurityErrorHandler(...) - event:&quot; + event );
			}

			/**
			 * Dispatched when download is complete or when upload generates an HTTP status code of 200.
			 */
			public function file_CompleteHandler(event:Event):void
			{
				console(className + &quot;.file_CompleteHandler(...) file uploaded complete&quot;);
			}

			/**
			 * Dispatched after data is received from the server after a successful upload.
			 */
			public function file_UploadCompleteDataHandler(event:DataEvent):void
			{
				var result:XML = new XML(event.data);
				console(className + &quot;.file_UploadCompleteDataHandler(...) - STATUS:&quot; + result.status );
				console(className + &quot;.file_UploadCompleteDataHandler(...) - LOG:\n&quot; + result.log );

			}

			/**
			 * Update the UI trace log
			 */
			private function console(msg:String):void
			{
				trace(msg);
				console_ta.appendText(msg + &quot;\n&quot;);
			}

		]]&gt;
	&lt;/fx:Script&gt;
	&lt;s:layout&gt;
		&lt;s:VerticalLayout paddingLeft=&quot;10&quot; paddingTop=&quot;10&quot;/&gt;
	&lt;/s:layout&gt;
	&lt;s:Label  text=&quot;Adobe AIR Drag and Drop File Upload&quot; fontSize=&quot;20&quot; fontWeight=&quot;bold&quot;/&gt;
	&lt;s:Line yFrom=&quot;10&quot; yTo = &quot;100&quot; xFrom=&quot;0&quot; xTo=&quot;0&quot; width=&quot;100%&quot; height=&quot;0&quot;  &gt;
		&lt;!-- Define the border color of the line. --&gt;
		&lt;s:stroke&gt;
			&lt;s:SolidColorStroke color=&quot;0x000000&quot; weight=&quot;1&quot; caps=&quot;square&quot;/&gt;
		&lt;/s:stroke&gt;
	&lt;/s:Line&gt;

	&lt;mx:ProgressBar id=&quot;progressBar&quot; labelPlacement=&quot;bottom&quot; minimum=&quot;0&quot; visible=&quot;true&quot; maximum=&quot;100&quot;
					color=&quot;0x323232&quot; label=&quot;CurrentProgress 0%&quot; direction=&quot;right&quot; mode=&quot;manual&quot; width=&quot;100%&quot;/&gt;

	&lt;s:TextArea  width=&quot;100%&quot; height=&quot;100%&quot; id=&quot;console_ta&quot; fontFamily=&quot;_typewriter&quot; lineBreak=&quot;explicit&quot;/&gt;
	&lt;s:HGroup horizontalAlign=&quot;center&quot; width=&quot;100%&quot;&gt;
		&lt;s:Button   label=&quot;Clear&quot; click=&quot;{console_ta.text = '';}&quot;/&gt;
	&lt;/s:HGroup&gt;
&lt;/s:WindowedApplication&gt;
</pre>
<p>The AIR application and the PHP script do not check for maximum upload file size allowed. A typical upload limit number imposed by web hosting is the default 2 megabytes.  You may be able to increase that number in the php.ini file or the .htaccess file. The latter is the alternative is you have no control over the php.ini file and the code is included here to bump the size up. In any case you are at the mercy of your hosting provider.</p>
<p><strong>.htaccess</strong></p>
<pre class="brush: plain; wrap-lines: false;">
php_value upload_max_filesize 10M
php_value post_max_size 10M
php_value memory_limit 128M
</pre>
<p>In the AIR application you can create a FILE object in the onDragEnterHandler in the same way as the onDragDropHandler and use the file.size parameter for a check. For the PHP script you can use the $_FILES['Filedata']['size'] value.</p>
<div id="fb-root"></div>
<p><script src="http://connect.facebook.net/en_US/all.js#xfbml=1"></script><fb:comments href="http://www.lonhosford.com/lonblog/2010/07/29/adobe-air-native-drag-and-drop-file-upload-with-progressbar-and-php/" num_posts="2" width="500"></fb:comments></p>
]]></content:encoded>
			<wfw:commentRss>http://www.lonhosford.com/lonblog/2010/07/29/adobe-air-native-drag-and-drop-file-upload-with-progressbar-and-php/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>

