how to print a large size sprite by as3 - actionscript-3

I've an app in which users first insert some data and then print the data in an aligned and table form. But I have a problem with printing the final document when I print this with a custom class FilePrinter which takes only one non-optional parameter "Sprite". I clearly and rightly follow through the codes but when I print, the printed document is nothing but a clear page. I'm not understanding why this so?
But one thing that should be noted is that the sprite is consisted of an image (with resolution not normal) and the size of printed Document becomes much (Round About 12mb to 15mb) when it is ready to print.
If any one knows about the issue so please help me...
Thanks...!
var rslt:MovieClip = new MAIN_DOC();
rslt.box1.text = arr[0].toString();
rslt.box2.text = arr[1].toString();
rslt.box3.text = arr[2].toString();
rslt.box4.text = arr[3].toString();
rslt.box5.text = arr[4].toString();
rslt.box6.text = arr[5].toString();
var BackM:MovieClip = new BackGround();
BackM.PIcont.addChild(picData);
var sprite:Sprite = new Sprite();
sprite.addChild(BackM);
sprite.addChild(rslt);
var print:FilePrinter = new FilePrinter(sprite);
function printBtnClick(e:MouseEvent):void
{
print.print();
}
FilePrinter Class
public class FilePrinter {
private var pJob:PrintJob;
private var opt:PrintJobOptions = new PrintJobOptions(true);
private var sp:Sprite;
public function FilePrinter(sprite:Sprite) {
sp = new Sprite();
sp = sprite;
pJob = new PrintJob();
if (pJob.start())
{
pJob.addPage(sp, null, opt);
}
}
public function startPrint():void {
pJob.send();
}
}

From the Actionscript documentation:
Additionally, a 15 second script timeout limit applies to the following intervals:
PrintJob.start() and the first PrintJob.addPage()
PrintJob.addPage() and the next PrintJob.addPage()
The last PrintJob.addPage() and PrintJob.send()
If any of the above intervals span more than 15 seconds, the next call to PrintJob.start() on the PrintJob instance returns false, and the next PrintJob.addPage() on the PrintJob instance causes the Flash Player or Adobe AIR to throw a runtime exception.
I'd recommend not initalising your FilePrinter class until the button press event triggers, also you seem to be calling print() in your button click listener, but there is no such function in your FilePrinter class, only startPrint()
var print:FilePrinter;
function printBtnClick(e:MouseEvent):void
{
print = new FilePrinter(sprite);
print.startPrint();
}

Related

Actionscript 3: How can I change the name property of content returned by Loader.load()? Error 1074

I have a Scaleform movie that I want to serve as the container for my game's user interface. I want it to be able to load and unload other swf files that will serve as different HUDs and menus. And when I load a swf, I need Actionscript to register the name of the DisplayObject, so the game will know which "view" (i.e., HUD, pause menu, shop menu, etc.) just loaded.
I am able to load other swfs using Loader.load(), but for some reason I can't change their names. I keep getting error 1074.
[Edit: Adding more info on the error. "Error #1074: Illegal write to read-only property." Apparently I'm trying to write to a read-only property. So how do I make that property not-read-only? name isn't read-only in any other UIComponents I'm loading.]
public function loadView(viewName:String, movieFileName:String):void
{
var loader:Loader = new Loader();
var url:URLRequest = new URLRequest(movieFileName);
loader.contentLoaderInfo.addEventListener(Event.COMPLETE, loaderComplete);
loader.name = viewName;
loader.load(url);
}
private function loaderComplete(e:Event):void
{
var loader:Loader = e.currentTarget.loader as Loader;
var content:DisplayObject = LoaderInfo(e.target).content; // This returns the content I'm looking for, but I always get error 1074 if I try changing its name
// var content:DisplayObject = loader.getChildAt(0); // This also returns the content I'm looking for, but it also gives me error 1074 if I try changing its name
// content.name = loader.name; // This line always gives me error 1074
// var newView:View = View(content); // Even if I try casting the content as a custom .as class...
// newView.setName(loader.name); // public function setName(newName:String):void { this.name = newName; } // ...I still get error 1074
addChild(content);
}
Am I just not allowed to change the name property of swf movies that get returned? Can I set the name in the document class of the swf? I tried that too, but no matter where I change the name inside the document class (their class extends scaleform.clik.core.UIComponent, and I try setting the name in the constructor and in configUI), it always seems to get overwritten when I addChild().
[And another edit. Apparently there is some confusion over the "name" property. Here's how it works...]
I start off with this code. I just put it in frame 1 of my movie.
import TestUIComponent;
var testUIComponent:TestUIComponent = new TestUIComponent();
testUIComponent.name = "Something something";
trace("This is the testUIComponent's name: " + testUIComponent.name);
addChild(testUIComponent);
This is the class TestUIComponent:
package {
import scaleform.clik.core.UIComponent;
public class TestUIComponent extends UIComponent {
public function TestUIComponent() {
}
override protected function configUI():void {
super.configUI();
enableInitCallback = true;
}
}
}
Nothing fancy there. It's just an Actionscript 3 scaleform.clik.core.UIComponent (need to specify that because I think there are at least 3 different UIComponents in different packages). enableInitCallback is a property that used to be visible in Flash's properties panel, but now in AS 3, it seems you can only change it in code.
So I run that code, and this is what I see:
This is the testUIComponent's name: Something something
CLIK Load: root.Something something
If I comment out the line
// testUIComponent.name = "Something something";
and then run the code, this is what I see:
This is the testUIComponent's name: instance1
CLIK Load: root.instance1
Going back to my original problem, the text that comes after "CLIK Load:" is the name that is getting sent from the UI to the game. I need that name to be something meaningful so the game knows what just got loaded. The swf files I am trying to load have Document Classes that are children of scaleform.clik.core.UIComponent, so I thought their name properties would work the same way as the TestUIComponent above. Apparently it doesn't. And as you can see all the way back up at the top, I even cast the loader.content as a View (which is a child of UIComponent), and I still can't change the name.
This is what I meant in the comments. Try something like this:
//... where you declare your variables, make them public to use in other functions...
public var myString = "";
//... later where you declare functions...
public function loadView(viewName:String, movieFileName:String):void
{
var loader:Loader = new Loader();
var url:URLRequest = new URLRequest(movieFileName);
loader.contentLoaderInfo.addEventListener(Event.COMPLETE, loaderComplete);
//loader.name = viewName;
myString = viewName; //# 1) update String here to re-access in next function...
loader.load(url);
}
private function loaderComplete(e:Event):void
{
var content:DisplayObject = LoaderInfo(e.target).content; // This returns the content I'm looking for, but I always get error 1074 if I try changing its name
// content.name = loader.name; // This line always gives me error 1074
content.name = myString; //# 2) set content name to whatever myString holds (ie the viewName)...
addChild(content);
}

Why is my action script event not firing?

Presently, I am attempting to add the ability to capture all the slides of a presentation to images and save them to disk. It works now where the first page is capture, then I want an async event to fire when the second page has loaded to capture that page, and so on. Here is where I have added an event listener, though I'm not sure if I should be using stage or this:
import flash.events.Event;
private var jpgEncoder:JPGEncoder;
// ...
private function init():void{
// ...
// Add async event to capture second page after loading
stage.loaderInfo.addEventListener(Event.COMPLETE,onLoadComplete);
// ...
}
private function onPrintButtonClicked():void {
// screen capture code
jpgEncoder = new JPGEncoder(90);
// Page 1 capture
bitmapData1 = new BitmapData(stage.width, stage.height);
bitmapData1.draw(stage, new Matrix());
// move to next page
var curPage:Page = PresentationModel.getInstance().getCurrentPage();
if (curPage != null) {
LOGGER.debug("Go to next page. Current page [{0}]", [curPage.id]);
pageCount++;
dispatchEvent(new GoToNextPageCommand(curPage.id));
} else {
LOGGER.debug("Go to next page. CanNOT find current page.");
}
}
private function onLoadComplete(e:Event)
{
// Get page 2 capture
bitmapData2 = new BitmapData(stage.width, stage.height);
bitmapData2.draw(stage, new Matrix());
// Copy two pages to one bitmap
var rect1:Rectangle = new Rectangle(0, 0, stage.width, stage.height);
var pt1:Point = new Point(0, 0);
bitmapData3 = new BitmapData(stage.width, stage.height * 2);
bitmapData3.copyPixels(bitmapData1, rect1, pt1)
var rect2:Rectangle = new Rectangle(0, 0, stage.width, stage.height);
var pt2:Point = new Point(0, stage.height);
bitmapData3.copyPixels(bitmapData2, rect2, pt2)
// Convert to image
var img:ByteArray = jpgEncoder.encode(bitmapData3);
var file:FileReference = new FileReference();
file.save(img, "capture1.jpg");
}
Does anyone have any ideas as to why the OnLoadComplete function is never called? FYI, here is the full source code: https://github.com/john1726/bigbluebutton/blob/master/bigbluebutton-client/src/org/bigbluebutton/main/views/MainToolbar.mxml
TIA
Please note that I've found that the stage was still null in the init() method so an exception was being thrown:
stage.loaderInfo.addEventListener(Event.COMPLETE,onLoadComplete);
Also, after resolving that stage error I found that I have been receiving this error using this tool: https://github.com/capilkey/Vizzy-Flash-Tracer
Error #2176: Certain actions, such as those that display a pop-up window, may only be invoked upon user interaction, for example by a mouse click or button press.
So the solution is either to re-work the UI so that there is a button press to prepare the files and a second button press to actually save the image or setup them mouseup and mousedown events to call different functions:
s:Button mouseDown="prepare_PDF()" mouseUp="save_PDF()"
Source: Flex's FileReference.save() can only be called in a user event handler -- how can I get around this?
Thank you!

AS3 Serialization

What I'm trying to do:
-Have objects in a toolbar, drag and dropable onto a movieclip (they then become a child of the movieclip). Once this is done, I want to be able serialize this object, so I can save it to a file. Then, I can reload this file, and continue draging/dropping things onto/off of this movieclip.
How I'm doing it:
public class Serialization {
public static function serializeToString(value:Object):String{
if(value==null){
trace("null isn't a legal serialization candidate");
}
var bytes:ByteArray = new ByteArray();
bytes.writeObject(value);
bytes.position = 0;
var be:Base64Encoder = new Base64Encoder();
be.encode(bytes.readUTFBytes(bytes.length));
return be.drain();
}
public static function readObjectFromStringBytes(value:String):Object{
var dec:Base64Decoder=new Base64Decoder();
dec.decode(value);
var result:ByteArray=dec.drain();
result.position=0;
return result.readObject();
}
}
This is where call the function/write it to the file:
var fr:FileReference = new FileReference;
fr.addEventListener(Event.COMPLETE, success);
var txtString:String = new String();
txtString = save.Serialization.serializeToString(pagePic);
trace(txtString);
fr.save(txtString, "test.txt");
Unfortunately, txtString appears to be blank. Am I approaching this wrong?
Side notes:
This is being developed for a mobile platform.
Unfortunately MovieClips, Sounds, and other resources cannot be serialized. My solution is to create a custom class that will store all my properties and reassign them upon loading, or just write to/parse a text file when saving/loading.

Dynamically Handling Events with Function Expression

I have a class which exposes literally dozens of events(before you get of on a tangent about whether that's good/bad design, just know that I didn't make that class). The event object of each event(eventParam in the below code) always has a toDebugString function, that basically creates a string containing all of the event object's property values:
propertyName1: propertyValue1
propertyName2: propertyValue2
propertyName3: propertyValue3
It works so far as creating all of the panels, with the title of each panel being the name of the event. However, the big problem is that all of events end up in the TextArea of the last panel. So there is something I don't understand about the anonymous method. It's as if each iteration of the loop uses the same function, and on the last iteration of the loop it decides that the debugPanel that was just created will be the one that all instances of that function will reference. In other words, a new unique debugSubPanel and TextArea is created in each iteration of the loop, but there is only one debugResponseListener event handler shared by all iterations of the loop. So my question is, how can I dynamically create the event handler function dynamically so that it stays associated with the debugSubPanel that I want it to?
public function debugPanelCreated(event:FlexEvent)
{
//iterate through all of the events exposed by mClient.ResponsesDispatcher
//where key is the name of the event
for (var key:String in mClient.ResponsesDispatcher.respMap)
{
//for each event, create a panel containing a text box
var debugSubPanel:Panel = new Panel();
debugSubPanel.title = debugSubPanel.label = key;
var debugSubPanelTextArea:TextArea = new TextArea();
debugSubPanel.addChild(debugSubPanelTextArea);
var debugResponseListener:Function =
function (eventParam :Object) : void
{
//use debugString function to write the properties
//of eventParam to the text box
debugSubPanelTextArea.text = eventParam .toDebugString();
};
//listen to this event:
mClient.ResponsesDispatcher.addEventListener(key,debugResponseListener);
//add the panel for this event
debugPanel.addChild(debugSubPanel);
}
}
Actionscript includes a feature called closures, which means that when you create an inner function and call it, the variables of its parent function are still available. (This is how debugResponseListener = function() ... works at all.) The issue is that a closure is only created when that function is called, and it uses the variable values from their last setting.
You can get around this by making a function that returns the listener function you want.
function makePanelListener(debugSubPanelTextArea:TextArea) : Function
{
return function(eventParam :Object) : void {
//use debugString function to write the properties
//of eventParam to the text box
debugSubPanelTextArea.text = eventParam .toDebugString();
}
}
and in your original code:
var debugResponseListener:Function = makePanelListener(debugSubPanelTextArea);
(There's a little explanation of what's going on in Explaining JavaScript scope and closures, look for the section called "The Infamous Loop Problem". More on closures at jibbering.)
This is the hack I came up with. I really don't like it, but it'll work for now. Open to suggestions still.
public class ResponseDispatcherToDebugStringHelper
{
public var textArea:TextArea;
public function responseToDebugStringHandler(eventParam:Object) : void
{
//use debugString function to write the properties
//of eventParam to the text box
textArea.text = eventParam.toDebugString();
}
}
public function debugPanelCreated(event:FlexEvent)
{
//iterate through all of the events exposed by mClient.ResponsesDispatcher
//where key is the name of the event
for (var key:String in mClient.ResponsesDispatcher.respMap)
{
//for each event, create a panel containing a text box
var debugSubPanel:Panel = new Panel();
debugSubPanel.title = debugSubPanel.label = key;
var debugSubPanelTextArea:TextArea = new TextArea();
debugSubPanel.addChild(debugSubPanelTextArea);
var helper:ResponseDispatcherToDebugStringHelper =
new ResponseDispatcherToDebugStringHelper();
helper.textArea = debugSubPanelTextArea;
//listen to this event:
mClient.ResponsesDispatcher.addEventListener(key,helper.responseToDebugStringHandler);
//add the panel for this event
debugPanel.addChild(debugSubPanel);
}
}

Finding (loaded) image size in AS3 (Action Script 3.0)

Im currently using the following function to load an image, however i could not figure out a way to find the width of the loaded image, which i intend to use before placing the next image using the same function.
Note that q is a a variable (a number) which is used to load differant images.
=X i need help obtainning the loaded image width...
function LoadImage(q)
{
var imageLoader:Loader = new Loader();
var image:URLRequest = new URLRequest("GalleryImages/Album1/"+q+".jpg");
imageLoader.load(image);
addChild (imageLoader);
imageLoader.x = 0 + CurrentXLength;
imageLoader.y = 0;
imageLoader.name = "image"+q;
trace(imageLoader.x)
}
You can't know the width of the bitmap until it's actually loaded:
function LoadImage(q)
{
var imageLoader:Loader = new Loader();
var image:URLRequest = new URLRequest("GalleryImages/Album1/"+q+".jpg");
imageLoader.contentLoader.addEventListener(Event.COMPLETE, ImageLoaded);
imageLoader.load(image);
addChild (imageLoader);
...
private function ImageLoaded(e:Event):void
{
var imageLoader:Loader = Loader(e.target.loader);
var bm:Bitmap = Bitmap(imageLoader.content);
var CurrentXLength = bm.width;
....
Alternativly this link might be helpful? Haven't tried it myself ...
I just asked for de width property of loader object:
var loader:Loader;
function loadImage(dir:String):void {
loader = new Loader();
loader.contentLoaderInfo.addEventListener(Event.COMPLETE, placeImage);
var urlReq:URLRequest = new URLRequest(direccion);
loader.load(urlReq);
}
function placeImage(o:Event):void {
loader.x = (1360 - loader.width)/2;
loader.y = (768 - loader.height)/2;
addChild(loader);
}
Where 1360 and 768 are the canvas dimensions...
To access the width you must do it within the function assigned to handle Event.COMPLETE.
"You will want an array containing the items you wish to load. You should probably load this data in with XML so it is dynamic and scalable. Once the xml data is loaded it should be assigned to an array in whatever fashion you like. The reason that we must use an array in this situation, rather then just using the XML object, which is essentially an array, is because you need the know an objects width so that you can base the next objects X position off of the last objects X position plus its WIDTH.
With XML it is common to use a for loop and just iterate through "x" amount of times. We do not want this, in this case. To obtain the "WIDTH" property of the loaded asset, it must be accessed from within the function assigned to fire when the loader fires Event.COMPLETE. Once the image has completed it will remove the item from the array, set a variable as to the lastX and lastWidth, and then get the next item in the array and start all over. Eventually the array is empty and the process is complete.
-Note: I will skip loading the XML and just inject the data into the array myself.
package
{
import flash.display.Sprite;
import flash.display.Loader;
import flash.net.URLRequest;
public class DocumentClass extends Sprite
{
private var _array:Array;
private var _lastX:Number;
private var _lastWidth:Number;
public function DocumentClass():void
{
_array = new Array();
//Add Items to an array however you wish. I did it
//this way to make it easier to read on this site.
_array.push({name: "image1", path: "path/to/image1"});
_array.push({name: "image2", path: "path/to/image2"});
_array.push({name: "image3", path: "path/to/image3"});
loadImage();
}
private function loadImage():void
{
if(_array.length > 0)
{
var loader:Loader = new Loader();
addChild(loader);
var request:URLRequest = new URLRequest(_array[0].path); //Loads first item in the array each time.
loader.contentLoaderInfo.addEventListener(Event.COMPLETE, onImageLoaded);
loader.x = _lastX + _lastWidth;
laoder.load(request);
_lastX = loader.x; //We set after so we are ready for the next.
_array.shift(); //Remove the first item from the array.
}
}
function onImageLoaded(e:Event):void
{
_lastWidth = e.target.width;
loadImage(); //Attempt to load another image if the array isn't empty.
}
}
}
I hope this helps, the code isn't tested, but the concept seems valid.
Yeah I used scott's answer but it's worth noting that 'imageLoader.contentLoader' should be 'imageLoader.contentLoaderInfo' in the LoadImage() function. Solved the width prob for me-- thanks mang