URLRequest with HTTP authentication

I found a good explanation how to support HTTP authentication with URLRequests here.

That’s the most interesting part:

Best I can tell, for some reason, this only works where request method is POST; the headers don’t get set with GET requests.

Interestingly, it also fails unless at least one URLVariables name-value pair gets packaged with the request, as indicated above. That’s why many of the examples you see out there (including mine) attach “name=John+Doe” — it’s just a placeholder for some data that URLRequest seems to require when setting any custom HTTP headers. Without it, even a properly authenticated POST request will also fail.

You’ll almost surely have to modify your crossdomain.xml file to accommodate the header(s) you’re going to be sending. In my case, I’m using this, which is a rather wide-open policy file in that it accepts from any domain, so in your case, you might want to limit things a bit more, depending on how security-conscious you are:

<?xml version="1.0"?>
<cross-domain-policy>
    <allow-access-from domain="*" />
    <allow-http-request-headers-from domain="*" headers="Authorization" />
</cross-domain-policy>

… and that seems to work; more information on this one is available from Adobe here).

Apparently, Flash player version 9.0.115.0 completely blocks all Authorization headers (more information on this one here), so you’ll probably want to keep that in mind, too.

So this little code snippet explains the basics:

// Base64Encoder contained in Flex SDK
import mx.utils.Base64Encoder;
 
// Encode username and password
var base64Encoder : Base64Encoder = new Base64Encoder();        
    base64Encoder.encode( "username:password" );
 
// Create authorization request header
var urlRequestHeader : URLRequestHeader = new URLRequestHeader( "Authorization", "Basic " + base64Encoder.toString() );
 
// URLRequest setup
var urlRequest : URLRequest = new URLRequest( "url" );        
    // Needs to send some data!!        
    urlRequest.data = new URLVariables( "name=John+Doe" );        
    // Only supported with POST method!!        
    urlRequest.method = URLRequestMethod.POST;        
    // Apply authorization request header        
    urlRequest.requestHeaders = new Array( urlRequestHeader );

DisplayObject.rotationX,Y,Z

Just a quick note about working with DisplayObject.rotationX,Y,Z:

Keep in mind that your DisplayObject rotates around it’s center point. This may result in weird optics if you forget to align your DisplayObject properly.

Update: Well, there is much more to know!

There’s need for an DisplayObjectContainer with an properly assigned PerspectiveProjection.

Copy & paste this code and you’ll see what I’m talking about:

stage.align = StageAlign.TOP_LEFT;
stage.scaleMode = StageScaleMode.NO_SCALE;
stage.addEventListener( Event.RESIZE, onStageResize );
 
function onStageResize( event : Event ) : void
{
	draw();
}
 
function createRectSprite( w : int, h : int, rotationY : Number, container : Sprite, useProjection : Boolean ) : Sprite
{
	// 3D object
	var graphicSprite : Sprite = new Sprite();
		graphicSprite.name = "graphicSprite";
		graphicSprite.rotationY = rotationY;
		graphicSprite.buttonMode = true;
		graphicSprite.useHandCursor = true;
 
	// Make sure 3D objects content is centered
	var g : Graphics = graphicSprite.graphics;
		g.beginFill( 0, .5 );
		g.drawRect( w * -.5, h * -.5, w, h );
		g.endFill();
 
	// 3D Container
    var containerSprite : Sprite = new Sprite();
        containerSprite.addChild( graphicSprite );
 
	// Apply projection to 3D Container instead of 3D Object
	if ( useProjection )
	{
		var projection:PerspectiveProjection = new PerspectiveProjection();             
			projection.projectionCenter = new Point( 0, 0 );
 
		containerSprite.transform.perspectiveProjection = projection;
    }
 
	// Visualize center
	g = containerSprite.graphics;
	g.beginFill( 0xff0000 );
	g.drawCircle( 0, 0, 2 );
	g.endFill();
 
    container.addChild( containerSprite );    
    return containerSprite;
}
 
var left : Sprite;
var right : Sprite;
 
function draw() : void
{
	while ( numChildren > 0 ) { removeChildAt( 0 ); }
 
	var container : Sprite = new Sprite()
		container.name = "container";
		container.x = stage.stageWidth * .5;
		container.y = stage.stageHeight * .5;
	addChild( container );
 
	var stepX : int = stage.stageWidth / 3;
 
	var w : int = 200;
	var h : int = 200;
 
	left = createRectSprite( w, h, -60, this, true );
	left.name = "left";
	left.x = stepX;
 
	right = createRectSprite( w, h, 60, this, true );
	right.name = "right";
	right.x = stepX * 2;
 
    left.y =
    right.y = stage.stageHeight * .5;
}
 
function onEventHitTest( event : Event ) : void
{
	var containerSprite : DisplayObjectContainer;
	var graphicSprite : DisplayObject;
 
	containerSprite = this.getChildByName( "left" ) as DisplayObjectContainer;
	graphicSprite = containerSprite.getChildByName( "graphicSprite" ) as DisplayObject;
 
	trace( "hit left containerSprite:", containerSprite.hitTestPoint( mouseX, mouseY, true ) );
	trace( "hit left graphicSprite:", graphicSprite.hitTestPoint( mouseX, mouseY, true ) );
 
	containerSprite = this.getChildByName( "right" ) as DisplayObjectContainer;
	graphicSprite = containerSprite.getChildByName( "graphicSprite" ) as DisplayObject;
 
	trace( "hit right containerSprite:", containerSprite.hitTestPoint( mouseX, mouseY, true ) );
	trace( "hit right graphicSprite:", graphicSprite.hitTestPoint( mouseX, mouseY, true ) );
	trace( "" );
}
 
stage.addEventListener( MouseEvent.MOUSE_DOWN, onEventHitTest );
 
draw();

No flash events after PrintJob.start()

While developing the print functionality within a framework I got stuck on the behaviour of the Flash Player print implementation.

I couldn’t solve the following steps:

  1. Start the PrintJob and display the print dialog
  2. Procceed the following tasks for every print page:
    • Load dynamic data into view templates (images, text, video…)
    • Wait for the onLoadComplete event and add the displayed contents using PrintJob.addPage()
    • Repeat for next page…
  3. Call PrintJob.send()

I really thought this would be an easy task but now I think it’s not possible. Several attempts later I figured out that after calling PrintJob.start() there is no way to use the flash event model! This means loading contents after calling PrintJob.start() is impossible!

WOW!

So I used a workaround: I create all the print pages like I mentioned before, but without calls on the PrintJob-API. I simply store every print page as BitmapData and afterwards I create the PrintJob within a loop.

Now I have multiple other problems:

  • Hundreds of pages use a huge amount of RAM and often crash the application.
  • The bitmap resolution sucks so printout text quality is unsatisfying and due to point 1 I have no chance to work with higher quality.
  • I must avoid skript timeouts because after preparing all the bitmaps it takes much more that 15 seconds to add all these pages to a PrintJob.

Did I slip something?

Finally I created a small example to show you the disfunctionality (PrintJobProblem.as):

package  
{
	import flash.display.Sprite;
	import flash.events.Event;
	import flash.geom.Rectangle;
	import flash.printing.PrintJob;
	import flash.text.TextField;
	import flash.text.TextFieldAutoSize;
	import flash.utils.getTimer;	
 
	/**
	 * @author Markus Raab - derRaab.com / superclass.de
	 */
	public class PrintJobProblem extends Sprite 
	{
		private var _numPrintOuts : int;
		private var _printOutIndex : int;
		private var _printJob : PrintJob;
 
		/**
		 * Starts a event based printout test after a loop based
		 * test has successfully finished.
		 */
		public function PrintJobProblem()
		{
			_numPrintOuts = 2;
 
			runLoopBasedTest( );
			runEventBasedTest( );
		}
 
		/**
		 * Demonstrates and ensures the functionality.
		 */
		private function runLoopBasedTest() : void
		{
			_printJob = new PrintJob();
 
			if ( _printJob.start() )
			{	
				for ( var i : int = 0; i < _numPrintOuts; i++ )
				{
					addPage( i );
				}
 
				_printJob.send();
			}
		}
 
		/**
		 * Demonstrates that after calling PrintJob.start() the internal
		 * eventmodel doesn't dispatch any events at all. Everything is blocked
		 * until the script times out.
		 */
		private function runEventBasedTest() : void
		{
			_printJob = new PrintJob();
 
			if ( _printJob.start() )
			{
				trace( getTimer(), "PrintJob.start() called" );
				_printOutIndex = 0;
 
				startAddPagesOnEnterFrame();
			}
		}
 
		private function startAddPagesOnEnterFrame() : void
		{
			addEventListener( Event.ENTER_FRAME, onAddPagesOnEnterFrameEvent );
		}
 
		private function onAddPagesOnEnterFrameEvent(event : Event) : void
		{
			if ( _printOutIndex == _numPrintOuts )
			{
				stopAddPagesOnEnterFrame();
 
				_printJob.send();
				trace( getTimer(), "PrintJob.send()" );
			}
			else
			{
				addPage( _printOutIndex++ );
			}
		}
 
		private function stopAddPagesOnEnterFrame() : void
		{
			removeEventListener( Event.ENTER_FRAME, onAddPagesOnEnterFrameEvent );
		}
 
		private function addPage( pageIndex : int ) : void
		{
			var page : Sprite = createPrintPage( pageIndex );
 
			stage.addChild( page );
 
			var viewWidth : Number = page.width;
			var viewHeight : Number = page.height;
 
			var scaleX : Number = _printJob.pageWidth / viewWidth;
			var scaleY : Number = _printJob.pageHeight / viewHeight;
			var scale : Number = Math.min( scaleX, scaleY );
 
			page.scaleX = page.scaleY = scale;
 
			var printRect : Rectangle = new Rectangle( 0, 0, viewWidth, viewHeight );
 
			try
			{
				_printJob.addPage( page, printRect );
			}
			catch( e: Error )
			{
				trace( getTimer(), "PrintJob.addPage() failed" );
			}
 
			stage.removeChild( page );
		}
 
		private function createPrintPage( pageIndex : int) : Sprite
		{
			var page : Sprite = new Sprite();
 
			var textField : TextField = new TextField();
				textField.autoSize = TextFieldAutoSize.LEFT;
				textField.text = "Page " + pageIndex;
 
			page.addChild( textField );	
 
			return page;
		}
	}
}

Export multiple .fla files using .jsfl

Another very basic information. I had to export multiple .fla files so I wrote my first .jsfl file:

// Binary Directory path (file:// syntax)
var binDir = "file:///Users/.../swf/";
// Source Directory path (file:// syntax)
var srcDir = "file:///Users/.../fla/";
// .fla file names
var fileNames = new Array( "file1", "file2", "file3" );
 
var fileName;
var fileSrc;
var fileBin;
var fla;
 
for ( var i = 0; i < fileNames.length; i++ )
{
	fileName = fileNames[ i ];
	fileSrc = srcDir + fileName + ".fla";
	fileBin = binDir + fileName + ".swf";
 
	fla = fl.openDocument( fileSrc );
	fla.exportSWF( fileBin, true ); 
 
	fl.closeDocument( fla, false );
}

Adobe AIR flash.filesystem.File methods throw Error 2037 when no nativePath is set

While developing an Flex based AIR Application I figured out an issue with the flash.filesystem.File class. I tried to access properties like exists, nativePath etc. before a nativePath was set using a browse method. So the while debugging the following error was triggered:

Error #2037: Functions called in incorrect sequence, or earlier

Well, I would expect to get at least some default values. For example File.exists should be false or something. But you will always get an exception. So after research I found a note about it here.

Anyway, the only chance you have is to catch the error.

But there is one thing more: If you publish the AIR application, install it and try it then, you won’t get an exception. Everything works fine than.

Select and access local SWF files within an AIR application

Well, it took me a little time to figure out how to select and load a local SWF file into an SWFLoader instance. Several times I got this error message:

SecurityError: Error #3015: Loader.loadBytes() is not permitted to load content with executable code.

After some research I found this “dreaming in flash” blog entry which basically contains the solution I was looking for.

But then it took me a little more time to figure out how to use this with the SWFLoader. So here is a basic code example:

import flash.display.Loader;
import flash.filesystem.File;
import flash.filesystem.FileMode;
import flash.filesystem.FileStream;
import flash.system.LoaderContext;
import flash.utils.ByteArray;
import mx.controls.SWFLoader;
 
// File reference
var swfFile: File;
 
// Open the SWF file
var fileStream: FileStream = new FileStream();
	fileStream.open( swfFile, FileMode.READ );
 
// Read SWF bytes into byte array and close file
var swfBytes: ByteArray = new ByteArray();
	fileStream.readBytes( swfBytes );
	fileStream.close();
 
// Prepare the loader context to avoid security error
var loaderContext: LoaderContext = new LoaderContext();
	loaderContext.allowLoadBytesCodeExecution = true; // that's it!
 
// Now you could use this with a Loader instance
var loader: Loader = new Loader();
	loader.loadBytes( swfBytes, loaderContext );
 
// Or you could use this with a SWFLoader instance
var swfLoader: SWFLoader = new SWFLoader();
	swfLoader.loaderContext = loaderContext;
	swfLoader.source = swfBytes;